8331947: Preview creates checkbox for JEP-less preview feature

Reviewed-by: liach, prappo
This commit is contained in:
Hannes Wallnöfer 2024-06-05 12:39:56 +00:00
parent 8e903eeb1f
commit 765ad0e40b
8 changed files with 86 additions and 51 deletions
src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit
test/langtools/jdk/javadoc/doclet/testPreview

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -31,9 +31,9 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.stream.Collectors;
import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element; import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.ModuleElement; import javax.lang.model.element.ModuleElement;
@ -48,15 +48,12 @@ import javax.tools.JavaFileManager.Location;
import com.sun.source.util.TreePath; import com.sun.source.util.TreePath;
import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Scope;
import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Symbol.ClassSymbol; import com.sun.tools.javac.code.Symbol.ClassSymbol;
import com.sun.tools.javac.code.Symbol.MethodSymbol; import com.sun.tools.javac.code.Symbol.MethodSymbol;
import com.sun.tools.javac.code.Symbol.ModuleSymbol; import com.sun.tools.javac.code.Symbol.ModuleSymbol;
import com.sun.tools.javac.code.Symbol.PackageSymbol; import com.sun.tools.javac.code.Symbol.PackageSymbol;
import com.sun.tools.javac.code.Symbol.VarSymbol; import com.sun.tools.javac.code.Symbol.VarSymbol;
import com.sun.tools.javac.comp.AttrContext;
import com.sun.tools.javac.comp.Env;
import com.sun.tools.javac.util.Names; import com.sun.tools.javac.util.Names;
import com.sun.tools.javac.util.Options; import com.sun.tools.javac.util.Options;
@ -408,7 +405,7 @@ public class WorkArounds {
* @param feature the name of the PreviewFeature.Feature enum value * @param feature the name of the PreviewFeature.Feature enum value
* @return the map of PreviewFeature.JEP annotation element values, or an empty map * @return the map of PreviewFeature.JEP annotation element values, or an empty map
*/ */
public Map<? extends ExecutableElement, ? extends AnnotationValue> getJepInfo(String feature) { public Map<String, Object> getJepInfo(String feature) {
TypeElement featureType = elementUtils.getTypeElement("jdk.internal.javac.PreviewFeature.Feature"); TypeElement featureType = elementUtils.getTypeElement("jdk.internal.javac.PreviewFeature.Feature");
TypeElement jepType = elementUtils.getTypeElement("jdk.internal.javac.PreviewFeature.JEP"); TypeElement jepType = elementUtils.getTypeElement("jdk.internal.javac.PreviewFeature.JEP");
var featureVar = featureType.getEnclosedElements().stream() var featureVar = featureType.getEnclosedElements().stream()
@ -416,7 +413,11 @@ public class WorkArounds {
if (featureVar.isPresent()) { if (featureVar.isPresent()) {
for (AnnotationMirror anno : featureVar.get().getAnnotationMirrors()) { for (AnnotationMirror anno : featureVar.get().getAnnotationMirrors()) {
if (anno.getAnnotationType().asElement().equals(jepType)) { if (anno.getAnnotationType().asElement().equals(jepType)) {
return anno.getElementValues(); return anno.getElementValues().entrySet()
.stream()
.collect(Collectors.toMap(
e -> e.getKey().getSimpleName().toString(),
e -> e.getValue().getValue()));
} }
} }
} }

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -47,7 +47,7 @@ public class DeprecatedAPIListBuilder extends SummaryAPIListBuilder {
* @param since list of releases passed via <code>--since</code> option * @param since list of releases passed via <code>--since</code> option
*/ */
public DeprecatedAPIListBuilder(BaseConfiguration configuration, List<String> since) { public DeprecatedAPIListBuilder(BaseConfiguration configuration, List<String> since) {
super(configuration, configuration.utils::isDeprecated); super(configuration);
this.foundReleases = new HashSet<>(); this.foundReleases = new HashSet<>();
buildSummaryAPIInfo(); buildSummaryAPIInfo();
// The releases list is set to the intersection of releases defined via `--since` option // The releases list is set to the intersection of releases defined via `--since` option
@ -73,6 +73,11 @@ public class DeprecatedAPIListBuilder extends SummaryAPIListBuilder {
return forRemoval; return forRemoval;
} }
@Override
protected boolean belongsToSummary(Element element) {
return utils.isDeprecated(element);
}
@Override @Override
protected void handleElement(Element e) { protected void handleElement(Element e) {
foundReleases.add(utils.getDeprecatedSince(e)); foundReleases.add(utils.getDeprecatedSince(e));

@ -46,16 +46,17 @@ public class NewAPIBuilder extends SummaryAPIListBuilder {
public final List<String> releases; public final List<String> releases;
public NewAPIBuilder(BaseConfiguration configuration, List<String> releases) { public NewAPIBuilder(BaseConfiguration configuration, List<String> releases) {
super(configuration, element -> isNewAPI(element, configuration.utils, releases)); super(configuration);
this.releases = releases; this.releases = releases;
buildSummaryAPIInfo(); buildSummaryAPIInfo();
} }
private static boolean isNewAPI(Element e, Utils utils, List<String> releases) { @Override
if (!utils.hasDocCommentTree(e)) { protected boolean belongsToSummary(Element element) {
if (!utils.hasDocCommentTree(element)) {
return false; return false;
} }
var sinceTrees = utils.getBlockTags(e, SINCE, SinceTree.class); var sinceTrees = utils.getBlockTags(element, SINCE, SinceTree.class);
if (sinceTrees.isEmpty()) { if (sinceTrees.isEmpty()) {
return false; return false;
} }

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -27,14 +27,13 @@ package jdk.javadoc.internal.doclets.toolkit.util;
import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration; import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element; import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.stream.Collectors;
/** /**
* Build list of all the preview packages, classes, constructors, fields and methods. * Build list of all the preview packages, classes, constructors, fields and methods.
@ -43,6 +42,7 @@ public class PreviewAPIListBuilder extends SummaryAPIListBuilder {
private final Map<Element, JEP> elementJeps = new HashMap<>(); private final Map<Element, JEP> elementJeps = new HashMap<>();
private final Map<String, JEP> jeps = new HashMap<>(); private final Map<String, JEP> jeps = new HashMap<>();
private static final JEP NULL_SENTINEL = new JEP(0, "", "");
/** /**
* The JEP for a preview feature in this release. * The JEP for a preview feature in this release.
@ -60,40 +60,51 @@ public class PreviewAPIListBuilder extends SummaryAPIListBuilder {
* @param configuration the current configuration of the doclet * @param configuration the current configuration of the doclet
*/ */
public PreviewAPIListBuilder(BaseConfiguration configuration) { public PreviewAPIListBuilder(BaseConfiguration configuration) {
super(configuration, configuration.utils::isPreviewAPI); super(configuration);
buildSummaryAPIInfo(); buildSummaryAPIInfo();
} }
@Override @Override
protected void handleElement(Element e) { protected boolean belongsToSummary(Element element) {
String feature = Objects.requireNonNull(utils.getPreviewFeature(e), if (!utils.isPreviewAPI(element)) {
return false;
}
String feature = Objects.requireNonNull(utils.getPreviewFeature(element),
"Preview feature not specified").toString(); "Preview feature not specified").toString();
JEP jep = jeps.computeIfAbsent(feature, (featureName) -> { JEP jep = jeps.computeIfAbsent(feature, featureName -> {
Map<? extends ExecutableElement, ? extends AnnotationValue> anno = configuration.workArounds.getJepInfo(featureName); Map<String, Object> jepInfo = configuration.workArounds.getJepInfo(featureName);
int number = 0; if (!jepInfo.isEmpty()) {
String title = ""; int number = 0;
String status = "Preview"; // Default value is not returned by the method we use above. String title = "";
for (var entry : anno.entrySet()) { String status = "Preview"; // Default value is not returned by the method we used above.
if ("number".equals(entry.getKey().getSimpleName().toString())) { for (var entry : jepInfo.entrySet()) {
number = (int) entry.getValue().getValue(); switch (entry.getKey()) {
} else if ("title".equals(entry.getKey().getSimpleName().toString())) { case "number" -> number = (int) entry.getValue();
title = (String) entry.getValue().getValue(); case "title" -> title = (String) entry.getValue();
} else if ("status".equals(entry.getKey().getSimpleName().toString())) { case "status" -> status = (String) entry.getValue();
status = (String) entry.getValue().getValue(); default -> throw new IllegalArgumentException(entry.getKey());
} else { }
throw new IllegalArgumentException(entry.getKey().getSimpleName().toString());
} }
return new JEP(number, title, status);
} }
return new JEP(number, title, status); return NULL_SENTINEL;
}); });
elementJeps.put(e, jep); if (jep != NULL_SENTINEL) {
elementJeps.put(element, jep);
return true;
}
// Preview features without JEP are not included.
return false;
} }
/** /**
* {@return a sorted set of preview feature JEPs in this release} * {@return a sorted set of preview feature JEPs in this release}
*/ */
public Set<JEP> getJEPs() { public Set<JEP> getJEPs() {
return new TreeSet<>(jeps.values()); return jeps.values()
.stream()
.filter(jep -> jep != NULL_SENTINEL)
.collect(Collectors.toCollection(TreeSet::new));
} }
/** /**

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -41,7 +41,12 @@ public class RestrictedAPIListBuilder extends SummaryAPIListBuilder {
* @param configuration the current configuration of the doclet * @param configuration the current configuration of the doclet
*/ */
public RestrictedAPIListBuilder(BaseConfiguration configuration) { public RestrictedAPIListBuilder(BaseConfiguration configuration) {
super(configuration, configuration.utils::isRestrictedAPI); super(configuration);
buildSummaryAPIInfo(); buildSummaryAPIInfo();
} }
@Override
protected boolean belongsToSummary(Element element) {
return utils.isRestrictedAPI(element);
}
} }

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -26,7 +26,6 @@
package jdk.javadoc.internal.doclets.toolkit.util; package jdk.javadoc.internal.doclets.toolkit.util;
import java.util.*; import java.util.*;
import java.util.function.Predicate;
import javax.lang.model.element.Element; import javax.lang.model.element.Element;
import javax.lang.model.element.ModuleElement; import javax.lang.model.element.ModuleElement;
@ -39,14 +38,13 @@ import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration;
/** /**
* Build list of all the summary packages, classes, constructors, fields and methods. * Build list of all the summary packages, classes, constructors, fields and methods.
*/ */
public class SummaryAPIListBuilder { public abstract class SummaryAPIListBuilder {
/** /**
* List of summary type Lists. * List of summary type Lists.
*/ */
private final Map<SummaryElementKind, SortedSet<Element>> summaryMap; private final Map<SummaryElementKind, SortedSet<Element>> summaryMap;
protected final BaseConfiguration configuration; protected final BaseConfiguration configuration;
protected final Utils utils; protected final Utils utils;
private final Predicate<Element> belongsToSummary;
public enum SummaryElementKind { public enum SummaryElementKind {
MODULE, MODULE,
@ -69,11 +67,9 @@ public class SummaryAPIListBuilder {
* *
* @param configuration the current configuration of the doclet * @param configuration the current configuration of the doclet
*/ */
public SummaryAPIListBuilder(BaseConfiguration configuration, protected SummaryAPIListBuilder(BaseConfiguration configuration) {
Predicate<Element> belongsToSummary) {
this.configuration = configuration; this.configuration = configuration;
this.utils = configuration.utils; this.utils = configuration.utils;
this.belongsToSummary = belongsToSummary;
summaryMap = new EnumMap<>(SummaryElementKind.class); summaryMap = new EnumMap<>(SummaryElementKind.class);
for (SummaryElementKind kind : SummaryElementKind.values()) { for (SummaryElementKind kind : SummaryElementKind.values()) {
summaryMap.put(kind, createSummarySet()); summaryMap.put(kind, createSummarySet());
@ -93,7 +89,7 @@ public class SummaryAPIListBuilder {
SortedSet<ModuleElement> modules = configuration.modules; SortedSet<ModuleElement> modules = configuration.modules;
SortedSet<Element> mset = summaryMap.get(SummaryElementKind.MODULE); SortedSet<Element> mset = summaryMap.get(SummaryElementKind.MODULE);
for (Element me : modules) { for (Element me : modules) {
if (belongsToSummary.test(me)) { if (belongsToSummary(me)) {
mset.add(me); mset.add(me);
handleElement(me); handleElement(me);
} }
@ -101,14 +97,14 @@ public class SummaryAPIListBuilder {
SortedSet<PackageElement> packages = configuration.packages; SortedSet<PackageElement> packages = configuration.packages;
SortedSet<Element> pset = summaryMap.get(SummaryElementKind.PACKAGE); SortedSet<Element> pset = summaryMap.get(SummaryElementKind.PACKAGE);
for (Element pe : packages) { for (Element pe : packages) {
if (belongsToSummary.test(pe)) { if (belongsToSummary(pe)) {
pset.add(pe); pset.add(pe);
handleElement(pe); handleElement(pe);
} }
} }
for (TypeElement te : configuration.getIncludedTypeElements()) { for (TypeElement te : configuration.getIncludedTypeElements()) {
SortedSet<Element> eset; SortedSet<Element> eset;
if (belongsToSummary.test(te)) { if (belongsToSummary(te)) {
switch (te.getKind()) { switch (te.getKind()) {
case ANNOTATION_TYPE -> { case ANNOTATION_TYPE -> {
eset = summaryMap.get(SummaryElementKind.ANNOTATION_TYPE); eset = summaryMap.get(SummaryElementKind.ANNOTATION_TYPE);
@ -149,7 +145,7 @@ public class SummaryAPIListBuilder {
} }
if (utils.isRecord(te)) { if (utils.isRecord(te)) {
for (RecordComponentElement component : te.getRecordComponents()) { for (RecordComponentElement component : te.getRecordComponents()) {
if (belongsToSummary.test(component)) { if (belongsToSummary(component)) {
throw new AssertionError("record components not supported in summary builders: " + throw new AssertionError("record components not supported in summary builders: " +
"component: " + component.getSimpleName() + "component: " + component.getSimpleName() +
" of record: " + te.getQualifiedName()); " of record: " + te.getQualifiedName());
@ -164,6 +160,14 @@ public class SummaryAPIListBuilder {
} }
} }
/**
* This method decides whether Element {@code element} should be included in this summary list.
*
* @param element an element
* @return true if the element should be included
*/
protected abstract boolean belongsToSummary(Element element);
/** /**
* Add the members into a single list of summary members. * Add the members into a single list of summary members.
* *
@ -172,7 +176,7 @@ public class SummaryAPIListBuilder {
*/ */
private void composeSummaryList(SortedSet<Element> sset, List<? extends Element> members) { private void composeSummaryList(SortedSet<Element> sset, List<? extends Element> members) {
for (Element member : members) { for (Element member : members) {
if (belongsToSummary.test(member)) { if (belongsToSummary(member)) {
sset.add(member); sset.add(member);
handleElement(member); handleElement(member);
} }

@ -24,6 +24,7 @@
/* /*
* @test * @test
* @bug 8250768 8261976 8277300 8282452 8287597 8325325 8325874 8297879 * @bug 8250768 8261976 8277300 8282452 8287597 8325325 8325874 8297879
* 8331947
* @summary test generated docs for items declared using preview * @summary test generated docs for items declared using preview
* @library ../../lib * @library ../../lib
* @modules jdk.javadoc/jdk.javadoc.internal.tool * @modules jdk.javadoc/jdk.javadoc.internal.tool
@ -156,6 +157,9 @@ public class TestPreview extends JavadocTester {
<li><a href="package-summary.html">preview</a></li> <li><a href="package-summary.html">preview</a></li>
<li><a href="Core.html" class="current-selection">Core</a></li> <li><a href="Core.html" class="current-selection">Core</a></li>
</ol>"""); </ol>""");
// 8331947: Support preview features without JEP should not be included in Preview API page
checkOutput("preview-list.html", false, "supportMethod");
} }
@Test @Test

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -34,4 +34,8 @@ public class NoPreview {
@PreviewFeature(feature=Feature.TEST) @PreviewFeature(feature=Feature.TEST)
public static class T {} public static class T {}
// Preview support feature without JEP should not be listed
@PreviewFeature(feature=Feature.LANGUAGE_MODEL)
public void supportMethod() {}
} }