Skip to content
This repository has been archived by the owner on Dec 19, 2023. It is now read-only.

Commit

Permalink
#432 Adds support for Java 14 records (#437)
Browse files Browse the repository at this point in the history
  • Loading branch information
fdutton authored Apr 19, 2021
1 parent 9333bcc commit 8e8a041
Show file tree
Hide file tree
Showing 6 changed files with 287 additions and 47 deletions.
94 changes: 70 additions & 24 deletions spring-auto-restdocs-json-doclet-jdk9/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,24 @@
<description>Doclet exporting JavaDoc to JSON for Spring Auto REST Docs</description>

<properties>
<maven.javadoc.skip>true</maven.javadoc.skip>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<!--
| Jackson Databind 2.12.0 has a breaking API change that is corrected in 2.12.1
| If you are using Java 14 records, you must use version 2.12.1 or later.
| Otherwise, you can use any version other than 2.12.0.
|
| NOTE:
| This version range may be removed once the parent project specifies a range
| greater than 2.12.0.
+-->
<version>[2.11.2,2.12.0),[2.12.1,2.99)</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
Expand Down Expand Up @@ -54,35 +65,70 @@
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<release>9</release>
</configuration>
</plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<extensions>true</extensions>
<executions>
<execution>
<id>generate-javadoc-json</id>
<phase>compile</phase>
<goals>
<goal>test-javadoc-no-fork</goal>
</goals>
<configuration>
<doclet>capital.scalable.restdocs.jsondoclet.ExtractDocumentationAsJsonDoclet</doclet>
<docletArtifact>
<groupId>capital.scalable</groupId>
<artifactId>spring-auto-restdocs-json-doclet-jdk9</artifactId>
<version>${project.parent.version}</version>
</docletArtifact>
<reportOutputDirectory>${project.build.directory}</reportOutputDirectory>
<useStandardDocletOptions>false</useStandardDocletOptions>
<show>package</show>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

<profiles>

<profile>
<id>java14</id>
<activation>
<jdk>[14,17)</jdk>
</activation>
<build>

<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>
--enable-preview
--illegal-access=permit
</argLine>
<!-- Force alphabetical order to have a reproducible build -->
<runOrder>alphabetical</runOrder>
</configuration>
</plugin>
</plugins>
</pluginManagement>

<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<id>test-compile-java-14</id>
<phase>test-compile</phase>
<goals>
<goal>testCompile</goal>
</goals>
<configuration>
<release>${java.specification.version}</release>
<multiReleaseOutput>true</multiReleaseOutput>
<compilerArgs>
<!-- Enables use of record and text blocks -->
<compilerArg>--enable-preview</compilerArg>
</compilerArgs>
<compileSourceRoots>
<compileSourceRoot>${project.basedir}/src/test/java14</compileSourceRoot>
</compileSourceRoots>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>

</profiles>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,17 @@
*/
package capital.scalable.restdocs.jsondoclet;

import static java.util.Optional.ofNullable;
import static capital.scalable.restdocs.jsondoclet.DocletUtils.cleanupDocComment;

import com.sun.source.doctree.DocCommentTree;
import com.sun.source.doctree.ParamTree;

import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import jdk.javadoc.doclet.DocletEnvironment;

Expand All @@ -42,14 +47,41 @@ public static ClassDocumentation fromClassDoc(DocletEnvironment docEnv,
ClassDocumentation cd = new ClassDocumentation();
cd.setComment(cleanupDocComment(docEnv.getElementUtils().getDocComment(element)));

element.getEnclosedElements().forEach(fieldOrMethod -> {
if (fieldOrMethod.getKind().equals(ElementKind.FIELD)) {
cd.addField(docEnv, fieldOrMethod);
} else if (fieldOrMethod.getKind().equals(ElementKind.METHOD)
|| fieldOrMethod.getKind().equals(ElementKind.CONSTRUCTOR)) {
cd.addMethod(docEnv, fieldOrMethod);
}
});
if ("RECORD".equals(element.getKind().name())) {
ofNullable(docEnv.getDocTrees().getDocCommentTree(element))
.stream()
.map(DocCommentTree::getBlockTags)
.flatMap(List::stream)
.filter(ParamTree.class::isInstance)
.map(ParamTree.class::cast)
.filter(p -> !p.isTypeParameter())
.forEach(p -> {
String name = p.getName().getName().toString();
String desc = p.getDescription()
.stream()
.map(Object::toString)
.collect(Collectors.joining(" "))
;

cd.fields.put(name, FieldDocumentation.fromString(desc));
})
;
} else {
element.getEnclosedElements().forEach(fieldOrMethod -> {
switch (fieldOrMethod.getKind()) {
case FIELD:
cd.addField(docEnv, fieldOrMethod);
break;
case METHOD:
case CONSTRUCTOR:
cd.addMethod(docEnv, fieldOrMethod);
break;
default:
// Ignored
break;
}
});
}

return cd;
}
Expand All @@ -67,4 +99,5 @@ private void addMethod(DocletEnvironment docEnv, Element element) {
this.methods.put(element.getSimpleName().toString(),
MethodDocumentation.fromMethodDoc(docEnv, element));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ public class FieldDocumentation {
private String comment;
private final Map<String, String> tags = new HashMap<>();

private FieldDocumentation(String comment) {
this.comment = comment;
}

private void addTag(DocTree tag) {
if (tag instanceof BlockTagTree) {
tags.put(
Expand All @@ -47,13 +51,17 @@ private void addTag(DocTree tag) {

public static FieldDocumentation fromFieldDoc(DocletEnvironment docEnv,
Element fieldElement) {
FieldDocumentation fd = new FieldDocumentation();
fd.comment = cleanupDocComment(docEnv.getElementUtils().getDocComment(fieldElement));
FieldDocumentation fd = fromString(
cleanupDocComment(docEnv.getElementUtils().getDocComment(fieldElement)));

Optional.ofNullable(docEnv.getDocTrees().getDocCommentTree(fieldElement))
.ifPresent(docCommentTree -> docCommentTree.getBlockTags()
.forEach(tag -> fd.addTag(tag)));

return fd;
}

public static FieldDocumentation fromString(String comment) {
return new FieldDocumentation(comment);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,32 +19,89 @@
*/
package capital.scalable.restdocs.jsondoclet;

import static java.nio.charset.StandardCharsets.UTF_8;

import java.io.File;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.List;

import javax.tools.DocumentationTool.DocumentationTask;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.ToolProvider;
import static org.junit.Assert.assertTrue;

import static java.nio.charset.StandardCharsets.UTF_8;

import org.apache.commons.io.IOUtils;
import org.json.JSONException;
import org.junit.Before;
import org.junit.Test;
import org.skyscreamer.jsonassert.JSONAssert;

public class ExtractDocumentationAsJsonDocletTest {

private static final Path SRC_PATH = FileSystems.getDefault().getPath("src/test/resources").toAbsolutePath();
private static final Path TGT_PATH = FileSystems.getDefault().getPath("target/test/generated-javadoc-json").toAbsolutePath();

private static final String JSON_PATH =
"capital/scalable/restdocs/jsondoclet/DocumentedClass.json";

/**
* The test requires that the Doclet is executed before. This is ensured by
* the Maven configuration, but not when the test is executed on its own.
*/
private static final List<String> args = List.of(
"--release", "9",
"-private",
"-d", TGT_PATH.toString()
);

@Before
public void setup() throws IOException {
Files.createDirectories(TGT_PATH);
}

@Test
public void testDocumentedClass() throws IOException, JSONException {
String generated = IOUtils.toString(
new FileInputStream(new File("target/generated-javadoc-json/" + JSON_PATH)), UTF_8);
String expected = IOUtils.toString(
this.getClass().getClassLoader().getResourceAsStream(JSON_PATH), UTF_8);
JSONAssert.assertEquals(expected, generated, false);
Path source = SRC_PATH.resolve("capital/scalable/restdocs/jsondoclet/DocumentedClass.java");
Iterable<JavaFileObject> compilationUnits = compilationUnits(source);

DocumentationTask task = ToolProvider.getSystemDocumentationTool().getTask(null, null, null, ExtractDocumentationAsJsonDoclet.class, args, compilationUnits);

boolean result = task.call();
assertTrue(result);

try (
InputStream expectedStream = new FileInputStream(SRC_PATH.resolve(JSON_PATH).toFile());
InputStream generatedStream = new FileInputStream(TGT_PATH.resolve(JSON_PATH).toFile());
) {
String expected = IOUtils.toString(expectedStream, UTF_8);
String generated = IOUtils.toString(generatedStream, UTF_8);
JSONAssert.assertEquals(expected, generated, false);
}
}

Iterable<JavaFileObject> compilationUnits(Path path) throws IOException {
try (InputStream in = new FileInputStream(path.toFile())) {
String content = IOUtils.toString(in, UTF_8);
return List.of(new InMemoryJavaFileObject(path.toUri(), content));
}
}

static class InMemoryJavaFileObject extends SimpleJavaFileObject {

private final String source;

public InMemoryJavaFileObject(URI uri, String sourceCode) {
super(uri, Kind.SOURCE);
this.source = sourceCode;
}

@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return source;
}

}

}
Loading

0 comments on commit 8e8a041

Please sign in to comment.