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 /**
  35  * This class provides basic JavaFX property related utility methods.
  36  * Refer to the JavaFX conventions in the VisibleMemberTable comments.
  37  */
  38 public class PropertyUtils {
  39 
  40     final TypeMirror jbObservableType;
  41 
  42     final Pattern fxMethodPatterns;
  43 
  44     final boolean javafx;
  45 
  46     final Types typeUtils;
  47 
  48     PropertyUtils(BaseConfiguration configuration) {
  49 
  50         javafx = configuration.javafx;
  51 
  52         typeUtils = configuration.docEnv.getTypeUtils();
  53 
  54         // Disable strict check for JDK's without FX.
  55         TypeMirror jboType = configuration.disableJavaFxStrictChecks
  56                 ? null
  57                 : configuration.utils.getSymbol("javafx.beans.Observable");
  58 
  59         jbObservableType = jboType != null
  60                 ? configuration.docEnv.getTypeUtils().erasure(jboType)
  61                 : null;
  62 
  63         fxMethodPatterns = javafx
  64                 ? Pattern.compile("[sg]et\\p{Upper}.*||is\\p{Upper}.*")
  65                 : null;
  66     }
  67 
  68     /**
  69      * Returns a base name for a property method. Supposing we
  70      * have {@code BooleanProperty acmeProperty()}, then "acme"
  71      * will be returned.
  72      * @param propertyMethod
  73      * @return the base name of a property method.
  74      */
  75     public String getBaseName(ExecutableElement propertyMethod) {
  76         String name = propertyMethod.getSimpleName().toString();
  77         String baseName = name.substring(0, name.indexOf("Property"));
  78         return baseName;
  79     }
  80 
  81     /**
  82      * Returns a property getter's name. Supposing we have a property
  83      * method {@code DoubleProperty acmeProperty()}, then "getAcme"
  84      * will be returned.
  85      * @param propertyMethod
  86      * @return the property getter's name.
  87      */
  88     public String getGetName(ExecutableElement propertyMethod) {
  89         String baseName = getBaseName(propertyMethod);
  90         String fnUppercased = "" +
  91                 Character.toUpperCase(baseName.charAt(0)) + baseName.substring(1);
  92         return "get" + fnUppercased;
  93     }
  94 
  95     /**
  96      * Returns an "is" method's name for a property method. Supposing
  97      * we have a property method {@code BooleanProperty acmeProperty()},
  98      * then "isAcme" will be returned.
  99      * @param propertyMethod
 100      * @return the property is getter's name.
 101      */
 102     public String getIsName(ExecutableElement propertyMethod) {
 103         String baseName = getBaseName(propertyMethod);
 104         String fnUppercased = "" +
 105                 Character.toUpperCase(baseName.charAt(0)) + baseName.substring(1);
 106         return "is" + fnUppercased;
 107     }
 108 
 109     /**
 110      * Returns true if a property method could have an "is" method, meaning
 111      * {@code isAcme} could exist for a property method.
 112      * @param propertyMethod
 113      * @return true if the property could have an "is" method, false otherwise.
 114      */
 115     public boolean hasIsMethod(ExecutableElement propertyMethod) {
 116         String propertyTypeName = propertyMethod.getReturnType().toString();
 117         return "boolean".equals(propertyTypeName) ||
 118                 propertyTypeName.endsWith("BooleanProperty");
 119     }
 120 
 121     /**
 122      * Returns a property setter's name. Supposing we have a property
 123      * method {@code DoubleProperty acmeProperty()}, then "setAcme"
 124      * will be returned.
 125      * @param propertyMethod
 126      * @return the property setter's method name.
 127      */
 128     public String getSetName(ExecutableElement propertyMethod) {
 129         String baseName = getBaseName(propertyMethod);
 130         String fnUppercased = "" +
 131                 Character.toUpperCase(baseName.charAt(0)) + baseName.substring(1);
 132         return "set" + fnUppercased;
 133     }
 134 
 135     /**
 136      * Returns true if the given setter method is a valid property setter
 137      * method.
 138      * @param setterMethod
 139      * @return true if setter method, false otherwise.
 140      */
 141     public boolean isValidSetterMethod(ExecutableElement setterMethod) {
 142         return setterMethod.getReturnType().getKind() == TypeKind.VOID;
 143     }
 144 
 145     /**
 146      * Returns true if the method is a property method.
 147      * @param propertyMethod
 148      * @return true if the method is a property method, false otherwise.
 149      */
 150     public boolean isPropertyMethod(ExecutableElement propertyMethod) {
 151         if (!javafx ||
 152                 !propertyMethod.getParameters().isEmpty() ||
 153                 !propertyMethod.getTypeParameters().isEmpty()) {
 154             return false;
 155         }
 156         String methodName = propertyMethod.getSimpleName().toString();
 157         if (!methodName.endsWith("Property") ||
 158                 fxMethodPatterns.matcher(methodName).matches()) {
 159             return false;
 160         }
 161 
 162         TypeMirror returnType = propertyMethod.getReturnType();
 163         if (jbObservableType == null) {
 164             // JavaFX references missing, make a lazy backward compatible check.
 165             return returnType.getKind() != TypeKind.VOID;
 166         } else {
 167             // Apply strict checks since JavaFX references are available
 168             returnType = typeUtils.erasure(propertyMethod.getReturnType());
 169             return typeUtils.isAssignable(returnType, jbObservableType);
 170         }
 171     }
 172 }