8270195: Add missing links between methods of JavaFX properties

Reviewed-by: kcr, hannesw
This commit is contained in:
Jonathan Gibbons 2021-08-24 16:10:25 +00:00
parent f608e81ad8
commit d34f17c697
12 changed files with 867 additions and 284 deletions

View File

@ -90,10 +90,8 @@ import jdk.javadoc.internal.doclets.formats.html.markup.RawHtml;
import jdk.javadoc.internal.doclets.formats.html.markup.Script;
import jdk.javadoc.internal.doclets.formats.html.markup.TagName;
import jdk.javadoc.internal.doclets.formats.html.markup.Text;
import jdk.javadoc.internal.doclets.toolkit.ClassWriter;
import jdk.javadoc.internal.doclets.toolkit.Content;
import jdk.javadoc.internal.doclets.toolkit.Messages;
import jdk.javadoc.internal.doclets.toolkit.PackageSummaryWriter;
import jdk.javadoc.internal.doclets.toolkit.Resources;
import jdk.javadoc.internal.doclets.toolkit.taglets.DocRootTaglet;
import jdk.javadoc.internal.doclets.toolkit.taglets.Taglet;
@ -1228,10 +1226,10 @@ public class HtmlDocletWriter {
}
/**
* Adds the inline comment.
* Adds the full-body content of the given element.
*
* @param element the Element for which the inline comments will be generated
* @param htmltree the documentation tree to which the inline comments will be added
* @param element the element for which the content will be added
* @param htmltree the documentation tree to which the content will be added
*/
public void addInlineComment(Element element, Content htmltree) {
addCommentTags(element, utils.getFullBody(element), false, false, false, htmltree);

View File

@ -23,14 +23,6 @@
* questions.
*/
/**
* A utility class.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
package jdk.javadoc.internal.doclets.toolkit;
@ -57,9 +49,13 @@ import com.sun.source.doctree.AttributeTree;
import com.sun.source.doctree.DocCommentTree;
import com.sun.source.doctree.DocTree;
import com.sun.source.doctree.IdentifierTree;
import com.sun.source.doctree.LiteralTree;
import com.sun.source.doctree.ParamTree;
import com.sun.source.doctree.ReferenceTree;
import com.sun.source.doctree.ReturnTree;
import com.sun.source.doctree.SinceTree;
import com.sun.source.doctree.TextTree;
import com.sun.source.doctree.UnknownBlockTagTree;
import com.sun.source.util.DocTreeFactory;
import com.sun.source.util.DocTreePath;
import com.sun.source.util.DocTrees;
@ -67,7 +63,16 @@ import com.sun.source.util.TreePath;
import com.sun.tools.javac.util.DefinedBy;
import com.sun.tools.javac.util.DefinedBy.Api;
import jdk.javadoc.internal.doclets.toolkit.util.Utils;
import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable;
/**
* A utility class for handling documentation comments.
*
* <p><b>This is NOT part of any supported API.
* If you write code that depends on this, you do so at your own risk.
* This code and its internal interfaces are subject to change or
* deletion without notice.</b>
*/
public class CommentUtils {
final BaseConfiguration configuration;
@ -96,31 +101,28 @@ public class CommentUtils {
}
public List<? extends DocTree> makePropertyDescriptionTree(List<? extends DocTree> content) {
List<DocTree> out = new ArrayList<>();
Name name = elementUtils.getName("propertyDescription");
out.add(treeFactory.newUnknownBlockTagTree(name, content));
return out;
return List.of(treeFactory.newUnknownBlockTagTree(name, content));
}
public List<? extends DocTree> makePropertyDescriptionTree(String content) {
List<DocTree> inlist = new ArrayList<>();
inlist.add(treeFactory.newCommentTree(content));
List<DocTree> out = new ArrayList<>();
Name name = elementUtils.getName("propertyDescription");
out.add(treeFactory.newUnknownBlockTagTree(name, inlist));
return out;
public LiteralTree makeCodeTree(String text) {
return treeFactory.newCodeTree(makeTextTree(text));
}
public List<? extends DocTree> makeFirstSentenceTree(String content) {
List<DocTree> out = new ArrayList<>();
out.add(treeFactory.newTextTree(content));
return out;
return List.of(treeFactory.newTextTree(content));
}
public ParamTree makeParamTree(Name name, List<? extends DocTree> description) {
return treeFactory.newParamTree(false, treeFactory.newIdentifierTree(name), description);
}
public ReturnTree makeReturnTree(List<? extends DocTree> description) {
return treeFactory.newReturnTree(false, description);
}
public DocTree makeSeeTree(String sig, Element e) {
List<DocTree> list = new ArrayList<>();
list.add(treeFactory.newReferenceTree(sig));
return treeFactory.newSeeTree(list);
return treeFactory.newSeeTree(List.of(treeFactory.newReferenceTree(sig)));
}
public TextTree makeTextTree(String content) {
@ -189,7 +191,6 @@ public class CommentUtils {
makeDescriptionWithName("doclet.record_constructor_doc.fullbody", te.getSimpleName());
List<DocTree> tags = new ArrayList<>();
java.util.List<? extends VariableElement> parameters = ee.getParameters();
for (VariableElement param : ee.getParameters()) {
Name name = param.getSimpleName();
IdentifierTree id = treeFactory.newIdentifierTree(name);
@ -318,6 +319,107 @@ public class CommentUtils {
dcInfoMap.put(ve, new DocCommentInfo(null, docTree));
}
/**
* Update the property method, property setter and/or property getter
* comment text so that it contains the documentation from
* the preferred property description (field or property method).
* The method adds the leading sentence, copied documentation including
* the defaultValue tag and the {@code @see} tags if the appropriate methods are
* available.
*
* @param member the member which is to be augmented
* @param property the element containing the preferred property description
*/
public void updatePropertyMethodComment(ExecutableElement member,
Element property) {
final String memberName = member.getSimpleName().toString();
final boolean isSetter = memberName.startsWith("set");
final boolean isGetter = memberName.startsWith("get") || memberName.startsWith("is");
List<DocTree> fullBody = new ArrayList<>();
List<DocTree> blockTags = new ArrayList<>();
if (isGetter || isSetter) {
DocTree propName = makeCodeTree(utils.propertyName(member));
if (isGetter) {
// Set the body and @return
fullBody.addAll(getComment("doclet.PropertyGetterWithName", propName));
blockTags.add(makeReturnTree(
getComment("doclet.PropertyGetterReturn", propName)));
}
if (isSetter) {
// Set the body and @param
fullBody.addAll(getComment("doclet.PropertySetterWithName", propName));
VariableElement arg0 = member.getParameters().get(0);
blockTags.add(makeParamTree(arg0.getSimpleName(),
getComment("doclet.PropertySetterParam", propName)));
}
// Set the @propertyDescription
List<? extends DocTree> propertyTags = utils.getBlockTags(property,
t -> (t instanceof UnknownBlockTagTree tree)
&& (tree.getTagName().equals("propertyDescription")));
if (propertyTags.isEmpty()) {
List<? extends DocTree> comment = utils.getFullBody(property);
blockTags.addAll(makePropertyDescriptionTree(comment));
}
} else {
// property method
fullBody.addAll(utils.getFullBody(property));
// Set the @return
DocTree propName = makeCodeTree(configuration.propertyUtils.getBaseName(member));
List<? extends DocTree> returnTags = utils.getBlockTags(property, DocTree.Kind.RETURN);
if (returnTags.isEmpty()) {
blockTags.add(makeReturnTree(
getComment("doclet.PropertyMethodReturn", propName)));
} else {
blockTags.addAll(returnTags);
}
}
// copy certain tags
List<? extends SinceTree> sinceTags = utils.getBlockTags(property, DocTree.Kind.SINCE, SinceTree.class);
blockTags.addAll(sinceTags);
List<? extends DocTree> bTags = utils.getBlockTags(property,
t -> (t instanceof UnknownBlockTagTree tree)
&& (tree.getTagName().equals("defaultValue")));
blockTags.addAll(bTags);
//add @see tags
TypeElement te = (TypeElement) member.getEnclosingElement();
VisibleMemberTable vmt = configuration.getVisibleMemberTable(te);
ExecutableElement getter = vmt.getPropertyGetter(member);
ExecutableElement setter = vmt.getPropertySetter(member);
ExecutableElement propMethod = vmt.getPropertyMethod(member);
if (getter != null && getter != member) {
String sig = "#" + getter.getSimpleName() + "()";
blockTags.add(makeSeeTree(sig, getter));
}
if (setter != null && setter != member) {
VariableElement param = setter.getParameters().get(0);
StringBuilder sb = new StringBuilder("#");
sb.append(setter.getSimpleName());
if (!utils.isTypeVariable(param.asType())) {
sb.append("(").append(utils.getTypeSignature(param.asType(), false, true)).append(")");
}
blockTags.add(makeSeeTree(sb.toString(), setter));
}
if (propMethod != member) {
String sig = "#" + propMethod.getSimpleName() + "()";
blockTags.add(makeSeeTree(sig, propMethod));
}
setDocCommentTree(member, fullBody, blockTags);
}
/**
* Creates a description that contains a reference to a state component of a record.
* The description is looked up as a resource, and should contain {@code {0}} where the
@ -401,6 +503,59 @@ public class CommentUtils {
}
}
/**
* Returns a list containing the string for a given key in the doclet's resources,
* formatted with given arguments.
*
* @param key the key for the desired string
* @param o0 string or tree argument to be formatted into the result
* @return a content tree for the text
*/
public List<? extends DocTree> getComment(String key, Object o0) {
return getComment(key, o0, null, null);
}
/**
* Returns a list containing the string for a given key in the doclet's resources,
* formatted with given arguments.
*
* @param key the key for the desired string
* @param o0 string or tree argument to be formatted into the result
* @param o1 string or tree argument to be formatted into the result
* @param o2 string or tree argument to be formatted into the result
* @return a content tree for the text
*/
public List<? extends DocTree> getComment(String key, Object o0, Object o1, Object o2) {
List<DocTree> l = new ArrayList<>();
Pattern p = Pattern.compile("\\{([012])\\}");
String text = resources.getText(key);
Matcher m = p.matcher(text);
int start = 0;
while (m.find(start)) {
l.add(makeTextTree(text.substring(start, m.start())));
Object o = null;
switch (m.group(1).charAt(0)) {
case '0': o = o0; break;
case '1': o = o1; break;
case '2': o = o2; break;
}
if (o == null) {
l.add(makeTextTree("{" + m.group(1) + "}"));
} else if (o instanceof String str) {
l.add(makeTextTree(str));
} else if (o instanceof DocTree t) {
l.add(t);
}
start = m.end();
}
l.add(makeTextTree(text.substring(start)));
return l;
}
/*
* Returns the TreePath/DocCommentTree info that has been generated for an element.
* @param e the element
@ -453,13 +608,18 @@ public class CommentUtils {
});
}
public void setDocCommentTree(Element element, List<? extends DocTree> fullBody,
public DocCommentInfo setDocCommentTree(Element element, List<? extends DocTree> fullBody,
List<? extends DocTree> blockTags) {
DocCommentTree docTree = treeFactory.newDocCommentTree(fullBody, blockTags);
dcInfoMap.put(element, new DocCommentInfo(null, docTree));
return setDocCommentInfo(element, new DocCommentInfo(null, docTree));
}
public DocCommentInfo setDocCommentInfo(Element element, DocCommentInfo dci) {
DocCommentInfo prev = dcInfoMap.put(element, dci);
// A method having null comment (no comment) that might need to be replaced
// with a generated comment, remove such a comment from the cache.
utils.removeCommentHelper(element);
return prev;
}
/**

View File

@ -74,7 +74,7 @@ public interface PropertyWriter extends MemberWriter {
* Add the preview output for the given member.
*
* @param member the member being documented
* @param annotationDocTree content tree to which the preview information will be added
* @param contentTree content tree to which the preview information will be added
*/
void addPreview(ExecutableElement member, Content contentTree);

View File

@ -25,20 +25,26 @@
package jdk.javadoc.internal.doclets.toolkit.builders;
import java.text.MessageFormat;
import java.util.*;
import java.util.Collection;
import java.util.Comparator;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.SortedSet;
import java.util.TreeSet;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.ElementFilter;
import javax.tools.Diagnostic;
import com.sun.source.doctree.DocCommentTree;
import com.sun.source.doctree.DocTree;
import com.sun.source.doctree.DocTree.Kind;
import com.sun.source.doctree.SinceTree;
import com.sun.source.doctree.UnknownBlockTagTree;
import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration;
import jdk.javadoc.internal.doclets.toolkit.ClassWriter;
import jdk.javadoc.internal.doclets.toolkit.Content;
import jdk.javadoc.internal.doclets.toolkit.MemberSummaryWriter;
@ -47,7 +53,6 @@ import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper;
import jdk.javadoc.internal.doclets.toolkit.util.DocFinder;
import jdk.javadoc.internal.doclets.toolkit.util.Utils;
import jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable;
import jdk.javadoc.internal.doclets.toolkit.CommentUtils;
import static jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable.Kind.*;
@ -258,8 +263,8 @@ public abstract class MemberSummaryBuilder extends AbstractMemberBuilder {
if (!members.isEmpty()) {
for (Element member : members) {
final Element property = pHelper.getPropertyElement(member);
if (property != null) {
processProperty(member, property);
if (property != null && member instanceof ExecutableElement ee) {
configuration.cmtUtils.updatePropertyMethodComment(ee, property);
}
List<? extends DocTree> firstSentenceTags = utils.getFirstSentenceTrees(member);
if (utils.isExecutableElement(member) && firstSentenceTags.isEmpty()) {
@ -279,104 +284,6 @@ public abstract class MemberSummaryBuilder extends AbstractMemberBuilder {
}
}
/**
* Process the property method, property setter and/or property getter
* comment text so that it contains the documentation from
* the property field. The method adds the leading sentence,
* copied documentation including the defaultValue tag and
* the see tags if the appropriate property getter and setter are
* available.
*
* @param member the member which is to be augmented.
* @param property the original property documentation.
*/
private void processProperty(Element member,
Element property) {
CommentUtils cmtutils = configuration.cmtUtils;
final boolean isSetter = isSetter(member);
final boolean isGetter = isGetter(member);
List<DocTree> fullBody = new ArrayList<>();
List<DocTree> blockTags = new ArrayList<>();
if (isGetter || isSetter) {
//add "[GS]ets the value of the property PROPERTY_NAME."
if (isSetter) {
String text = MessageFormat.format(
resources.getText("doclet.PropertySetterWithName"),
utils.propertyName((ExecutableElement)member));
fullBody.addAll(cmtutils.makeFirstSentenceTree(text));
}
if (isGetter) {
String text = MessageFormat.format(
resources.getText("doclet.PropertyGetterWithName"),
utils.propertyName((ExecutableElement) member));
fullBody.addAll(cmtutils.makeFirstSentenceTree(text));
}
List<? extends DocTree> propertyTags = utils.getBlockTags(property,
t -> (t instanceof UnknownBlockTagTree tree)
&& (tree.getTagName().equals("propertyDescription")));
if (propertyTags.isEmpty()) {
List<? extends DocTree> comment = utils.getFullBody(property);
blockTags.addAll(cmtutils.makePropertyDescriptionTree(comment));
}
} else {
fullBody.addAll(utils.getFullBody(property));
}
// copy certain tags
List<? extends SinceTree> tags = utils.getBlockTags(property, Kind.SINCE, SinceTree.class);
blockTags.addAll(tags);
List<? extends DocTree> bTags = utils.getBlockTags(property,
t -> (t instanceof UnknownBlockTagTree tree)
&& (tree.getTagName().equals("defaultValue")));
blockTags.addAll(bTags);
//add @see tags
if (!isGetter && !isSetter) {
ExecutableElement getter = pHelper.getGetterForProperty((ExecutableElement)member);
ExecutableElement setter = pHelper.getSetterForProperty((ExecutableElement)member);
if (null != getter) {
StringBuilder sb = new StringBuilder("#");
sb.append(utils.getSimpleName(getter)).append("()");
blockTags.add(cmtutils.makeSeeTree(sb.toString(), getter));
}
if (null != setter) {
VariableElement param = setter.getParameters().get(0);
StringBuilder sb = new StringBuilder("#");
sb.append(utils.getSimpleName(setter));
if (!utils.isTypeVariable(param.asType())) {
sb.append("(").append(utils.getTypeSignature(param.asType(), false, true)).append(")");
}
blockTags.add(cmtutils.makeSeeTree(sb.toString(), setter));
}
}
cmtutils.setDocCommentTree(member, fullBody, blockTags);
}
/**
* Test whether the method is a getter.
* @param element property method documentation. Needs to be either property
* method, property getter, or property setter.
* @return true if the given documentation belongs to a getter.
*/
private boolean isGetter(Element element) {
final String pedName = element.getSimpleName().toString();
return pedName.startsWith("get") || pedName.startsWith("is");
}
/**
* Test whether the method is a setter.
* @param element property method documentation. Needs to be either property
* method, property getter, or property setter.
* @return true if the given documentation belongs to a setter.
*/
private boolean isSetter(Element element) {
return element.getSimpleName().toString().startsWith("set");
}
/**
* Build the inherited member summary for the given methods.
*
@ -456,6 +363,19 @@ public abstract class MemberSummaryBuilder extends AbstractMemberBuilder {
return out;
}
/**
* A utility class to manage the property-related methods that should be
* synthesized or updated.
*
* A property may comprise a field (that is typically private, if present),
* a {@code fooProperty()} method (which is the defining characteristic for
* a property), a {@code getFoo()} method and/or a {@code setFoo(Foo foo)} method.
*
* Either the field (if present) or the {@code fooProperty()} method should have a
* comment. If there is no field, or no comment on the field, the description for
* the property will be derived from the description of the {@code fooProperty()}
* method. If any method does not have a comment, one will be provided.
*/
static class PropertyHelper {
private final Map<Element, Element> classPropertiesMap = new HashMap<>();
@ -483,22 +403,28 @@ public abstract class MemberSummaryBuilder extends AbstractMemberBuilder {
VariableElement field,
ExecutableElement getter,
ExecutableElement setter) {
if (field == null || !builder.utils.hasDocCommentTree(field)) {
addToPropertiesMap(propertyMethod, propertyMethod);
addToPropertiesMap(getter, propertyMethod);
addToPropertiesMap(setter, propertyMethod);
} else {
addToPropertiesMap(propertyMethod, field);
addToPropertiesMap(getter, field);
addToPropertiesMap(setter, field);
// determine the preferred element from which to derive the property description
Element e = field == null || !builder.utils.hasDocCommentTree(field)
? propertyMethod : field;
if (e == field && builder.utils.hasDocCommentTree(propertyMethod)) {
BaseConfiguration configuration = builder.configuration;
configuration.getReporter().print(Diagnostic.Kind.WARNING,
propertyMethod, configuration.getDocResources().getText("doclet.duplicate.comment.for.property"));
}
addToPropertiesMap(propertyMethod, e);
addToPropertiesMap(getter, e);
addToPropertiesMap(setter, e);
}
private void addToPropertiesMap(Element propertyMethod,
Element commentSource) {
if (null == propertyMethod || null == commentSource) {
Objects.requireNonNull(commentSource);
if (propertyMethod == null) {
return;
}
Utils utils = builder.utils;
DocCommentTree docTree = utils.hasDocCommentTree(propertyMethod)
? utils.getDocCommentTree(propertyMethod)
@ -514,30 +440,12 @@ public abstract class MemberSummaryBuilder extends AbstractMemberBuilder {
}
/**
* Returns the property field documentation belonging to the given member.
* Returns the element for the property documentation belonging to the given member.
* @param element the member for which the property documentation is needed.
* @return the property field documentation, null if there is none.
* @return the element for the property documentation, null if there is none.
*/
public Element getPropertyElement(Element element) {
return classPropertiesMap.get(element);
}
/**
* Returns the getter documentation belonging to the given property method.
* @param propertyMethod the method for which the getter is needed.
* @return the getter documentation, null if there is none.
*/
public ExecutableElement getGetterForProperty(ExecutableElement propertyMethod) {
return builder.getVisibleMemberTable().getPropertyGetter(propertyMethod);
}
/**
* Returns the setter documentation belonging to the given property method.
* @param propertyMethod the method for which the setter is needed.
* @return the setter documentation, null if there is none.
*/
public ExecutableElement getSetterForProperty(ExecutableElement propertyMethod) {
return builder.getVisibleMemberTable().getPropertySetter(propertyMethod);
}
}
}

View File

@ -25,18 +25,23 @@
package jdk.javadoc.internal.doclets.toolkit.builders;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import com.sun.source.doctree.DocCommentTree;
import com.sun.source.doctree.DocTree;
import jdk.javadoc.internal.doclets.toolkit.BaseOptions;
import jdk.javadoc.internal.doclets.toolkit.CommentUtils;
import jdk.javadoc.internal.doclets.toolkit.Content;
import jdk.javadoc.internal.doclets.toolkit.DocletException;
import jdk.javadoc.internal.doclets.toolkit.PropertyWriter;
import static jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable.Kind.*;
import static jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable.Kind.PROPERTIES;
/**
* Builds documentation for a property.
@ -179,7 +184,25 @@ public class PropertyBuilder extends AbstractMemberBuilder {
* @param propertyDocTree the content tree to which the documentation will be added
*/
protected void buildTagInfo(Content propertyDocTree) {
writer.addTags(currentProperty, propertyDocTree);
CommentUtils cmtUtils = configuration.cmtUtils;
DocCommentTree dct = utils.getDocCommentTree(currentProperty);
var fullBody = dct.getFullBody();
ArrayList<DocTree> blockTags = dct.getBlockTags().stream()
.filter(t -> t.getKind() != DocTree.Kind.RETURN)
.collect(Collectors.toCollection(ArrayList::new));
String sig = "#" + currentProperty.getSimpleName() + "()";
blockTags.add(cmtUtils.makeSeeTree(sig, currentProperty));
// The property method is used as a proxy for the property
// (which does not have an explicit element of its own.)
// Temporarily override the doc comment for the property method
// by removing the `@return` tag, which should not be displayed for
// the property.
CommentUtils.DocCommentInfo prev = cmtUtils.setDocCommentTree(currentProperty, fullBody, blockTags);
try {
writer.addTags(currentProperty, propertyDocTree);
} finally {
cmtUtils.setDocCommentInfo(currentProperty, prev);
}
}
/**

View File

@ -86,7 +86,10 @@ doclet.Author=Author:
doclet.DefaultValue=Default value:
doclet.PropertyDescription=Property description:
doclet.PropertyGetterWithName=Gets the value of the property {0}.
doclet.PropertyGetterReturn=the value of the property {0}
doclet.PropertySetterWithName=Sets the value of the property {0}.
doclet.PropertySetterParam=the value for the property {0}
doclet.PropertyMethodReturn=the property {0}
doclet.Default=Default:
doclet.Parameters=Parameters:
doclet.TypeParameters=Type Parameters:
@ -237,6 +240,8 @@ doclet.linkMismatch_PackagedLinkedtoModule=The code being documented uses packag
doclet.linkMismatch_ModuleLinkedtoPackage=The code being documented uses modules but the packages defined \
in {0} are in the unnamed module.
doclet.urlRedirected=URL {0} was redirected to {1} -- Update the command-line options to suppress this warning.
doclet.duplicate.comment.for.property=Duplicate comment for property.\n\
Remove the comment on the property field or on this method to suppress this warning.
#Documentation for Enums
doclet.enum_values_doc.fullbody=\

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2021, 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
@ -351,38 +351,57 @@ public class VisibleMemberTable {
}
/**
* Returns the property field associated with the property method.
* @param propertyMethod the identifying property method
* Returns the field for a property identified by any of the methods
* for that property.
*
* @param ee the method
* @return the field or null if absent
*/
public VariableElement getPropertyField(ExecutableElement propertyMethod) {
public VariableElement getPropertyField(ExecutableElement ee) {
ensureInitialized();
PropertyMembers pm = propertyMap.get(propertyMethod);
PropertyMembers pm = propertyMap.get(ee);
return pm == null ? null : pm.field;
}
/**
* Returns the getter method associated with the property method.
* @param propertyMethod the identifying property method
* Returns the getter method for a property identified by any of the methods
* for that property.
*
* @param ee the method
* @return the getter or null if absent
*/
public ExecutableElement getPropertyGetter(ExecutableElement propertyMethod) {
public ExecutableElement getPropertyGetter(ExecutableElement ee) {
ensureInitialized();
PropertyMembers pm = propertyMap.get(propertyMethod);
PropertyMembers pm = propertyMap.get(ee);
return pm == null ? null : pm.getter;
}
/**
* Returns the setter method associated with the property method.
* @param propertyMethod the identifying property method
* Returns the setter method for a property identified by any of the methods
* for that property.
*
* @param ee the method
* @return the setter or null if absent
*/
public ExecutableElement getPropertySetter(ExecutableElement propertyMethod) {
public ExecutableElement getPropertySetter(ExecutableElement ee) {
ensureInitialized();
PropertyMembers pm = propertyMap.get(propertyMethod);
PropertyMembers pm = propertyMap.get(ee);
return pm == null ? null : pm.setter;
}
/**
* Returns the property method for a property identified by any of the methods
* for that property.
*
* @param ee the method
* @return the property method or null if absent
*/
public ExecutableElement getPropertyMethod(ExecutableElement ee) {
ensureInitialized();
PropertyMembers pm = propertyMap.get(ee);
return pm == null ? null : pm.propertyMethod;
}
private void computeParents() {
// suppress parents of annotation types
if (utils.isAnnotationType(te)) {
@ -817,35 +836,28 @@ public class VisibleMemberTable {
}
List<Element> getMembers(String key, Kind kind) {
Map <String, List<Element>> map = memberMap.get(kind);
Map<String, List<Element>> map = memberMap.get(kind);
return map.getOrDefault(key, Collections.emptyList());
}
List<Element> getPropertyMethods(String methodName, int argcount) {
<T extends Element> List<T> getMembers(String key, Kind kind, Class<T> clazz) {
Map<String, List<Element>> map = memberMap.get(kind);
return map.getOrDefault(key, Collections.emptyList())
.stream()
.map(e -> clazz.cast(e))
.toList();
}
List<ExecutableElement> getPropertyMethods(String methodName, int argcount) {
return getMembers(methodName + ":" + argcount, Kind.METHODS).stream()
.filter(m -> (utils.isPublic(m) || utils.isProtected(m)))
.map(m -> (ExecutableElement) m)
.toList();
}
}
/**
* The properties triad for a property method.
*/
static class PropertyMembers {
final VariableElement field;
final ExecutableElement getter;
final ExecutableElement setter;
PropertyMembers(VariableElement field, ExecutableElement getter, ExecutableElement setter) {
this.field = field;
this.getter = getter;
this.setter = setter;
}
public String toString() {
return ("field: " + field + ", getter: " + getter + ", setter: " + setter);
}
}
record PropertyMembers(ExecutableElement propertyMethod, VariableElement field,
ExecutableElement getter, ExecutableElement setter) { }
/*
* JavaFX convention notes.
@ -901,11 +913,11 @@ public class VisibleMemberTable {
// Compute additional properties related sundries.
for (ExecutableElement propertyMethod : propertyMethods) {
String baseName = pUtils.getBaseName(propertyMethod);
List<Element> flist = lmt.getMembers(baseName, Kind.FIELDS);
Element field = flist.isEmpty() ? null : flist.get(0);
List<VariableElement> flist = lmt.getMembers(baseName, Kind.FIELDS, VariableElement.class);
VariableElement field = flist.isEmpty() ? null : flist.get(0);
Element getter = null, setter = null;
List<Element> found = lmt.getPropertyMethods(pUtils.getGetName(propertyMethod), 0);
ExecutableElement getter = null, setter = null;
List<ExecutableElement> found = lmt.getPropertyMethods(pUtils.getGetName(propertyMethod), 0);
if (!found.isEmpty()) {
// Getters have zero params, no overloads! pick the first.
getter = found.get(0);
@ -914,7 +926,6 @@ public class VisibleMemberTable {
// Check if isProperty methods are present ?
found = lmt.getPropertyMethods(pUtils.getIsName(propertyMethod), 0);
if (!found.isEmpty()) {
String propertyTypeName = propertyMethod.getReturnType().toString();
// Check if the return type of property method matches an isProperty method.
if (pUtils.hasIsMethod(propertyMethod)) {
// Getters have zero params, no overloads!, pick the first.
@ -924,16 +935,22 @@ public class VisibleMemberTable {
}
found = lmt.getPropertyMethods(pUtils.getSetName(propertyMethod), 1);
if (found != null) {
for (Element e : found) {
if (pUtils.isValidSetterMethod((ExecutableElement)e)) {
for (ExecutableElement e : found) {
if (pUtils.isValidSetterMethod(e)) {
setter = e;
break;
}
}
}
propertyMap.put(propertyMethod, new PropertyMembers((VariableElement)field,
(ExecutableElement)getter, (ExecutableElement)setter));
PropertyMembers pm = new PropertyMembers(propertyMethod, field, getter, setter);
propertyMap.put(propertyMethod, pm);
if (getter != null) {
propertyMap.put(getter, pm);
}
if (setter != null) {
propertyMap.put(setter, pm);
}
// Debugging purposes
// System.out.println("te: " + te + ": " + utils.getEnclosingTypeElement(propertyMethod) +

View File

@ -54,6 +54,7 @@ public class TestJavaFX extends JavadocTester {
"-sourcepath", testSrc,
"-javafx",
"--disable-javafx-strict-checks",
"-Xdoclint:all,-missing",
"-package",
"pkg1");
checkExit(Exit.OK);
@ -71,14 +72,14 @@ public class TestJavaFX extends JavadocTester {
<div class="member-signature"><span class="modifiers">public final</span>&nbsp;<\
span class="return-type">void</span>&nbsp;<span class="element-name">setRate</span><wbr>\
<span class="parameters">(double&nbsp;value)</span></div>
<div class="block">Sets the value of the property rate.</div>
<div class="block">Sets the value of the property <code>rate</code>.</div>
<dl class="notes">
<dt>Property description:</dt>""",
"""
<div class="member-signature"><span class="modifiers">public final</span>&nbsp;<\
span class="return-type">double</span>&nbsp;<span class="element-name">getRate</span>()<\
/div>
<div class="block">Gets the value of the property rate.</div>
<div class="block">Gets the value of the property <code>rate</code>.</div>
<dl class="notes">
<dt>Property description:</dt>""",
"""
@ -116,14 +117,14 @@ public class TestJavaFX extends JavadocTester {
<div class="member-signature"><span class="modifiers">public final</span>&nbsp;<\
span class="return-type">double</span>&nbsp;<span class="element-name">isPaused<\
/span>()</div>
<div class="block">Gets the value of the property paused.</div>""",
<div class="block">Gets the value of the property <code>paused</code>.</div>""",
"""
<section class="detail" id="setPaused(boolean)">
<h3>setPaused</h3>
<div class="member-signature"><span class="modifiers">public final</span>&nbsp;<\
span class="return-type">void</span>&nbsp;<span class="element-name">setPaused</\
span><wbr><span class="parameters">(boolean&nbsp;value)</span></div>
<div class="block">Sets the value of the property paused.</div>
<div class="block">Sets the value of the property <code>paused</code>.</div>
<dl class="notes">
<dt>Property description:</dt>
<dd>Defines if paused. The second line.</dd>
@ -135,7 +136,7 @@ public class TestJavaFX extends JavadocTester {
<div class="member-signature"><span class="modifiers">public final</span>&nbsp;<\
span class="return-type">double</span>&nbsp;<span class="element-name">isPaused<\
/span>()</div>
<div class="block">Gets the value of the property paused.</div>
<div class="block">Gets the value of the property <code>paused</code>.</div>
<dl class="notes">
<dt>Property description:</dt>
<dd>Defines if paused. The second line.</dd>
@ -150,35 +151,39 @@ public class TestJavaFX extends JavadocTester {
<div class="block">Defines the direction/speed at which the <code>Timeline</code> is expected to
be played. This is the second line.</div>""",
"""
<section class="detail" id="setRate(double)">
<h3>setRate</h3>
<div class="member-signature"><span class="modifiers">public final</span>&nbsp;<\
span class="return-type">void</span>&nbsp;<span class="element-name">setRate</sp\
an><wbr><span class="parameters">(double&nbsp;value)</span></div>
<div class="block">Sets the value of the property rate.</div>
<dl class="notes">
<dt>Property description:</dt>
<dd>Defines the direction/speed at which the <code>Timeline</code> is expected to
be played. This is the second line.</dd>
<dt>Default value:</dt>
<dd>11</dd>
<dt>Since:</dt>
<dd>JavaFX 8.0</dd>""",
<section class="detail" id="setRate(double)">
<h3>setRate</h3>
<div class="member-signature"><span class="modifiers">public final</span>&nbsp;<\
span class="return-type">void</span>&nbsp;<span class="element-name">setRate</sp\
an><wbr><span class="parameters">(double&nbsp;value)</span></div>
<div class="block">Sets the value of the property <code>rate</code>.</div>
<dl class="notes">
<dt>Property description:</dt>
<dd>Defines the direction/speed at which the <code>Timeline</code> is expected to
be played. This is the second line.</dd>
<dt>Default value:</dt>
<dd>11</dd>
<dt>Parameters:</dt>
<dd><code>value</code> - the value for the property <code>rate</code></dd>
<dt>Since:</dt>
<dd>JavaFX 8.0</dd>""",
"""
<section class="detail" id="getRate()">
<h3>getRate</h3>
<div class="member-signature"><span class="modifiers">public final</span>&nbsp;<\
span class="return-type">double</span>&nbsp;<span class="element-name">getRate</span>()<\
/div>
<div class="block">Gets the value of the property rate.</div>
<dl class="notes">
<dt>Property description:</dt>
<dd>Defines the direction/speed at which the <code>Timeline</code> is expected to
be played. This is the second line.</dd>
<dt>Default value:</dt>
<dd>11</dd>
<dt>Since:</dt>
<dd>JavaFX 8.0</dd>""",
<section class="detail" id="getRate()">
<h3>getRate</h3>
<div class="member-signature"><span class="modifiers">public final</span>&nbsp;<\
span class="return-type">double</span>&nbsp;<span class="element-name">getRate</span>()<\
/div>
<div class="block">Gets the value of the property <code>rate</code>.</div>
<dl class="notes">
<dt>Property description:</dt>
<dd>Defines the direction/speed at which the <code>Timeline</code> is expected to
be played. This is the second line.</dd>
<dt>Default value:</dt>
<dd>11</dd>
<dt>Returns:</dt>
<dd>the value of the property <code>rate</code></dd>
<dt>Since:</dt>
<dd>JavaFX 8.0</dd>""",
"""
<section class="property-summary" id="property-summary">
<h2>Property Summary</h2>
@ -221,7 +226,7 @@ public class TestJavaFX extends JavadocTester {
checkOutput("index-all.html", true,
"""
<div class="block">Gets the value of the property paused.</div>""",
<div class="block">Gets the value of the property <code>paused</code>.</div>""",
"""
<div class="block">Defines if paused.</div>""");
@ -245,40 +250,65 @@ public class TestJavaFX extends JavadocTester {
"-javafx",
"--disable-javafx-strict-checks",
"--no-platform-links",
"-Xdoclint:all,-missing",
"-package",
"pkg2");
checkExit(Exit.OK);
checkOutput("pkg2/Test.html", true,
"""
<section class="property-details" id="property-detail">
<h2>Property Details</h2>
<ul class="member-list">
<li>
<section class="detail" id="betaProperty">
<h3>beta</h3>
<div class="member-signature"><span class="modifiers">public</span>&nbsp;<span c\
lass="return-type">java.lang.Object</span>&nbsp;<span class="element-name">betaProperty<\
/span></div>
</section>
</li>
<li>
<section class="detail" id="gammaProperty">
<h3>gamma</h3>
<div class="member-signature"><span class="modifiers">public final</span>&nbsp;<\
span class="return-type">java.util.List&lt;java.lang.String&gt;</span>&nbsp;<spa\
n class="element-name">gammaProperty</span></div>
</section>
</li>
<li>
<section class="detail" id="deltaProperty">
<h3>delta</h3>
<div class="member-signature"><span class="modifiers">public final</span>&nbsp;<\
span class="return-type">java.util.List&lt;java.util.Set&lt;? super java.lang.Ob\
ject&gt;&gt;</span>&nbsp;<span class="element-name">deltaProperty</span></div>
</section>
</li>
</ul>
</section>""",
<section class="property-details" id="property-detail">
<h2>Property Details</h2>
<ul class="member-list">
<li>
<section class="detail" id="betaProperty">
<h3>beta</h3>
<div class="member-signature"><span class="modifiers">public</span>&nbsp;<span c\
lass="return-type">java.lang.Object</span>&nbsp;<span class="element-name">betaProperty<\
/span></div>
<dl class="notes">
<dt>See Also:</dt>
<dd>
<ul class="see-list">
<li><a href="#betaProperty()"><code>betaProperty()</code></a></li>
</ul>
</dd>
</dl>
</section>
</li>
<li>
<section class="detail" id="gammaProperty">
<h3>gamma</h3>
<div class="member-signature"><span class="modifiers">public final</span>&nbsp;<\
span class="return-type">java.util.List&lt;java.lang.String&gt;</span>&nbsp;<spa\
n class="element-name">gammaProperty</span></div>
<dl class="notes">
<dt>See Also:</dt>
<dd>
<ul class="see-list">
<li><a href="#gammaProperty()"><code>gammaProperty()</code></a></li>
</ul>
</dd>
</dl>
</section>
</li>
<li>
<section class="detail" id="deltaProperty">
<h3>delta</h3>
<div class="member-signature"><span class="modifiers">public final</span>&nbsp;<\
span class="return-type">java.util.List&lt;java.util.Set&lt;? super java.lang.Ob\
ject&gt;&gt;</span>&nbsp;<span class="element-name">deltaProperty</span></div>
<dl class="notes">
<dt>See Also:</dt>
<dd>
<ul class="see-list">
<li><a href="#deltaProperty()"><code>deltaProperty()</code></a></li>
</ul>
</dd>
</dl>
</section>
</li>
</ul>
</section>""",
"""
<section class="property-summary" id="property-summary">
<h2>Property Summary</h2>

View File

@ -0,0 +1,423 @@
/*
* Copyright (c) 2021, 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 8270195
* @summary Add missing links between methods of JavaFX properties
* @library /tools/lib ../../lib
* @modules jdk.javadoc/jdk.javadoc.internal.tool
* @build toolbox.ToolBox javadoc.tester.*
* @run main TestJavaFXCombo
*/
import java.io.IOException;
import java.nio.file.Path;
import java.util.EnumSet;
import java.util.Set;
import javadoc.tester.JavadocTester;
import toolbox.ToolBox;
/**
* Combo-test for JavaFX properties and related methods.
* The test generates instances of a class with various combinations of
* a property field, property method, getter method and setter method,
* each in combinations of with and without doc comments.
* For each instance, it runs javadoc and verifies the generated
* code and any diagnostics are as expected.
*/
public class TestJavaFXCombo extends JavadocTester {
public static void main(String... args) throws Exception {
TestJavaFXCombo tester = new TestJavaFXCombo(args);
tester.runTests(m -> new Object[] { Path.of(m.getName())});
}
ToolBox tb = new ToolBox();
enum Kind { NONE, NO_COMMENT, COMMENT }
private final Set<Kind> fieldValues = EnumSet.allOf(Kind.class);
private final Set<Kind> propertyMethodValues = EnumSet.allOf(Kind.class);
private final Set<Kind> getterMethodValues = EnumSet.allOf(Kind.class);
private final Set<Kind> setterMethodValues = EnumSet.allOf(Kind.class);
TestJavaFXCombo(String... args) {
// for testing, allow subsets of combinations to be specified
for (int i = 0; i < args.length; i++) {
String arg = args[1];
switch (arg) {
case "-f" -> set(fieldValues, args[++i]);
case "-p" -> set(propertyMethodValues, args[++i]);
case "-g" -> set(getterMethodValues, args[++i]);
case "-s" -> set(setterMethodValues, args[++i]);
}
}
// A property method is always required for any property,
propertyMethodValues.remove(Kind.NONE);
}
private void set(Set<Kind> set, String values) {
set.clear();
for (String v : values.split("[, ]")) {
set.add(Kind.valueOf(v));
}
}
@Test
public void test(Path base) throws IOException {
for (Kind pk : propertyMethodValues) {
for (Kind fk : fieldValues) {
for (Kind gk : getterMethodValues) {
for (Kind sk: setterMethodValues) {
test(base, fk, pk, gk, sk);
}
}
}
}
}
void test(Path base, Kind fk, Kind pk, Kind gk, Kind sk) throws IOException {
String description = "Field:" + fk + " Property:" + pk + " Getter:" + gk + " Setter:" + sk;
out.println("Test: " + description);
Path sub = base.resolve(String.format("%s-%s-%s-%s", abbrev(fk), abbrev(pk), abbrev(gk), abbrev(sk)));
Path src = sub.resolve("src");
tb.writeJavaFiles(src, """
package p;
/** Dummy property class. */
public class BooleanProperty { }
""", """
package p;
/** Class comment. ## */
public class C {
""".replace("##", description)
+ getFieldText(fk)
+ getPropertyMethodText(pk)
+ getGetterMethodText(gk)
+ getSetterMethodText(sk)
+ """
}
"""
);
javadoc("-d", sub.resolve("api").toString(),
"-javafx",
"--disable-javafx-strict-checks",
"-Xdoclint:all,-missing",
"-nohelp", "-noindex",
"-sourcepath", src.toString(),
"p");
checkExit(Exit.OK);
checkField(fk, pk, gk, sk);
checkGetter(fk, pk, gk, sk);
checkSetter(fk, pk, gk, sk);
checkPropertyMethod(fk, pk, gk, sk);
checkDiags(fk, pk, gk, sk);
}
void checkField(Kind fk, Kind pk, Kind gk, Kind sk) {
// the field is private and so should never show up
checkOutput("p/C.html", false,
"field.detail");
}
void checkGetter(Kind fk, Kind pk, Kind gk, Kind sk) {
switch (gk) {
case NONE ->
checkOutput("p/C.html", false,
"getExample");
case NO_COMMENT ->
// comment gets auto-created
checkOutput("p/C.html", true,
"""
<section class="detail" id="getExample()">
<h3>getExample</h3>
<div class="member-signature"><span class="modifiers">public</span>&nbsp;<span class="return-type">boolean</span>&nbsp;<span class="element-name">getExample</span>()</div>
<div class="block">Gets the value of the property <code>example</code>.</div>
<dl class="notes">
<dt>Property description:</dt>
#DESC#
<dt>Returns:</dt>
<dd>the value of the property <code>example</code></dd>
#SEE#
</dl>
</section>
"""
.replace("#DESC#", getPropertyDescription(fk, pk))
.replace("#SEE#", getSee(pk, null, sk))
.replaceAll("\n\n", "\n")
);
case COMMENT ->
// existing comments do not get modified
checkOutput("p/C.html", true,
"""
<section class="detail" id="getExample()">
<h3>getExample</h3>
<div class="member-signature"><span class="modifiers">public</span>&nbsp;<span class="return-type">boolean</span>&nbsp;<span class="element-name">getExample</span>()</div>
<div class="block">Getter method description. More getter method description.</div>
<dl class="notes">
<dt>Returns:</dt>
<dd>the property <code>example</code></dd>
</dl>
</section>
""");
}
}
void checkSetter(Kind fk, Kind pk, Kind gk, Kind sk) {
switch (sk) {
case NONE ->
checkOutput("p/C.html", false,
"setExample");
case NO_COMMENT ->
// comment gets auto-created
checkOutput("p/C.html", true,
"""
<section class="detail" id="setExample(boolean)">
<h3>setExample</h3>
<div class="member-signature"><span class="modifiers">public</span>&nbsp;<span class="return-type">void</span>&nbsp;<span class="element-name">setExample</span><wbr><span class="parameters">(boolean&nbsp;b)</span></div>
<div class="block">Sets the value of the property <code>example</code>.</div>
<dl class="notes">
<dt>Property description:</dt>
#DESC#
<dt>Parameters:</dt>
<dd><code>b</code> - the value for the property <code>example</code></dd>
#SEE#
</dl>
</section>
"""
.replace("#DESC#", getPropertyDescription(fk, pk))
.replace("#SEE#", getSee(pk, gk, null))
.replaceAll("\n\n", "\n"));
case COMMENT ->
// existing comments do not get modified
checkOutput("p/C.html", true,
"""
<section class="detail" id="setExample(boolean)">
<h3>setExample</h3>
<div class="member-signature"><span class="modifiers">public</span>&nbsp;<span class="return-type">void</span>&nbsp;<span class="element-name">setExample</span><wbr><span class="parameters">(boolean&nbsp;b)</span></div>
<div class="block">Setter method description. More setter method description.</div>
<dl class="notes">
<dt>Parameters:</dt>
<dd><code>b</code> - the new value for the property</dd>
</dl>
</section>
""");
}
}
void checkPropertyMethod(Kind fk, Kind pk, Kind gk, Kind sk) {
switch (pk) {
case NONE ->
// should not happen; there is always a property method
throw new IllegalArgumentException();
case NO_COMMENT ->
checkOutput("p/C.html", true,
"""
<section class="detail" id="exampleProperty()">
<h3>exampleProperty</h3>
<div class="member-signature"><span class="modifiers">public</span>&nbsp;<span class="return-type"><a href="BooleanProperty.html" title="class in p">BooleanProperty</a></span>&nbsp;<span class="element-name">exampleProperty</span>()</div>
#PCOMM#
<dl class="notes">
<dt>Returns:</dt>
<dd>the property <code>example</code></dd>
#SEE#
</dl>
</section>
"""
.replace("#PCOMM#", getPropertyMethodComment(fk, pk))
.replace("#SEE#", getSee(null, gk, sk))
.replaceAll("\n\n", "\n"));
case COMMENT ->
// @see tags are added to an existing method if it is the primary source of info
// for the property (i.e. there is no comment on a corresponding property field.
checkOutput("p/C.html", true,
"""
<section class="detail" id="exampleProperty()">
<h3>exampleProperty</h3>
<div class="member-signature"><span class="modifiers">public</span>&nbsp;<span class="return-type"><a href="BooleanProperty.html" title="class in p">BooleanProperty</a></span>&nbsp;<span class="element-name">exampleProperty</span>()</div>
<div class="block">Property method description. More property method description.</div>
<dl class="notes">
<dt>Returns:</dt>
<dd>the property <code>example</code></dd>
#SEE#
</dl>
</section>
"""
.replace("#SEE#", (fk == Kind.COMMENT ? "" : getSee(null, gk, sk)))
.replaceAll("\n\n", "\n"));
}
}
void checkDiags(Kind fk, Kind pk, Kind gk, Kind sk) {
// A warning is generated if there is a comment on both the property field and property method
checkOutput(Output.OUT, (fk == Kind.COMMENT && pk == Kind.COMMENT),
"warning: Duplicate comment for property",
"Remove the comment on the property field or on this method to suppress this warning.");
}
String getPropertyComment(Kind fk, Kind pk) {
return switch (fk) {
case NONE, NO_COMMENT ->
switch (pk) {
case NONE, NO_COMMENT ->
"";
case COMMENT ->
"Property method description. More property method description.";
};
case COMMENT ->
"Field description. More field description.";
};
}
String getPropertyDescription(Kind fk, Kind pk) {
String s = getPropertyComment(fk, pk);
return s.isEmpty() ? s : "<dd>" + s + "</dd>";
}
String getPropertyMethodComment(Kind fk, Kind pk) {
String s = getPropertyComment(fk, pk);
return s.isEmpty() ? s : "<div class=\"block\">" + s + "</div>";
}
String getSee(Kind pk, Kind gk, Kind sk) {
StringBuilder sb = new StringBuilder();
if (gk != null && gk != Kind.NONE) {
sb.append("""
<li><a href="#getExample()"><code>getExample()</code></a></li>
""");
}
if (sk != null && sk != Kind.NONE) {
sb.append("""
<li><a href="#setExample(boolean)"><code>setExample(boolean)</code></a></li>
""");
}
if (pk != null && pk != Kind.NONE) {
sb.append("""
<li><a href="#exampleProperty()"><code>exampleProperty()</code></a></li>
""");
}
return sb.isEmpty() ? "" : """
<dt>See Also:</dt>
<dd>
<ul class="see-list">
""" + sb + """
</ul>
</dd>""";
}
String abbrev(Kind k) {
return k.name().substring(0, 4);
}
String getFieldText(Kind fk) {
return switch (fk) {
case NONE -> """
// no field declaration
""";
case NO_COMMENT -> """
// no field comment
private BooleanProperty example;
""";
case COMMENT -> """
/** Field description. More field description. */
private BooleanProperty example;
""";
};
}
String getPropertyMethodText(Kind fk) {
return switch (fk) {
case NONE -> """
// no property method declaration
""";
case NO_COMMENT -> """
// no property method comment
public BooleanProperty exampleProperty();
""";
case COMMENT -> """
/**
* Property method description. More property method description.
* @return the property {@code example}
*/
public BooleanProperty exampleProperty();
""";
};
}
String getGetterMethodText(Kind fk) {
return switch (fk) {
case NONE -> """
// no getter method declaration
""";
case NO_COMMENT -> """
// no getter method comment
public boolean getExample();
""";
case COMMENT -> """
/**
* Getter method description. More getter method description.
* @return the property {@code example}
*/
public boolean getExample();
""";
};
}
String getSetterMethodText(Kind fk) {
return switch (fk) {
case NONE -> """
// no setter method declaration
""";
case NO_COMMENT -> """
// no setter method comment
public void setExample(boolean b);
""";
case COMMENT -> """
/**
* Setter method description. More setter method description.
* @param b the new value for the property
*/
public void setExample(boolean b);
""";
};
}
}

View File

@ -92,10 +92,18 @@ public class TestJavaFXMissingPropComments extends JavadocTester {
<div class="member-signature"><span class="modifiers">public</span>&nbsp;<span c\
lass="return-type">boolean</span>&nbsp;<span class="element-name">getValue</span\
>()</div>
<div class="block">Gets the value of the property value.</div>
<div class="block">Gets the value of the property <code>value</code>.</div>
<dl class="notes">
<dt>Property description:</dt>
<dd>The value property (property method comment).</dd>
<dt>Returns:</dt>
<dd>the value of the property <code>value</code></dd>
<dt>See Also:</dt>
<dd>
<ul class="see-list">
<li><a href="#valueProperty()"><code>valueProperty()</code></a></li>
</ul>
</dd>
</dl>
</section>"""
);
@ -148,10 +156,18 @@ public class TestJavaFXMissingPropComments extends JavadocTester {
<div class="member-signature"><span class="modifiers">public</span>&nbsp;<span c\
lass="return-type">boolean</span>&nbsp;<span class="element-name">getValue</span\
>()</div>
<div class="block">Gets the value of the property value.</div>
<div class="block">Gets the value of the property <code>value</code>.</div>
<dl class="notes">
<dt>Property description:</dt>
<dd>The value property (field comment).</dd>
<dt>Returns:</dt>
<dd>the value of the property <code>value</code></dd>
<dt>See Also:</dt>
<dd>
<ul class="see-list">
<li><a href="#valueProperty()"><code>valueProperty()</code></a></li>
</ul>
</dd>
</dl>
</section>"""
);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2021, 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,10 +26,9 @@ package pkg2;
import java.util.List;
import java.util.Set;
public class Test {
public <T> Object alphaProperty(List<T> foo) { return null; }
public Object betaProperty() { return null; }
public final List<String> gammaProperty() {return null;}
public final List<Set<? super Object>> deltaProperty() {return null;}
public final List<String> gammaProperty() { return null;}
public final List<Set<? super Object>> deltaProperty() { return null;}
}

View File

@ -46,6 +46,7 @@ public class TestProperty extends JavadocTester {
"--no-platform-links",
"-javafx",
"--disable-javafx-strict-checks",
"-Xdoclint:all,-missing",
"-sourcepath", testSrc,
"pkg");
checkExit(Exit.OK);
@ -63,6 +64,7 @@ public class TestProperty extends JavadocTester {
<ul class="see-list">
<li><a href="#getGood()"><code>getGood()</code></a></li>
<li><a href="#setGood(pkg.MyObj)"><code>setGood(MyObj)</code></a></li>
<li><a href="#goodProperty()"><code>goodProperty()</code></a></li>
</ul>
</dd>
</dl>""",
@ -79,6 +81,7 @@ public class TestProperty extends JavadocTester {
<ul class="see-list">
<li><a href="#getBad()"><code>getBad()</code></a></li>
<li><a href="#setBad(pkg.MyObj%5B%5D)"><code>setBad(MyObj[])</code></a></li>
<li><a href="#badProperty()"><code>badProperty()</code></a></li>
</ul>
</dd>
</dl>""",
@ -117,6 +120,7 @@ public class TestProperty extends JavadocTester {
<ul class="see-list">
<li><a href="#getList()"><code>getList()</code></a></li>
<li><a href="#setList(java.util.List)"><code>setList(List)</code></a></li>
<li><a href="#listProperty()"><code>listProperty()</code></a></li>
</ul>
</dd>
</dl>"""