Skip to content

Commit 31d60b7

Browse files
committed
Split API reports by module and package
1 parent 7030418 commit 31d60b7

7 files changed

+142
-67
lines changed

documentation/documentation.gradle.kts

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ dependencies {
5555
// Pull in all "modular projects" to ensure that they are included
5656
// in reports generated by the ApiReportGenerator.
5757
modularProjects.forEach { apiReport(it) }
58+
apiReport(libs.openTestReporting.tooling.spi)
5859

5960
// Pull in all "mavenized projects" to ensure that they are included
6061
// in the generation of build provenance attestation.

documentation/src/tools/java/org/junit/api/tools/AbstractApiReportWriter.java

+23-6
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,13 @@
1111
package org.junit.api.tools;
1212

1313
import static java.lang.String.format;
14+
import static java.util.stream.Collectors.groupingBy;
15+
import static java.util.stream.Collectors.toList;
1416

1517
import java.io.PrintWriter;
1618
import java.util.List;
1719
import java.util.Set;
20+
import java.util.TreeMap;
1821

1922
import org.apiguardian.api.API.Status;
2023

@@ -49,12 +52,22 @@ public void printDeclarationInfo(PrintWriter out, Set<Status> statuses) {
4952
protected void printDeclarationSection(Set<Status> statuses, Status status, List<Declaration> declarations,
5053
PrintWriter out) {
5154
printDeclarationSectionHeader(statuses, status, declarations, out);
52-
if (!declarations.isEmpty()) {
53-
printDeclarationTableHeader(out);
54-
declarations.forEach(it -> printDeclarationTableRow(it, out));
55-
printDeclarationTableFooter(out);
56-
out.println();
57-
}
55+
declarations.stream() //
56+
.collect(groupingBy(Declaration::moduleName, TreeMap::new, toList())) //
57+
.forEach((moduleName, moduleDeclarations) -> {
58+
out.println(h4("Module " + moduleName));
59+
out.println();
60+
moduleDeclarations.stream() //
61+
.collect(groupingBy(Declaration::packageName, TreeMap::new, toList())) //
62+
.forEach((packageName, packageDeclarations) -> {
63+
out.println(h5("Package " + packageName));
64+
out.println();
65+
printDeclarationTableHeader(out);
66+
packageDeclarations.forEach(it -> printDeclarationTableRow(it, out));
67+
printDeclarationTableFooter(out);
68+
out.println();
69+
});
70+
});
5871
}
5972

6073
protected void printDeclarationSectionHeader(Set<Status> statuses, Status status, List<Declaration> declarations,
@@ -74,6 +87,10 @@ protected void printDeclarationSectionHeader(Set<Status> statuses, Status status
7487

7588
protected abstract String h2(String header);
7689

90+
protected abstract String h4(String header);
91+
92+
protected abstract String h5(String header);
93+
7794
protected abstract String code(String element);
7895

7996
protected abstract String italic(String element);

documentation/src/tools/java/org/junit/api/tools/ApiReportGenerator.java

+52-45
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,16 @@
1010

1111
package org.junit.api.tools;
1212

13+
import static java.nio.charset.StandardCharsets.UTF_8;
1314
import static java.util.stream.Collectors.toCollection;
1415

1516
import java.io.BufferedOutputStream;
17+
import java.io.File;
1618
import java.io.IOException;
1719
import java.io.OutputStream;
1820
import java.io.PrintWriter;
1921
import java.io.UncheckedIOException;
22+
import java.lang.module.ModuleFinder;
2023
import java.nio.file.Files;
2124
import java.nio.file.Path;
2225
import java.util.ArrayList;
@@ -25,6 +28,7 @@
2528
import java.util.EnumSet;
2629
import java.util.List;
2730
import java.util.Map;
31+
import java.util.Set;
2832
import java.util.SortedSet;
2933
import java.util.TreeSet;
3034
import java.util.stream.Stream;
@@ -52,30 +56,30 @@ public static void main(String... args) {
5256
// CAUTION: The output produced by this method is used to
5357
// generate a table in the User Guide.
5458

55-
var reportGenerator = new ApiReportGenerator();
59+
try (var scanResult = scanClasspath()) {
5660

57-
// scan all types below "org.junit" package
58-
var apiReport = reportGenerator.generateReport("org.junit");
59-
60-
// ApiReportWriter reportWriter = new MarkdownApiReportWriter(apiReport);
61-
ApiReportWriter reportWriter = new AsciidocApiReportWriter(apiReport);
62-
// ApiReportWriter reportWriter = new HtmlApiReportWriter(apiReport);
63-
64-
// reportWriter.printReportHeader(new PrintWriter(System.out, true));
65-
66-
// Print report for all Usage enum constants
67-
// reportWriter.printDeclarationInfo(new PrintWriter(System.out, true), EnumSet.allOf(Status.class));
68-
69-
// Print report only for specific Status constants, defaults to only EXPERIMENTAL
70-
parseArgs(args).forEach((status, opener) -> {
71-
try (var stream = opener.openStream()) {
72-
var writer = new PrintWriter(stream == null ? System.out : stream, true);
73-
reportWriter.printDeclarationInfo(writer, EnumSet.of(status));
74-
}
75-
catch (IOException e) {
76-
throw new UncheckedIOException("Failed to write report", e);
77-
}
78-
});
61+
var apiReport = generateReport(scanResult);
62+
63+
// ApiReportWriter reportWriter = new MarkdownApiReportWriter(apiReport);
64+
ApiReportWriter reportWriter = new AsciidocApiReportWriter(apiReport);
65+
// ApiReportWriter reportWriter = new HtmlApiReportWriter(apiReport);
66+
67+
// reportWriter.printReportHeader(new PrintWriter(System.out, true));
68+
69+
// Print report for all Usage enum constants
70+
// reportWriter.printDeclarationInfo(new PrintWriter(System.out, true), EnumSet.allOf(Status.class));
71+
72+
// Print report only for specific Status constants, defaults to only EXPERIMENTAL
73+
parseArgs(args).forEach((status, opener) -> {
74+
try (var stream = opener.openStream()) {
75+
var writer = new PrintWriter(stream == null ? System.out : stream, true, UTF_8);
76+
reportWriter.printDeclarationInfo(writer, EnumSet.of(status));
77+
}
78+
catch (IOException e) {
79+
throw new UncheckedIOException("Failed to write report", e);
80+
}
81+
});
82+
}
7983
}
8084

8185
// -------------------------------------------------------------------------
@@ -102,45 +106,48 @@ private interface StreamOpener {
102106
OutputStream openStream() throws IOException;
103107
}
104108

105-
ApiReport generateReport(String... packages) {
109+
private static ApiReport generateReport(ScanResult scanResult) {
106110
Map<Status, List<Declaration>> declarations = new EnumMap<>(Status.class);
107111
for (var status : Status.values()) {
108112
declarations.put(status, new ArrayList<>());
109113
}
110114

111-
try (var scanResult = scanClasspath(packages)) {
112-
113-
var types = collectTypes(scanResult);
114-
types.stream() //
115-
.map(Declaration.Type::new) //
116-
.forEach(type -> declarations.get(type.status()).add(type));
115+
var types = collectTypes(scanResult);
116+
types.stream() //
117+
.map(Declaration.Type::new) //
118+
.forEach(type -> declarations.get(type.status()).add(type));
117119

118-
collectMethods(scanResult) //
119-
.map(Declaration.Method::new) //
120-
.filter(method -> !declarations.get(method.status()) //
121-
.contains(new Declaration.Type(method.classInfo()))) //
122-
.forEach(method -> {
123-
types.add(method.classInfo());
124-
declarations.get(method.status()).add(method);
125-
});
120+
collectMethods(scanResult) //
121+
.map(Declaration.Method::new) //
122+
.filter(method -> !declarations.get(method.status()) //
123+
.contains(new Declaration.Type(method.classInfo()))) //
124+
.forEach(method -> {
125+
types.add(method.classInfo());
126+
declarations.get(method.status()).add(method);
127+
});
126128

127-
declarations.values().forEach(list -> list.sort(null));
129+
declarations.values().forEach(list -> list.sort(null));
128130

129-
return new ApiReport(types, declarations);
130-
}
131+
return new ApiReport(types, declarations);
131132
}
132133

133-
private static ScanResult scanClasspath(String[] packages) {
134+
private static ScanResult scanClasspath() {
135+
// scan all types below "org.junit" package
134136
var classGraph = new ClassGraph() //
135-
.acceptPackages(packages) //
136-
.rejectPackages("*.shadow.*") //
137+
.acceptPackages("org.junit") //
138+
.rejectPackages("*.shadow.*", "org.opentest4j.*") //
137139
.disableNestedJarScanning() //
138140
.enableClassInfo() //
139141
.enableMethodInfo() //
140142
.enableAnnotationInfo(); //
141143
var apiClasspath = System.getProperty("api.classpath");
142144
if (apiClasspath != null) {
143-
classGraph = classGraph.overrideClasspath(apiClasspath);
145+
var paths = Arrays.stream(apiClasspath.split(File.pathSeparator)).map(Path::of).toArray(Path[]::new);
146+
var bootLayer = ModuleLayer.boot();
147+
var configuration = bootLayer.configuration().resolveAndBind(ModuleFinder.of(), ModuleFinder.of(paths),
148+
Set.of());
149+
var layer = bootLayer.defineModulesWithOneLoader(configuration, ClassLoader.getPlatformClassLoader());
150+
classGraph = classGraph.overrideModuleLayers(layer);
144151
}
145152
return classGraph.scan();
146153
}

documentation/src/tools/java/org/junit/api/tools/AsciidocApiReportWriter.java

+14-4
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
*/
1818
class AsciidocApiReportWriter extends AbstractApiReportWriter {
1919

20-
private static final String ASCIIDOC_FORMAT = "| %-52s | %-" + NAME_COLUMN_WIDTH + "s | %-12s%n";
20+
private static final String ASCIIDOC_FORMAT = "|%-" + NAME_COLUMN_WIDTH + "s | %-12s%n";
2121

2222
AsciidocApiReportWriter(ApiReport apiReport) {
2323
super(apiReport);
@@ -33,6 +33,16 @@ protected String h2(String header) {
3333
return "== " + header;
3434
}
3535

36+
@Override
37+
protected String h4(String header) {
38+
return "[discrete]%n==== %s".formatted(header);
39+
}
40+
41+
@Override
42+
protected String h5(String header) {
43+
return "[discrete]%n===== %s".formatted(header);
44+
}
45+
3646
@Override
3747
protected String code(String element) {
3848
return "`" + element + "`";
@@ -45,16 +55,16 @@ protected String italic(String element) {
4555

4656
@Override
4757
protected void printDeclarationTableHeader(PrintWriter out) {
58+
out.println("[cols=\"99,1\"]");
4859
out.println("|===");
49-
out.printf(ASCIIDOC_FORMAT, "Package Name", "Name", "Since");
60+
out.printf(ASCIIDOC_FORMAT, "Name", "Since");
5061
out.println();
5162
}
5263

5364
@Override
5465
protected void printDeclarationTableRow(Declaration declaration, PrintWriter out) {
5566
out.printf(ASCIIDOC_FORMAT, //
56-
code(declaration.packageName()), //
57-
code(declaration.name()) + " " + italic("(" + declaration.kind() + ")"), //
67+
code(declaration.name().replace(".", ".&ZeroWidthSpace;")) + " " + italic("(" + declaration.kind() + ")"), //
5868
code(declaration.since()) //
5969
);
6070
}

documentation/src/tools/java/org/junit/api/tools/Declaration.java

+26-4
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424

2525
sealed interface Declaration extends Comparable<Declaration> {
2626

27+
String moduleName();
28+
2729
String packageName();
2830

2931
String fullName();
@@ -43,6 +45,11 @@ default int compareTo(Declaration o) {
4345

4446
record Type(ClassInfo classInfo) implements Declaration {
4547

48+
@Override
49+
public String moduleName() {
50+
return classInfo.getModuleRef().getName();
51+
}
52+
4653
@Override
4754
public String packageName() {
4855
return classInfo.getPackageName();
@@ -55,7 +62,8 @@ public String fullName() {
5562

5663
@Override
5764
public String name() {
58-
return getShortClassName(classInfo);
65+
var shortClassName = getShortClassName(classInfo);
66+
return classInfo.isAnnotation() ? "@" + shortClassName : shortClassName;
5967
}
6068

6169
@Override
@@ -86,6 +94,11 @@ private AnnotationParameterValueList getParameterValues() {
8694

8795
record Method(MethodInfo methodInfo) implements Declaration {
8896

97+
@Override
98+
public String moduleName() {
99+
return classInfo().getModuleRef().getName();
100+
}
101+
89102
@Override
90103
public String packageName() {
91104
return classInfo().getPackageName();
@@ -98,14 +111,23 @@ public String fullName() {
98111

99112
@Override
100113
public String name() {
114+
if (classInfo().isAnnotation()) {
115+
return "@%s(%s=...)".formatted(getShortClassName(classInfo()), methodInfo.getName());
116+
}
117+
if (methodInfo.isConstructor()) {
118+
return "%s%s".formatted(getShortClassName(classInfo()), methodParameters());
119+
}
101120
return "%s.%s".formatted(getShortClassName(classInfo()), methodSignature());
102121
}
103122

104123
private String methodSignature() {
105-
var parameters = Arrays.stream(methodInfo.getParameterInfo()) //
124+
return methodInfo.getName() + methodParameters();
125+
}
126+
127+
private String methodParameters() {
128+
return Arrays.stream(methodInfo.getParameterInfo()) //
106129
.map(parameterInfo -> parameterInfo.getTypeSignatureOrTypeDescriptor().toStringWithSimpleNames()) //
107130
.collect(joining(", ", "(", ")"));
108-
return methodInfo.getName() + parameters;
109131
}
110132

111133
@Override
@@ -152,6 +174,6 @@ private static String getShortClassName(ClassInfo classInfo) {
152174
if (typeName.startsWith(packageName + '.')) {
153175
typeName = typeName.substring(packageName.length() + 1);
154176
}
155-
return typeName;
177+
return typeName.replace('$', '.');
156178
}
157179
}

documentation/src/tools/java/org/junit/api/tools/HtmlApiReportWriter.java

+13-4
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
*/
1818
class HtmlApiReportWriter extends AbstractApiReportWriter {
1919

20-
private static final String HTML_HEADER_FORMAT = "\t<tr><th>%s</th><th>%s</th><th>%s</th></tr>%n";
21-
private static final String HTML_ROW_FORMAT = "\t<tr><td>%s</td><td>%s</td><td>%s</td></tr>%n";
20+
private static final String HTML_HEADER_FORMAT = "\t<tr><th>%s</th><th>%s</th></tr>%n";
21+
private static final String HTML_ROW_FORMAT = "\t<tr><td>%s</td><td>%s</td></tr>%n";
2222

2323
HtmlApiReportWriter(ApiReport apiReport) {
2424
super(apiReport);
@@ -34,6 +34,16 @@ protected String h2(String header) {
3434
return "<h2>" + header + "</h2>";
3535
}
3636

37+
@Override
38+
protected String h4(String header) {
39+
return "<h4>" + header + "</h4>";
40+
}
41+
42+
@Override
43+
protected String h5(String header) {
44+
return "<h5>" + header + "</h5>";
45+
}
46+
3747
@Override
3848
protected String code(String element) {
3949
return "<span class='code'>" + element + "</span>";
@@ -52,13 +62,12 @@ protected String paragraph(String element) {
5262
@Override
5363
protected void printDeclarationTableHeader(PrintWriter out) {
5464
out.println("<table>");
55-
out.printf(HTML_HEADER_FORMAT, "Package Name", "Name", "Since");
65+
out.printf(HTML_HEADER_FORMAT, "Name", "Since");
5666
}
5767

5868
@Override
5969
protected void printDeclarationTableRow(Declaration declaration, PrintWriter out) {
6070
out.printf(HTML_ROW_FORMAT, //
61-
code(declaration.packageName()), //
6271
code(declaration.name()) + " " + italic("(" + declaration.kind() + ")"), //
6372
code(declaration.since()) //
6473
);

0 commit comments

Comments
 (0)