8385738: Javadoc does not produce reproducible output due to the snippet ids

Reviewed-by: nbenalla
This commit is contained in:
Vladimir Petko 2026-06-03 06:35:57 +00:00 committed by SendaoYan
parent 39de79eae2
commit 2688bf7305
2 changed files with 98 additions and 5 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2026, 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
@ -49,6 +49,7 @@ import com.sun.source.util.DocTreePath;
import jdk.javadoc.doclet.Taglet;
import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration;
import jdk.javadoc.internal.doclets.formats.html.HtmlDocletWriter;
import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles;
import jdk.javadoc.internal.doclets.formats.html.taglets.snippet.Action;
import jdk.javadoc.internal.doclets.formats.html.taglets.snippet.ParseException;
@ -124,7 +125,8 @@ public class SnippetTaglet extends BaseTaglet {
if (id != null && !id.isBlank()) {
pre.put(HtmlAttr.ID, id);
} else {
pre.put(HtmlAttr.ID, config.htmlIds.forSnippet(element, ids).name());
var set = ids.computeIfAbsent(tagletWriter.htmlWriter, _ -> new HashSet<>());
pre.put(HtmlAttr.ID, config.htmlIds.forSnippet(element, set).name());
}
var code = HtmlTree.CODE()
.addUnchecked(Text.EMPTY); // Make sure the element is always rendered
@ -207,7 +209,7 @@ public class SnippetTaglet extends BaseTaglet {
return snippetContainer.add(pre.add(code));
}
private final Set<String> ids = new HashSet<>();
private final HashMap<HtmlDocletWriter, Set<String>> ids = new HashMap<>();
private static final class BadSnippetException extends Exception {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2026, 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
@ -23,7 +23,7 @@
/*
* @test
* @bug 8266666 8275788 8276964 8299080 8276966
* @bug 8266666 8275788 8276964 8299080 8276966 8385738
* @summary Implementation for snippets
* @library /tools/lib ../../lib
* @modules jdk.compiler/com.sun.tools.javac.api
@ -39,6 +39,7 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
@ -2685,4 +2686,94 @@ public class TestSnippetTag extends SnippetTester {
""");
checkNoCrashes();
}
@Test
public void testSnippetIdCounterResetsPerPage(Path base) throws IOException {
Path src = base.resolve("src");
tb.writeJavaFiles(src,
"""
package p;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface ACustomAnnotation {
}
""",
"""
package p;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface ZCustomAnnotation {
}
""",
"""
package p;
/** Class A. */
public class A {
/**
* First snippet on A.foo:
* {@snippet :
* int x = 1; // first
* }
* Second snippet on A.foo:
* {@snippet :
* int y = 2; // second
* }
*/
@ACustomAnnotation
@ZCustomAnnotation
public void foo() {}
}
""",
"""
package p;
/** Class B. */
public class B {
/**
* First snippet on B.foo:
* {@snippet :
* int x = 1; // first
* }
* Second snippet on B.foo:
* {@snippet :
* int y = 2; // second
* }
*/
@ACustomAnnotation
@ZCustomAnnotation
public void foo() {}
}
""");
javadoc("-d", base.resolve("out").toString(),
"-use",
"-sourcepath", src.toString(),
"p");
checkExit(Exit.OK);
for (String cls : new String[] {
"A.html",
"B.html",
"class-use/ACustomAnnotation.html",
"class-use/ZCustomAnnotation.html"}) {
var file = base
.resolve("out")
.resolve("p")
.resolve(cls);
String content = Files.readString(file);
for (var snippetId : new String[] {"snippet-foo()1", "snippet-foo()2"}) {
checking("id \"" + snippetId + "\" present in " + cls);
if (content.contains("id=\"" + snippetId + "\"")) {
passed("found");
} else {
failed("" + snippetId + " not found in " +
String.join("\n", Arrays.asList(content.split("\n"))
.stream()
.filter(l -> l.contains("pre class=\"snippet\""))
.toList()));
}
}
}
}
}