/* * Copyright (c) 2021, 2023, 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 { var tester = new TestJavaFXCombo(args); tester.runTests(); } ToolBox tb = new ToolBox(); enum Kind { NONE, NO_COMMENT, COMMENT } private final Set fieldValues = EnumSet.allOf(Kind.class); private final Set propertyMethodValues = EnumSet.allOf(Kind.class); private final Set getterMethodValues = EnumSet.allOf(Kind.class); private final Set 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 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, """

getExample

public boolean getExample()
Gets the value of the example property.
Property description:
#DESC#
Returns:
the value of the example property
#SEE#
""" .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, """

getExample

public boolean getExample()
Getter method description. More getter method description.
Returns:
the example property
"""); } } 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, """

setExample

public void setExample(boolean b)
Sets the value of the example property.
Property description:
#DESC#
Parameters:
b - the value for the example property
#SEE#
""" .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, """

setExample

public void setExample(boolean b)
Setter method description. More setter method description.
Parameters:
b - the new value for the property
"""); } } 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, """

exampleProperty

public BooleanProperty exampleProperty()
#PCOMM#
Returns:
the example property
#SEE#
""" .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, """

exampleProperty

public BooleanProperty exampleProperty()
Property method description. More property method description.
Returns:
the example property
#SEE#
""" .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 : "
" + s + "
"; } String getPropertyMethodComment(Kind fk, Kind pk) { String s = getPropertyComment(fk, pk); return s.isEmpty() ? s : "
" + s + "
"; } String getSee(Kind pk, Kind gk, Kind sk) { StringBuilder sb = new StringBuilder(); if (gk != null && gk != Kind.NONE) { sb.append("""
  • getExample()
  • """); } if (sk != null && sk != Kind.NONE) { sb.append("""
  • setExample(boolean)
  • """); } if (pk != null && pk != Kind.NONE) { sb.append("""
  • exampleProperty()
  • """); } return sb.isEmpty() ? "" : """
    See Also:
      """ + sb + """
    """; } 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 {@code example} property */ 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 {@code example} property */ 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); """; }; } }