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

View File

@ -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.
*
* 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.SortedSet;
import java.util.TreeSet;
import java.util.stream.Collectors;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.ModuleElement;
@ -48,15 +48,12 @@ import javax.tools.JavaFileManager.Location;
import com.sun.source.util.TreePath;
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.ClassSymbol;
import com.sun.tools.javac.code.Symbol.MethodSymbol;
import com.sun.tools.javac.code.Symbol.ModuleSymbol;
import com.sun.tools.javac.code.Symbol.PackageSymbol;
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.Options;
@ -408,7 +405,7 @@ public class WorkArounds {
* @param feature the name of the PreviewFeature.Feature enum value
* @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 jepType = elementUtils.getTypeElement("jdk.internal.javac.PreviewFeature.JEP");
var featureVar = featureType.getEnclosedElements().stream()
@ -416,7 +413,11 @@ public class WorkArounds {
if (featureVar.isPresent()) {
for (AnnotationMirror anno : featureVar.get().getAnnotationMirrors()) {
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()));
}
}
}

View File

@ -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.
*
* 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
*/
public DeprecatedAPIListBuilder(BaseConfiguration configuration, List<String> since) {
super(configuration, configuration.utils::isDeprecated);
super(configuration);
this.foundReleases = new HashSet<>();
buildSummaryAPIInfo();
// 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;
}
@Override
protected boolean belongsToSummary(Element element) {
return utils.isDeprecated(element);
}
@Override
protected void handleElement(Element e) {
foundReleases.add(utils.getDeprecatedSince(e));

View File

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

View File

@ -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.
*
* 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 javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
/**
* 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<String, JEP> jeps = new HashMap<>();
private static final JEP NULL_SENTINEL = new JEP(0, "", "");
/**
* 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
*/
public PreviewAPIListBuilder(BaseConfiguration configuration) {
super(configuration, configuration.utils::isPreviewAPI);
super(configuration);
buildSummaryAPIInfo();
}
@Override
protected void handleElement(Element e) {
String feature = Objects.requireNonNull(utils.getPreviewFeature(e),
protected boolean belongsToSummary(Element element) {
if (!utils.isPreviewAPI(element)) {
return false;
}
String feature = Objects.requireNonNull(utils.getPreviewFeature(element),
"Preview feature not specified").toString();
JEP jep = jeps.computeIfAbsent(feature, (featureName) -> {
Map<? extends ExecutableElement, ? extends AnnotationValue> anno = configuration.workArounds.getJepInfo(featureName);
int number = 0;
String title = "";
String status = "Preview"; // Default value is not returned by the method we use above.
for (var entry : anno.entrySet()) {
if ("number".equals(entry.getKey().getSimpleName().toString())) {
number = (int) entry.getValue().getValue();
} else if ("title".equals(entry.getKey().getSimpleName().toString())) {
title = (String) entry.getValue().getValue();
} else if ("status".equals(entry.getKey().getSimpleName().toString())) {
status = (String) entry.getValue().getValue();
} else {
throw new IllegalArgumentException(entry.getKey().getSimpleName().toString());
JEP jep = jeps.computeIfAbsent(feature, featureName -> {
Map<String, Object> jepInfo = configuration.workArounds.getJepInfo(featureName);
if (!jepInfo.isEmpty()) {
int number = 0;
String title = "";
String status = "Preview"; // Default value is not returned by the method we used above.
for (var entry : jepInfo.entrySet()) {
switch (entry.getKey()) {
case "number" -> number = (int) entry.getValue();
case "title" -> title = (String) entry.getValue();
case "status" -> status = (String) entry.getValue();
default -> throw new IllegalArgumentException(entry.getKey());
}
}
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}
*/
public Set<JEP> getJEPs() {
return new TreeSet<>(jeps.values());
return jeps.values()
.stream()
.filter(jep -> jep != NULL_SENTINEL)
.collect(Collectors.toCollection(TreeSet::new));
}
/**

View File

@ -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.
*
* 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
*/
public RestrictedAPIListBuilder(BaseConfiguration configuration) {
super(configuration, configuration.utils::isRestrictedAPI);
super(configuration);
buildSummaryAPIInfo();
}
@Override
protected boolean belongsToSummary(Element element) {
return utils.isRestrictedAPI(element);
}
}

View File

@ -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.
*
* This code is free software; you can redistribute it and/or modify it
@ -26,7 +26,6 @@
package jdk.javadoc.internal.doclets.toolkit.util;
import java.util.*;
import java.util.function.Predicate;
import javax.lang.model.element.Element;
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.
*/
public class SummaryAPIListBuilder {
public abstract class SummaryAPIListBuilder {
/**
* List of summary type Lists.
*/
private final Map<SummaryElementKind, SortedSet<Element>> summaryMap;
protected final BaseConfiguration configuration;
protected final Utils utils;
private final Predicate<Element> belongsToSummary;
public enum SummaryElementKind {
MODULE,
@ -69,11 +67,9 @@ public class SummaryAPIListBuilder {
*
* @param configuration the current configuration of the doclet
*/
public SummaryAPIListBuilder(BaseConfiguration configuration,
Predicate<Element> belongsToSummary) {
protected SummaryAPIListBuilder(BaseConfiguration configuration) {
this.configuration = configuration;
this.utils = configuration.utils;
this.belongsToSummary = belongsToSummary;
summaryMap = new EnumMap<>(SummaryElementKind.class);
for (SummaryElementKind kind : SummaryElementKind.values()) {
summaryMap.put(kind, createSummarySet());
@ -93,7 +89,7 @@ public class SummaryAPIListBuilder {
SortedSet<ModuleElement> modules = configuration.modules;
SortedSet<Element> mset = summaryMap.get(SummaryElementKind.MODULE);
for (Element me : modules) {
if (belongsToSummary.test(me)) {
if (belongsToSummary(me)) {
mset.add(me);
handleElement(me);
}
@ -101,14 +97,14 @@ public class SummaryAPIListBuilder {
SortedSet<PackageElement> packages = configuration.packages;
SortedSet<Element> pset = summaryMap.get(SummaryElementKind.PACKAGE);
for (Element pe : packages) {
if (belongsToSummary.test(pe)) {
if (belongsToSummary(pe)) {
pset.add(pe);
handleElement(pe);
}
}
for (TypeElement te : configuration.getIncludedTypeElements()) {
SortedSet<Element> eset;
if (belongsToSummary.test(te)) {
if (belongsToSummary(te)) {
switch (te.getKind()) {
case ANNOTATION_TYPE -> {
eset = summaryMap.get(SummaryElementKind.ANNOTATION_TYPE);
@ -149,7 +145,7 @@ public class SummaryAPIListBuilder {
}
if (utils.isRecord(te)) {
for (RecordComponentElement component : te.getRecordComponents()) {
if (belongsToSummary.test(component)) {
if (belongsToSummary(component)) {
throw new AssertionError("record components not supported in summary builders: " +
"component: " + component.getSimpleName() +
" 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.
*
@ -172,7 +176,7 @@ public class SummaryAPIListBuilder {
*/
private void composeSummaryList(SortedSet<Element> sset, List<? extends Element> members) {
for (Element member : members) {
if (belongsToSummary.test(member)) {
if (belongsToSummary(member)) {
sset.add(member);
handleElement(member);
}

View File

@ -24,6 +24,7 @@
/*
* @test
* @bug 8250768 8261976 8277300 8282452 8287597 8325325 8325874 8297879
* 8331947
* @summary test generated docs for items declared using preview
* @library ../../lib
* @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="Core.html" class="current-selection">Core</a></li>
</ol>""");
// 8331947: Support preview features without JEP should not be included in Preview API page
checkOutput("preview-list.html", false, "supportMethod");
}
@Test

View File

@ -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.
*
* This code is free software; you can redistribute it and/or modify it
@ -34,4 +34,8 @@ public class NoPreview {
@PreviewFeature(feature=Feature.TEST)
public static class T {}
// Preview support feature without JEP should not be listed
@PreviewFeature(feature=Feature.LANGUAGE_MODEL)
public void supportMethod() {}
}