8324342: Doclet should default @since for a nested class to that of its enclosing class

Reviewed-by: prappo, hannesw
This commit is contained in:
Jonathan Gibbons 2024-04-18 18:49:08 +00:00
parent 235ba9a702
commit 6ee8407758
6 changed files with 286 additions and 34 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 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
@ -29,10 +29,13 @@ import java.util.EnumSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.NestingKind;
import javax.lang.model.element.TypeElement;
import com.sun.source.doctree.BlockTagTree;
import com.sun.source.doctree.DocTree;
@ -100,6 +103,7 @@ public class SimpleTaglet extends BaseTaglet implements InheritableTaglet {
* @param tagName the name of this tag
* @param header the header to output
* @param locations the possible locations that this tag can appear in
* @param enabled whether this tag is enabled
*/
private SimpleTaglet(HtmlConfiguration config, String tagName, String header, Set<Taglet.Location> locations, boolean enabled) {
super(config, tagName, false, locations);
@ -113,6 +117,7 @@ public class SimpleTaglet extends BaseTaglet implements InheritableTaglet {
* @param tagKind the kind of this tag
* @param header the header to output
* @param locations the possible locations that this tag can appear in
* @param enabled whether this tag is enabled
*/
protected SimpleTaglet(HtmlConfiguration config, DocTree.Kind tagKind, String header, Set<Taglet.Location> locations, boolean enabled) {
super(config, tagKind, false, locations);
@ -120,6 +125,37 @@ public class SimpleTaglet extends BaseTaglet implements InheritableTaglet {
this.enabled = enabled;
}
/**
* Constructs a {@code SimpleTaglet} that will look for tags on enclosing type elements
* if there are no relevant tags on a nested (member) type element.
*
* @param tagKind the kind of this tag
* @param header the header to output
* @param locations the possible locations that this tag can appear in
* @param enabled whether this tag is enabled
*/
static SimpleTaglet createWithDefaultForNested(HtmlConfiguration config, DocTree.Kind tagKind, String header, Set<Taglet.Location> locations, boolean enabled) {
return new SimpleTaglet(config, tagKind, header, locations, enabled) {
@Override
protected List<? extends BlockTagTree> getDefaultBlockTags(Element e, Predicate<? super BlockTagTree> accepts) {
while (isNestedType(e)) {
e = e.getEnclosingElement();
var tags = utils.getBlockTags(e, accepts);
if (!tags.isEmpty()) {
return tags;
}
}
return List.of();
}
private boolean isNestedType(Element e) {
return e.getKind().isDeclaredType()
&& ((TypeElement) e).getNestingKind() == NestingKind.MEMBER;
}
};
}
@Override
public Output inherit(Element dst, Element src, DocTree tag, boolean isFirstSentence) {
assert dst.getKind() == ElementKind.METHOD;
@ -153,8 +189,8 @@ public class SimpleTaglet extends BaseTaglet implements InheritableTaglet {
/**
* Returns whether this taglet accepts a {@code BlockTagTree} node.
* The taglet accepts a tree node if it has the same kind, and
* if the kind is {@code UNKNOWN_BLOCK_TAG} the same tag name.
* The taglet accepts a tree node if it has the same kind, or
* if the kind is {@code UNKNOWN_BLOCK_TAG} with the same tag name.
*
* @param tree the tree node
* @return {@code true} if this taglet accepts this tree node
@ -168,7 +204,7 @@ public class SimpleTaglet extends BaseTaglet implements InheritableTaglet {
record Documentation(DocTree tag, List<? extends DocTree> description, ExecutableElement method) { }
private Optional<Documentation> extractFirst(ExecutableElement m) {
List<? extends DocTree> tags = utils.getBlockTags(m, this::accepts);
List<? extends DocTree> tags = getBlockTags(m);
if (tags.isEmpty()) {
return Optional.empty();
}
@ -179,13 +215,25 @@ public class SimpleTaglet extends BaseTaglet implements InheritableTaglet {
@Override
public Content getAllBlockTagOutput(Element holder, TagletWriter tagletWriter) {
this.tagletWriter = tagletWriter;
List<? extends DocTree> tags = utils.getBlockTags(holder, this::accepts);
List<? extends DocTree> tags = getBlockTags(holder);
if (header == null || tags.isEmpty()) {
return null;
}
return simpleBlockTagOutput(holder, tags, header);
}
private List<? extends BlockTagTree> getBlockTags(Element e) {
var tags = utils.getBlockTags(e, this::accepts);
if (tags.isEmpty()) {
tags = getDefaultBlockTags(e, this::accepts);
}
return tags;
}
protected List<? extends BlockTagTree> getDefaultBlockTags(Element e, Predicate<? super BlockTagTree> accepts) {
return List.of();
}
/**
* Returns the output for a series of simple tags.
*

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 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
@ -590,13 +590,13 @@ public class TagletManager {
addStandardTaglet(new ReturnTaglet(config));
addStandardTaglet(new ThrowsTaglet(config), EXCEPTION);
addStandardTaglet(
new SimpleTaglet(config, SINCE, resources.getText("doclet.Since"),
SimpleTaglet.createWithDefaultForNested(config, SINCE, resources.getText("doclet.Since"),
EnumSet.allOf(Location.class), !nosince));
addStandardTaglet(
new SimpleTaglet(config, VERSION, resources.getText("doclet.Version"),
SimpleTaglet.createWithDefaultForNested(config, VERSION, resources.getText("doclet.Version"),
EnumSet.of(Location.OVERVIEW, Location.MODULE, Location.PACKAGE, Location.TYPE), showversion));
addStandardTaglet(
new SimpleTaglet(config, AUTHOR, resources.getText("doclet.Author"),
SimpleTaglet.createWithDefaultForNested(config, AUTHOR, resources.getText("doclet.Author"),
EnumSet.of(Location.OVERVIEW, Location.MODULE, Location.PACKAGE, Location.TYPE), showauthor));
addStandardTaglet(
new SimpleTaglet(config, SERIAL_DATA, resources.getText("doclet.SerialData"),

View File

@ -1,4 +1,4 @@
.\" Copyright (c) 1994, 2023, Oracle and/or its affiliates. All rights reserved.
.\" Copyright (c) 1994, 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
@ -110,8 +110,8 @@ also recommended to check the generated output with any appropriate
conformance and other checking tools.
.PP
For more details on the conformance requirements for HTML5 documents,
see \f[B]Conformance requirements\f[R]
[https://www.w3.org/TR/html5/infrastructure.html#conformance-requirements]
see \f[B]Conformance requirements for authors\f[R]
[https://html.spec.whatwg.org/multipage/introduction.html#conformance-requirements-for-authors]
in the HTML5 Specification.
For more details on security issues related to web pages, see the
\f[B]Open Web Application Security Project (OWASP)\f[R]
@ -489,7 +489,8 @@ Allow JavaScript in documentation comments, and options whose value is
\f[I]html-code\f[R].
.TP
\f[V]-author\f[R]
Includes the \f[V]\[at]author\f[R] text in the generated docs.
Includes the text of any \f[V]author\f[R] tags in the generated
documentation.
.TP
\f[V]-bottom\f[R] \f[I]html-code\f[R]
Specifies the text to be placed at the bottom of each generated page.
@ -512,18 +513,16 @@ javadoc -charset \[dq]iso-8859-1\[dq] mypackage
\f[R]
.fi
.PP
This command inserts the following line in the head of every generated
page:
This command inserts the following line, containing a
\f[B]\f[VB]meta\f[B] element\f[R]
[https://html.spec.whatwg.org/multipage/semantics.html#the-meta-element]
in the head of every generated page:
.IP
.nf
\f[CB]
<meta http-equiv=\[dq]Content-Type\[dq] content=\[dq]text/html; charset=ISO-8859-1\[dq]>
\f[R]
.fi
.PP
The \f[V]meta\f[R] tag is described in the \f[B]HTML standard (4197265
and 4137321), HTML Document Representation\f[R]
[http://www.w3.org/TR/REC-html40/charset.html#h-5.2.2].
.RE
.TP
\f[V]-d\f[R] \f[I]directory\f[R]
@ -723,8 +722,8 @@ documentation shares the same file system.
In all cases, and on all operating systems, use a slash as the
separator, whether the URL is absolute or relative, and
\f[V]https:\f[R], \f[V]http:\f[R], or \f[V]file:\f[R] as specified in
the \f[B]URL Memo: Uniform Resource Locators\f[R]
[http://www.ietf.org/rfc/rfc1738.txt].
the \f[B]RFC 1738: Uniform Resource Locators (URL)\f[R]
[https://www.rfc-editor.org/info/rfc1738].
.IP
.nf
\f[CB]
@ -950,8 +949,8 @@ is used.
.RE
.TP
\f[V]-nosince\f[R]
Omits from the generated documents the \f[V]Since\f[R] sections
associated with the \f[V]\[at]since\f[R] tags.
Omits from the generated documentation the \f[V]Since\f[R] sections
derived from any \f[V]since\f[R] tags.
.TP
\f[V]-notimestamp\f[R]
Suppresses the time stamp, which is hidden in an HTML comment in the
@ -1181,10 +1180,11 @@ To access the generated Use page, go to the class or package and click
the \f[B]USE\f[R] link in the navigation bar.
.TP
\f[V]-version\f[R]
Includes the version text in the generated docs.
Includes the text of any \f[V]version\f[R] tags in the generated
documentation.
This text is omitted by default.
To find out what version of the \f[V]javadoc\f[R] tool you are using,
use the \f[V]--version\f[R] option (with two hyphens).
Note: To find out what version of the \f[V]javadoc\f[R] tool you are
using, use the \f[V]--version\f[R] option (with two hyphens).
.TP
\f[V]-windowtitle\f[R] \f[I]title\f[R]
Specifies the title to be placed in the HTML \f[V]<title>\f[R] tag.

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 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
@ -23,7 +23,7 @@
/*
* @test
* @bug 8202947 8239804
* @bug 8202947 8239804 8324342
* @summary test the at-author tag, and corresponding option
* @library /tools/lib ../../lib
* @modules jdk.javadoc/jdk.javadoc.internal.tool
@ -129,4 +129,104 @@ public class TestAuthor extends JavadocTester {
<dd>anonymous</dd>
</dl>""");
}
@Test
public void testAuthorDefault(Path base) throws Exception {
Path src = base.resolve("src");
tb.writeJavaFiles(src, """
package p;
/**
* Class C.
* @author J. Duke
*/
public class C {
/** Class Nested, with no explicit at-author. */
public class Nested { }
}""");
javadoc("-d", base.resolve("api").toString(),
"-author",
"-sourcepath", src.toString(),
"p");
checkExit(Exit.OK);
checkOutput("p/C.html", true,
"""
<dl class="notes">
<dt>Author:</dt>
<dd>J. Duke</dd>""");
checkOutput("p/C.Nested.html", true,
"""
<dl class="notes">
<dt>Author:</dt>
<dd>J. Duke</dd>""");
}
@Test
public void testAuthorDefault_Multiple(Path base) throws Exception {
Path src = base.resolve("src");
tb.writeJavaFiles(src, """
package p;
/**
* Class C.
* @author J. Duke
* @author A. N. Other
*/
public class C {
/** Class Nested, with no explicit at-author. */
public class Nested { }
}""");
javadoc("-d", base.resolve("api").toString(),
"-author",
"-sourcepath", src.toString(),
"p");
checkExit(Exit.OK);
checkOutput("p/C.html", true,
"""
<dl class="notes">
<dt>Author:</dt>
<dd>J. Duke, A. N. Other</dd>""");
checkOutput("p/C.Nested.html", true,
"""
<dl class="notes">
<dt>Author:</dt>
<dd>J. Duke, A. N. Other</dd>""");
}
@Test
public void testAuthorDefault_Nested(Path base) throws Exception {
Path src = base.resolve("src");
tb.writeJavaFiles(src, """
package p;
/**
* Class C.
* @author J. Duke
* @author A. N. Other
*/
public class C {
public class Nested1 {
/** Class Nested, with no explicit at-author. */
public class Nested { }
}
}""");
javadoc("-d", base.resolve("api").toString(),
"-author",
"-sourcepath", src.toString(),
"p");
checkExit(Exit.OK);
checkOutput("p/C.html", true,
"""
<dl class="notes">
<dt>Author:</dt>
<dd>J. Duke, A. N. Other</dd>""");
checkOutput("p/C.Nested1.Nested.html", true,
"""
<dl class="notes">
<dt>Author:</dt>
<dd>J. Duke, A. N. Other</dd>""");
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 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
@ -23,15 +23,18 @@
/*
* @test
* @bug 7180906 8026567 8239804
* @bug 7180906 8026567 8239804 8324342
* @summary Test to make sure that the since tag works correctly
* @library ../../lib
* @library /tools/lib ../../lib
* @modules jdk.javadoc/jdk.javadoc.internal.tool
* @build javadoc.tester.*
* @build toolbox.ToolBox javadoc.tester.*
* @run main TestSinceTag
*/
import java.nio.file.Path;
import javadoc.tester.JavadocTester;
import toolbox.ToolBox;
public class TestSinceTag extends JavadocTester {
@ -41,6 +44,8 @@ public class TestSinceTag extends JavadocTester {
tester.printSummary();
}
private final ToolBox tb = new ToolBox();
@Test
public void testSince() {
javadoc("-d", "out-since",
@ -75,4 +80,70 @@ public class TestSinceTag extends JavadocTester {
<dt>Since:</dt>
<dd>1.4</dd>""");
}
@Test
public void testSinceDefault(Path base) throws Exception {
Path src = base.resolve("src");
tb.writeJavaFiles(src, """
package p;
/**
* Class C.
* @since 99
*/
public class C {
/** Class Nested, with no explicit at-since. */
public class Nested { }
}""");
javadoc("-d", base.resolve("api").toString(),
"-sourcepath", src.toString(),
"p");
checkExit(Exit.OK);
checkOutput("p/C.html", true,
"""
<dl class="notes">
<dt>Since:</dt>
<dd>99</dd>""");
checkOutput("p/C.Nested.html", true,
"""
<dl class="notes">
<dt>Since:</dt>
<dd>99</dd>""");
}
@Test
public void testSinceDefault_Nested(Path base) throws Exception {
Path src = base.resolve("src");
tb.writeJavaFiles(src, """
package p;
/**
* Class C.
* @since 99
*/
public class C {
public class Nested1 {
/** Class Nested, with no explicit at-since. */
public class Nested { }
}
}""");
javadoc("-d", base.resolve("api").toString(),
"-sourcepath", src.toString(),
"p");
checkExit(Exit.OK);
checkOutput("p/C.html", true,
"""
<dl class="notes">
<dt>Since:</dt>
<dd>99</dd>""");
checkOutput("p/C.Nested1.Nested.html", true,
"""
<dl class="notes">
<dt>Since:</dt>
<dd>99</dd>""");
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 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
@ -23,7 +23,7 @@
/*
* @test
* @bug 8202947 8239804
* @bug 8202947 8239804 8324342
* @summary test the at-version tag, and corresponding option
* @library /tools/lib ../../lib
* @modules jdk.javadoc/jdk.javadoc.internal.tool
@ -129,4 +129,37 @@ public class TestVersionTag extends JavadocTester {
<dd>1.2.3</dd>
</dl>""");
}
@Test
public void testVersionDefault(Path base) throws Exception {
Path src = base.resolve("src");
tb.writeJavaFiles(src, """
package p;
/**
* Class C.
* @version 42
*/
public class C {
/** Class Nested, with no explicit at-version. */
public class Nested { }
}""");
javadoc("-d", base.resolve("api").toString(),
"-version",
"-sourcepath", src.toString(),
"p");
checkExit(Exit.OK);
checkOutput("p/C.html", true,
"""
<dl class="notes">
<dt>Version:</dt>
<dd>42</dd>""");
checkOutput("p/C.Nested.html", true,
"""
<dl class="notes">
<dt>Version:</dt>
<dd>42</dd>""");
}
}