diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractMemberWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractMemberWriter.java index e2e286d5856..fabca0a894d 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractMemberWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractMemberWriter.java @@ -319,7 +319,7 @@ public abstract class AbstractMemberWriter { inheritedHeader.add(links); if (utils.isIncluded(inheritedClass)) { - var pHelper = writer.getPropertyHelper(); + var pHelper = configuration.propertyUtils.getPropertyHelper(inheritedClass); Table table = createInheritedSummaryTable(inheritedClass); for (Element member : inheritedMembers) { diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassWriter.java index d301dd6e283..f5a5a48222c 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassWriter.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassWriter.java @@ -96,7 +96,7 @@ public class ClassWriter extends SubWriterHolderWriter { this.typeElement = typeElement; this.classTree = classTree; - pHelper = new PropertyUtils.PropertyHelper(configuration, typeElement); + pHelper = configuration.propertyUtils.getPropertyHelper(typeElement); switch (typeElement.getKind()) { case ENUM -> setEnumDocumentation(typeElement); diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/PropertyUtils.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/PropertyUtils.java index 823f172b360..8913be5b301 100644 --- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/PropertyUtils.java +++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/PropertyUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 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 @@ -54,6 +54,8 @@ import static jdk.javadoc.internal.doclets.toolkit.util.VisibleMemberTable.Kind. */ public class PropertyUtils { + final BaseConfiguration configuration; + final TypeMirror jbObservableType; final Pattern fxMethodPatterns; @@ -62,7 +64,10 @@ public class PropertyUtils { final Types typeUtils; + final Map propertyHelpers = new HashMap<>(); + PropertyUtils(BaseConfiguration configuration) { + this.configuration = configuration; BaseOptions options = configuration.getOptions(); javafx = options.javafx(); @@ -82,30 +87,37 @@ public class PropertyUtils { : null; } + /** + * Returns a property helper for the given type element. + * @param typeElement a type element + * @return the property helper + */ + public PropertyHelper getPropertyHelper(TypeElement typeElement) { + return propertyHelpers.computeIfAbsent(typeElement, te -> new PropertyHelper(configuration, te)); + } + /** * Returns a base name for a property method. Supposing we * have {@code BooleanProperty acmeProperty()}, then "acme" * will be returned. - * @param propertyMethod + * @param propertyMethod a property method * @return the base name of a property method. */ public String getBaseName(ExecutableElement propertyMethod) { String name = propertyMethod.getSimpleName().toString(); - String baseName = name.substring(0, name.indexOf("Property")); - return baseName; + return name.substring(0, name.indexOf("Property")); } /** * Returns a property getter's name. Supposing we have a property * method {@code DoubleProperty acmeProperty()}, then "getAcme" * will be returned. - * @param propertyMethod + * @param propertyMethod a property method * @return the property getter's name. */ public String getGetName(ExecutableElement propertyMethod) { String baseName = getBaseName(propertyMethod); - String fnUppercased = "" + - Character.toUpperCase(baseName.charAt(0)) + baseName.substring(1); + String fnUppercased = Character.toUpperCase(baseName.charAt(0)) + baseName.substring(1); return "get" + fnUppercased; } @@ -113,20 +125,19 @@ public class PropertyUtils { * Returns an "is" method's name for a property method. Supposing * we have a property method {@code BooleanProperty acmeProperty()}, * then "isAcme" will be returned. - * @param propertyMethod + * @param propertyMethod a property method * @return the property is getter's name. */ public String getIsName(ExecutableElement propertyMethod) { String baseName = getBaseName(propertyMethod); - String fnUppercased = "" + - Character.toUpperCase(baseName.charAt(0)) + baseName.substring(1); + String fnUppercased = Character.toUpperCase(baseName.charAt(0)) + baseName.substring(1); return "is" + fnUppercased; } /** * Returns true if a property method could have an "is" method, meaning * {@code isAcme} could exist for a property method. - * @param propertyMethod + * @param propertyMethod a property method * @return true if the property could have an "is" method, false otherwise. */ public boolean hasIsMethod(ExecutableElement propertyMethod) { @@ -139,20 +150,19 @@ public class PropertyUtils { * Returns a property setter's name. Supposing we have a property * method {@code DoubleProperty acmeProperty()}, then "setAcme" * will be returned. - * @param propertyMethod + * @param propertyMethod a property method * @return the property setter's method name. */ public String getSetName(ExecutableElement propertyMethod) { String baseName = getBaseName(propertyMethod); - String fnUppercased = "" + - Character.toUpperCase(baseName.charAt(0)) + baseName.substring(1); + String fnUppercased = Character.toUpperCase(baseName.charAt(0)) + baseName.substring(1); return "set" + fnUppercased; } /** * Returns true if the given setter method is a valid property setter * method. - * @param setterMethod + * @param setterMethod a setter method * @return true if setter method, false otherwise. */ public boolean isValidSetterMethod(ExecutableElement setterMethod) { @@ -161,28 +171,28 @@ public class PropertyUtils { /** * Returns true if the method is a property method. - * @param propertyMethod + * @param method a method * @return true if the method is a property method, false otherwise. */ - public boolean isPropertyMethod(ExecutableElement propertyMethod) { + public boolean isPropertyMethod(ExecutableElement method) { if (!javafx || - !propertyMethod.getParameters().isEmpty() || - !propertyMethod.getTypeParameters().isEmpty()) { + !method.getParameters().isEmpty() || + !method.getTypeParameters().isEmpty()) { return false; } - String methodName = propertyMethod.getSimpleName().toString(); + String methodName = method.getSimpleName().toString(); if (!methodName.endsWith("Property") || fxMethodPatterns.matcher(methodName).matches()) { return false; } - TypeMirror returnType = propertyMethod.getReturnType(); + TypeMirror returnType = method.getReturnType(); if (jbObservableType == null) { // JavaFX references missing, make a lazy backward compatible check. return returnType.getKind() != TypeKind.VOID; } else { // Apply strict checks since JavaFX references are available - returnType = typeUtils.erasure(propertyMethod.getReturnType()); + returnType = typeUtils.erasure(method.getReturnType()); return typeUtils.isAssignable(returnType, jbObservableType); } } @@ -202,20 +212,13 @@ public class PropertyUtils { * method. If any method does not have a comment, one will be provided. */ public static class PropertyHelper { - private final BaseConfiguration configuration; - private final Utils utils; - private final TypeElement typeElement; + private Map classPropertiesMap = null; - private final Map classPropertiesMap = new HashMap<>(); - - public PropertyHelper(BaseConfiguration configuration, TypeElement typeElement) { - this.configuration = configuration; - this.utils = configuration.utils; - this.typeElement = typeElement; - computeProperties(); + private PropertyHelper(BaseConfiguration configuration, TypeElement typeElement) { + computeProperties(configuration, typeElement); } - private void computeProperties() { + private void computeProperties(BaseConfiguration configuration, TypeElement typeElement) { VisibleMemberTable vmt = configuration.getVisibleMemberTable(typeElement); List props = ElementFilter.methodsIn(vmt.getVisibleMembers(PROPERTIES)); for (ExecutableElement propertyMethod : props) { @@ -223,37 +226,42 @@ public class PropertyUtils { ExecutableElement setter = vmt.getPropertySetter(propertyMethod); VariableElement field = vmt.getPropertyField(propertyMethod); - addToPropertiesMap(propertyMethod, field, getter, setter); + addToPropertiesMap(configuration, propertyMethod, field, getter, setter); } } - private void addToPropertiesMap(ExecutableElement propertyMethod, + private void addToPropertiesMap(BaseConfiguration configuration, + ExecutableElement propertyMethod, VariableElement field, ExecutableElement getter, ExecutableElement setter) { // determine the preferred element from which to derive the property description - Element e = field == null || !utils.hasDocCommentTree(field) + Element e = field == null || !configuration.utils.hasDocCommentTree(field) ? propertyMethod : field; - if (e == field && utils.hasDocCommentTree(propertyMethod)) { + if (e == field && configuration.utils.hasDocCommentTree(propertyMethod)) { configuration.getReporter().print(Diagnostic.Kind.WARNING, propertyMethod, configuration.getDocResources().getText("doclet.duplicate.comment.for.property")); } - addToPropertiesMap(propertyMethod, e); - addToPropertiesMap(getter, e); - addToPropertiesMap(setter, e); + if (classPropertiesMap == null) { + classPropertiesMap = new HashMap<>(); + } + addToPropertiesMap(configuration, propertyMethod, e); + addToPropertiesMap(configuration, getter, e); + addToPropertiesMap(configuration, setter, e); } - private void addToPropertiesMap(Element propertyMethod, + private void addToPropertiesMap(BaseConfiguration configuration, + Element propertyMethod, Element commentSource) { Objects.requireNonNull(commentSource); if (propertyMethod == null) { return; } - DocCommentTree docTree = utils.hasDocCommentTree(propertyMethod) - ? utils.getDocCommentTree(propertyMethod) + DocCommentTree docTree = configuration.utils.hasDocCommentTree(propertyMethod) + ? configuration.utils.getDocCommentTree(propertyMethod) : null; /* The second condition is required for the property buckets. In @@ -271,7 +279,7 @@ public class PropertyUtils { * @return the element for the property documentation, null if there is none. */ public Element getPropertyElement(Element element) { - return classPropertiesMap.get(element); + return classPropertiesMap == null ? null : classPropertiesMap.get(element); } } } diff --git a/test/langtools/jdk/javadoc/doclet/testJavaFX/TestJavaFX.java b/test/langtools/jdk/javadoc/doclet/testJavaFX/TestJavaFX.java index 4fa89ee0ead..04e032dbe05 100644 --- a/test/langtools/jdk/javadoc/doclet/testJavaFX/TestJavaFX.java +++ b/test/langtools/jdk/javadoc/doclet/testJavaFX/TestJavaFX.java @@ -25,7 +25,7 @@ * @test * @bug 7112427 8012295 8025633 8026567 8061305 8081854 8150130 8162363 * 8167967 8172528 8175200 8178830 8182257 8186332 8182765 8025091 - * 8203791 8184205 8249633 8261976 8350920 + * 8203791 8184205 8249633 8261976 8350920 8367007 * @summary Test of the JavaFX doclet features. * @library ../../lib * @modules jdk.javadoc/jdk.javadoc.internal.tool @@ -54,11 +54,18 @@ public class TestJavaFX extends JavadocTester { "-sourcepath", testSrc, "-javafx", "--disable-javafx-strict-checks", - "-Xdoclint:all,-missing", "-package", "pkg1"); checkExit(Exit.OK); + checkOutput(Output.OUT, true, + "C.java:78: warning: no comment"); + checkOutput(Output.OUT, false, + "C.java:59: warning: no comment", + "C.java:61: warning: no comment", + "C.java:63: warning: no comment", + "C.java:67: warning: no comment"); + checkOutput("pkg1/C.html", true, """
See Also:
@@ -266,6 +273,31 @@ public class TestJavaFX extends JavadocTester { """); checkOutput("pkg1/D.html", false, "shouldNotAppear"); + + // Test for inherited properties and property methods. + checkOrder("pkg1/B.html", + """ + Properties inherited from class C""", + """ +
Defines if paused.
""", + """ +
Defines the direction/speed at which the Timeline is expected to + be played.
""", + """ + Methods inherited from class C""", + """ +
Gets the value of the rate property.
""", + """ +
Gets the value of the paused property.
""", + """ +
Defines if paused.
""", + """ +
Defines the direction/speed at which the Timeline is expected to + be played.
""", + """ +
Sets the value of the paused property.
""", + """ +
Sets the value of the rate property.
"""); } /* diff --git a/test/langtools/jdk/javadoc/doclet/testJavaFX/pkg1/B.java b/test/langtools/jdk/javadoc/doclet/testJavaFX/pkg1/B.java new file mode 100644 index 00000000000..2ec6f77833a --- /dev/null +++ b/test/langtools/jdk/javadoc/doclet/testJavaFX/pkg1/B.java @@ -0,0 +1,27 @@ +/* + * 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 pkg1; + +public class B extends C { +}