1 /*
   2  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.javadoc.internal.doclets.toolkit;
  27 
  28 import java.util.regex.Pattern;
  29 import javax.lang.model.element.ExecutableElement;
  30 import javax.lang.model.type.TypeKind;
  31 import javax.lang.model.type.TypeMirror;
  32 import javax.lang.model.util.Types;
  33 
  34 import jdk.javadoc.internal.doclets.formats.html.HtmlOptions;
  35 
  36 /**
  37  * This class provides basic JavaFX property related utility methods.
  38  * Refer to the JavaFX conventions in the VisibleMemberTable comments.
  39  */
  40 public class PropertyUtils {
  41 
  42     final TypeMirror jbObservableType;
  43 
  44     final Pattern fxMethodPatterns;
  45 
  46     final boolean javafx;
  47 
  48     final Types typeUtils;
  49 
  50     PropertyUtils(BaseConfiguration configuration) {
  51         BaseOptions options = configuration.getOptions();
  52         javafx = options.javafx;
  53 
  54         typeUtils = configuration.docEnv.getTypeUtils();
  55 
  56         // Disable strict check for JDK's without FX.
  57         TypeMirror jboType = options.disableJavaFxStrictChecks
  58                 ? null
  59                 : configuration.utils.getSymbol("javafx.beans.Observable");
  60 
  61         jbObservableType = jboType != null
  62                 ? configuration.docEnv.getTypeUtils().erasure(jboType)
  63                 : null;
  64 
  65         fxMethodPatterns = javafx
  66                 ? Pattern.compile("[sg]et\\p{Upper}.*||is\\p{Upper}.*")
  67                 : null;
  68     }
  69 
  70     /**
  71      * Returns a base name for a property method. Supposing we
  72      * have {@code BooleanProperty acmeProperty()}, then "acme"
  73      * will be returned.
  74      * @param propertyMethod
  75      * @return the base name of a property method.
  76      */
  77     public String getBaseName(ExecutableElement propertyMethod) {
  78         String name = propertyMethod.getSimpleName().toString();
  79         String baseName = name.substring(0, name.indexOf("Property"));
  80         return baseName;
  81     }
  82 
  83     /**
  84      * Returns a property getter's name. Supposing we have a property
  85      * method {@code DoubleProperty acmeProperty()}, then "getAcme"
  86      * will be returned.
  87      * @param propertyMethod
  88      * @return the property getter's name.
  89      */
  90     public String getGetName(ExecutableElement propertyMethod) {
  91         String baseName = getBaseName(propertyMethod);
  92         String fnUppercased = "" +
  93                 Character.toUpperCase(baseName.charAt(0)) + baseName.substring(1);
  94         return "get" + fnUppercased;
  95     }
  96 
  97     /**
  98      * Returns an "is" method's name for a property method. Supposing
  99      * we have a property method {@code BooleanProperty acmeProperty()},
 100      * then "isAcme" will be returned.
 101      * @param propertyMethod
 102      * @return the property is getter's name.
 103      */
 104     public String getIsName(ExecutableElement propertyMethod) {
 105         String baseName = getBaseName(propertyMethod);
 106         String fnUppercased = "" +
 107                 Character.toUpperCase(baseName.charAt(0)) + baseName.substring(1);
 108         return "is" + fnUppercased;
 109     }
 110 
 111     /**
 112      * Returns true if a property method could have an "is" method, meaning
 113      * {@code isAcme} could exist for a property method.
 114      * @param propertyMethod
 115      * @return true if the property could have an "is" method, false otherwise.
 116      */
 117     public boolean hasIsMethod(ExecutableElement propertyMethod) {
 118         String propertyTypeName = propertyMethod.getReturnType().toString();
 119         return "boolean".equals(propertyTypeName) ||
 120                 propertyTypeName.endsWith("BooleanProperty");
 121     }
 122 
 123     /**
 124      * Returns a property setter's name. Supposing we have a property
 125      * method {@code DoubleProperty acmeProperty()}, then "setAcme"
 126      * will be returned.
 127      * @param propertyMethod
 128      * @return the property setter's method name.
 129      */
 130     public String getSetName(ExecutableElement propertyMethod) {
 131         String baseName = getBaseName(propertyMethod);
 132         String fnUppercased = "" +
 133                 Character.toUpperCase(baseName.charAt(0)) + baseName.substring(1);
 134         return "set" + fnUppercased;
 135     }
 136 
 137     /**
 138      * Returns true if the given setter method is a valid property setter
 139      * method.
 140      * @param setterMethod
 141      * @return true if setter method, false otherwise.
 142      */
 143     public boolean isValidSetterMethod(ExecutableElement setterMethod) {
 144         return setterMethod.getReturnType().getKind() == TypeKind.VOID;
 145     }
 146 
 147     /**
 148      * Returns true if the method is a property method.
 149      * @param propertyMethod
 150      * @return true if the method is a property method, false otherwise.
 151      */
 152     public boolean isPropertyMethod(ExecutableElement propertyMethod) {
 153         if (!javafx ||
 154                 !propertyMethod.getParameters().isEmpty() ||
 155                 !propertyMethod.getTypeParameters().isEmpty()) {
 156             return false;
 157         }
 158         String methodName = propertyMethod.getSimpleName().toString();
 159         if (!methodName.endsWith("Property") ||
 160                 fxMethodPatterns.matcher(methodName).matches()) {
 161             return false;
 162         }
 163 
 164         TypeMirror returnType = propertyMethod.getReturnType();
 165         if (jbObservableType == null) {
 166             // JavaFX references missing, make a lazy backward compatible check.
 167             return returnType.getKind() != TypeKind.VOID;
 168         } else {
 169             // Apply strict checks since JavaFX references are available
 170             returnType = typeUtils.erasure(propertyMethod.getReturnType());
 171             return typeUtils.isAssignable(returnType, jbObservableType);
 172         }
 173     }
 174 }