From 6ee8407758c92d32e18642b0758d2d5c71ad09f5 Mon Sep 17 00:00:00 2001 From: Jonathan Gibbons Date: Thu, 18 Apr 2024 18:49:08 +0000 Subject: [PATCH] 8324342: Doclet should default @since for a nested class to that of its enclosing class Reviewed-by: prappo, hannesw --- .../formats/html/taglets/SimpleTaglet.java | 58 +++++++++- .../formats/html/taglets/TagletManager.java | 8 +- src/jdk.javadoc/share/man/javadoc.1 | 34 +++--- .../javadoc/doclet/testAuthor/TestAuthor.java | 104 +++++++++++++++++- .../doclet/testSinceTag/TestSinceTag.java | 79 ++++++++++++- .../doclet/testVersionTag/TestVersionTag.java | 37 ++++++- 6 files changed, 286 insertions(+), 34 deletions(-) diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SimpleTaglet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SimpleTaglet.java index 119da6a7610..110a3e1246e 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SimpleTaglet.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/SimpleTaglet.java @@ -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 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 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 locations, boolean enabled) { + return new SimpleTaglet(config, tagKind, header, locations, enabled) { + @Override + protected List getDefaultBlockTags(Element e, Predicate 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 description, ExecutableElement method) { } private Optional extractFirst(ExecutableElement m) { - List tags = utils.getBlockTags(m, this::accepts); + List 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 tags = utils.getBlockTags(holder, this::accepts); + List tags = getBlockTags(holder); if (header == null || tags.isEmpty()) { return null; } return simpleBlockTagOutput(holder, tags, header); } + private List getBlockTags(Element e) { + var tags = utils.getBlockTags(e, this::accepts); + if (tags.isEmpty()) { + tags = getDefaultBlockTags(e, this::accepts); + } + return tags; + } + + protected List getDefaultBlockTags(Element e, Predicate accepts) { + return List.of(); + } + /** * Returns the output for a series of simple tags. * diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/TagletManager.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/TagletManager.java index 804704528ce..0e7b2959028 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/TagletManager.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/taglets/TagletManager.java @@ -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"), diff --git a/src/jdk.javadoc/share/man/javadoc.1 b/src/jdk.javadoc/share/man/javadoc.1 index a6437c0288b..30256207b3e 100644 --- a/src/jdk.javadoc/share/man/javadoc.1 +++ b/src/jdk.javadoc/share/man/javadoc.1 @@ -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] \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]\f[R] tag. diff --git a/test/langtools/jdk/javadoc/doclet/testAuthor/TestAuthor.java b/test/langtools/jdk/javadoc/doclet/testAuthor/TestAuthor.java index 5229f8ccab5..799185f4dee 100644 --- a/test/langtools/jdk/javadoc/doclet/testAuthor/TestAuthor.java +++ b/test/langtools/jdk/javadoc/doclet/testAuthor/TestAuthor.java @@ -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>"""); + } } diff --git a/test/langtools/jdk/javadoc/doclet/testSinceTag/TestSinceTag.java b/test/langtools/jdk/javadoc/doclet/testSinceTag/TestSinceTag.java index 5f47be2c09a..8d85fa176f6 100644 --- a/test/langtools/jdk/javadoc/doclet/testSinceTag/TestSinceTag.java +++ b/test/langtools/jdk/javadoc/doclet/testSinceTag/TestSinceTag.java @@ -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>"""); + + } } diff --git a/test/langtools/jdk/javadoc/doclet/testVersionTag/TestVersionTag.java b/test/langtools/jdk/javadoc/doclet/testVersionTag/TestVersionTag.java index e04fa956e74..6f01b407ae3 100644 --- a/test/langtools/jdk/javadoc/doclet/testVersionTag/TestVersionTag.java +++ b/test/langtools/jdk/javadoc/doclet/testVersionTag/TestVersionTag.java @@ -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>"""); + + } }