[joda-convert] 03/05: Imported Upstream version 1.4

Tony Mancill tmancill at moszumanska.debian.org
Sat Sep 5 21:38:38 UTC 2015


This is an automated email from the git hooks/post-receive script.

tmancill pushed a commit to branch master
in repository joda-convert.

commit 420fbdb00d44489589427e82e099b1c6b47cfb11
Author: tony mancill <tmancill at debian.org>
Date:   Sat Sep 5 14:37:22 2015 -0700

    Imported Upstream version 1.4
---
 RELEASE-NOTES.txt                                  |   6 +-
 checkstyle.xml                                     |  79 ----
 pom.xml                                            | 404 +++++++++++++++------
 src/changes/changes.xml                            |  30 +-
 src/conf/MANIFEST.MF                               |  17 -
 src/main/checkstyle/checkstyle.xml                 | 143 ++++++++
 src/main/java/org/joda/convert/FromString.java     |   2 +-
 .../java/org/joda/convert/FromStringFactory.java   |  49 +++
 .../convert/MethodConstructorStringConverter.java  |   8 +-
 .../org/joda/convert/MethodsStringConverter.java   |  10 +-
 .../joda/convert/ReflectionStringConverter.java    |  10 +-
 src/main/java/org/joda/convert/StringConvert.java  | 169 +++++++--
 src/site/resources/download.html                   |   6 +
 src/site/site.xml                                  |  29 +-
 src/site/xdoc/index.xml                            |  11 +-
 src/site/xdoc/userguide.xml                        |  43 ++-
 .../java/org/joda/convert/DistanceWithFactory.java |  37 ++
 .../joda/convert/DistanceWithFactoryFactory.java   |  29 ++
 .../java/org/joda/convert/TestStringConvert.java   |  81 ++++-
 .../java/org/joda/convert/test1/Test1Class.java    |  47 +++
 .../org/joda/convert/test1/Test1Interface.java     |  28 ++
 .../java/org/joda/convert/test2/Test2Class.java    |  39 ++
 .../java/org/joda/convert/test2/Test2Factory.java  |  31 ++
 .../org/joda/convert/test2/Test2Interface.java     |  30 ++
 .../java/org/joda/convert/test3/Test3Class.java    |  40 ++
 .../java/org/joda/convert/test3/Test3Factory.java  |  30 ++
 .../org/joda/convert/test3/Test3Interface.java     |  31 ++
 .../org/joda/convert/test3/Test3SuperClass.java    |  35 ++
 .../java/org/joda/convert/test4/Test4Class.java    |  39 ++
 .../java/org/joda/convert/test4/Test4Factory.java  |  37 ++
 .../org/joda/convert/test4/Test4Interface.java     |  30 ++
 31 files changed, 1284 insertions(+), 296 deletions(-)

diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 8b68324..17f4279 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -3,11 +3,9 @@ Joda-Convert
 ================================================
 Joda-Convert is a small library providing conversion to and from String.
 
-This is release 1.3.1 of Joda-Convert.
+The release runs on JDK 6 or later.
 
-The release runs on Java SE 6 or later.
-
-See http://joda-convert.sourceforge.net/changes-report.html for changes
+See http://www.joda.org/joda-convert/changes-report.html for changes
 
 Joda-Convert is licensed under the business-friendly Apache License Version 2.
 This is the same license as all of Apache, plus other open source projects such as Spring.
diff --git a/checkstyle.xml b/checkstyle.xml
deleted file mode 100644
index 999d5ae..0000000
--- a/checkstyle.xml
+++ /dev/null
@@ -1,79 +0,0 @@
-<?xml version="1.0"?>
-
-<!DOCTYPE module PUBLIC
-    "-//Puppy Crawl//DTD Check Configuration 1.1//EN"
-    "http://www.puppycrawl.com/dtds/configuration_1_1.dtd">
-
-<!-- customization of default Checkstyle behavior -->
-<module name="Checker">
-  <!--property name="basedir" value="."/-->
-  <property name="localeLanguage" value="en"/>
-  <!--module name="PackageHtml"/-->
-  <module name="TreeWalker">
-    <module name="MemberName">
-      <property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
-    </module>
-    <module name="LocalVariableName">
-      <property name="format" value="^ex[0-9]*$"/>
-      <property name="tokens" value="PARAMETER_DEF"/>
-    </module>
-    <module name="AvoidStarImport"/>
-    <module name="RedundantImport"/>
-    <module name="UnusedImports"/>
-    
-    <module name="TabCharacter"/>
-    <module name="NeedBraces"/>
-    <!--module name="TypecastParenPad"/-->
-    <module name="WhitespaceAfter"/>
-    <module name="WhitespaceAround"/>
-    <module name="ModifierOrder"/>
-    <module name="RedundantModifier"/>
-    
-    <module name="EmptyBlock"/>
-    <module name="LeftCurly"/>
-    <module name="NeedBraces"/>
-    <module name="RightCurly"/>
-    <!--module name="AvoidNestedBlocks">
-      <property name="allowInSwitchCase" value="true"/>
-    </module-->
-    
-    <!--module name="ArrayTrailingComma"/-->
-    <!--module name="CovariantEquals"/-->
-    <module name="DoubleCheckedLocking"/>
-    <module name="EmptyStatement"/>
-    <module name="EqualsHashCode"/>
-    <!--module name="HiddenField">
-      <property name="ignoreConstructorParameter" value="true"/>
-      <property name="ignoreSetter" value="true"/>
-    </module-->
-    <module name="IllegalInstantiation">
-      <property name="classes" value="java.lang.Boolean"/>
-    </module>
-    <!--module name="SuperClone"/-->
-    <!--module name="ExplicitInitialization"/-->
-              
-    <module name="GenericIllegalRegexp">
-      <property name="format" value="System\.out\.println"/>
-    </module>
-    <module name="GenericIllegalRegexp">
-      <property name="format" value="System\.err\.println"/>
-    </module>
-    <module name="TodoComment"/>
-    <module name="UpperEll"/>
-    <module name="ArrayTypeStyle"/>
-    <module name="Indentation"/>
-          
-    <module name="RedundantThrows">
-      <property name="allowUnchecked" value="true"/>
-    </module>
-    <module name="LineLength">
-      <property name="max" value="120"/>
-    </module>
-    <module name="JavadocVariable"/>
-    <module name="JavadocMethod">
-      <property name="allowUndeclaredRTE" value="true"/>
-    </module>
- </module>
-</module>
-                        
-
diff --git a/pom.xml b/pom.xml
index 790610e..96ebadf 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,16 +3,19 @@
     xmlns="http://maven.apache.org/POM/4.0.0"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
   <modelVersion>4.0.0</modelVersion>
   <groupId>org.joda</groupId>
   <artifactId>joda-convert</artifactId>
   <packaging>jar</packaging>
-  <name>Joda convert</name>
-  <version>1.3.1</version>
+  <name>Joda-Convert</name>
+  <version>1.4</version>
   <description>Library to convert Objects to and from String</description>
-  <url>http://joda-convert.sourceforge.net</url>
+  <url>http://www.joda.org/joda-convert/</url>
+
+  <!-- ==================================================================== -->
   <issueManagement>
-  	<system>GitHub</system>
+    <system>GitHub</system>
     <url>https://github.com/JodaOrg/joda-convert/issues</url>
   </issueManagement>
   <inceptionYear>2010</inceptionYear>
@@ -24,6 +27,8 @@
       <archive>http://sourceforge.net/mailarchive/forum.php?forum_name=joda-convert-interest</archive>
     </mailingList>
   </mailingLists>
+
+  <!-- ==================================================================== -->
   <developers>
     <developer>
       <id>scolebourne</id>
@@ -33,18 +38,21 @@
         <role>Project Lead</role>
       </roles>
       <timezone>0</timezone>
+      <url>https://github.com/jodastephen</url>
     </developer>
   </developers>
   <contributors>
     <contributor>
-      <email>cjkent</email>
       <name>Chris Kent</name>
+      <url>https://github.com/cjkent</url>
     </contributor>
     <contributor>
-      <email>rocketraman</email>
       <name>Raman Gupta</name>
+      <url>https://github.com/rocketraman</url>
     </contributor>
   </contributors>
+
+  <!-- ==================================================================== -->
   <licenses>
     <license>
       <name>Apache 2</name>
@@ -53,7 +61,7 @@
     </license>
   </licenses>
   <scm>
-    <connection>scm:git:git at github.com:JodaOrg/joda-convert.git</connection>
+    <connection>scm:git:https://github.com/JodaOrg/joda-convert.git</connection>
     <developerConnection>scm:git:git at github.com:JodaOrg/joda-convert.git</developerConnection>
     <url>https://github.com/JodaOrg/joda-convert</url>
   </scm>
@@ -61,70 +69,64 @@
     <name>Joda.org</name>
     <url>http://www.joda.org</url>
   </organization>
+
+  <!-- ==================================================================== -->
   <build>
     <resources>
       <resource>
         <targetPath>META-INF</targetPath>
-        <directory>${basedir}</directory>
+        <directory>${project.basedir}</directory>
         <includes>
           <include>LICENSE.txt</include>
           <include>NOTICE.txt</include>
         </includes>
       </resource>
     </resources>
+    <!-- define build -->
     <plugins>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-clean-plugin</artifactId>
-        <version>2.5</version>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-compiler-plugin</artifactId>
-        <version>3.0</version>
-        <configuration>
-          <verbose>true</verbose>
-          <fork>true</fork>
-          <compilerVersion>1.6</compilerVersion>
-          <source>1.6</source>
-          <target>1.6</target>
-          <debug>true</debug>
-          <debuglevel>lines,source</debuglevel>
-          <optimize>true</optimize>
-          <showDeprecation>false</showDeprecation>
-        </configuration>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-surefire-plugin</artifactId>
-        <version>2.13</version>
-        <configuration>
-          <includes>
-            <include>**/Test*.java</include>
-          </includes>
-        </configuration>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>run-checkstyle</id>
+            <phase>process-sources</phase>
+            <goals>
+              <goal>checkstyle</goal>
+            </goals>
+          </execution>
+        </executions>
       </plugin>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-jar-plugin</artifactId>
-        <version>2.4</version>
         <configuration>
           <archive>
-            <manifestFile>src/conf/MANIFEST.MF</manifestFile>
+            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+            <manifest>
+              <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+              <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
+            </manifest>
           </archive>
         </configuration>
       </plugin>
+      <plugin>   
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <version>2.4.0</version>
+        <executions>
+          <execution>
+            <id>bundle-manifest</id>
+            <phase>process-classes</phase>
+            <goals>    
+              <goal>manifest</goal>
+            </goals>   
+          </execution>
+        </executions>
+      </plugin>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-javadoc-plugin</artifactId>
-        <version>2.9</version>
-        <configuration>
-          <linksource>true</linksource>
-          <links>
-            <link>http://download.oracle.com/javase/6/docs/api/</link>
-          </links>
-          <encoding>UTF-8</encoding>
-        </configuration>
         <executions>
           <execution>
             <id>attach-javadocs</id>
@@ -138,7 +140,6 @@
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-source-plugin</artifactId>
-        <version>2.2.1</version>
         <executions>
           <execution>
             <id>attach-sources</id>
@@ -151,29 +152,9 @@
       </plugin>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-site-plugin</artifactId>
-        <version>3.2</version>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-repository-plugin</artifactId>
-        <version>2.3.1</version>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-resources-plugin</artifactId>
-        <version>2.6</version>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-deploy-plugin</artifactId>
-        <version>2.7</version>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-assembly-plugin</artifactId>
-        <version>2.4</version>
         <configuration>
+          <attach>false</attach>
           <descriptors>
             <descriptor>src/main/assembly/dist.xml</descriptor>
           </descriptors>
@@ -189,30 +170,208 @@
           </execution>
         </executions>
       </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-site-plugin</artifactId>
+        <configuration>
+          <skipDeploy>true</skipDeploy>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>com.github.github</groupId>
+        <artifactId>site-maven-plugin</artifactId>
+        <version>0.8</version>
+        <executions>
+          <execution>
+            <id>github-site</id>
+            <goals>
+              <goal>site</goal>
+            </goals>
+            <phase>site-deploy</phase>
+          </execution>
+        </executions>
+        <configuration>
+          <message>Create website for ${project.artifactId} v${project.version}</message>
+          <path>joda-convert</path>
+          <merge>true</merge>
+          <server>github</server>
+          <repositoryOwner>JodaOrg</repositoryOwner>
+          <repositoryName>jodaorg.github.io</repositoryName>
+          <branch>refs/heads/master</branch>
+        </configuration>
+      </plugin>
     </plugins>
+    <!-- Manage plugin versions -->
+    <pluginManagement>
+      <plugins>
+        <!-- Maven build and reporting plugins (alphabetical) -->
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-assembly-plugin</artifactId>
+          <version>${maven-assembly-plugin.version}</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-checkstyle-plugin</artifactId>
+          <version>${maven-checkstyle-plugin.version}</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-changes-plugin</artifactId>
+          <version>${maven-changes-plugin.version}</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-clean-plugin</artifactId>
+          <version>${maven-clean-plugin.version}</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-compiler-plugin</artifactId>
+          <version>${maven-compiler-plugin.version}</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-deploy-plugin</artifactId>
+          <version>${maven-deploy-plugin.version}</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-dependency-plugin</artifactId>
+          <version>${maven-dependency-plugin.version}</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-gpg-plugin</artifactId>
+          <version>${maven-gpg-plugin.version}</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-install-plugin</artifactId>
+          <version>${maven-install-plugin.version}</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-jar-plugin</artifactId>
+          <version>${maven-jar-plugin.version}</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-javadoc-plugin</artifactId>
+          <version>${maven-javadoc-plugin.version}</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-jxr-plugin</artifactId>
+          <version>${maven-jxr-plugin.version}</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-plugin-plugin</artifactId>
+          <version>${maven-plugin-plugin.version}</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-pmd-plugin</artifactId>
+          <version>${maven-pmd-plugin.version}</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-project-info-reports-plugin</artifactId>
+          <version>${maven-project-info-reports-plugin.version}</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-repository-plugin</artifactId>
+          <version>${maven-repository-plugin.version}</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-resources-plugin</artifactId>
+          <version>${maven-resources-plugin.version}</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-site-plugin</artifactId>
+          <version>${maven-site-plugin.version}</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-source-plugin</artifactId>
+          <version>${maven-source-plugin.version}</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-surefire-plugin</artifactId>
+          <version>${maven-surefire-plugin.version}</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-surefire-report-plugin</artifactId>
+          <version>${maven-surefire-report-plugin.version}</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-toolchains-plugin</artifactId>
+          <version>${maven-toolchains-plugin.version}</version>
+        </plugin>
+        <plugin>
+          <groupId>org.eclipse.m2e</groupId>
+          <artifactId>lifecycle-mapping</artifactId>
+          <version>1.0.0</version>
+          <configuration>
+            <lifecycleMappingMetadata>
+              <pluginExecutions>
+                <pluginExecution>
+                  <pluginExecutionFilter>
+                    <groupId>org.apache.felix</groupId>
+                    <artifactId>maven-bundle-plugin</artifactId>
+                    <versionRange>[2.4.0,)</versionRange>
+                    <goals>
+                      <goal>manifest</goal>
+                    </goals>
+                  </pluginExecutionFilter>
+                  <action>
+                    <ignore></ignore>
+                  </action>
+                </pluginExecution>
+              </pluginExecutions>
+            </lifecycleMappingMetadata>
+          </configuration>
+        </plugin>
+      </plugins>
+    </pluginManagement>
   </build>
+
+  <!-- ==================================================================== -->
+  <prerequisites>
+    <maven>3.0.4</maven>
+  </prerequisites>
   <dependencies>
     <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
-      <version>4.5</version>
+      <version>4.11</version>
       <scope>test</scope>
     </dependency>
   </dependencies>
+
+  <!-- ==================================================================== -->
   <reporting>
-  	<plugins>
+    <plugins>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-project-info-reports-plugin</artifactId>
+        <version>${maven-project-info-plugin.version}</version>
         <reportSets>
           <reportSet>
             <reports>
-              <report>index</report>
               <report>dependencies</report>
-              <report>project-team</report>
-              <report>mailing-list</report>
+              <report>dependency-info</report>
               <report>issue-tracking</report>
               <report>license</report>
+              <report>mailing-list</report>
+              <report>project-team</report>
               <report>scm</report>
               <report>summary</report>
             </reports>
@@ -222,28 +381,24 @@
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-checkstyle-plugin</artifactId>
-        <version>2.3</version>
-        <configuration>
-          <configLocation>${basedir}/checkstyle.xml</configLocation>
-          <enableRulesSummary>false</enableRulesSummary>
-        </configuration>
+        <version>${maven-checkstyle-plugin.version}</version>
       </plugin>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-javadoc-plugin</artifactId>
-        <version>2.9</version>
-        <configuration>
-          <linksource>true</linksource>
-          <links>
-            <link>http://download.oracle.com/javase/6/docs/api/</link>
-          </links>
-          <encoding>UTF-8</encoding>
-        </configuration>
+        <version>${maven-javadoc-plugin.version}</version>
+        <reportSets>
+          <reportSet>
+            <reports>
+              <report>javadoc</report>
+            </reports>
+          </reportSet>
+        </reportSets>
       </plugin>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-surefire-report-plugin</artifactId>
-        <version>2.13</version>
+        <version>${maven-surefire-report-plugin.version}</version>
         <configuration>
            <showSuccess>true</showSuccess>
         </configuration>
@@ -251,7 +406,7 @@
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-changes-plugin</artifactId>
-        <version>2.8</version>
+        <version>${maven-changes-plugin.version}</version>
         <reportSets>
           <reportSet>
             <reports>
@@ -263,21 +418,19 @@
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-jxr-plugin</artifactId>
-        <version>2.3</version>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-pmd-plugin</artifactId>
-        <version>2.7.1</version>
-        <configuration>
-          <linkXref>true</linkXref>
-          <sourceEncoding>utf-8</sourceEncoding>
-          <minimumTokens>100</minimumTokens>
-          <targetJdk>1.6</targetJdk>
-        </configuration>
+        <version>${maven-jxr-plugin.version}</version>
+        <reportSets>
+          <reportSet>
+            <reports>
+              <report>jxr</report>
+            </reports>
+          </reportSet>
+        </reportSets>
       </plugin>
-  	</plugins>
+    </plugins>
   </reporting>
+
+  <!-- ==================================================================== -->
   <distributionManagement>
     <repository>
       <id>sonatype-joda-staging</id>
@@ -292,13 +445,10 @@
       <url>http://oss.sonatype.org/content/repositories/joda-snapshots</url>
       <layout>default</layout>
     </snapshotRepository>
-    <site>
-      <id>sf-web-joda-convert</id>
-      <name>Sourceforge Site</name>
-      <url>scpexe://shell.sourceforge.net/home/project-web/joda-convert/htdocs</url>
-    </site>
     <downloadUrl>http://oss.sonatype.org/content/repositories/joda-releases</downloadUrl>
   </distributionManagement>
+
+  <!-- ==================================================================== -->
   <profiles>
     <profile>
       <id>repo-sign-artifacts</id>
@@ -313,7 +463,6 @@
           <plugin>
             <groupId>org.apache.maven.plugins</groupId>
             <artifactId>maven-toolchains-plugin</artifactId>
-            <version>1.0</version>
             <executions>
               <execution>
                 <phase>validate</phase>
@@ -334,7 +483,6 @@
           <plugin>
             <groupId>org.apache.maven.plugins</groupId>
             <artifactId>maven-gpg-plugin</artifactId>
-            <version>1.1</version>
             <executions>
               <execution>
                 <id>sign-artifacts</id>
@@ -349,7 +497,47 @@
       </build>
     </profile>
   </profiles>
+
+  <!-- ==================================================================== -->
   <properties>
+    <!-- Plugin version numbers -->
+    <maven-assembly-plugin.version>2.4</maven-assembly-plugin.version>
+    <maven-changes-plugin.version>2.9</maven-changes-plugin.version>
+    <maven-checkstyle-plugin.version>2.10</maven-checkstyle-plugin.version>
+    <maven-clean-plugin.version>2.5</maven-clean-plugin.version>
+    <maven-compiler-plugin.version>3.1</maven-compiler-plugin.version>
+    <maven-deploy-plugin.version>2.7</maven-deploy-plugin.version>
+    <maven-dependency-plugin.version>2.8</maven-dependency-plugin.version>
+    <maven-gpg-plugin.version>1.4</maven-gpg-plugin.version>
+    <maven-install-plugin.version>2.4</maven-install-plugin.version>
+    <maven-jar-plugin.version>2.4</maven-jar-plugin.version>
+    <maven-javadoc-plugin.version>2.9.1</maven-javadoc-plugin.version>
+    <maven-jxr-plugin.version>2.3</maven-jxr-plugin.version>
+    <maven-plugin-plugin.version>3.2</maven-plugin-plugin.version>
+    <maven-pmd-plugin.version>3.0.1</maven-pmd-plugin.version>
+    <maven-project-info-reports-plugin.version>2.7</maven-project-info-reports-plugin.version>
+    <maven-repository-plugin.version>2.3.1</maven-repository-plugin.version>
+    <maven-resources-plugin.version>2.6</maven-resources-plugin.version>
+    <maven-site-plugin.version>3.3</maven-site-plugin.version>
+    <maven-source-plugin.version>2.2.1</maven-source-plugin.version>
+    <maven-surefire-plugin.version>2.15</maven-surefire-plugin.version>
+    <maven-surefire-report-plugin.version>2.15</maven-surefire-report-plugin.version>
+    <maven-toolchains-plugin.version>1.0</maven-toolchains-plugin.version>
+    <!-- Properties for maven-compiler-plugin -->
+    <maven.compiler.compilerVersion>1.6</maven.compiler.compilerVersion>
+    <maven.compiler.source>1.6</maven.compiler.source>
+    <maven.compiler.target>1.6</maven.compiler.target>
+    <maven.compiler.fork>true</maven.compiler.fork>
+    <maven.compiler.verbose>true</maven.compiler.verbose>
+    <maven.compiler.debug>true</maven.compiler.debug>
+    <maven.compiler.optimize>true</maven.compiler.optimize>
+    <!-- Properties for maven-javadoc-plugin -->
+    <author>false</author>
+    <notimestamp>true</notimestamp>
+    <!-- Properties for maven-checkstyle-plugin -->
+    <checkstyle.config.location>${project.basedir}/src/main/checkstyle/checkstyle.xml</checkstyle.config.location>
+    <!-- Other properties -->
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
   </properties>
 </project>
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 05d0d12..5fc48d1 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -7,12 +7,26 @@
 
   <body>
     <!-- types are add, fix, remove, update -->
-    <release version="1.3.1" date="2013-03-08">
+    <release version="1.4" date="2013-08-15" description="Version 1.4">
+      <action dev="scolebourne" type="update" >
+        Change to use Maven plugin for OSGi, changing some published info.
+      </action>
+      <action dev="scolebourne" type="add" >
+        Home page at GitHub.
+      </action>
+      <action dev="scolebourne" type="add" >
+        Add support for FromString using a factory. Issue #5.
+      </action>
+      <action dev="scolebourne" type="update" >
+        Change to use m2e Maven Eclipse.
+      </action>
+    </release>
+    <release version="1.3.1" date="2013-03-08" description="Version 1.3.1">
       <action dev="rocketraman" type="fix" >
         Fix OSGI manifest.
       </action>
     </release>
-    <release version="1.3" date="2013-01-25">
+    <release version="1.3" date="2013-01-25" description="Version 1.3">
       <action dev="scolebourne" type="add" >
         Add register method designed for JDK 1.8 method references or lambdas.
       </action>
@@ -23,7 +37,7 @@
         Add alternate package locations for JSR-310 classes.
       </action>
     </release>
-    <release version="1.2" date="2011-10-27">
+    <release version="1.2" date="2011-10-27" description="Version 1.2">
       <action dev="scolebourne" type="add" >
         Add support for CharSequence based fromString methods.
       </action>
@@ -37,27 +51,27 @@
         Allow toString conversion to specify the desired type.
       </action>
     </release>
-    <release version="1.1.1" date="2011-10-21">
+    <release version="1.1.2" date="2011-10-21" description="Version 1.1.2">
       <action dev="scolebourne" type="fix" >
         Allow conversion of primitive types.
       </action>
     </release>
-    <release version="1.1.1" date="2011-10-24">
+    <release version="1.1.1" date="2011-10-24" description="Version 1.1.1">
       <action dev="scolebourne" type="fix" >
         Allow conversion of primitive types
       </action>
     </release>
-    <release version="1.1" date="2010-12-04">
+    <release version="1.1" date="2010-12-04" description="Version 1.1">
       <action dev="scolebourne" type="fix" >
         Enable superclass factories.
       </action>
     </release>
-    <release version="1.0" date="2010-09-05">
+    <release version="1.0" date="2010-09-05" description="Version 1.0">
       <action dev="scolebourne" type="fix" >
         Lots of tests and fixes.
       </action>
     </release>
-    <release version="0.5" date="unreleased">
+    <release version="0.5" date="unreleased" description="Version 0.5">
       <action dev="scolebourne" type="fix" >
         Initial checkin.
       </action>
diff --git a/src/conf/MANIFEST.MF b/src/conf/MANIFEST.MF
deleted file mode 100644
index 2fe0be5..0000000
--- a/src/conf/MANIFEST.MF
+++ /dev/null
@@ -1,17 +0,0 @@
-Package: org.joda.convert
-Extension-Name: joda-convert
-Specification-Title: Joda-Convert
-Specification-Vendor: Joda.org
-Specification-Version: 1.3
-Implementation-Vendor: Joda.org
-Implementation-Title: org.joda.convert
-Implementation-Version: 1.3.1
-Implementation-Vendor-Id: org.joda
-Bundle-ManifestVersion: 2
-Bundle-Vendor: Joda.org
-Bundle-Name: Joda-Convert
-Bundle-SymbolicName: joda-convert
-Bundle-Version: 1.3.1
-Export-Package: org.joda.convert;version=1.3.1
-Bundle-License: Apache 2.0
-Bundle-DocURL: http://joda-convert.sourceforge.net/
diff --git a/src/main/checkstyle/checkstyle.xml b/src/main/checkstyle/checkstyle.xml
new file mode 100644
index 0000000..63ea58c
--- /dev/null
+++ b/src/main/checkstyle/checkstyle.xml
@@ -0,0 +1,143 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN" "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
+
+<module name="Checker">
+  <property name="severity" value="warning"/>
+  <module name="TreeWalker">
+    <property name="tabWidth" value="4"/>
+    <module name="FileContentsHolder"/>
+    <module name="ConstantName">
+      <property name="format" value="^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$|^[a-z][a-zA-Z0-9]*$"/>
+    </module>
+    <module name="AvoidStarImport">
+      <property name="severity" value="error"/>
+    </module>
+    <module name="EmptyBlock">
+      <property name="tokens" value="LITERAL_DO,LITERAL_ELSE,LITERAL_FINALLY,LITERAL_IF,LITERAL_FOR,LITERAL_TRY,LITERAL_WHILE,STATIC_INIT"/>
+    </module>
+    <module name="EmptyForInitializerPad">
+      <property name="option" value="space"/>
+    </module>
+    <module name="EmptyForIteratorPad">
+      <property name="option" value="space"/>
+    </module>
+    <module name="EqualsHashCode"/>
+    <module name="IllegalImport"/>
+    <module name="IllegalInstantiation">
+      <property name="classes" value="Boolean"/>
+    </module>
+    <module name="JavadocType">
+      <property name="scope" value="protected"/>
+    </module>
+    <module name="JavadocMethod">
+      <property name="scope" value="protected"/>
+      <property name="allowUndeclaredRTE" value="true"/>
+      <property name="allowMissingThrowsTags" value="true"/>
+      <property name="allowMissingJavadoc" value="true"/>
+      <property name="allowMissingPropertyJavadoc" value="true"/>
+      <property name="logLoadErrors" value="true"/>
+      <property name="suppressLoadErrors" value="true"/>
+    </module>
+    <module name="JavadocVariable">
+      <property name="scope" value="protected"/>
+    </module>
+    <module name="LeftCurly">
+      <property name="severity" value="error"/>
+    </module>
+    <module name="LineLength">
+      <property name="ignorePattern" value="^ *\* *[^ ]+$"/>
+      <property name="max" value="200"/>
+      <property name="tabWidth" value="2"/>
+    </module>
+    <module name="LocalFinalVariableName"/>
+    <module name="LocalVariableName"/>
+    <module name="MemberName">
+      <property name="format" value="^[a-z][a-zA-Z0-9$]*$"/>
+    </module>
+    <module name="MethodLength">
+      <property name="max" value="300"/>
+    </module>
+    <module name="MethodName"/>
+    <module name="ModifierOrder">
+      <property name="severity" value="error"/>
+    </module>
+    <module name="NeedBraces">
+      <property name="severity" value="error"/>
+    </module>
+    <module name="NoWhitespaceAfter"/>
+    <module name="NoWhitespaceBefore">
+      <property name="allowLineBreaks" value="true"/>
+      <property name="tokens" value="SEMI,DOT,POST_DEC,POST_INC"/>
+    </module>
+    <module name="OperatorWrap">
+      <property name="option" value="eol"/>
+      <property name="tokens" value="ASSIGN, DIV_ASSIGN, PLUS_ASSIGN, MINUS_ASSIGN, STAR_ASSIGN, MOD_ASSIGN, SR_ASSIGN, BSR_ASSIGN, SL_ASSIGN, BXOR_ASSIGN, BOR_ASSIGN, BAND_ASSIGN"/>
+    </module>
+    <module name="PackageName"/>
+    <module name="ParameterName"/>
+    <module name="ParameterNumber">
+      <property name="max" value="20"/>
+    </module>
+    <module name="ParenPad"/>
+    <module name="RedundantImport"/>
+    <module name="RedundantModifier"/>
+    <module name="RightCurly">
+      <property name="severity" value="error"/>
+    </module>
+    <module name="StaticVariableName">
+      <property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
+    </module>
+    <module name="TypeName"/>
+    <module name="TypecastParenPad"/>
+    <module name="UpperEll">
+      <property name="severity" value="error"/>
+    </module>
+    <module name="VisibilityModifier"/>
+    <module name="WhitespaceAfter"/>
+    <module name="WhitespaceAround">
+      <property name="tokens" value="ASSIGN,BAND,BAND_ASSIGN,BOR,BOR_ASSIGN,BSR,BSR_ASSIGN,BXOR,BXOR_ASSIGN,COLON,DIV,DIV_ASSIGN,EQUAL,GE,GT,LAND,LCURLY,LE,LITERAL_ASSERT,LITERAL_CATCH,LITERAL_DO,LITERAL_ELSE,LITERAL_FINALLY,LITERAL_FOR,LITERAL_IF,LITERAL_RETURN,LITERAL_SYNCHRONIZED,LITERAL_TRY,LITERAL_WHILE,LOR,LT,MINUS,MINUS_ASSIGN,MOD,MOD_ASSIGN,NOT_EQUAL,PLUS,PLUS_ASSIGN,QUESTION,RCURLY,SL,SLIST,SL_ASSIGN,SR,SR_ASSIGN,STAR,STAR_ASSIGN,LITERAL_ASSERT,TYPE_EXTENSION_AND"/>
+      <property name="allowEmptyConstructors" value="true"/>
+      <property name="allowEmptyMethods" value="true"/>
+    </module>
+    <module name="MissingDeprecated"/>
+    <module name="MissingOverride"/>
+    <module name="PackageAnnotation"/>
+    <module name="CovariantEquals"/>
+    <module name="DefaultComesLast"/>
+    <module name="ExplicitInitialization"/>
+    <module name="FallThrough"/>
+    <module name="InnerAssignment"/>
+    <module name="StringLiteralEquality"/>
+    <module name="GenericWhitespace"/>
+    <module name="MethodParamPad"/>
+    <module name="FinalClass"/>
+    <module name="MutableException"/>
+    <module name="ArrayTypeStyle">
+      <property name="severity" value="error"/>
+    </module>
+    <module name="Indentation">
+      <property name="basicOffset" value="4"/>
+      <property name="caseIndent" value="4"/>
+    </module>
+  </module>
+  <!-- Header inlined due to m2e -->
+  <module name="RegexpHeader">
+    <property name="header" value="^/\*[*]?\n^ \*  Copyright 2010([-](20[01][0-9]))? Stephen Colebourne"/>
+    <property name="fileExtensions" value="java"/>
+  </module>
+  <module name="SuppressionCommentFilter">
+    <property name="offCommentFormat" value="CSOFF"/>
+    <property name="onCommentFormat" value="CSON"/>
+  </module>
+  <module name="FileLength"/>
+  <module name="FileTabCharacter">
+    <property name="eachLine" value="true"/>
+    <property name="severity" value="error"/>
+  </module>
+  <module name="NewlineAtEndOfFile"/>
+  <module name="SuppressWithNearbyCommentFilter">
+    <property name="commentFormat" value="CSIGNORE"/>
+    <property name="checkFormat" value=".*"/>
+    <property name="checkC" value="false"/>
+  </module>
+</module>
diff --git a/src/main/java/org/joda/convert/FromString.java b/src/main/java/org/joda/convert/FromString.java
index 4403300..f3432a6 100644
--- a/src/main/java/org/joda/convert/FromString.java
+++ b/src/main/java/org/joda/convert/FromString.java
@@ -32,7 +32,7 @@ import java.lang.annotation.Target;
  * When applying to a constructor, this annotation should be applied to the constructor
  * that takes one {@code String} parameter.
  */
- at Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
+ at Target({ElementType.METHOD, ElementType.CONSTRUCTOR })
 @Retention(RetentionPolicy.RUNTIME)
 public @interface FromString {
 
diff --git a/src/main/java/org/joda/convert/FromStringFactory.java b/src/main/java/org/joda/convert/FromStringFactory.java
new file mode 100644
index 0000000..6517e06
--- /dev/null
+++ b/src/main/java/org/joda/convert/FromStringFactory.java
@@ -0,0 +1,49 @@
+/*
+ *  Copyright 2010-2013 Stephen Colebourne
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.joda.convert;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation used on a type to indicate that another class, the factory,
+ * provides the 'from string' method.
+ * <p>
+ * This annotation is applied at the type level, typically to an interface.
+ * It indicates the class which contains the relevant {@code FromString}
+ * annotation, which follows the normal rules.
+ * <p>
+ * For example, the interface {@code Foo} could be annotated to define its
+ * associated factory as being {@code FooFactory}. The {@code FooFactory}
+ * class would then be expected to provide a method returning {@code Foo}
+ * with a single {@code String} parameter, annotated with {@code FromString}.
+ * 
+ * @since 1.4
+ */
+ at Target(ElementType.TYPE)
+ at Retention(RetentionPolicy.RUNTIME)
+public @interface FromStringFactory {
+
+    /**
+     * The factory class containing the static method.
+     * The static method must have a return type of the type that declares
+     * the factory annotation.
+     */
+    Class<?> factory();
+
+}
diff --git a/src/main/java/org/joda/convert/MethodConstructorStringConverter.java b/src/main/java/org/joda/convert/MethodConstructorStringConverter.java
index 3b31467..f6ed6a3 100644
--- a/src/main/java/org/joda/convert/MethodConstructorStringConverter.java
+++ b/src/main/java/org/joda/convert/MethodConstructorStringConverter.java
@@ -47,10 +47,10 @@ final class MethodConstructorStringConverter<T> extends ReflectionStringConverte
     MethodConstructorStringConverter(Class<T> cls, Method toString, Constructor<T> fromString) {
         super(cls, toString);
         if (cls.isInterface() || Modifier.isAbstract(cls.getModifiers()) || cls.isLocalClass() || cls.isMemberClass()) {
-            throw new IllegalArgumentException("FromString constructor must be on an instantiable class");
+            throw new IllegalArgumentException("FromString constructor must be on an instantiable class: " + fromString);
         }
         if (fromString.getDeclaringClass() != cls) {
-            throw new IllegalStateException("FromString constructor must be defined on specified class");
+            throw new IllegalStateException("FromString constructor must be defined on specified class: " + fromString);
         }
         this.fromString = fromString;
     }
@@ -66,9 +66,9 @@ final class MethodConstructorStringConverter<T> extends ReflectionStringConverte
         try {
             return fromString.newInstance(str);
         } catch (IllegalAccessException ex) {
-            throw new IllegalStateException("Constructor is not accessible");
+            throw new IllegalStateException("Constructor is not accessible: " + fromString);
         } catch (InstantiationException ex) {
-            throw new IllegalStateException("Constructor is not valid");
+            throw new IllegalStateException("Constructor is not valid: " + fromString);
         } catch (InvocationTargetException ex) {
             if (ex.getCause() instanceof RuntimeException) {
                 throw (RuntimeException) ex.getCause();
diff --git a/src/main/java/org/joda/convert/MethodsStringConverter.java b/src/main/java/org/joda/convert/MethodsStringConverter.java
index 330b5d9..7f5dd39 100644
--- a/src/main/java/org/joda/convert/MethodsStringConverter.java
+++ b/src/main/java/org/joda/convert/MethodsStringConverter.java
@@ -46,14 +46,14 @@ final class MethodsStringConverter<T> extends ReflectionStringConverter<T> {
     MethodsStringConverter(Class<T> cls, Method toString, Method fromString) {
         super(cls, toString);
         if (fromString.getParameterTypes().length != 1) {
-            throw new IllegalStateException("FromString method must have one parameter");
+            throw new IllegalStateException("FromString method must have one parameter: " + fromString);
         }
         Class<?> param = fromString.getParameterTypes()[0];
         if (param != String.class && param != CharSequence.class) {
-            throw new IllegalStateException("FromString method must take a String or CharSequence");
+            throw new IllegalStateException("FromString method must take a String or CharSequence: " + fromString);
         }
-        if (fromString.getReturnType().isAssignableFrom(cls) == false) {
-            throw new IllegalStateException("FromString method must return specified class or a superclass");
+        if (fromString.getReturnType().isAssignableFrom(cls) == false && cls.isAssignableFrom(fromString.getReturnType()) == false) {
+            throw new IllegalStateException("FromString method must return specified class or a supertype: " + fromString);
         }
         this.fromString = fromString;
     }
@@ -69,7 +69,7 @@ final class MethodsStringConverter<T> extends ReflectionStringConverter<T> {
         try {
             return cls.cast(fromString.invoke(null, str));
         } catch (IllegalAccessException ex) {
-            throw new IllegalStateException("Method is not accessible");
+            throw new IllegalStateException("Method is not accessible: " + fromString);
         } catch (InvocationTargetException ex) {
             if (ex.getCause() instanceof RuntimeException) {
                 throw (RuntimeException) ex.getCause();
diff --git a/src/main/java/org/joda/convert/ReflectionStringConverter.java b/src/main/java/org/joda/convert/ReflectionStringConverter.java
index 8c6b8cd..c3593d4 100644
--- a/src/main/java/org/joda/convert/ReflectionStringConverter.java
+++ b/src/main/java/org/joda/convert/ReflectionStringConverter.java
@@ -31,9 +31,9 @@ import java.lang.reflect.Method;
 abstract class ReflectionStringConverter<T> implements StringConverter<T> {
 
     /** The converted class. */
-    final Class<T> cls;
+    private final Class<T> cls;
     /** Conversion to a string. */
-    final Method toString;
+    private final Method toString;
 
     /**
      * Creates an instance using two methods.
@@ -43,10 +43,10 @@ abstract class ReflectionStringConverter<T> implements StringConverter<T> {
      */
     ReflectionStringConverter(Class<T> cls, Method toString) {
         if (toString.getParameterTypes().length != 0) {
-            throw new IllegalStateException("ToString method must have no parameters");
+            throw new IllegalStateException("ToString method must have no parameters: " + toString);
         }
         if (toString.getReturnType() != String.class) {
-            throw new IllegalStateException("ToString method must return a String");
+            throw new IllegalStateException("ToString method must return a String: " + toString);
         }
         this.cls = cls;
         this.toString = toString;
@@ -62,7 +62,7 @@ abstract class ReflectionStringConverter<T> implements StringConverter<T> {
         try {
             return (String) toString.invoke(object);
         } catch (IllegalAccessException ex) {
-            throw new IllegalStateException("Method is not accessible");
+            throw new IllegalStateException("Method is not accessible: " + toString);
         } catch (InvocationTargetException ex) {
             if (ex.getCause() instanceof RuntimeException) {
                 throw (RuntimeException) ex.getCause();
diff --git a/src/main/java/org/joda/convert/StringConvert.java b/src/main/java/org/joda/convert/StringConvert.java
index 61f6f5a..778bfdc 100644
--- a/src/main/java/org/joda/convert/StringConvert.java
+++ b/src/main/java/org/joda/convert/StringConvert.java
@@ -38,6 +38,19 @@ public final class StringConvert {
      * are picked up. To register your own converters, simply create an instance of this class.
      */
     public static final StringConvert INSTANCE = new StringConvert();
+    /**
+     * The cached null object.
+     */
+    private static final StringConverter<?> CACHED_NULL = new StringConverter<Object>() {
+        @Override
+        public String convertToString(Object object) {
+            return null;
+        }
+        @Override
+        public Object convertFromString(Class<? extends Object> cls, String str) {
+            return null;
+        }
+    };
 
     /**
      * The cache of converters.
@@ -209,9 +222,10 @@ public final class StringConvert {
      * This returns an instance of {@code StringConverter} for the specified class.
      * This could be useful in other frameworks.
      * <p>
-     * The search algorithm first searches the registered converters.
-     * It then searches for {@code ToString} and {@code FromString} annotations on the specified class.
-     * Both searches consider superclasses, but not interfaces.
+     * The search algorithm first searches the registered converters in the
+     * class hierarchy and immediate parent interfaces.
+     * It then searches for {@code ToString} and {@code FromString} annotations on the
+     * specified class, class hierarchy or immediate parent interfaces.
      * 
      * @param <T>  the type of the converter
      * @param cls  the class to find a converter for, not null
@@ -224,26 +238,47 @@ public final class StringConvert {
             throw new IllegalArgumentException("Class must not be null");
         }
         StringConverter<T> conv = (StringConverter<T>) registered.get(cls);
+        if (conv == CACHED_NULL) {
+            throw new IllegalStateException("No registered converter found: " + cls);
+        }
         if (conv == null) {
-            if (cls == Object.class) {
-                throw new IllegalStateException("No registered converter found: " + cls);
-            }
-            Class<?> loopCls = cls.getSuperclass();
-            while (loopCls != null && conv == null) {
-                conv = (StringConverter<T>) registered.get(loopCls);
-                loopCls = loopCls.getSuperclass();
-            }
+            conv = findAnnotationConverter(cls);
             if (conv == null) {
-                conv = findAnnotationConverter(cls);
-                if (conv == null) {
-                    throw new IllegalStateException("No registered converter found: " + cls);
-                }
+                registered.putIfAbsent(cls, CACHED_NULL);
+                throw new IllegalStateException("No registered converter found: " + cls);
             }
             registered.putIfAbsent(cls, conv);
         }
         return conv;
     }
 
+    @SuppressWarnings("unchecked")
+    private <T> StringConverter<T> findAnnotationConverter(final Class<T> cls) {
+        StringConverter<T> conv = null;
+        // check for registered on superclass
+        Class<?> loopCls = cls.getSuperclass();
+        while (loopCls != null && conv == null) {
+            conv = (StringConverter<T>) registered.get(loopCls);
+            if (conv != null && conv != CACHED_NULL) {
+                return conv;
+            }
+            loopCls = loopCls.getSuperclass();
+        }
+        // check for registered on interfaces
+        for (Class<?> loopIfc : cls.getInterfaces()) {
+            conv = (StringConverter<T>) registered.get(loopIfc);
+            if (conv != null && conv != CACHED_NULL) {
+                return conv;
+            }
+        }
+        // check for annotations
+        conv = findAnnotatedConverter(cls);
+        if (conv != null) {
+            return conv;
+        }
+        return null;
+    }
+
     /**
      * Finds the conversion method.
      * 
@@ -251,18 +286,18 @@ public final class StringConvert {
      * @param cls  the class to find a method for, not null
      * @return the method to call, null means use {@code toString}
      */
-    private <T> StringConverter<T> findAnnotationConverter(final Class<T> cls) {
-        Method toString = findToStringMethod(cls);
+    private <T> StringConverter<T> findAnnotatedConverter(final Class<T> cls) {
+        Method toString = findToStringMethod(cls);  // checks superclasses
         if (toString == null) {
             return null;
         }
         Constructor<T> con = findFromStringConstructor(cls);
-        Method fromString = findFromStringMethod(cls, con == null);
+        Method fromString = findFromStringMethod(cls, con == null);  // optionally checks superclasses
         if (con == null && fromString == null) {
-            throw new IllegalStateException("Class annotated with @ToString but not with @FromString");
+            throw new IllegalStateException("Class annotated with @ToString but not with @FromString: " + cls.getName());
         }
         if (con != null && fromString != null) {
-            throw new IllegalStateException("Both method and constructor are annotated with @FromString");
+            throw new IllegalStateException("Both method and constructor are annotated with @FromString: " + cls.getName());
         }
         if (con != null) {
             return new MethodConstructorStringConverter<T>(cls, toString, con);
@@ -279,6 +314,7 @@ public final class StringConvert {
      */
     private Method findToStringMethod(Class<?> cls) {
         Method matched = null;
+        // find in superclass hierarchy
         Class<?> loopCls = cls;
         while (loopCls != null && matched == null) {
             Method[] methods = loopCls.getDeclaredMethods();
@@ -286,13 +322,28 @@ public final class StringConvert {
                 ToString toString = method.getAnnotation(ToString.class);
                 if (toString != null) {
                     if (matched != null) {
-                        throw new IllegalStateException("Two methods are annotated with @ToString");
+                        throw new IllegalStateException("Two methods are annotated with @ToString: " + cls.getName());
                     }
                     matched = method;
                 }
             }
             loopCls = loopCls.getSuperclass();
         }
+        // find in immediate parent interfaces
+        if (matched == null) {
+            for (Class<?> loopIfc : cls.getInterfaces()) {
+                Method[] methods = loopIfc.getDeclaredMethods();
+                for (Method method : methods) {
+                    ToString toString = method.getAnnotation(ToString.class);
+                    if (toString != null) {
+                        if (matched != null) {
+                            throw new IllegalStateException("Two methods are annotated with @ToString on interfaces: " + cls.getName());
+                        }
+                        matched = method;
+                    }
+                }
+            }
+        }
         return matched;
     }
 
@@ -322,27 +373,67 @@ public final class StringConvert {
      * Finds the conversion method.
      * 
      * @param cls  the class to find a method for, not null
-     * @return the method to call, null means use {@code toString}
+     * @return the method to call, null means not found
      */
     private Method findFromStringMethod(Class<?> cls, boolean searchSuperclasses) {
         Method matched = null;
+        // find in superclass hierarchy
         Class<?> loopCls = cls;
         while (loopCls != null && matched == null) {
-            Method[] methods = loopCls.getDeclaredMethods();
-            for (Method method : methods) {
-                FromString fromString = method.getAnnotation(FromString.class);
-                if (fromString != null) {
-                    if (matched != null) {
-                        throw new IllegalStateException("Two methods are annotated with @ToString");
-                    }
-                    matched = method;
-                }
-            }
+            matched = findFromString(loopCls, matched);
             if (searchSuperclasses == false) {
                 break;
             }
             loopCls = loopCls.getSuperclass();
         }
+        // find in immediate parent interfaces
+        if (searchSuperclasses && matched == null) {
+            for (Class<?> loopIfc : cls.getInterfaces()) {
+                matched = findFromString(loopIfc, matched);
+            }
+        }
+        return matched;
+    }
+
+    /**
+     * Finds the conversion method.
+     * 
+     * @param cls  the class to find a method for, not null
+     * @param matched  the matched method, may be null
+     * @return the method to call, null means not found
+     */
+    private Method findFromString(Class<?> cls, Method matched) {
+        // find in declared methods
+        Method[] methods = cls.getDeclaredMethods();
+        for (Method method : methods) {
+            FromString fromString = method.getAnnotation(FromString.class);
+            if (fromString != null) {
+                if (matched != null) {
+                    throw new IllegalStateException("Two methods are annotated with @FromString: " + cls.getName());
+                }
+                matched = method;
+            }
+        }
+        // check for factory
+        FromStringFactory factory = cls.getAnnotation(FromStringFactory.class);
+        if (factory != null) {
+            if (matched != null) {
+                throw new IllegalStateException("Class annotated with @FromString and @FromStringFactory: " + cls.getName());
+            }
+            Method[] factoryMethods = factory.factory().getDeclaredMethods();
+            for (Method method : factoryMethods) {
+                // handle factory containing multiple FromString for different types
+                if (cls.isAssignableFrom(method.getReturnType())) {
+                    FromString fromString = method.getAnnotation(FromString.class);
+                    if (fromString != null) {
+                        if (matched != null) {
+                            throw new IllegalStateException("Two methods are annotated with @FromString on the factory: " + factory.factory().getName());
+                        }
+                        matched = method;
+                    }
+                }
+            }
+        }
         return matched;
     }
 
@@ -361,7 +452,7 @@ public final class StringConvert {
      * @throws IllegalStateException if trying to alter the global singleton
      */
     public <T> void register(final Class<T> cls, StringConverter<T> converter) {
-        if (cls == null ) {
+        if (cls == null) {
             throw new IllegalArgumentException("Class must not be null");
         }
         if (converter == null) {
@@ -427,7 +518,7 @@ public final class StringConvert {
      * @throws IllegalStateException if trying to alter the global singleton
      */
     public <T> void registerMethods(final Class<T> cls, String toStringMethodName, String fromStringMethodName) {
-        if (cls == null ) {
+        if (cls == null) {
             throw new IllegalArgumentException("Class must not be null");
         }
         if (toStringMethodName == null || fromStringMethodName == null) {
@@ -461,7 +552,7 @@ public final class StringConvert {
      * @throws IllegalStateException if trying to alter the global singleton
      */
     public <T> void registerMethodConstructor(final Class<T> cls, String toStringMethodName) {
-        if (cls == null ) {
+        if (cls == null) {
             throw new IllegalArgumentException("Class must not be null");
         }
         if (toStringMethodName == null) {
@@ -488,10 +579,10 @@ public final class StringConvert {
         try {
             m = cls.getMethod(methodName);
         } catch (NoSuchMethodException ex) {
-          throw new IllegalArgumentException(ex);
+            throw new IllegalArgumentException(ex);
         }
         if (Modifier.isStatic(m.getModifiers())) {
-          throw new IllegalArgumentException("Method must not be static: " + methodName);
+            throw new IllegalArgumentException("Method must not be static: " + methodName);
         }
         return m;
     }
@@ -515,7 +606,7 @@ public final class StringConvert {
             }
         }
         if (Modifier.isStatic(m.getModifiers()) == false) {
-          throw new IllegalArgumentException("Method must be static: " + methodName);
+            throw new IllegalArgumentException("Method must be static: " + methodName);
         }
         return m;
     }
@@ -534,7 +625,7 @@ public final class StringConvert {
             try {
                 return cls.getDeclaredConstructor(CharSequence.class);
             } catch (NoSuchMethodException ex2) {
-              throw new IllegalArgumentException("Constructor not found", ex2);
+                throw new IllegalArgumentException("Constructor not found", ex2);
             }
         }
     }
diff --git a/src/site/resources/download.html b/src/site/resources/download.html
new file mode 100644
index 0000000..c8048a5
--- /dev/null
+++ b/src/site/resources/download.html
@@ -0,0 +1,6 @@
+<head>
+<title>OpenGamma</title>
+<meta http-equiv="REFRESH" content="0;url=http://sourceforge.net/projects/joda-convert/files/joda-convert/">
+</head>
+<body></body>
+</html>
diff --git a/src/site/site.xml b/src/site/site.xml
index fc5e83a..239f80e 100644
--- a/src/site/site.xml
+++ b/src/site/site.xml
@@ -3,13 +3,11 @@
   <publishDate position="navigation-bottom" format="yyyy-MM-dd"/>
   <bannerLeft>
     <name>Joda.org</name>
-    <!--src>http://joda-convert.sourceforge.net/images/joda.png</src-->
-    <href>http://joda.sourceforge.net/</href>
+    <href>http://www.joda.org/</href>
   </bannerLeft>
   <bannerRight>
     <name>Joda-Convert</name>
-    <!--src>http://joda-convert.sourceforge.net/images/jodaconvert.png</src-->
-    <href>http://joda-convert.sourceforge.net/</href>
+    <href>http://www.joda.org/joda-convert/</href>
   </bannerRight>
 
   <body>
@@ -20,22 +18,21 @@
       <item name="Javadoc" href="apidocs/index.html"/>
       <item name="License" href="license.html"/>
       <item name="Release notes" href="changes-report.html"/>
-      <item name="Download" href="http://sourceforge.net/projects/joda-convert/files/joda-convert/"/>
+      <item name="Download" href="download.html"/>
     </menu>
 
     <menu name="Development">
-      <item name="GitHub (Source code)" href="https://github.com/JodaOrg/joda-convert"/>
-      <item name="Sourceforge" href="http://sourceforge.net/projects/joda-convert/"/>
+      <item name="GitHub" href="https://github.com/JodaOrg/joda-convert"/>
       <item name="Bugs/Issues" href="https://github.com/JodaOrg/joda-convert/issues"/>
-      <item name="Mailing lists" href="mail-lists.html"/>
+      <item name="Sourceforge" href="http://sourceforge.net/projects/joda-convert/"/>
     </menu>
 
     <menu name="Joda">
-      <item name="Joda home" href="http://joda.sourceforge.net"/>
-      <item name="Beans" href="http://joda-beans.sourceforge.net/index.html"/>
-      <item name="Money" href="http://joda-money.sourceforge.net/index.html"/>
-      <item name="Primitives" href="http://joda-primitives.sourceforge.net/index.html"/>
-      <item name="Time" href="http://joda-time.sourceforge.net/index.html"/>
+      <item name="Joda home" href="http://www.joda.org"/>
+      <item name="Beans" href="http://www.joda.org/joda-beans/"/>
+      <item name="Money" href="http://www.joda.org/joda-money/"/>
+      <item name="Primitives" href="http://www.joda.org/joda-primitives/"/>
+      <item name="Time" href="http://www.joda.org/joda-time/"/>
     </menu>
 
     <menu ref="reports"/>
@@ -46,7 +43,9 @@
   </body>
 
   <poweredBy>
-    <logo name="Sourceforge" href="http://sourceforge.net/projects/joda-convert/"
-        img="http://sflogo.sourceforge.net/sflogo.php?group_id=344317&type=13" width="120" height="30" />
+    <logo name="Maven" href="http://maven.apache.org/"
+          img="http://maven.apache.org/images/logos/maven-feather.png"/>
+    <logo name="GitHub" href="https://github.com/JodaOrg/joda-convert"
+          img="http://www.joda.org/images/github.png"/>
   </poweredBy>
 </project>
diff --git a/src/site/xdoc/index.xml b/src/site/xdoc/index.xml
index 410f718..c1b2154 100644
--- a/src/site/xdoc/index.xml
+++ b/src/site/xdoc/index.xml
@@ -62,8 +62,8 @@ Joda-Convert is licensed under the business-friendly <a href="license.html">Apac
 Various documentation is available:
 <ul>
 <li>The helpful <a href="userguide.html">user guide</a></li>
-<li>The javadoc for the <a href="apidocs/index.html">current release</a></li>
-<li>The change notes <a href="changes-report.html">for the releases</a></li>
+<li>The <a href="apidocs/index.html">Javadoc</a></li>
+<li>The change notes for the <a href="changes-report.html">releases</a></li>
 <li>The <a href="https://github.com/JodaOrg/joda-convert">GitHub</a> source repository</li>
 </ul>
 </p>
@@ -72,13 +72,12 @@ Various documentation is available:
 
 <section name="Releases">
 <p>
-<a href="http://sourceforge.net/projects/joda-convert/files/joda-convert/1.3.1/">Release 1.3.1</a>
-is the current latest release.
+<a href="download.html">Release 1.4</a> is the current latest release.
 This release is considered stable and worthy of the 1.x tag.
 It depends on JDK 1.6 or later.
 </p>
 <p>
-Available in the <a href="http://search.maven.org/#artifactdetails|org.joda|joda-convert|1.3.1|jar">Maven Central repository</a>.
+Available in the <a href="http://search.maven.org/#artifactdetails|org.joda|joda-convert|1.4|jar">Maven Central repository</a>.
 </p>
 </section>
 
@@ -86,8 +85,6 @@ Available in the <a href="http://search.maven.org/#artifactdetails|org.joda|joda
 <section name="Support">
 <p>
 Support on bugs, library usage or enhancement requests is available on a best efforts basis.
-If you have any questions, or want to volunteer to help, just email Stephen Colebourne
-via scolebourne.at.users.sourceforge.net.
 </p>
 <p>
 To suggest enhancements or contribute, please <a href="https://github.com/JodaOrg/joda-convert">fork the source code</a> on GitHub.
diff --git a/src/site/xdoc/userguide.xml b/src/site/xdoc/userguide.xml
index 1f19d57..2c089e4 100644
--- a/src/site/xdoc/userguide.xml
+++ b/src/site/xdoc/userguide.xml
@@ -89,17 +89,54 @@ To be valid, the class must contain one <code>ToString</code> annotation and one
 The <code>ToString</code> annotation must be an instance method taking no parameters and returning a String.
 The <code>FromString</code> annotation must be either a static method or a constructor taking a String parameter and
 returning the correct type.
-If the annotations are not found on the target class, then superclasses are searched.
+If the annotations are not found on the target class, then superclasses are searched, followed by immediate parent interfaces.
 </p>
 <p>
+Sometimes, you want to provide to/from string conversions for interfaces.
+In JDK 8 this can be done using static methods on interfaces.
+However in earlier versions, a separate "factory" class is necessary.
+This can also be annotated:
+</p>
+<div style="border:1px solid black; padding: 0px 6px; margin: 4px;"><pre>
+ at FromStringFactory(factory = DistanceFactory.class)
+public interface Distance {
+
+  @ToString
+  String standardFormat();
+
+}
+
+public class Metres implements Distance {
+
+  @Override
+  public String standardFormat() { ... }
+
+}
+
+public class DistanceFactory {
+
+  @FromString
+  public static Distance parseDistance(String str) { ... }
+
+}
+</pre></div>
+<p>
+The <code>FromStringFactory</code> annotation points at the factory class that will provide the factory method.
+Although intended for use with interfaces, it can also be used on the target class or any superclass.
+Note that only the immediate parent interfaces of a class will be searched.
+</p>
+</subsection>
+
+<subsection name="Rationale">
+<p>
 The concept is that other open source libraries, as well as your application code, will implement these two annotations.
 For open source projects, a key point is that adding the annotations is a compile-time only event.
-The Joda-Convert jar file is not needed by your users unless they want to.
+The Joda-Convert jar file is not needed by your users unless they want to use conversion.
 If they don't want to use Joda-Convert then the annotations are effectively ignored.
 </p>
 <p>
 Joda-Time v2.0 and Joda-Money will both contain the annotations.
-However, in both cases, the dependency is compile-time only, and not at runtime.
+In both cases, the dependency is not a runtime for users of the projects.
 </p>
 </subsection>
 
diff --git a/src/test/java/org/joda/convert/DistanceWithFactory.java b/src/test/java/org/joda/convert/DistanceWithFactory.java
new file mode 100644
index 0000000..8627929
--- /dev/null
+++ b/src/test/java/org/joda/convert/DistanceWithFactory.java
@@ -0,0 +1,37 @@
+/*
+ *  Copyright 2010-2013 Stephen Colebourne
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.joda.convert;
+
+/**
+ * Example class with no annotations.
+ */
+ at FromStringFactory(factory = DistanceWithFactoryFactory.class)
+public class DistanceWithFactory {
+
+    /** Amount. */
+    final int amount;
+
+    public DistanceWithFactory(int amount) {
+        this.amount = amount;
+    }
+
+    @ToString
+    @Override
+    public String toString() {
+        return amount + "m";
+    }
+
+}
diff --git a/src/test/java/org/joda/convert/DistanceWithFactoryFactory.java b/src/test/java/org/joda/convert/DistanceWithFactoryFactory.java
new file mode 100644
index 0000000..88fa2e9
--- /dev/null
+++ b/src/test/java/org/joda/convert/DistanceWithFactoryFactory.java
@@ -0,0 +1,29 @@
+/*
+ *  Copyright 2010-2013 Stephen Colebourne
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.joda.convert;
+
+/**
+ * Example factory.
+ */
+public class DistanceWithFactoryFactory {
+
+    @FromString
+    public static DistanceWithFactory parse(String amount) {
+        amount = amount.substring(0, amount.length() - 1);
+        return new DistanceWithFactory(Integer.parseInt(amount));
+    }
+
+}
diff --git a/src/test/java/org/joda/convert/TestStringConvert.java b/src/test/java/org/joda/convert/TestStringConvert.java
index 3a729ec..ddca01d 100644
--- a/src/test/java/org/joda/convert/TestStringConvert.java
+++ b/src/test/java/org/joda/convert/TestStringConvert.java
@@ -22,6 +22,12 @@ import static org.junit.Assert.fail;
 import java.math.RoundingMode;
 import java.text.ParseException;
 
+import org.joda.convert.test1.Test1Class;
+import org.joda.convert.test2.Test2Class;
+import org.joda.convert.test2.Test2Interface;
+import org.joda.convert.test3.Test3Class;
+import org.joda.convert.test4.Test4Class;
+import org.joda.convert.test4.Test4Interface;
 import org.junit.Test;
 
 /**
@@ -256,7 +262,7 @@ public class TestStringConvert {
         assertEquals("25m", test.convertToString(d));
     }
 
-    // TODO problem is fwks, that just request a converter baed on the type of the object
+    // TODO problem is fwks, that just request a converter based on the type of the object
     @Test(expected = ClassCastException.class)
     public void test_convert_annotationSuperFactorySubViaSub2() {
         StringConvert test = new StringConvert();
@@ -289,6 +295,79 @@ public class TestStringConvert {
     }
 
     //-----------------------------------------------------------------------
+    @Test
+    public void test_convert_annotationFactoryMethod() {
+        StringConvert test = new StringConvert();
+        DistanceWithFactory d = new DistanceWithFactory(25);
+        assertEquals("25m", test.convertToString(d));
+        assertEquals(d.amount, test.convertFromString(DistanceWithFactory.class, "25m").amount);
+        StringConverter<DistanceWithFactory> conv = test.findConverter(DistanceWithFactory.class);
+        assertEquals(true, conv instanceof MethodsStringConverter<?>);
+        assertSame(conv, test.findConverter(DistanceWithFactory.class));
+        assertEquals(true, conv.toString().startsWith("RefectionStringConverter"));
+    }
+
+    @Test
+    public void test_convert_annotation_ToStringOnInterface() {
+        StringConvert test = new StringConvert();
+        Test1Class d = new Test1Class(25);
+        assertEquals("25g", test.convertToString(d));
+        assertEquals(d.amount, test.convertFromString(Test1Class.class, "25g").amount);
+        StringConverter<Test1Class> conv = test.findConverter(Test1Class.class);
+        assertEquals(true, conv instanceof MethodsStringConverter<?>);
+        assertSame(conv, test.findConverter(Test1Class.class));
+        assertEquals(true, conv.toString().startsWith("RefectionStringConverter"));
+    }
+
+    @Test
+    public void test_convert_annotation_FactoryAndToStringOnInterface() {
+        StringConvert test = new StringConvert();
+        Test2Class d = new Test2Class(25);
+        assertEquals("25g", test.convertToString(d));
+        assertEquals(d.amount, test.convertFromString(Test2Class.class, "25g").amount);
+        StringConverter<Test2Class> conv = test.findConverter(Test2Class.class);
+        assertEquals(true, conv instanceof MethodsStringConverter<?>);
+        assertSame(conv, test.findConverter(Test2Class.class));
+        assertEquals(true, conv.toString().startsWith("RefectionStringConverter"));
+    }
+
+    @Test
+    public void test_convert_annotation_FactoryAndToStringOnInterface_usingInterface() {
+        StringConvert test = new StringConvert();
+        Test2Class d = new Test2Class(25);
+        assertEquals("25g", test.convertToString(d));
+        assertEquals("25g", test.convertFromString(Test2Interface.class, "25g").print());
+        StringConverter<Test2Interface> conv = test.findConverter(Test2Interface.class);
+        assertEquals(true, conv instanceof MethodsStringConverter<?>);
+        assertSame(conv, test.findConverter(Test2Interface.class));
+        assertEquals(true, conv.toString().startsWith("RefectionStringConverter"));
+    }
+
+    @Test
+    public void test_convert_annotation_ToStringFromStringOnSuperClassBeatsInterface() {
+        StringConvert test = new StringConvert();
+        Test3Class d = new Test3Class(25);
+        assertEquals("25g", test.convertToString(d));
+        assertEquals(d.amount, test.convertFromString(Test3Class.class, "25g").amount);
+        StringConverter<Test3Class> conv = test.findConverter(Test3Class.class);
+        assertEquals(true, conv instanceof MethodsStringConverter<?>);
+        assertSame(conv, test.findConverter(Test3Class.class));
+        assertEquals(true, conv.toString().startsWith("RefectionStringConverter"));
+    }
+
+    @Test(expected=IllegalStateException.class)
+    public void test_convert_annotation_FromStringFactoryClashingMethods_fromClass() {
+        StringConvert test = new StringConvert();
+        test.findConverter(Test4Class.class);
+    }
+
+    @Test(expected=IllegalStateException.class)
+    public void test_convert_annotation_FromStringFactoryClashingMethods_fromInterface() {
+        StringConvert test = new StringConvert();
+        test.findConverter(Test4Interface.class);
+    }
+
+    //-----------------------------------------------------------------------
     @Test(expected=IllegalStateException.class)
     public void test_convert_annotationNoMethods() {
         StringConvert test = new StringConvert();
diff --git a/src/test/java/org/joda/convert/test1/Test1Class.java b/src/test/java/org/joda/convert/test1/Test1Class.java
new file mode 100644
index 0000000..46e29e1
--- /dev/null
+++ b/src/test/java/org/joda/convert/test1/Test1Class.java
@@ -0,0 +1,47 @@
+/*
+ *  Copyright 2010-2013 Stephen Colebourne
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.joda.convert.test1;
+
+import org.joda.convert.FromString;
+
+/**
+ * Example class with annotated methods.
+ */
+public class Test1Class implements Test1Interface {
+
+    /** Amount. */
+    public final int amount;
+
+    @FromString
+    public static Test1Class parse(String amount) {
+        amount = amount.substring(0, amount.length() - 1);
+        return new Test1Class(Integer.parseInt(amount));
+    }
+
+    public Test1Class(int amount) {
+        this.amount = amount;
+    }
+
+    public String print() {
+        return amount + "g";
+    }
+
+    @Override
+    public String toString() {
+        return "Weight[" + amount + "g]";
+    }
+
+}
diff --git a/src/test/java/org/joda/convert/test1/Test1Interface.java b/src/test/java/org/joda/convert/test1/Test1Interface.java
new file mode 100644
index 0000000..34624aa
--- /dev/null
+++ b/src/test/java/org/joda/convert/test1/Test1Interface.java
@@ -0,0 +1,28 @@
+/*
+ *  Copyright 2010-2013 Stephen Colebourne
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.joda.convert.test1;
+
+import org.joda.convert.ToString;
+
+/**
+ * Example interface with annotated methods.
+ */
+public interface Test1Interface {
+
+    @ToString
+    String print();
+
+}
diff --git a/src/test/java/org/joda/convert/test2/Test2Class.java b/src/test/java/org/joda/convert/test2/Test2Class.java
new file mode 100644
index 0000000..2cfe734
--- /dev/null
+++ b/src/test/java/org/joda/convert/test2/Test2Class.java
@@ -0,0 +1,39 @@
+/*
+ *  Copyright 2010-2013 Stephen Colebourne
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.joda.convert.test2;
+
+/**
+ * Example class with annotated methods.
+ */
+public class Test2Class implements Test2Interface {
+
+    /** Amount. */
+    public final int amount;
+
+    public Test2Class(int amount) {
+        this.amount = amount;
+    }
+
+    public String print() {
+        return amount + "g";
+    }
+
+    @Override
+    public String toString() {
+        return "Weight[" + amount + "g]";
+    }
+
+}
diff --git a/src/test/java/org/joda/convert/test2/Test2Factory.java b/src/test/java/org/joda/convert/test2/Test2Factory.java
new file mode 100644
index 0000000..8e83ca4
--- /dev/null
+++ b/src/test/java/org/joda/convert/test2/Test2Factory.java
@@ -0,0 +1,31 @@
+/*
+ *  Copyright 2010-2013 Stephen Colebourne
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.joda.convert.test2;
+
+import org.joda.convert.FromString;
+
+/**
+ * Example class with annotated methods.
+ */
+public class Test2Factory {
+
+    @FromString
+    public static Test2Class parse(String amount) {
+        amount = amount.substring(0, amount.length() - 1);
+        return new Test2Class(Integer.parseInt(amount));
+    }
+
+}
diff --git a/src/test/java/org/joda/convert/test2/Test2Interface.java b/src/test/java/org/joda/convert/test2/Test2Interface.java
new file mode 100644
index 0000000..f8937f5
--- /dev/null
+++ b/src/test/java/org/joda/convert/test2/Test2Interface.java
@@ -0,0 +1,30 @@
+/*
+ *  Copyright 2010-2013 Stephen Colebourne
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.joda.convert.test2;
+
+import org.joda.convert.FromStringFactory;
+import org.joda.convert.ToString;
+
+/**
+ * Example interface with annotated methods.
+ */
+ at FromStringFactory(factory = Test2Factory.class)
+public interface Test2Interface {
+
+    @ToString
+    String print();
+
+}
diff --git a/src/test/java/org/joda/convert/test3/Test3Class.java b/src/test/java/org/joda/convert/test3/Test3Class.java
new file mode 100644
index 0000000..3c4ed74
--- /dev/null
+++ b/src/test/java/org/joda/convert/test3/Test3Class.java
@@ -0,0 +1,40 @@
+/*
+ *  Copyright 2010-2013 Stephen Colebourne
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.joda.convert.test3;
+
+/**
+ * Example class with annotated methods.
+ */
+public class Test3Class extends Test3SuperClass implements Test3Interface {
+
+    /** Amount. */
+    public final int amount;
+
+    public Test3Class(int amount) {
+        this.amount = amount;
+    }
+
+    @Override
+    public String print() {
+        return amount + "g";
+    }
+
+    @Override
+    public String toString() {
+        return "Weight[" + amount + "g]";
+    }
+
+}
diff --git a/src/test/java/org/joda/convert/test3/Test3Factory.java b/src/test/java/org/joda/convert/test3/Test3Factory.java
new file mode 100644
index 0000000..bd364c6
--- /dev/null
+++ b/src/test/java/org/joda/convert/test3/Test3Factory.java
@@ -0,0 +1,30 @@
+/*
+ *  Copyright 2010-2013 Stephen Colebourne
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.joda.convert.test3;
+
+import org.joda.convert.FromString;
+
+/**
+ * Example class with annotated methods.
+ */
+public class Test3Factory {
+
+    @FromString
+    public static Test3Interface parse(String amount) {
+        throw new UnsupportedOperationException();
+    }
+
+}
diff --git a/src/test/java/org/joda/convert/test3/Test3Interface.java b/src/test/java/org/joda/convert/test3/Test3Interface.java
new file mode 100644
index 0000000..ac69dc1
--- /dev/null
+++ b/src/test/java/org/joda/convert/test3/Test3Interface.java
@@ -0,0 +1,31 @@
+/*
+ *  Copyright 2010-2013 Stephen Colebourne
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.joda.convert.test3;
+
+import org.joda.convert.FromStringFactory;
+import org.joda.convert.ToString;
+
+/**
+ * Example interface with annotated methods.
+ */
+ at FromStringFactory(factory = Test3Factory.class)
+public interface Test3Interface {
+
+    @ToString
+    @Override
+    String toString();
+
+}
diff --git a/src/test/java/org/joda/convert/test3/Test3SuperClass.java b/src/test/java/org/joda/convert/test3/Test3SuperClass.java
new file mode 100644
index 0000000..b0225ee
--- /dev/null
+++ b/src/test/java/org/joda/convert/test3/Test3SuperClass.java
@@ -0,0 +1,35 @@
+/*
+ *  Copyright 2010-2013 Stephen Colebourne
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.joda.convert.test3;
+
+import org.joda.convert.FromString;
+import org.joda.convert.ToString;
+
+/**
+ * Example class with annotated methods.
+ */
+public abstract class Test3SuperClass {
+
+    @FromString
+    public static Test3SuperClass parse(String amount) {
+        amount = amount.substring(0, amount.length() - 1);
+        return new Test3Class(Integer.parseInt(amount));
+    }
+
+    @ToString
+    public abstract String print();
+
+}
diff --git a/src/test/java/org/joda/convert/test4/Test4Class.java b/src/test/java/org/joda/convert/test4/Test4Class.java
new file mode 100644
index 0000000..c026a2f
--- /dev/null
+++ b/src/test/java/org/joda/convert/test4/Test4Class.java
@@ -0,0 +1,39 @@
+/*
+ *  Copyright 2010-2013 Stephen Colebourne
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.joda.convert.test4;
+
+/**
+ * Example class with annotated methods.
+ */
+public class Test4Class implements Test4Interface {
+
+    /** Amount. */
+    public final int amount;
+
+    public Test4Class(int amount) {
+        this.amount = amount;
+    }
+
+    public String print() {
+        return amount + "g";
+    }
+
+    @Override
+    public String toString() {
+        return "Weight[" + amount + "g]";
+    }
+
+}
diff --git a/src/test/java/org/joda/convert/test4/Test4Factory.java b/src/test/java/org/joda/convert/test4/Test4Factory.java
new file mode 100644
index 0000000..dd5de49
--- /dev/null
+++ b/src/test/java/org/joda/convert/test4/Test4Factory.java
@@ -0,0 +1,37 @@
+/*
+ *  Copyright 2010-2013 Stephen Colebourne
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.joda.convert.test4;
+
+import org.joda.convert.FromString;
+
+/**
+ * Example class with annotated methods.
+ */
+public class Test4Factory {
+
+    @FromString
+    public static Test4Interface parseInterface(String amount) {
+        amount = amount.substring(0, amount.length() - 1);
+        return new Test4Class(Integer.parseInt(amount));
+    }
+
+    @FromString
+    public static Test4Class parseClass(String amount) {
+        amount = amount.substring(0, amount.length() - 1);
+        return new Test4Class(Integer.parseInt(amount));
+    }
+
+}
diff --git a/src/test/java/org/joda/convert/test4/Test4Interface.java b/src/test/java/org/joda/convert/test4/Test4Interface.java
new file mode 100644
index 0000000..fd8d64f
--- /dev/null
+++ b/src/test/java/org/joda/convert/test4/Test4Interface.java
@@ -0,0 +1,30 @@
+/*
+ *  Copyright 2010-2013 Stephen Colebourne
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.joda.convert.test4;
+
+import org.joda.convert.FromStringFactory;
+import org.joda.convert.ToString;
+
+/**
+ * Example interface with annotated methods.
+ */
+ at FromStringFactory(factory = Test4Factory.class)
+public interface Test4Interface {
+
+    @ToString
+    String print();
+
+}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/joda-convert.git



More information about the pkg-java-commits mailing list