From 7902c1d69dac769772acccafe6f62b8583c17947 Mon Sep 17 00:00:00 2001 From: Chris Kirk Date: Mon, 14 Sep 2020 06:17:28 +0100 Subject: [PATCH 1/2] Resolves #21 --- .gitignore | 3 +- .../internal/parser/JavadocParser.java | 14 ++-- .../internal/parser/ThrowsTagParser.java | 26 +++++++ .../internal/parser/ThrowsTagParserTest.java | 71 +++++++++++++++++++ 4 files changed, 109 insertions(+), 5 deletions(-) create mode 100644 therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/internal/parser/ThrowsTagParser.java create mode 100644 therapi-runtime-javadoc/src/test/java/com/github/therapi/runtimejavadoc/internal/parser/ThrowsTagParserTest.java diff --git a/.gitignore b/.gitignore index 124acf9..b13cfd5 100644 --- a/.gitignore +++ b/.gitignore @@ -15,7 +15,8 @@ hs_err_pid* *.ipr *.iml .idea +out /therapi-runtime-javadoc-scribe/build /therapi-runtime-javadoc/build -.gradle \ No newline at end of file +.gradle diff --git a/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/internal/parser/JavadocParser.java b/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/internal/parser/JavadocParser.java index a074c9d..9edfe3a 100644 --- a/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/internal/parser/JavadocParser.java +++ b/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/internal/parser/JavadocParser.java @@ -59,6 +59,7 @@ public static MethodJavadoc parseMethodJavadoc(String owningClass, String method List otherDocs = new ArrayList<>(); List seeAlsoDocs = new ArrayList<>(); List paramDocs = new ArrayList<>(); + List throwsDocs = new ArrayList<>(); Comment returns = null; @@ -72,9 +73,14 @@ public static MethodJavadoc parseMethodJavadoc(String owningClass, String method } else if (t.name.equals("return")) { returns = CommentParser.parse(owningClass, t.value); } else if (t.name.equals("see")) { - SeeAlsoJavadoc seeAlso = SeeAlsoParser.parseSeeAlso(owningClass, t.value); - if (seeAlso != null) { - seeAlsoDocs.add(seeAlso); + SeeAlsoJavadoc seeAlso = SeeAlsoParser.parseSeeAlso( owningClass, t.value ); + if ( seeAlso != null ) { + seeAlsoDocs.add( seeAlso ); + } + } else if (t.name.equals("throws") || t.name.equals("exception")) { + ThrowsJavadoc throwsDoc = ThrowsTagParser.parseTag(owningClass, t.value); + if (throwsDoc != null) { + throwsDocs.add( throwsDoc ); } } else { otherDocs.add(new OtherJavadoc(t.name, CommentParser.parse(owningClass, t.value))); @@ -82,7 +88,7 @@ public static MethodJavadoc parseMethodJavadoc(String owningClass, String method } return new MethodJavadoc(methodName, paramTypes, CommentParser.parse(owningClass, parsed.getDescription()), paramDocs, - new ArrayList(), otherDocs, returns, seeAlsoDocs); + throwsDocs, otherDocs, returns, seeAlsoDocs); } private static ParsedJavadoc parse(String javadoc) { diff --git a/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/internal/parser/ThrowsTagParser.java b/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/internal/parser/ThrowsTagParser.java new file mode 100644 index 0000000..b5ff516 --- /dev/null +++ b/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/internal/parser/ThrowsTagParser.java @@ -0,0 +1,26 @@ +package com.github.therapi.runtimejavadoc.internal.parser; + +import com.github.therapi.runtimejavadoc.ThrowsJavadoc; + +import java.util.regex.Pattern; + + +public class ThrowsTagParser { + private static final Pattern whitespace = Pattern.compile("\\s"); + + public static ThrowsJavadoc parseTag( String owningClass, String value ) { + if ( value == null || value.trim().length() == 0 ) { + return null; + } + + String[] exceptionClassAndComment = whitespace.split(value.trim(), 2); + String exceptionClass = toFullClassName(owningClass, exceptionClassAndComment[0]); + String comment = exceptionClassAndComment.length == 1 ? "" :exceptionClassAndComment[1]; + + return new ThrowsJavadoc( exceptionClass, CommentParser.parse(owningClass,comment) ); + } + + private static String toFullClassName( String owningClass, String exceptionClass ) { + return exceptionClass; + } +} diff --git a/therapi-runtime-javadoc/src/test/java/com/github/therapi/runtimejavadoc/internal/parser/ThrowsTagParserTest.java b/therapi-runtime-javadoc/src/test/java/com/github/therapi/runtimejavadoc/internal/parser/ThrowsTagParserTest.java new file mode 100644 index 0000000..6e3a2c6 --- /dev/null +++ b/therapi-runtime-javadoc/src/test/java/com/github/therapi/runtimejavadoc/internal/parser/ThrowsTagParserTest.java @@ -0,0 +1,71 @@ +package com.github.therapi.runtimejavadoc.internal.parser; + +import com.github.therapi.runtimejavadoc.CommentText; +import com.github.therapi.runtimejavadoc.ThrowsJavadoc; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + + +public class ThrowsTagParserTest { + @Test + public void givenNullText_expectNoDocs() { + ThrowsJavadoc doc = ThrowsTagParser.parseTag( "com.github.Mission", null ); + + assertNull( doc ); + } + @Test + public void givenNoExceptionOrDescription_expectNoDocs() { + ThrowsJavadoc doc = ThrowsTagParser.parseTag( "com.github.Mission", "" ); + + assertNull( doc ); + } + + @Test + public void givenFullyQualifiedExceptionAndNoDescription_expectEmptyDescription() { + ThrowsJavadoc doc = ThrowsTagParser.parseTag( "com.github.Mission", "com.github.voyage.OutOfFuelException" ); + + assertEquals( "com.github.voyage.OutOfFuelException", doc.getName() ); + assertEquals( Collections.emptyList(), doc.getComment().getElements() ); + } + + @Test + public void givenWhitespaceAroundFullyQualifiedExceptionAndNoDescription_expectEmptyDescription() { + ThrowsJavadoc doc = ThrowsTagParser.parseTag( "com.github.Mission", " com.github.voyage.OutOfFuelException " ); + + assertEquals( "com.github.voyage.OutOfFuelException", doc.getName() ); + assertEquals( Collections.emptyList(), doc.getComment().getElements() ); + } + + @Test + public void givenRelativeExceptionAndNoDescription_expectExceptionToNotBeResolvedYet() { + ThrowsJavadoc doc = ThrowsTagParser.parseTag( "com.github.Mission", "OutOfFuelException" ); + + assertEquals( "OutOfFuelException", doc.getName() ); + assertEquals( Collections.emptyList(), doc.getComment().getElements() ); + } + + @Test + public void givenFullyQualifiedExceptionWithASingleLineDescription_expectDescription() { + ThrowsJavadoc doc = ThrowsTagParser.parseTag( "com.github.Mission", "com.github.voyage.OutOfFuelException Lorem ipsum dolor sit amet" ); + + assertEquals( "com.github.voyage.OutOfFuelException", doc.getName() ); + assertEquals( Arrays.asList(new CommentText("Lorem ipsum dolor sit amet")), doc.getComment().getElements() ); + } + + @Test + public void givenFullyQualifiedExceptionWithMultiLineDescription_expectDescription() { + ThrowsJavadoc doc = ThrowsTagParser.parseTag( + "com.github.Mission", + " com.github.voyage.OutOfFuelException Lorem ipsum dolor sit amet, consectetur \n" + + " adipiscing elit, sed do eiusmod tempor incididunt. " ); + + assertEquals( "com.github.voyage.OutOfFuelException", doc.getName() ); + assertEquals( Arrays.asList(new CommentText("Lorem ipsum dolor sit amet, consectetur \n" + + " adipiscing elit, sed do eiusmod tempor incididunt.")), doc.getComment().getElements() ); + } +} From 1f021af12ed11749272e747f9ed13cfc3728d40a Mon Sep 17 00:00:00 2001 From: Chris Kirk Date: Sat, 19 Sep 2020 04:50:15 +0100 Subject: [PATCH 2/2] issue 21: add runtime resolution of class names --- .gitignore | 3 +- acceptance-tests/build.gradle | 8 + acceptance-tests/gradle.properties | 1 + .../com/therapi/javadoc/acctests/Example.java | 19 ++ .../therapi/javadoc/acctests/ExampleTest.java | 51 +++++ settings.gradle | 1 + therapi-runtime-javadoc-scribe/build.gradle | 2 +- .../internal/JavacImportAccessor.java | 10 +- .../internal/JsonJavadocBuilder.java | 15 ++ .../therapi/runtimejavadoc/ClassJavadoc.java | 10 + .../therapi/runtimejavadoc/ClassResolver.java | 175 ++++++++++++++++++ .../therapi/runtimejavadoc/Comment.java | 16 ++ .../github/therapi/runtimejavadoc/Import.java | 99 ++++++++++ .../runtimejavadoc/SeeAlsoJavadoc.java | 49 ++++- .../therapi/runtimejavadoc/ThrowsJavadoc.java | 27 +++ .../internal/JsonJavadocReader.java | 81 ++++++-- .../internal/RuntimeJavadocHelper.java | 4 + .../internal/parser/CommentParser.java | 25 +-- .../internal/parser/ImportParser.java | 58 ++++++ .../internal/parser/JavadocParser.java | 29 +-- .../internal/parser/LinkParser.java | 11 +- .../internal/parser/SeeAlsoParser.java | 11 +- .../internal/parser/ThrowsTagParser.java | 13 +- .../runtimejavadoc/ClassResolverTest.java | 74 ++++++++ .../therapi/runtimejavadoc/ImportTest.java | 17 ++ .../internal/parser/ClassResolverMock.java | 27 +++ .../internal/parser/CommentParserTest.java | 116 +++++++----- .../internal/parser/ImportParserTest.java | 18 ++ .../internal/parser/ThrowsTagParserTest.java | 24 ++- .../runtimejavadoc/resolvers/a/Car.java | 7 + .../runtimejavadoc/resolvers/b/Car.java | 7 + 31 files changed, 880 insertions(+), 128 deletions(-) create mode 100644 acceptance-tests/build.gradle create mode 100644 acceptance-tests/gradle.properties create mode 100644 acceptance-tests/src/main/java/com/therapi/javadoc/acctests/Example.java create mode 100644 acceptance-tests/src/test/java/com/therapi/javadoc/acctests/ExampleTest.java create mode 100644 therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/ClassResolver.java create mode 100644 therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/Import.java create mode 100644 therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/internal/parser/ImportParser.java create mode 100644 therapi-runtime-javadoc/src/test/java/com/github/therapi/runtimejavadoc/ClassResolverTest.java create mode 100644 therapi-runtime-javadoc/src/test/java/com/github/therapi/runtimejavadoc/ImportTest.java create mode 100644 therapi-runtime-javadoc/src/test/java/com/github/therapi/runtimejavadoc/internal/parser/ClassResolverMock.java create mode 100644 therapi-runtime-javadoc/src/test/java/com/github/therapi/runtimejavadoc/internal/parser/ImportParserTest.java create mode 100644 therapi-runtime-javadoc/src/test/java/com/github/therapi/runtimejavadoc/resolvers/a/Car.java create mode 100644 therapi-runtime-javadoc/src/test/java/com/github/therapi/runtimejavadoc/resolvers/b/Car.java diff --git a/.gitignore b/.gitignore index b13cfd5..8d502d4 100644 --- a/.gitignore +++ b/.gitignore @@ -16,7 +16,6 @@ hs_err_pid* *.iml .idea out -/therapi-runtime-javadoc-scribe/build -/therapi-runtime-javadoc/build +build .gradle diff --git a/acceptance-tests/build.gradle b/acceptance-tests/build.gradle new file mode 100644 index 0000000..8f1453f --- /dev/null +++ b/acceptance-tests/build.gradle @@ -0,0 +1,8 @@ + +dependencies { + compile project(':therapi-runtime-javadoc') + compile project(':therapi-runtime-javadoc-scribe') + + annotationProcessor project(':therapi-runtime-javadoc') + annotationProcessor project(':therapi-runtime-javadoc-scribe') +} diff --git a/acceptance-tests/gradle.properties b/acceptance-tests/gradle.properties new file mode 100644 index 0000000..6d649d8 --- /dev/null +++ b/acceptance-tests/gradle.properties @@ -0,0 +1 @@ +description = End to end tests that demonstrate capturing and accessing javadocs diff --git a/acceptance-tests/src/main/java/com/therapi/javadoc/acctests/Example.java b/acceptance-tests/src/main/java/com/therapi/javadoc/acctests/Example.java new file mode 100644 index 0000000..f241ea8 --- /dev/null +++ b/acceptance-tests/src/main/java/com/therapi/javadoc/acctests/Example.java @@ -0,0 +1,19 @@ +package com.therapi.javadoc.acctests; + +import java.util.HashMap; +import java.util.Map ; + + +/** + * Class java doc. + */ +public class Example { + /** + * + * @see Map + * @throws RuntimeException Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt. + */ + public Map method() { + return new HashMap<>(); + } +} diff --git a/acceptance-tests/src/test/java/com/therapi/javadoc/acctests/ExampleTest.java b/acceptance-tests/src/test/java/com/therapi/javadoc/acctests/ExampleTest.java new file mode 100644 index 0000000..437a3bd --- /dev/null +++ b/acceptance-tests/src/test/java/com/therapi/javadoc/acctests/ExampleTest.java @@ -0,0 +1,51 @@ +package com.therapi.javadoc.acctests; + +import com.github.therapi.runtimejavadoc.ClassJavadoc; +import com.github.therapi.runtimejavadoc.Comment; +import com.github.therapi.runtimejavadoc.CommentElement; +import com.github.therapi.runtimejavadoc.CommentText; +import com.github.therapi.runtimejavadoc.Link; +import com.github.therapi.runtimejavadoc.RuntimeJavadoc; +import com.github.therapi.runtimejavadoc.SeeAlsoJavadoc; +import com.github.therapi.runtimejavadoc.ThrowsJavadoc; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.assertEquals; + + +public class ExampleTest { + @Test + public void givenRelativeClassNameInSeeTag_expectClassNameToBeResolved() { + ClassJavadoc classJavadoc = RuntimeJavadoc.getJavadoc(Example.class); + List expected = Collections.singletonList( new SeeAlsoJavadoc( new Link( "Map", "java.util.Map", null, Collections.emptyList() ) ) ); + List actual = classJavadoc.getMethod( "method" ).getSeeAlso(); + + assertEquals( expected, actual ); + } + + @Test + public void givenRelativeExceptionInThrowsTag_expectExceptionClassNameToBeResolved() { + ClassJavadoc classJavadoc = RuntimeJavadoc.getJavadoc(Example.class); + + List expected = Collections.singletonList( + new ThrowsJavadoc( + "java.lang.RuntimeException", + new Comment( + Collections.singletonList( + new CommentText( + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt." + ) + ) + ) + ) + ); + + List actual = classJavadoc.getMethod( "method" ).getThrows(); + + assertEquals( expected, actual ); + } +} diff --git a/settings.gradle b/settings.gradle index ff4c169..8db03ba 100644 --- a/settings.gradle +++ b/settings.gradle @@ -2,5 +2,6 @@ rootProject.name = 'therapi-runtime-javadoc' include 'therapi-runtime-javadoc' include 'therapi-runtime-javadoc-scribe' +include 'acceptance-tests' enableFeaturePreview('STABLE_PUBLISHING') diff --git a/therapi-runtime-javadoc-scribe/build.gradle b/therapi-runtime-javadoc-scribe/build.gradle index d511a39..b7027ef 100644 --- a/therapi-runtime-javadoc-scribe/build.gradle +++ b/therapi-runtime-javadoc-scribe/build.gradle @@ -39,4 +39,4 @@ publishing { } } } -} \ No newline at end of file +} diff --git a/therapi-runtime-javadoc-scribe/src/main/java/com/github/therapi/runtimejavadoc/internal/JavacImportAccessor.java b/therapi-runtime-javadoc-scribe/src/main/java/com/github/therapi/runtimejavadoc/internal/JavacImportAccessor.java index 90aee58..dce43e9 100644 --- a/therapi-runtime-javadoc-scribe/src/main/java/com/github/therapi/runtimejavadoc/internal/JavacImportAccessor.java +++ b/therapi-runtime-javadoc-scribe/src/main/java/com/github/therapi/runtimejavadoc/internal/JavacImportAccessor.java @@ -1,15 +1,15 @@ package com.github.therapi.runtimejavadoc.internal; -import static java.util.Collections.emptySet; +import com.sun.source.tree.ImportTree; +import com.sun.source.util.TreePath; +import com.sun.source.util.Trees; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.Element; import java.util.HashSet; import java.util.Set; -import com.sun.source.tree.ImportTree; -import com.sun.source.util.TreePath; -import com.sun.source.util.Trees; +import static java.util.Collections.emptySet; /** * Implementation in separate class file so it fails gracefully in case @@ -31,7 +31,7 @@ public Set getImports(Element element) { } Set imports = new HashSet<>(); for (ImportTree importTree : path.getCompilationUnit().getImports()) { - imports.add(importTree.getQualifiedIdentifier().toString()); + imports.add(importTree.toString().trim()); } return imports; } diff --git a/therapi-runtime-javadoc-scribe/src/main/java/com/github/therapi/runtimejavadoc/internal/JsonJavadocBuilder.java b/therapi-runtime-javadoc-scribe/src/main/java/com/github/therapi/runtimejavadoc/internal/JsonJavadocBuilder.java index 0c70199..ab64eac 100644 --- a/therapi-runtime-javadoc-scribe/src/main/java/com/github/therapi/runtimejavadoc/internal/JsonJavadocBuilder.java +++ b/therapi-runtime-javadoc-scribe/src/main/java/com/github/therapi/runtimejavadoc/internal/JsonJavadocBuilder.java @@ -17,12 +17,14 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import static com.github.therapi.runtimejavadoc.internal.RuntimeJavadocHelper.constructorsFieldName; import static com.github.therapi.runtimejavadoc.internal.RuntimeJavadocHelper.elementDocFieldName; import static com.github.therapi.runtimejavadoc.internal.RuntimeJavadocHelper.elementNameFieldName; import static com.github.therapi.runtimejavadoc.internal.RuntimeJavadocHelper.enumConstantsFieldName; import static com.github.therapi.runtimejavadoc.internal.RuntimeJavadocHelper.fieldsFieldName; +import static com.github.therapi.runtimejavadoc.internal.RuntimeJavadocHelper.importsFieldName; import static com.github.therapi.runtimejavadoc.internal.RuntimeJavadocHelper.isBlank; import static com.github.therapi.runtimejavadoc.internal.RuntimeJavadocHelper.methodsFieldName; import static com.github.therapi.runtimejavadoc.internal.RuntimeJavadocHelper.paramTypesFieldName; @@ -55,11 +57,13 @@ JsonObject getClassJavadocAsJsonOrNull(TypeElement classElement) { } final List emptyList = Collections.emptyList(); + Set imports = ImportUtils.getImports( classElement, processingEnv ); List enclosedFields = defaultIfNull(children.get(FIELD), emptyList); List enclosedEnumConstants = defaultIfNull(children.get(ENUM_CONSTANT), emptyList); List enclosedMethods = defaultIfNull(children.get(METHOD), emptyList); List encolsedConstructors = defaultIfNull(children.get(CONSTRUCTOR), emptyList); + JsonArray importsJson = asJsonArray(imports); JsonArray fieldDocs = getJavadocsAsJson(enclosedFields, new FieldJavadocAsJson()); JsonArray enumConstantDocs = getJavadocsAsJson(enclosedEnumConstants, new FieldJavadocAsJson()); JsonArray methodDocs = getJavadocsAsJson(enclosedMethods, new MethodJavadocAsJson()); @@ -70,6 +74,7 @@ JsonObject getClassJavadocAsJsonOrNull(TypeElement classElement) { } JsonObject json = new JsonObject(); + json.add(importsFieldName(), importsJson); json.add(elementDocFieldName(), classDoc); json.add(fieldsFieldName(), fieldDocs); json.add(enumConstantsFieldName(), enumConstantDocs); @@ -78,6 +83,16 @@ JsonObject getClassJavadocAsJsonOrNull(TypeElement classElement) { return json; } + private JsonArray asJsonArray( Set imports ) { + JsonArray jsonArray = new JsonArray(); + + for ( String imp : imports ) { + jsonArray.add(imp); + } + + return jsonArray; + } + private static JsonArray getJavadocsAsJson(List elements, ElementToJsonFunction createDoc) { JsonArray jsonArray = new JsonArray(); for (Element e : elements) { diff --git a/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/ClassJavadoc.java b/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/ClassJavadoc.java index fc85cb4..0a363cc 100755 --- a/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/ClassJavadoc.java +++ b/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/ClassJavadoc.java @@ -45,6 +45,16 @@ public List getConstructors() { return constructors; } + public MethodJavadoc getMethod(String methodName) { + for ( MethodJavadoc m : methods ) { + if ( m.getName().equals(methodName) ) { + return m; + } + } + + return null; + } + @Override public String toString() { return "ClassJavadoc{" + diff --git a/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/ClassResolver.java b/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/ClassResolver.java new file mode 100644 index 0000000..0be9645 --- /dev/null +++ b/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/ClassResolver.java @@ -0,0 +1,175 @@ +package com.github.therapi.runtimejavadoc; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + + +/** + * Searches a list of imports for a specified class. + */ +public abstract class ClassResolver implements Comparable { + + public static ClassResolver createClassResolverFor( String sourcePackage, Import...imports ) { + return createClassResolverFor( sourcePackage, Arrays.asList(imports) ); + } + + public static ClassResolver createClassResolverFor( String sourcePackage, List imports ) { + return new CompositeClassResolver( createClassResolversFor(sourcePackage,imports) ); + } + + + private final int comparisonOrder; + + protected ClassResolver( int comparisonOrder ) { + this.comparisonOrder = comparisonOrder; + } + + /** + * Do ones best to convert classRef into an instance of java.lang.Class. + * + * @return null if no match was found. + */ + public abstract Class resolveClass( String classRef ); + + + public int compareTo( ClassResolver o ) { + return Integer.compare( this.comparisonOrder, o.comparisonOrder ); + } + + + private static Class fetchClass( String fqn ) { + try { + return Class.forName( fqn ); + } catch ( ClassNotFoundException e ) { + return null; + } + } + + + private static List createClassResolversFor( String sourcePackage, List imports ) { + List classResolvers = new ArrayList<>(); + + classResolvers.add( new FQNResolver() ); + classResolvers.add( new FullPackageResolver(sourcePackage) ); + classResolvers.add( new FullPackageResolver("java.lang") ); + + for ( Import declaredImport : imports ) { + ClassResolver newResolver = createClassResolverFor( declaredImport ); + + if ( newResolver != null ) { + classResolvers.add( newResolver ); + } + } + + // ensure that the fully qualified comparators appear ahead of the 'all in package' imports + Collections.sort(classResolvers); + + return classResolvers; + } + + private static ClassResolver createClassResolverFor( Import declaredImport ) { + if ( declaredImport.getStaticMember() == null ) { + if ( "*".equals(declaredImport.getClassName()) ) { + return new FullPackageResolver(declaredImport.getPkg()); + } else { + Class importedClass = fetchClass( declaredImport.getFullyQualifiedClass() ); + + if ( importedClass != null ) { + return new ExactClassResolver(declaredImport.getClassName(), importedClass); + } + } + } + + return null; + } + + public String resolveRef( String classRef ) { + Class resolvedClass = resolveClass( classRef ); + + return resolvedClass == null ? classRef : resolvedClass.getName(); + } + + + /** + * Given a list of ClassResolvers, return the result of the first one that returns a non-null + * result. + */ + private static class CompositeClassResolver extends ClassResolver { + private List classResolvers; + + private CompositeClassResolver( List classResolvers ) { + super(0); + + this.classResolvers = classResolvers; + } + + public Class resolveClass( String classRef ) { + for ( ClassResolver resolver : classResolvers ) { + Class resolvedClass = resolver.resolveClass( classRef ); + + if ( resolvedClass != null ) { + return resolvedClass; + } + } + + return null; + } + } + + /** + * Resolves absolute class refs (eg x.y.Z). + */ + private static class FQNResolver extends ClassResolver { + public FQNResolver() { + super(1); + } + + public Class resolveClass( String ref ) { + return fetchClass(ref); + } + } + + /** + * Given 'import x.y.Z', match relative class refs that equal 'Z' and return the class + * for x.y.Z. + */ + private static class ExactClassResolver extends ClassResolver { + private String targetRelativeRef; + private Class explicitClass; + + private ExactClassResolver( String targetRelativeRef, Class matchingClass ) { + super(2); + + this.targetRelativeRef = targetRelativeRef; + this.explicitClass = matchingClass; + } + + public Class resolveClass( String ref ) { + if ( !Objects.equals(targetRelativeRef, ref) ) { + return null; + } + + return explicitClass; + } + } + /** + * Given 'import x.y.*', attempt to load any class ref as though it is within package x.y. + */ + private static class FullPackageResolver extends ClassResolver { + + private String basePackage; + + private FullPackageResolver( String basePackage ) { + super(3); + + this.basePackage = basePackage; + } + public Class resolveClass( String ref ) { + return fetchClass(basePackage + "." + ref); + } + + } +} diff --git a/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/Comment.java b/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/Comment.java index 96fe26e..b155cee 100755 --- a/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/Comment.java +++ b/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/Comment.java @@ -3,6 +3,7 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.Objects; import static com.github.therapi.runtimejavadoc.internal.RuntimeJavadocHelper.unmodifiableDefensiveCopy; @@ -40,6 +41,21 @@ public Iterator iterator() { return elements.iterator(); } + @Override + public boolean equals( Object o ) { + if ( this == o ) + return true; + if ( o == null || getClass() != o.getClass() ) + return false; + Comment that = (Comment) o; + return Objects.equals( elements, that.elements ); + } + + @Override + public int hashCode() { + return Objects.hash( elements ); + } + @Override public String toString() { return new CommentFormatter().format(this); diff --git a/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/Import.java b/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/Import.java new file mode 100644 index 0000000..7e7d74c --- /dev/null +++ b/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/Import.java @@ -0,0 +1,99 @@ +package com.github.therapi.runtimejavadoc; + +import java.util.Objects; + + +public final class Import { + /** + * The imported package, for example com.bar.foo. A null value means that the root package. + */ + private String pkg; + + /** + * The name of the imported class, for example Bar. If all classes in the package are to be + * imported, then className will be set to '*'. className will never be set to null. + */ + private String className; + + /** + * The static field or method from the class. Will be set to null when there no static + * members have been imported, and '*' when all static members of the class are to be imported. + */ + private String staticMember; + + + public Import( String pkg, String className ) { + this(pkg,className,null); + } + + public Import( String pkg, String className, String staticMember ) { + this.pkg = pkg; + this.className = className; + this.staticMember = staticMember; + } + + public String getPkg() { + return pkg; + } + + public String getClassName() { + return className; + } + + public String getStaticMember() { + return staticMember; + } + + public boolean importsAllClassesWithinPackage() { + return "*".equals( className ); + } + + public boolean importsAllStaticMembersWithingClass() { + return "*".equals( staticMember ); + } + + public String getFullyQualifiedClass() { + if ( importsAllClassesWithinPackage() ) { + return null; + } + + return pkg == null ? className : pkg + '.' + className; + } + + + public boolean equals( Object o ) { + if ( this == o ) + return true; + if ( o == null || getClass() != o.getClass() ) + return false; + Import anImport = (Import) o; + return Objects.equals( pkg, anImport.pkg ) && + Objects.equals( className, anImport.className ) && + Objects.equals( staticMember, anImport.staticMember ); + } + + public int hashCode() { + return Objects.hash( pkg, className, staticMember ); + } +//TODO test other methods +//TODO wire up to AST parser +//TODO write to json output + public String toString() { + StringBuilder buf = new StringBuilder(); + + if ( pkg == null ) { + buf.append( className ); + } else { + buf.append( pkg ); + buf.append( '.' ); + buf.append( className ); + } + + if ( staticMember != null ) { + buf.append( '.' ); + buf.append( staticMember ); + } + + return buf.toString(); + } +} diff --git a/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/SeeAlsoJavadoc.java b/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/SeeAlsoJavadoc.java index be8b369..acf60d4 100644 --- a/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/SeeAlsoJavadoc.java +++ b/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/SeeAlsoJavadoc.java @@ -1,5 +1,8 @@ package com.github.therapi.runtimejavadoc; +import java.util.Objects; + + /** * Represents a {@code @see} tag on a class or method. */ @@ -48,7 +51,33 @@ public HtmlLink getHtmlLink() { public Link getLink() { return link; } - + + public boolean equals( Object o ) { + if ( this == o ) + return true; + if ( o == null || getClass() != o.getClass() ) + return false; + SeeAlsoJavadoc that = (SeeAlsoJavadoc) o; + return seeAlsoType == that.seeAlsoType && + Objects.equals( stringLiteral, that.stringLiteral ) && + Objects.equals( htmlLink, that.htmlLink ) && + Objects.equals( link, that.link ); + } + + public int hashCode() { + return Objects.hash( seeAlsoType, stringLiteral, htmlLink, link ); + } + + @Override + public String toString() { + return "SeeAlsoJavadoc{" + + "seeAlsoType=" + seeAlsoType + + ", stringLiteral='" + stringLiteral + '\'' + + ", htmlLink=" + htmlLink + + ", link=" + link + + '}'; + } + public static class HtmlLink { private final String text; private final String link; @@ -65,5 +94,21 @@ public String getText() { public String getLink() { return link; } + + @Override + public boolean equals( Object o ) { + if ( this == o ) + return true; + if ( o == null || getClass() != o.getClass() ) + return false; + HtmlLink htmlLink = (HtmlLink) o; + return Objects.equals( text, htmlLink.text ) && + Objects.equals( link, htmlLink.link ); + } + + @Override + public int hashCode() { + return Objects.hash( text, link ); + } } -} \ No newline at end of file +} diff --git a/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/ThrowsJavadoc.java b/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/ThrowsJavadoc.java index 67a0970..ceb5bdd 100644 --- a/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/ThrowsJavadoc.java +++ b/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/ThrowsJavadoc.java @@ -1,5 +1,8 @@ package com.github.therapi.runtimejavadoc; +import java.util.Objects; + + public class ThrowsJavadoc { private final String name; private final Comment comment; @@ -16,4 +19,28 @@ public String getName() { public Comment getComment() { return comment; } + + @Override + public boolean equals( Object o ) { + if ( this == o ) + return true; + if ( o == null || getClass() != o.getClass() ) + return false; + ThrowsJavadoc that = (ThrowsJavadoc) o; + return Objects.equals( name, that.name ) && + Objects.equals( comment, that.comment ); + } + + @Override + public int hashCode() { + return Objects.hash( name, comment ); + } + + @Override + public String toString() { + return "ThrowsJavadoc{" + + "name='" + name + '\'' + + ", comment=" + comment + + '}'; + } } diff --git a/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/internal/JsonJavadocReader.java b/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/internal/JsonJavadocReader.java index 5fa68bf..1beda6f 100644 --- a/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/internal/JsonJavadocReader.java +++ b/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/internal/JsonJavadocReader.java @@ -4,8 +4,11 @@ import com.eclipsesource.json.JsonObject; import com.eclipsesource.json.JsonValue; import com.github.therapi.runtimejavadoc.ClassJavadoc; +import com.github.therapi.runtimejavadoc.ClassResolver; import com.github.therapi.runtimejavadoc.FieldJavadoc; +import com.github.therapi.runtimejavadoc.Import; import com.github.therapi.runtimejavadoc.MethodJavadoc; +import com.github.therapi.runtimejavadoc.internal.parser.ImportParser; import com.github.therapi.runtimejavadoc.internal.parser.JavadocParser; import java.util.ArrayList; @@ -17,56 +20,94 @@ import static com.github.therapi.runtimejavadoc.internal.RuntimeJavadocHelper.elementNameFieldName; import static com.github.therapi.runtimejavadoc.internal.RuntimeJavadocHelper.enumConstantsFieldName; import static com.github.therapi.runtimejavadoc.internal.RuntimeJavadocHelper.fieldsFieldName; +import static com.github.therapi.runtimejavadoc.internal.RuntimeJavadocHelper.importsFieldName; import static com.github.therapi.runtimejavadoc.internal.RuntimeJavadocHelper.methodsFieldName; import static com.github.therapi.runtimejavadoc.internal.RuntimeJavadocHelper.paramTypesFieldName; public class JsonJavadocReader { public static ClassJavadoc readClassJavadoc(String qualifiedClassName, JsonObject json) { - String className = qualifiedClassName.replace("$", "."); - List fields = readFieldDocs(qualifiedClassName, json.get(fieldsFieldName())); - List enumConstants = readFieldDocs(qualifiedClassName, json.get(enumConstantsFieldName())); - List methods = readMethodDocs(qualifiedClassName, json.get(methodsFieldName())); - List constructors = readMethodDocs(qualifiedClassName, json.get(constructorsFieldName())); - String classJavadocString = json.getString(elementDocFieldName(), null); - return JavadocParser.parseClassJavadoc(className, classJavadocString, fields, enumConstants, methods, constructors); + String className = qualifiedClassName.replace("$", "."); + String packageRef = extractPackageFrom(qualifiedClassName); + List imports = readImports(json.get(importsFieldName())); + ClassResolver classResolver = ClassResolver.createClassResolverFor( packageRef, imports ); + List fields = readFieldDocs(qualifiedClassName, classResolver, json.get(fieldsFieldName())); + List enumConstants = readFieldDocs(qualifiedClassName, classResolver, json.get(enumConstantsFieldName())); + List methods = readMethodDocs(qualifiedClassName, classResolver, json.get(methodsFieldName())); + List constructors = readMethodDocs(qualifiedClassName, classResolver, json.get(constructorsFieldName())); + String classJavadocString = json.getString(elementDocFieldName(), null); + + return JavadocParser.parseClassJavadoc(className, classResolver, classJavadocString, fields, enumConstants, methods, constructors); + } + + private static String extractPackageFrom( String qualifiedClassName ) { + int i = qualifiedClassName.lastIndexOf( '.' ); + if ( i < 0 ) { + return null; + } + + return qualifiedClassName.substring( 0, i ); + } + + private static List readImports( JsonValue jsonValue ) { + if (jsonValue == null) { + // old versions might not have this JSON field + return Collections.emptyList(); + } + + JsonArray fieldsArray = jsonValue.asArray(); + List imports = new ArrayList<>(fieldsArray.size()); + + for (JsonValue fieldValue : fieldsArray) { + Import parsedImport = ImportParser.parseImport( fieldValue.asString() ); + + if ( parsedImport != null ) { + imports.add( parsedImport ); + } + } + + return imports; } - private static List readFieldDocs(String owningClass, JsonValue fieldsValue) { + private static List readFieldDocs(String owningClass, ClassResolver classResolver, JsonValue fieldsValue) { if (fieldsValue == null) { // old versions might not have this JSON field return Collections.emptyList(); } - JsonArray fieldsArray = fieldsValue.asArray(); - List fields = new ArrayList<>(fieldsArray.size()); + + JsonArray fieldsArray = fieldsValue.asArray(); + List fields = new ArrayList<>(fieldsArray.size()); + for (JsonValue fieldValue : fieldsArray) { - fields.add(readFieldDoc(owningClass, fieldValue)); + fields.add(readFieldDoc(owningClass, classResolver, fieldValue)); } + return fields; } - private static FieldJavadoc readFieldDoc(String owningClass, JsonValue fieldValue) { - JsonObject field = fieldValue.asObject(); - String fieldName = field.getString(elementNameFieldName(), null); - String fieldDoc = field.getString(elementDocFieldName(), null); - return JavadocParser.parseFieldJavadoc(owningClass, fieldName, fieldDoc); + private static FieldJavadoc readFieldDoc(String owningClass, ClassResolver classResolver, JsonValue fieldValue) { + JsonObject field = fieldValue.asObject(); + String fieldName = field.getString(elementNameFieldName(), null); + String fieldDoc = field.getString(elementDocFieldName(), null); + + return JavadocParser.parseFieldJavadoc(owningClass, classResolver, fieldName, fieldDoc); } - private static List readMethodDocs(String owningClass, JsonValue methodsValue) { + private static List readMethodDocs(String owningClass, ClassResolver classResolver, JsonValue methodsValue) { JsonArray methodArray = methodsValue.asArray(); List methods = new ArrayList<>(methodArray.size()); for (JsonValue methodValue : methodArray) { - methods.add(readMethodDoc(owningClass, methodValue)); + methods.add(readMethodDoc(owningClass, classResolver, methodValue)); } return methods; } - private static MethodJavadoc readMethodDoc(String owningClass, JsonValue methodValue) { + private static MethodJavadoc readMethodDoc(String owningClass, ClassResolver classResolver, JsonValue methodValue) { JsonObject method = methodValue.asObject(); String methodName = method.getString(elementNameFieldName(), null); List paramTypes = readParamTypes(method.get(paramTypesFieldName())); String methodDoc = method.getString(elementDocFieldName(), null); - return JavadocParser.parseMethodJavadoc(owningClass, methodName, paramTypes, methodDoc); + return JavadocParser.parseMethodJavadoc(owningClass, classResolver, methodName, paramTypes, methodDoc); } private static List readParamTypes(JsonValue paramTypesValue) { diff --git a/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/internal/RuntimeJavadocHelper.java b/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/internal/RuntimeJavadocHelper.java index 68dcfb0..63c5786 100644 --- a/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/internal/RuntimeJavadocHelper.java +++ b/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/internal/RuntimeJavadocHelper.java @@ -70,4 +70,8 @@ public static String elementNameFieldName() { public static String elementDocFieldName() { return "doc"; } + + public static String importsFieldName() { + return "imports"; + } } diff --git a/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/internal/parser/CommentParser.java b/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/internal/parser/CommentParser.java index 10a6204..f4b03fd 100644 --- a/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/internal/parser/CommentParser.java +++ b/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/internal/parser/CommentParser.java @@ -1,5 +1,6 @@ package com.github.therapi.runtimejavadoc.internal.parser; +import com.github.therapi.runtimejavadoc.ClassResolver; import com.github.therapi.runtimejavadoc.Comment; import com.github.therapi.runtimejavadoc.CommentElement; import com.github.therapi.runtimejavadoc.CommentText; @@ -24,11 +25,11 @@ class CommentParser { // https://regex101.com/r/KhEo62/4 private static final Pattern valuePattern = compile("^(?:(?[\\w.]+)#)?#?(?\\w+)$"); - static Comment parse(String owningClass, String commentText) { - return isBlank(commentText) ? Comment.createEmpty() : new Comment(parseElements(owningClass, commentText.trim())); + static Comment parse(String owningClass, ClassResolver classResolver, String commentText) { + return isBlank(commentText) ? Comment.createEmpty() : new Comment(parseElements(owningClass, classResolver, commentText.trim())); } - private static List parseElements(String owningClass, String commentText) { + private static List parseElements(String owningClass, ClassResolver classResolver, String commentText) { Matcher matcher = inlineTag.matcher(commentText); List elements = new ArrayList<>(); int pos = 0; @@ -37,7 +38,7 @@ private static List parseElements(String owningClass, String com if (start > pos) { elements.add(new CommentText(commentText.substring(pos, start))); } - CommentElement elt = createTagElement(owningClass, matcher.group(1), matcher.group(2)); + CommentElement elt = createTagElement(owningClass, classResolver, matcher.group(1), matcher.group(2)); if (elt != null) { elements.add(elt); } @@ -53,11 +54,11 @@ private static List parseElements(String owningClass, String com /** * @return null if tag is malformed */ - private static CommentElement createTagElement(String owningClass, String name, String value) { + private static CommentElement createTagElement(String owningClass, ClassResolver classResolver, String name, String value) { if ("link".equals(name)) { - return createLinkElement(owningClass, value); + return createLinkElement(owningClass, classResolver, value); } else if ("value".equals(name)) { - return createValueElement(owningClass, value); + return createValueElement(owningClass, classResolver, value); } else { return new InlineTag(name, value); } @@ -66,7 +67,7 @@ private static CommentElement createTagElement(String owningClass, String name, /** * @return null if tag is malformed */ - private static InlineValue createValueElement(String owningClass, String value) { + private static InlineValue createValueElement(String owningClass, ClassResolver classResolver, String value) { if (value == null || value.trim().isEmpty()) { return new InlineValue(new Value(null, null)); } @@ -79,16 +80,16 @@ private static InlineValue createValueElement(String owningClass, String value) String classRef = linkMatcher.group("classname"); String memberRef = linkMatcher.group("member"); - String effectiveClassName = classRef == null ? owningClass : classRef; + String effectiveClassName = classRef == null ? owningClass : classResolver.resolveRef(classRef); return new InlineValue(new Value(effectiveClassName, memberRef)); } - private static InlineLink createLinkElement(String owningClass, String value) { - Link javadocLink = LinkParser.createLinkElement(owningClass, value); + private static InlineLink createLinkElement(String owningClass, ClassResolver classResolver, String value) { + Link javadocLink = LinkParser.createLinkElement(owningClass, classResolver, value); if (javadocLink == null) { // malformed link return null; } return new InlineLink(javadocLink); } -} \ No newline at end of file +} diff --git a/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/internal/parser/ImportParser.java b/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/internal/parser/ImportParser.java new file mode 100644 index 0000000..7121158 --- /dev/null +++ b/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/internal/parser/ImportParser.java @@ -0,0 +1,58 @@ +package com.github.therapi.runtimejavadoc.internal.parser; + +import com.github.therapi.runtimejavadoc.Import; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + +public class ImportParser { + private static final Pattern IMPORT_REGEXP = Pattern.compile( "import (?static )?(?[^;]+);" ); + + public static Import parseImport( String importLine ) { + Matcher matcher = IMPORT_REGEXP.matcher( importLine ); + if ( !matcher.matches() ) { + return null; + } + + boolean isStatic = matcher.group("static") != null; + RefParser refParser = new RefParser(matcher.group("ref")); + + if ( isStatic ) { + String member = refParser.pop(); + String className = refParser.pop(); + String packageRef = refParser.getRemaining(); + + return new Import( packageRef, className, member ); + } else { + String className = refParser.pop(); + String packageRef = refParser.getRemaining(); + + return new Import( packageRef, className ); + } + } + + private static class RefParser { + private String remaining; + + public RefParser( String ref ) { + this.remaining = ref; + } + + public String pop() { + int i = remaining.lastIndexOf( '.' ); + if ( i < 0 || i == remaining.length() - 1 ) { + return null; + } + + String next = remaining.substring( i+1 ); + remaining = remaining.substring( 0, i ); + + return next; + } + + public String getRemaining() { + return remaining; + } + } +} diff --git a/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/internal/parser/JavadocParser.java b/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/internal/parser/JavadocParser.java index 9edfe3a..70ab2e7 100644 --- a/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/internal/parser/JavadocParser.java +++ b/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/internal/parser/JavadocParser.java @@ -1,6 +1,7 @@ package com.github.therapi.runtimejavadoc.internal.parser; import com.github.therapi.runtimejavadoc.ClassJavadoc; +import com.github.therapi.runtimejavadoc.ClassResolver; import com.github.therapi.runtimejavadoc.Comment; import com.github.therapi.runtimejavadoc.FieldJavadoc; import com.github.therapi.runtimejavadoc.MethodJavadoc; @@ -19,21 +20,21 @@ public class JavadocParser { private static final Pattern whitespace = Pattern.compile("\\s"); - public static ClassJavadoc parseClassJavadoc(String className, String javadoc, List fields, + public static ClassJavadoc parseClassJavadoc(String className, ClassResolver classResolver, String javadoc, List fields, List enumConstants, List methods, List constructors) { ParsedJavadoc parsed = parse(javadoc); List otherDocs = new ArrayList<>(); for (BlockTag t : parsed.getBlockTags()) { - otherDocs.add(new OtherJavadoc(t.name, CommentParser.parse(className, t.value))); + otherDocs.add(new OtherJavadoc(t.name, CommentParser.parse(className, classResolver, t.value))); } - return new ClassJavadoc(className, CommentParser.parse(className, parsed.getDescription()), fields, enumConstants, methods, + return new ClassJavadoc(className, CommentParser.parse(className, classResolver, parsed.getDescription()), fields, enumConstants, methods, constructors, otherDocs, new ArrayList()); } - public static FieldJavadoc parseFieldJavadoc(String owningClass, String fieldName, String javadoc) { + public static FieldJavadoc parseFieldJavadoc(String owningClass, ClassResolver classResolver, String fieldName, String javadoc) { ParsedJavadoc parsed = parse(javadoc); List otherDocs = new ArrayList<>(); @@ -41,19 +42,19 @@ public static FieldJavadoc parseFieldJavadoc(String owningClass, String fieldNam for (BlockTag t : parsed.getBlockTags()) { if (t.name.equals("see")) { - SeeAlsoJavadoc seeAlso = SeeAlsoParser.parseSeeAlso(owningClass, t.value); + SeeAlsoJavadoc seeAlso = SeeAlsoParser.parseSeeAlso(owningClass, classResolver, t.value); if (seeAlso != null) { seeAlsoDocs.add(seeAlso); } } else { - otherDocs.add( new OtherJavadoc( t.name, CommentParser.parse( owningClass, t.value ) ) ); + otherDocs.add( new OtherJavadoc( t.name, CommentParser.parse( owningClass, classResolver, t.value ) ) ); } } - return new FieldJavadoc(fieldName, CommentParser.parse(owningClass, parsed.getDescription()), otherDocs, seeAlsoDocs); + return new FieldJavadoc(fieldName, CommentParser.parse(owningClass, classResolver, parsed.getDescription()), otherDocs, seeAlsoDocs); } - public static MethodJavadoc parseMethodJavadoc(String owningClass, String methodName, List paramTypes, String javadoc) { + public static MethodJavadoc parseMethodJavadoc(String owningClass, ClassResolver classResolver, String methodName, List paramTypes, String javadoc) { ParsedJavadoc parsed = parse(javadoc); List otherDocs = new ArrayList<>(); @@ -69,25 +70,25 @@ public static MethodJavadoc parseMethodJavadoc(String owningClass, String method String paramName = paramNameAndComment[0]; String paramComment = paramNameAndComment.length == 1 ? "" :paramNameAndComment[1]; - paramDocs.add(new ParamJavadoc(paramName, CommentParser.parse(owningClass, paramComment))); + paramDocs.add(new ParamJavadoc(paramName, CommentParser.parse(owningClass, classResolver, paramComment))); } else if (t.name.equals("return")) { - returns = CommentParser.parse(owningClass, t.value); + returns = CommentParser.parse(owningClass, classResolver, t.value); } else if (t.name.equals("see")) { - SeeAlsoJavadoc seeAlso = SeeAlsoParser.parseSeeAlso( owningClass, t.value ); + SeeAlsoJavadoc seeAlso = SeeAlsoParser.parseSeeAlso( owningClass, classResolver, t.value ); if ( seeAlso != null ) { seeAlsoDocs.add( seeAlso ); } } else if (t.name.equals("throws") || t.name.equals("exception")) { - ThrowsJavadoc throwsDoc = ThrowsTagParser.parseTag(owningClass, t.value); + ThrowsJavadoc throwsDoc = ThrowsTagParser.parseTag(owningClass, classResolver, t.value); if (throwsDoc != null) { throwsDocs.add( throwsDoc ); } } else { - otherDocs.add(new OtherJavadoc(t.name, CommentParser.parse(owningClass, t.value))); + otherDocs.add(new OtherJavadoc(t.name, CommentParser.parse(owningClass, classResolver, t.value))); } } - return new MethodJavadoc(methodName, paramTypes, CommentParser.parse(owningClass, parsed.getDescription()), paramDocs, + return new MethodJavadoc(methodName, paramTypes, CommentParser.parse(owningClass, classResolver, parsed.getDescription()), paramDocs, throwsDocs, otherDocs, returns, seeAlsoDocs); } diff --git a/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/internal/parser/LinkParser.java b/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/internal/parser/LinkParser.java index 993cd84..db5a62f 100644 --- a/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/internal/parser/LinkParser.java +++ b/therapi-runtime-javadoc/src/main/java/com/github/therapi/runtimejavadoc/internal/parser/LinkParser.java @@ -1,5 +1,6 @@ package com.github.therapi.runtimejavadoc.internal.parser; +import com.github.therapi.runtimejavadoc.ClassResolver; import com.github.therapi.runtimejavadoc.InlineLink; import com.github.therapi.runtimejavadoc.Link; @@ -16,18 +17,20 @@ public class LinkParser { // https://regex101.com/r/3DoNfK/2 private static final Pattern linkPattern = compile("^(?[\\w.]+)?(?:#(?\\w+))?(?:\\((?.*)\\))?(?:\\s(?