diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/DeprecatedListWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/DeprecatedListWriter.java index 3f58ef90ad50a..b053a5e09a831 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/DeprecatedListWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/DeprecatedListWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, 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 @@ -101,16 +101,20 @@ protected void addContentSelectors(Content target) { } @Override - protected void addExtraSection(Content content) { - addSummaryAPI(builder.getForRemoval(), HtmlIds.FOR_REMOVAL, - TERMINALLY_DEPRECATED_KEY, "doclet.Element", content); + protected List getIndexLinks() { + var list = super.getIndexLinks(); + if (!builder.getForRemoval().isEmpty()) { + list.addFirst(getIndexLink(HtmlIds.FOR_REMOVAL, "doclet.Terminally_Deprecated")); + } + return list; } @Override - protected void addExtraIndexLink(Content target) { - if (!builder.getForRemoval().isEmpty()) { - addIndexLink(HtmlIds.FOR_REMOVAL, "doclet.Terminally_Deprecated", target); - } + protected void addSummaries(Content content) { + // Add terminally deprecated APIs before other deprecated APIs + addSummaryAPI(builder.getForRemoval(), HtmlIds.FOR_REMOVAL, + TERMINALLY_DEPRECATED_KEY, "doclet.Element", content); + super.addSummaries(content); } @Override diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java index 34ef40ed650a4..6896b86279fb3 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java @@ -79,6 +79,7 @@ import com.sun.source.doctree.RawTextTree; import com.sun.source.doctree.StartElementTree; import com.sun.source.doctree.TextTree; +import com.sun.source.doctree.UnknownBlockTagTree; import com.sun.source.util.DocTreePath; import com.sun.source.util.SimpleDocTreeVisitor; @@ -2493,6 +2494,24 @@ public void addRestrictedSummary(Element forWhat, Content target) { public void addPreviewInfo(Element forWhat, Content target) { if (utils.isPreviewAPI(forWhat)) { + // Preview note tag may be used to provide an alternative preview note. + String previewNoteTag = configuration.getOptions().previewNoteTag(); + if (previewNoteTag != null) { + List tags = utils.getBlockTags(forWhat, + t -> t.getTagName().equals(previewNoteTag), UnknownBlockTagTree.class); + if (tags != null && !tags.isEmpty()) { + if (tags.size() > 1) { + messages.warning(utils.getCommentHelper(forWhat).getDocTreePath(tags.get(1)), + "doclet.PreviewMultipleNotes", utils.getSimpleName(forWhat)); + } + var previewDiv = HtmlTree.DIV(HtmlStyles.previewBlock); + previewDiv.setId(htmlIds.forPreviewSection(forWhat)); + previewDiv.add(HtmlTree.DIV(HtmlStyles.previewComment, + commentTagsToContent(forWhat, tags.getFirst().getContent(), false))); + target.add(previewDiv); + return; + } + } //in Java platform: var previewDiv = HtmlTree.DIV(HtmlStyles.previewBlock); previewDiv.setId(htmlIds.forPreviewSection(forWhat)); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PreviewListWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PreviewListWriter.java index b1c1bc42c58ce..d1cbed27e8660 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PreviewListWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PreviewListWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, 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 @@ -32,6 +32,7 @@ import com.sun.source.doctree.DocTree; +import com.sun.source.doctree.UnknownBlockTagTree; import jdk.javadoc.internal.doclets.formats.html.Navigation.PageMode; import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyles; import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; @@ -86,9 +87,15 @@ protected void addContentSelectors(Content target) { target.add(HtmlTree.P(contents.getContent("doclet.Preview_API_Checkbox_Label"))); Content list = HtmlTree.UL(HtmlStyles.previewFeatureList).addStyle(HtmlStyles.checkboxes); for (var jep : jeps) { - String jepUrl = resources.getText("doclet.Preview_JEP_URL", String.valueOf(jep.number())); - Content label = new ContentBuilder(Text.of(jep.number() + ": ")) - .add(HtmlTree.A(jepUrl, Text.of(jep.title() + " (" + jep.status() + ")"))); + Content label; + if (jep.number() != 0) { + String jepUrl = resources.getText("doclet.Preview_JEP_URL", String.valueOf(jep.number())); + label = new ContentBuilder(Text.of(jep.number() + ": ")) + .add(HtmlTree.A(jepUrl, Text.of(jep.title() + " (" + jep.status() + ")"))); + } else { + // Pseudo-JEP created from javadoc tag - use description as label + label = Text.of(jep.title()); + } list.add(HtmlTree.LI(getCheckbox(label, String.valueOf(index++), "feature-"))); } Content label = contents.getContent("doclet.Preview_API_Checkbox_Toggle_All"); @@ -98,18 +105,23 @@ protected void addContentSelectors(Content target) { } @Override - protected void addExtraSection(Content content) { + protected List getIndexLinks() { + var list = super.getIndexLinks(); var notes = builder.getElementNotes(); if (!notes.isEmpty()) { - addSummaryAPI(notes, HtmlId.of("preview-api-notes"), - "doclet.Preview_Notes_Elements", "doclet.Element", content); + list.add(getIndexLink(HtmlId.of("preview-api-notes"), "doclet.Preview_Notes")); } + return list; } @Override - protected void addExtraIndexLink(Content target) { - if (!builder.getElementNotes().isEmpty()) { - addIndexLink(HtmlId.of("preview-api-notes"), "doclet.Preview_Notes", target); + protected void addSummaries(Content content) { + var notes = builder.getElementNotes(); + super.addSummaries(content); + // Add permanent APIs with preview notes below preview API tables + if (!notes.isEmpty()) { + addSummaryAPI(notes, HtmlId.of("preview-api-notes"), + "doclet.Preview_Notes", "doclet.Element", content); } } @@ -129,7 +141,7 @@ protected void addTableTabs(Table table, String headingKey) { .setDefaultTab(getTableCaption(headingKey)) .setRenderTabs(false); for (PreviewAPIListBuilder.JEP jep : builder.getJEPs()) { - table.addTab(Text.EMPTY, element -> jep == builder.getJEP(element)); + table.addTab(Text.EMPTY, element -> jep.equals(builder.getJEP(element))); } } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SummaryListWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SummaryListWriter.java index 8d2f621e5d455..b7e23530477a1 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SummaryListWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SummaryListWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, 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 @@ -25,7 +25,10 @@ package jdk.javadoc.internal.doclets.formats.html; +import java.util.ArrayList; +import java.util.List; import java.util.SortedSet; +import java.util.function.Function; import javax.lang.model.element.Element; import javax.lang.model.element.ModuleElement; @@ -146,15 +149,9 @@ public void buildPage() throws DocFileIOException { addContentSelectors(content); if (showContentsList()) { content.add(HtmlTree.HEADING_TITLE(Headings.CONTENT_HEADING, contents.contentsHeading)); - content.add(getContentsList()); - } - addExtraSection(content); - for (SummaryElementKind kind : SummaryElementKind.values()) { - if (builder.hasDocumentation(kind)) { - addSummaryAPI(builder.getSet(kind), HtmlIds.forSummaryKind(kind), - getHeadingKey(kind), getHeaderKey(kind), content); - } + content.add(HtmlTree.UL(HtmlStyles.contentsList, getIndexLinks(), Function.identity())); } + addSummaries(content); bodyContents.addMainContent(content); // The script below enables checkboxes in the page and invokes their click handler // to restore any previous state when the page is loaded via back/forward button. @@ -177,17 +174,16 @@ public void buildPage() throws DocFileIOException { } /** - * Add the index link. + * Returns a list item containing a link for the table of contents. * * @param id the id for the link * @param headingKey the key for the heading content - * @param content the content to which the index link will be added + * @return a list item containing an index link */ - protected void addIndexLink(HtmlId id, String headingKey, Content content) { + protected Content getIndexLink(HtmlId id, String headingKey) { // The "contents-" + id value is used in JavaScript code to toggle visibility of the link. - var li = HtmlTree.LI(links.createLink(id, + return HtmlTree.LI(links.createLink(id, contents.getContent(headingKey))).setId(HtmlId.of("contents-" + id.name())); - content.add(li); } /** @@ -198,19 +194,16 @@ protected boolean showContentsList() { } /** - * Get the contents list. - * - * @return the contents list + * {@return a list of list items containing the links for the table of contents} */ - public Content getContentsList() { - var ul= HtmlTree.UL(HtmlStyles.contentsList); - addExtraIndexLink(ul); + protected List getIndexLinks() { + var list = new ArrayList(); for (SummaryElementKind kind : SummaryElementKind.values()) { if (builder.hasDocumentation(kind)) { - addIndexLink(HtmlIds.forSummaryKind(kind), getHeadingKey(kind), ul); + list.add(getIndexLink(HtmlIds.forSummaryKind(kind), getHeadingKey(kind))); } } - return ul; + return list; } /** @@ -225,6 +218,20 @@ public HtmlTree getHeader(PageMode pageMode, String titleKey) { return body; } + /** + * Add all API summary tables to the main content of the page. + * + * @param content the content to add the API summary tables to + */ + protected void addSummaries(Content content) { + for (SummaryElementKind kind : SummaryElementKind.values()) { + if (builder.hasDocumentation(kind)) { + addSummaryAPI(builder.getSet(kind), HtmlIds.forSummaryKind(kind), + getHeadingKey(kind), getHeaderKey(kind), content); + } + } + } + /** * Add summary information to the documentation * @@ -237,7 +244,7 @@ public HtmlTree getHeader(PageMode pageMode, String titleKey) { protected void addSummaryAPI(SortedSet apiList, HtmlId id, String headingKey, String headerKey, Content content) { - if (apiList.size() > 0) { + if (!apiList.isEmpty()) { TableHeader tableHeader = getTableHeader(headerKey); var table = new Table(HtmlStyles.summaryTable) @@ -319,22 +326,6 @@ protected Content getCheckbox(Content label, String id, String htmlPrefix) { .add(HtmlTree.SPAN(label)); } - /** - * Add an extra optional section to the content. - * - * @param target the content to which the section should be added - */ - protected void addExtraSection(Content target) { - } - - /** - * Add an extra optional index link. - * - * @param target the content to which the link should be added - */ - protected void addExtraIndexLink(Content target) { - } - /** * Allow Subclasses to add a content selector UI such as a row of radio buttons * near the top of the page. This method does not add anything. @@ -345,7 +336,7 @@ protected void addContentSelectors(Content target) {} /** * Allow subclasses to add an extra table column for an element. - * This methods does not add any content by returning {@code null}. + * Return {@code null} for no extra content. * * @param element the element * @return content for extra content or null diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties index 9b111cb5edf35..f49174f6dcdcf 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties @@ -136,8 +136,7 @@ doclet.Preview_API_Checkbox_Toggle_All=Toggle all doclet.Preview_JEP_URL=https://openjdk.org/jeps/{0} doclet.Preview_Label=Preview doclet.Preview_Mark=PREVIEW -doclet.Preview_Notes=Preview API Notes -doclet.Preview_Notes_Elements=Elements containing Preview Notes +doclet.Preview_Notes=Permanent APIs affected by Preview Features doclet.Restricted_Methods=Restricted Methods doclet.Restricted_Mark=RESTRICTED doclet.searchTag=Search Tag @@ -462,6 +461,7 @@ Programs can only use requires transitive java.base when \ preview features are enabled.
\ Preview features may be removed in a future release, or upgraded \ to permanent features of the Java Platform.
+doclet.PreviewMultipleNotes=Multiple preview notes in {0}. doclet.RestrictedMethod=restricted method doclet.RestrictedLeadingNote={0} is a {1} of the Java platform. doclet.RestrictedTrailingNote1=Programs can only use {0} when access to restricted methods is enabled. diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/BaseOptions.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/BaseOptions.java index be698d270ae7f..8bace0530fc64 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/BaseOptions.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/BaseOptions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, 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 @@ -239,11 +239,19 @@ public enum ModularityMismatchPolicy { /** * Argument for command-line option {@code --preview-note-tag}. - * If set, the JavaDoc inline tag with the given name is considered - * a preview note and added to the preview API page. + * If set, the JavaDoc tag with the given name can be used to add + * preview-related notes to permanent APIs or override the default + * preview note for preview APIs. */ private String previewNoteTag = null; + /** + * Argument for command-line option {@code --preview-feature-tag}. + * If set, the JavaDoc inline tag with the given name is used to + * add mark an API element as preview feature in non-JDK contexts. + */ + private String previewFeatureTag = null; + /** * Argument for command-line option {@code -quiet}. * Suppress all messages @@ -565,6 +573,14 @@ public boolean process(String option, List args) { } }, + new Hidden(resources, "--preview-feature-tag", 1) { + @Override + public boolean process(String option, List args) { + previewFeatureTag = args.getFirst(); + return true; + } + }, + new Hidden(resources, "-quiet") { @Override public boolean process(String opt, List args) { @@ -958,10 +974,16 @@ public boolean noTimestamp() { /** * Argument for command-line option {@code --preview-note-tag}. - * Name of inline tag for preview notes. + * Name of inline tag for preview notes on permanent APIs. */ public String previewNoteTag() { return previewNoteTag; } + /** + * Argument for command-line option {@code --preview-feature-tag}. + * Name of inline tag for marking APIs as preview feature. + */ + public String previewFeatureTag() { return previewFeatureTag; } + /** * Argument for command-line option {@code -quiet}. * Suppress all messages diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/PreviewAPIListBuilder.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/PreviewAPIListBuilder.java index 4c43e40eca6e0..44129b78df46a 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/PreviewAPIListBuilder.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/PreviewAPIListBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2025, 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 @@ -26,6 +26,7 @@ package jdk.javadoc.internal.doclets.toolkit.util; import com.sun.source.doctree.DocTree; +import com.sun.source.doctree.UnknownBlockTagTree; import com.sun.source.doctree.UnknownInlineTagTree; import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration; @@ -51,6 +52,7 @@ public class PreviewAPIListBuilder extends SummaryAPIListBuilder { private final SortedSet elementNotes = createSummarySet(); private final Map jeps = new HashMap<>(); private final String previewNoteTag; + public final String previewFeatureTag; /** * The JEP for a preview feature in this release. @@ -58,7 +60,7 @@ public class PreviewAPIListBuilder extends SummaryAPIListBuilder { public record JEP(int number, String title, String status) implements Comparable { @Override public int compareTo(JEP o) { - return number - o.number; + return number == o.number ? title.compareTo(o.title) : number - o.number; } } @@ -70,9 +72,10 @@ public int compareTo(JEP o) { public PreviewAPIListBuilder(BaseConfiguration configuration) { super(configuration); this.previewNoteTag = configuration.getOptions().previewNoteTag(); + this.previewFeatureTag = configuration.getOptions().previewFeatureTag(); // retrieve preview JEPs buildPreviewFeatureInfo(); - if (!jeps.isEmpty()) { + if (!jeps.isEmpty() || previewFeatureTag != null) { // map elements to preview JEPs and preview tags buildSummaryAPIInfo(); // remove unused preview JEPs @@ -114,17 +117,31 @@ private R getAnnotationElementValue(Map t.getTagName().equals(previewFeatureTag), + UnknownBlockTagTree.class) + .stream() + .map(t -> t.getContent().toString().trim()) + .findFirst(); + // Create pseudo-JEP for preview tag + desc.ifPresent(s -> { + var jep = jeps.computeIfAbsent(s, s2 -> new JEP(0, s2, "")); + elementJeps.put(element, jep); + }); return true; + } else { + String feature = Objects.requireNonNull(utils.getPreviewFeature(element), + "Preview feature not specified").toString(); + // Preview features without JEP are not included in the list. + JEP jep = jeps.get(feature); + if (jep != null) { + elementJeps.put(element, jep); + return true; + } } - } - // If preview tag is defined map elements to preview tags - if (previewNoteTag != null) { + } else if (previewNoteTag != null) { + // If preview tag is defined map elements to preview tags CommentHelper ch = utils.getCommentHelper(element); if (ch.dcTree != null) { var jep = ch.dcTree.getFullBody().stream() @@ -137,7 +154,7 @@ protected boolean belongsToSummary(Element element) { if (jep.isPresent()) { elementNotes.add(element); elementJeps.put(element, jep.get()); - // Don't return true as this is not actual preview API. + return false; // Not part of preview API. } } } diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java index 20794160ab550..ddadd33a18bb5 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java @@ -2678,6 +2678,10 @@ public boolean isPreviewAPI(Element el) { parentPreviewAPI = configuration.workArounds.isPreviewAPI(enclosing); } } + String previewFeatureTag = configuration.getOptions().previewFeatureTag(); + if (previewFeatureTag != null && hasBlockTag(el, UNKNOWN_BLOCK_TAG, previewFeatureTag)) { + return true; + } boolean previewAPI = configuration.workArounds.isPreviewAPI(el); return !parentPreviewAPI && previewAPI; } @@ -2754,6 +2758,7 @@ private boolean hasNoPreviewAnnotation(Element el) { public boolean isPreview(Element el) { PreviewSummary previewAPIs = declaredUsingPreviewAPIs(el); Element enclosing = el.getEnclosingElement(); + String previewFeatureTag = configuration.getOptions().previewFeatureTag(); return ( !previewLanguageFeaturesUsed(el).isEmpty() || configuration.workArounds.isPreviewAPI(el) @@ -2761,7 +2766,9 @@ public boolean isPreview(Element el) { && configuration.workArounds.isPreviewAPI(enclosing)) || !previewAPIs.previewAPI.isEmpty() || !previewAPIs.reflectivePreviewAPI.isEmpty() - || !previewAPIs.declaredUsingPreviewFeature.isEmpty()) + || !previewAPIs.declaredUsingPreviewFeature.isEmpty() + || ( previewFeatureTag != null + && hasBlockTag(el, Kind.UNKNOWN_BLOCK_TAG, previewFeatureTag))) && !hasNoPreviewAnnotation(el); } }; diff --git a/test/langtools/jdk/javadoc/doclet/testPreview/TestPreview.java b/test/langtools/jdk/javadoc/doclet/testPreview/TestPreview.java index b883de1920b96..b93c3715ddcb0 100644 --- a/test/langtools/jdk/javadoc/doclet/testPreview/TestPreview.java +++ b/test/langtools/jdk/javadoc/doclet/testPreview/TestPreview.java @@ -335,10 +335,10 @@ public interface NonPrevieFeature { """

Contents

    -
  • Preview API Notes
  • -
  • Interfaces
  • """, +
  • Interfaces
  • +
  • Permanent APIs affected by Preview Features
  • """, """ -
    Elements containing Preview Notes
    """, +
    Permanent APIs affected by Preview Features
    """, """ diff --git a/test/langtools/jdk/javadoc/doclet/testPreviewTag/TestPreviewTag.java b/test/langtools/jdk/javadoc/doclet/testPreviewTag/TestPreviewTag.java new file mode 100644 index 0000000000000..535637007aebc --- /dev/null +++ b/test/langtools/jdk/javadoc/doclet/testPreviewTag/TestPreviewTag.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2025, 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 8356975 + * @summary Provide alternative way to generate preview API docs + * @library /tools/lib ../../lib + * @modules jdk.javadoc/jdk.javadoc.internal.tool + * @build toolbox.ToolBox javadoc.tester.* taglet.PreviewFeature taglet.PreviewNote + * @run main TestPreviewTag + */ + +import java.nio.file.Path; + +import javadoc.tester.JavadocTester; + +public class TestPreviewTag extends JavadocTester { + + public static void main(String... args) throws Exception { + var tester = new TestPreviewTag(); + tester.runTests(); + } + + @Test + public void testPreviewTag(Path base) throws Exception { + + javadoc("-d", base.resolve("out").toString(), + "-tagletpath", System.getProperty("test.classes"), + "-taglet", "taglet.PreviewFeature", + "-taglet", "taglet.PreviewNote", + "--preview-note-tag", "previewNote", + "--preview-feature-tag", "previewFeature", + "-sourcepath", testSrc, + "api"); + checkExit(Exit.OK); + + checkOutput(Output.OUT, true, + """ + warning: Multiple preview notes in otherPreviewMethod. + * @previewNote Extra note tag triggers a warning"""); + + checkOrder("api/package-summary.html", + """ + PreviewApiPREVIEW""", + """ +
    Preview.
    +
    This is a preview API marked by javadoc tags.
    """); + + checkOrder("api/OtherApi.html", + """ +
    +
    Alternative preview note. PreviewApiPREVIEW is a preview API.
    """, + """ +
    +
    Alternative preview note for second preview feature.
    """); + + checkOrder("api/PreviewApi.html", + """ +
    +
    Alternative preview note. PreviewApiPREVIEW is a preview API.
    """); + + checkOrder("preview-list.html", + """ +
  • +
  • +
  • """, + """ + +
    First preview feature
    +
    +
    This is a preview API marked by javadoc tags.
    """, + """ + +
    Second preview feature
    +
    +
    This is another preview method.
    """, + """ + +
    First preview feature
    +
    +
    This is a preview method.
    """); + } +} diff --git a/test/langtools/jdk/javadoc/doclet/testPreviewTag/api/OtherApi.java b/test/langtools/jdk/javadoc/doclet/testPreviewTag/api/OtherApi.java new file mode 100644 index 0000000000000..cfe0d73be5537 --- /dev/null +++ b/test/langtools/jdk/javadoc/doclet/testPreviewTag/api/OtherApi.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2025, 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. + */ + +package api; + +/** + * Non-preview class with a preview members. + */ +public class OtherApi { + + /** + * This is a preview method. + * + * @previewFeature First preview feature + * @previewNote Alternative preview note. {@link PreviewApi} is a preview API. + */ + public void previewMethod() {} + + /** + * This is another preview method. + * + * @previewFeature Second preview feature + * @previewNote Alternative preview note for second preview feature. + * @previewNote Extra note tag triggers a warning + */ + public void otherPreviewMethod() {} + +} diff --git a/test/langtools/jdk/javadoc/doclet/testPreviewTag/api/PreviewApi.java b/test/langtools/jdk/javadoc/doclet/testPreviewTag/api/PreviewApi.java new file mode 100644 index 0000000000000..0bd479110f811 --- /dev/null +++ b/test/langtools/jdk/javadoc/doclet/testPreviewTag/api/PreviewApi.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2025, 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. + */ + +package api; + +/** + * This is a preview API marked by javadoc tags. + * + * @previewFeature First preview feature + * @previewNote Alternative preview note. {@link PreviewApi} is a preview API. + */ +public class PreviewApi { + private PreviewApi() {} +} diff --git a/test/langtools/jdk/javadoc/doclet/testPreviewTag/taglet/PreviewFeature.java b/test/langtools/jdk/javadoc/doclet/testPreviewTag/taglet/PreviewFeature.java new file mode 100644 index 0000000000000..6db608bb494a0 --- /dev/null +++ b/test/langtools/jdk/javadoc/doclet/testPreviewTag/taglet/PreviewFeature.java @@ -0,0 +1,68 @@ +/* Copyright (c) 2025, 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. + */ + +package taglet; + +import java.util.EnumSet; +import java.util.List; +import java.util.Set; + +import javax.lang.model.element.Element; + +import com.sun.source.doctree.DocTree; +import jdk.javadoc.doclet.Doclet; +import jdk.javadoc.doclet.DocletEnvironment; +import jdk.javadoc.doclet.Taglet; + +/** + * Javadoc block tag to mark elements as preview feature. + * The tag has no output, tag contents are used to describe + * the preview feature in the Preview API page. + */ +public class PreviewFeature implements Taglet { + + static final String TAG_NAME = "previewFeature"; + + @Override + public void init(DocletEnvironment env, Doclet doclet) { + } + + @Override + public Set getAllowedLocations() { + return EnumSet.allOf(Taglet.Location.class); + } + + @Override + public boolean isInlineTag() { + return false; + } + + @Override + public String getName() { + return TAG_NAME; + } + + @Override + public String toString(List tags, Element elem) { + return ""; + } +} diff --git a/test/langtools/jdk/javadoc/doclet/testPreviewTag/taglet/PreviewNote.java b/test/langtools/jdk/javadoc/doclet/testPreviewTag/taglet/PreviewNote.java new file mode 100644 index 0000000000000..dccb2ba717e48 --- /dev/null +++ b/test/langtools/jdk/javadoc/doclet/testPreviewTag/taglet/PreviewNote.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2025, 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. + */ + +package taglet; + +import java.util.EnumSet; +import java.util.List; +import java.util.Set; + +import javax.lang.model.element.Element; + +import com.sun.source.doctree.DocTree; +import jdk.javadoc.doclet.Doclet; +import jdk.javadoc.doclet.DocletEnvironment; +import jdk.javadoc.doclet.Taglet; + +/** + * A Javadoc block tag that provides an alternative preview note. + * The tag has no direct output, tag contents are used as + * preview note by javadoc. + */ +public class PreviewNote implements Taglet { + + static final String TAG_NAME = "previewNote"; + + @Override + public void init(DocletEnvironment env, Doclet doclet) { + } + + @Override + public Set getAllowedLocations() { + return EnumSet.allOf(Taglet.Location.class); + } + + @Override + public boolean isInlineTag() { + return false; + } + + @Override + public String getName() { + return TAG_NAME; + } + + @Override + public String toString(List tags, Element elem) { + return ""; + } +}