jdk-24/test/langtools/jdk/javadoc/doclet/testSingletonLists/TestSingletonLists.java
2024-01-18 19:06:26 +00:00

322 lines
11 KiB
Java

/*
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8219998 8221991
* @summary Eliminate inherently singleton lists
* @library /tools/lib ../../lib
* @modules jdk.javadoc/jdk.javadoc.internal.tool
* @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.main
* jdk.javadoc/jdk.javadoc.internal.api
* jdk.javadoc/jdk.javadoc.internal.tool
* @build toolbox.ToolBox toolbox.JavacTask javadoc.tester.*
* @run main TestSingletonLists
*/
import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.TreeMap;
import java.util.function.Function;
import javadoc.tester.HtmlChecker;
import javadoc.tester.JavadocTester;
import toolbox.ModuleBuilder;
import toolbox.ToolBox;
public class TestSingletonLists extends JavadocTester {
public static void main(String... args) throws Exception {
var tester = new TestSingletonLists();
tester.runTests();
}
enum Index { SINGLE, SPLIT };
enum Source { PACKAGES, MODULES };
final ToolBox tb = new ToolBox();
public void runTests() throws Exception {
for (Source s : Source.values()) {
Path src = genSource(s);
for (Index i : Index.values()) {
List<String> args = new ArrayList<>();
args.add("-d");
args.add(String.format("out-%s-%s", s, i));
args.add("-use");
if (s != Source.MODULES) {
args.add("-linksource"); // broken, with modules: JDK-8219060
}
if (i == Index.SPLIT) {
args.add("-splitIndex");
}
if (s == Source.PACKAGES) {
args.add("-sourcepath");
args.add(src.toString());
args.add("p1");
args.add("p2");
args.add("p3");
} else {
args.add("--module-source-path");
args.add(src.toString());
args.add("--module");
args.add("mA,mB,mC");
}
javadoc(args.toArray(new String[args.size()]));
checkExit(Exit.OK);
checkLists();
}
}
printSummary();
}
Path genSource(Source s) throws IOException {
Path src = Path.of("src-" + s);
switch (s) {
case PACKAGES:
for (String p : new String[] { "1", "2", "3" }) {
tb.writeJavaFiles(src, genClasses("p" + p));
}
break;
case MODULES:
for (String m : new String[] { "A", "B", "C"}) {
ModuleBuilder mb = new ModuleBuilder(tb, "m" + m);
for (String p : new String[] { "1", "2", "3" } ) {
mb.exports("p" + m + p);
mb.classes(genClasses("p" + m + p));
}
mb.write(src);
}
break;
}
return src;
}
String[] genClasses(String pkg) {
List<String> list = new ArrayList<>();
list.add("package " + pkg + ";");
for (int i = 0; i < 3; i++) {
list.add(genClass(pkg, i));
list.add(genAnno(pkg, i));
list.add(genEnum(pkg, i));
}
return list.toArray(new String[list.size()]);
}
String genClass(String pkg, int index) {
String cn = (pkg + "c" + index).toUpperCase();
StringBuilder sb = new StringBuilder();
int pkgIndex = Character.getNumericValue(pkg.charAt(pkg.length()-1));
String pkgdependency = pkg.substring(0, pkg.length()-1) + (pkgIndex == 3 ? 1 : pkgIndex + 1);
String enumClassName = pkgdependency.toUpperCase() + "E" + index;
sb.append("package ").append(pkg).append(";\n")
.append("import " + pkgdependency + ".*;\n")
.append("public class ").append(cn).append(" {\n");
// fields
for (int f = 0; f < 3; f++) {
sb.append("public int f").append(f).append(";\n");
}
// constructors
for (int c = 0; c < 3; c++) {
sb.append("public ").append(cn).append("(");
for (int i = 0; i < c; i++) {
sb.append(i == 0 ? "" : ", ").append("int i").append(i);
}
sb.append(") { }\n");
}
// methods
for (int m = 0; m < 3; m++) {
sb.append("public void m").append(m).append("() { }\n");
}
sb.append("public void n(").append(enumClassName).append(" e){}");
sb.append("}\n");
return sb.toString();
}
String genAnno(String pkg, int index) {
String an = (pkg + "a" + index).toUpperCase();
StringBuilder sb = new StringBuilder();
sb.append("package ").append(pkg).append(";\n")
.append("public @interface ").append(an).append(" {\n");
// fields
for (int f = 0; f < 3; f++) {
sb.append("public static final int f").append(f).append(" = 0;\n");
}
// values
for (int v = 0; v < 6; v++) {
sb.append("public int v").append(v).append("()").append(v< 3 ? "" : " default " + v).append(";\n");
}
sb.append("}\n");
return sb.toString();
}
String genEnum(String pkg, int index) {
String en = (pkg + "e" + index).toUpperCase();
StringBuilder sb = new StringBuilder();
sb.append("package ").append(pkg).append(";\n")
.append("public enum ").append(en).append(" {\n");
// enum members
for (int e = 0; e < 3; e++) {
sb.append(e == 0 ? "" : ", ").append("E").append(e);
}
sb.append(";\n");
// fields
for (int f = 0; f < 3; f++) {
sb.append("public int f").append(f).append(";\n");
}
// methods
for (int m = 0; m < 3; m++) {
sb.append("public void m").append(m).append("() { }\n");
}
sb.append("}\n");
return sb.toString();
}
void checkLists() {
checking("Check lists");
ListChecker c = new ListChecker(out, this::readFile);
try {
c.checkDirectory(outputDir);
c.report();
int errors = c.getErrorCount();
if (errors == 0) {
passed("No list errors found");
} else {
failed(errors + " errors found when checking lists");
}
} catch (IOException e) {
failed("exception thrown when reading files: " + e);
}
}
/**
* A class to check the presence of singleton lists.
*/
public class ListChecker extends HtmlChecker {
private int listErrors;
// Ignore "Constant Field Values" @see items for final fields created by javadoc
private int inSeeOrTocList = 0;
private Stack<Map<String,Integer>> counts = new Stack<>();
private String fileName;
private List<String> excludeFiles = List.of(
"overview-tree.html",
"package-summary.html",
"package-tree.html",
"module-summary.html",
"help-doc.html");
ListChecker(PrintStream out, Function<Path,String> fileReader) {
super(out, fileReader);
}
protected int getErrorCount() {
return errors;
}
@Override
public void report() {
if (listErrors == 0) {
out.println("All lists OK");
} else {
out.println(listErrors + " list errors");
}
}
@Override
public void startFile(Path path) {
fileName = path.getFileName().toString();
}
@Override
public void endFile() {
}
@Override
public void docType(String doctype) {
}
@Override
public void startElement(String name, Map<String,String> attrs, boolean selfClosing) {
switch (name) {
case "ul": case "ol": case "dl":
counts.push(new TreeMap<>());
String classAttr = attrs.get("class");
if (classAttr != null && Set.of("tag-list", "toc-list", "sub-nav-list").contains(classAttr)) {
inSeeOrTocList++;
}
break;
case "li": case "dd": case "dt": {
Map<String, Integer> c = counts.peek();
c.put(name, 1 + c.computeIfAbsent(name, n -> 0));
break;
}
}
}
@Override
public void endElement(String name) {
switch (name) {
case "ul": case "ol": {
Map<String,Integer> c = counts.pop();
if (inSeeOrTocList > 0) {
// Ignore "Constant Field Values" @see items for final fields created by javadoc
inSeeOrTocList--;
} else if (!c.containsKey("li")) {
error(currFile, getLineNumber(), "empty list");
listErrors++;
} else if (c.get("li") == 1 && fileName != null && !excludeFiles.contains(fileName)) {
error(currFile, getLineNumber(), "singleton list");
listErrors++;
}
break;
}
case "dl": {
Map<String, Integer> c = counts.pop();
if (c.get("dd") == 0 || c.get("dt") == 0) {
error(currFile, getLineNumber(), "empty list");
listErrors++;
}
/*if (c.get("dd") == 1 || c.get("dt") == 1) {
error(currFile, getLineNumber(), "singleton list");
listErrors++;
}*/
break;
}
}
}
}
}