src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/VisibleMemberMap.java
branchihse-jdk-library-branch
changeset 56485 00776cd28457
parent 56484 dd465b7efca9
parent 49899 b99fcb855107
child 56488 a6cb200daa5d
equal deleted inserted replaced
56484:dd465b7efca9 56485:00776cd28457
     1 /*
       
     2  * Copyright (c) 1999, 2017, 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.util;
       
    27 
       
    28 import java.util.*;
       
    29 import java.util.regex.Pattern;
       
    30 
       
    31 import javax.lang.model.element.Element;
       
    32 import javax.lang.model.element.ElementKind;
       
    33 import javax.lang.model.element.ExecutableElement;
       
    34 import javax.lang.model.element.TypeElement;
       
    35 import javax.lang.model.element.VariableElement;
       
    36 import javax.lang.model.type.TypeKind;
       
    37 import javax.lang.model.type.TypeMirror;
       
    38 import javax.lang.model.util.ElementFilter;
       
    39 
       
    40 import com.sun.source.doctree.DocCommentTree;
       
    41 import com.sun.source.doctree.DocTree;
       
    42 
       
    43 import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration;
       
    44 import jdk.javadoc.internal.doclets.toolkit.Messages;
       
    45 
       
    46 /**
       
    47  * A data structure that encapsulates the visible members of a particular
       
    48  * type for a given class tree.  To use this data structure, you must specify
       
    49  * the type of member you are interested in (nested class, field, constructor
       
    50  * or method) and the leaf of the class tree.  The data structure will map
       
    51  * all visible members in the leaf and classes above the leaf in the tree.
       
    52  *
       
    53  *  <p><b>This is NOT part of any supported API.
       
    54  *  If you write code that depends on this, you do so at your own risk.
       
    55  *  This code and its internal interfaces are subject to change or
       
    56  *  deletion without notice.</b>
       
    57  *
       
    58  * @author Atul M Dambalkar
       
    59  * @author Jamie Ho (rewrite)
       
    60  */
       
    61 public class VisibleMemberMap {
       
    62 
       
    63     private boolean noVisibleMembers = true;
       
    64 
       
    65     public static enum Kind {
       
    66         INNER_CLASSES,
       
    67         ENUM_CONSTANTS,
       
    68         FIELDS,
       
    69         CONSTRUCTORS,
       
    70         METHODS,
       
    71         ANNOTATION_TYPE_FIELDS,
       
    72         ANNOTATION_TYPE_MEMBER_OPTIONAL,
       
    73         ANNOTATION_TYPE_MEMBER_REQUIRED,
       
    74         PROPERTIES;
       
    75 
       
    76         public static final EnumSet<Kind> summarySet = EnumSet.range(INNER_CLASSES, METHODS);
       
    77         public static final EnumSet<Kind> detailSet = EnumSet.range(ENUM_CONSTANTS, METHODS);
       
    78         public static String getNavLinkLabels(Kind kind) {
       
    79             switch (kind) {
       
    80                 case INNER_CLASSES:
       
    81                     return "doclet.navNested";
       
    82                 case ENUM_CONSTANTS:
       
    83                     return "doclet.navEnum";
       
    84                 case FIELDS:
       
    85                     return "doclet.navField";
       
    86                 case CONSTRUCTORS:
       
    87                     return "doclet.navConstructor";
       
    88                 case METHODS:
       
    89                     return "doclet.navMethod";
       
    90                 default:
       
    91                     throw new AssertionError("unknown kind:" + kind);
       
    92             }
       
    93         }
       
    94     }
       
    95 
       
    96     public static final String STARTLEVEL = "start";
       
    97 
       
    98     // properties aren't named setA* or getA*
       
    99     private static final Pattern GETTERSETTERPATTERN = Pattern.compile("[sg]et\\p{Upper}.*");
       
   100     /**
       
   101      * List of TypeElement objects for which ClassMembers objects are built.
       
   102      */
       
   103     private final Set<TypeElement> visibleClasses;
       
   104 
       
   105     /**
       
   106      * Map for each member name on to a map which contains members with same
       
   107      * name-signature. The mapped map will contain mapping for each MemberDoc
       
   108      * onto it's respecive level string.
       
   109      */
       
   110     private final Map<Object, Map<Element, String>> memberNameMap = new HashMap<>();
       
   111 
       
   112     /**
       
   113      * Map of class and it's ClassMembers object.
       
   114      */
       
   115     private final Map<TypeElement, ClassMembers> classMap = new HashMap<>();
       
   116 
       
   117     /**
       
   118      * Type whose visible members are requested.  This is the leaf of
       
   119      * the class tree being mapped.
       
   120      */
       
   121     private final TypeElement typeElement;
       
   122 
       
   123     /**
       
   124      * Member kind: InnerClasses/Fields/Methods?
       
   125      */
       
   126     public final Kind kind;
       
   127 
       
   128     /**
       
   129      * The configuration this VisibleMemberMap was created with.
       
   130      */
       
   131     private final BaseConfiguration configuration;
       
   132     private final Messages messages;
       
   133     private final Utils utils;
       
   134     private final Comparator<Element> comparator;
       
   135 
       
   136     private final Map<TypeElement, List<Element>> propertiesCache;
       
   137     private final Map<Element, Element> classPropertiesMap;
       
   138     private final Map<Element, GetterSetter> getterSetterMap;
       
   139 
       
   140     /**
       
   141      * Construct a VisibleMemberMap of the given type for the given class.
       
   142      *
       
   143      * @param typeElement whose members are being mapped.
       
   144      * @param kind the kind of member that is being mapped.
       
   145      * @param configuration the configuration to use to construct this
       
   146      * VisibleMemberMap. If the field configuration.nodeprecated is true the
       
   147      * deprecated members are excluded from the map. If the field
       
   148      * configuration.javafx is true the JavaFX features are used.
       
   149      */
       
   150     public VisibleMemberMap(TypeElement typeElement,
       
   151                             Kind kind,
       
   152                             BaseConfiguration configuration) {
       
   153         this.typeElement = typeElement;
       
   154         this.kind = kind;
       
   155         this.configuration = configuration;
       
   156         this.messages = configuration.getMessages();
       
   157         this.utils = configuration.utils;
       
   158         propertiesCache = configuration.propertiesCache;
       
   159         classPropertiesMap = configuration.classPropertiesMap;
       
   160         getterSetterMap = configuration.getterSetterMap;
       
   161         comparator  = utils.makeGeneralPurposeComparator();
       
   162         visibleClasses = new LinkedHashSet<>();
       
   163         new ClassMembers(typeElement, STARTLEVEL).build();
       
   164     }
       
   165 
       
   166     /**
       
   167      * Return the list of visible classes in this map.
       
   168      *
       
   169      * @return the list of visible classes in this map.
       
   170      */
       
   171     public SortedSet<TypeElement> getVisibleClasses() {
       
   172         SortedSet<TypeElement> vClasses = new TreeSet<>(comparator);
       
   173         vClasses.addAll(visibleClasses);
       
   174         return vClasses;
       
   175     }
       
   176 
       
   177     /**
       
   178      * Returns the first method where the given method is visible.
       
   179      * @param e the method whose visible enclosing type is to be found.
       
   180      * @return the method found or null
       
   181      */
       
   182     public ExecutableElement getVisibleMethod(ExecutableElement e) {
       
   183         if (kind != Kind.METHODS || e.getKind() != ElementKind.METHOD) {
       
   184             throw new AssertionError("incompatible member type or visible member map" + e);
       
   185         }
       
   186         // start with the current class
       
   187         for (Element m : getMembers(typeElement)) {
       
   188             ExecutableElement mthd = (ExecutableElement)m;
       
   189             if (utils.executableMembersEqual(mthd, e)) {
       
   190                 return mthd;
       
   191             }
       
   192         }
       
   193 
       
   194         for (TypeElement te : visibleClasses) {
       
   195             if (te == typeElement)
       
   196                 continue;
       
   197             for (Element m : getMembers(te)) {
       
   198                 ExecutableElement mthd = (ExecutableElement)m;
       
   199                 if (utils.executableMembersEqual(mthd, e)) {
       
   200                     return mthd;
       
   201                 }
       
   202             }
       
   203         }
       
   204         return null;
       
   205     }
       
   206 
       
   207     /**
       
   208      * Returns the property field documentation belonging to the given member.
       
   209      * @param element the member for which the property documentation is needed.
       
   210      * @return the property field documentation, null if there is none.
       
   211      */
       
   212     public Element getPropertyElement(Element element) {
       
   213         return classPropertiesMap.get(element);
       
   214     }
       
   215 
       
   216     /**
       
   217      * Returns the getter documentation belonging to the given property method.
       
   218      * @param propertyMethod the method for which the getter is needed.
       
   219      * @return the getter documentation, null if there is none.
       
   220      */
       
   221     public Element getGetterForProperty(Element propertyMethod) {
       
   222         return getterSetterMap.get(propertyMethod).getGetter();
       
   223     }
       
   224 
       
   225     /**
       
   226      * Returns the setter documentation belonging to the given property method.
       
   227      * @param propertyMethod the method for which the setter is needed.
       
   228      * @return the setter documentation, null if there is none.
       
   229      */
       
   230     public Element getSetterForProperty(Element propertyMethod) {
       
   231         return getterSetterMap.get(propertyMethod).getSetter();
       
   232     }
       
   233 
       
   234     /**
       
   235      * Return the package private members inherited by the class.  Only return
       
   236      * if parent is package private and not documented.
       
   237      *
       
   238      * @return the package private members inherited by the class.
       
   239      */
       
   240     private List<Element> getInheritedPackagePrivateMethods() {
       
   241         List<Element> results = new ArrayList<>();
       
   242         for (TypeElement currentClass : visibleClasses) {
       
   243             if (currentClass != typeElement &&
       
   244                 utils.isPackagePrivate(currentClass) &&
       
   245                 !utils.isLinkable(currentClass)) {
       
   246                 // Document these members in the child class because
       
   247                 // the parent is inaccessible.
       
   248                 results.addAll(classMap.get(currentClass).members);
       
   249             }
       
   250         }
       
   251         return results;
       
   252     }
       
   253 
       
   254     /**
       
   255      * Returns a list of visible enclosed members of the mapped type element.
       
   256      *
       
   257      * In the case of methods, the list may contain those methods that are
       
   258      * extended with no specification changes as indicated by the existence
       
   259      * of a sole &commat;inheritDoc or devoid of any API commments.
       
   260      *
       
   261      * This list may also contain appended members, inherited by inaccessible
       
   262      * super types. These members are documented in the subtype when the
       
   263      * super type is not documented.
       
   264      *
       
   265      * @return a list of visible enclosed members
       
   266      */
       
   267 
       
   268     public List<Element> getLeafMembers() {
       
   269         List<Element> result = new ArrayList<>();
       
   270         result.addAll(getMembers(typeElement));
       
   271         result.addAll(getInheritedPackagePrivateMethods());
       
   272         return result;
       
   273     }
       
   274 
       
   275     // Cache to improve performance
       
   276     private HashMap<ExecutableElement, Boolean> overridenMethodCache = new HashMap<>();
       
   277 
       
   278     private boolean hasOverridden(ExecutableElement method) {
       
   279         return overridenMethodCache.computeIfAbsent(method, m -> hasOverriddenCompute(m));
       
   280     }
       
   281 
       
   282     private boolean hasOverriddenCompute(ExecutableElement method) {
       
   283         if (kind != Kind.METHODS) {
       
   284             throw new AssertionError("Unexpected kind: " + kind);
       
   285         }
       
   286         for (TypeElement t : visibleClasses) {
       
   287             for (Element member : classMap.get(t).members) {
       
   288                 ExecutableElement inheritedMethod = (ExecutableElement)member;
       
   289                 if (utils.elementUtils.overrides(method, inheritedMethod, t)) {
       
   290                     return true;
       
   291                 }
       
   292             }
       
   293         }
       
   294         return false;
       
   295     }
       
   296 
       
   297     /**
       
   298      * Returns a list of enclosed members for the given type.
       
   299      *
       
   300      * @param typeElement the given type
       
   301      *
       
   302      * @return a list of enclosed members
       
   303      */
       
   304     public List<Element> getMembers(TypeElement typeElement) {
       
   305         List<Element> result = new ArrayList<>();
       
   306         if (this.kind == Kind.METHODS) {
       
   307             for (Element member : classMap.get(typeElement).members) {
       
   308                 ExecutableElement method = (ExecutableElement)member;
       
   309                 if (hasOverridden(method)) {
       
   310                     if (!utils.isSimpleOverride(method)) {
       
   311                         result.add(method);
       
   312                     }
       
   313                 } else {
       
   314                     result.add(method);
       
   315                 }
       
   316             }
       
   317         } else {
       
   318             result.addAll(classMap.get(typeElement).members);
       
   319         }
       
   320         return result;
       
   321     }
       
   322 
       
   323     public boolean hasMembers(TypeElement typeElement) {
       
   324         return !classMap.get(typeElement).members.isEmpty();
       
   325     }
       
   326 
       
   327     private void fillMemberLevelMap(List<? extends Element> list, String level) {
       
   328         for (Element element : list) {
       
   329             Object key = getMemberKey(element);
       
   330             Map<Element, String> memberLevelMap = memberNameMap.get(key);
       
   331             if (memberLevelMap == null) {
       
   332                 memberLevelMap = new HashMap<>();
       
   333                 memberNameMap.put(key, memberLevelMap);
       
   334             }
       
   335             memberLevelMap.put(element, level);
       
   336         }
       
   337     }
       
   338 
       
   339     private void purgeMemberLevelMap(Iterable<? extends Element> list, String level) {
       
   340         for (Element element : list) {
       
   341             Object key = getMemberKey(element);
       
   342             Map<Element, String> memberLevelMap = memberNameMap.get(key);
       
   343             if (memberLevelMap != null && level.equals(memberLevelMap.get(element)))
       
   344                 memberLevelMap.remove(element);
       
   345         }
       
   346     }
       
   347 
       
   348     /**
       
   349      * Represents a class member.
       
   350      */
       
   351     private class ClassMember {
       
   352         private Set<Element> members;
       
   353 
       
   354         public ClassMember(Element element) {
       
   355             members = new HashSet<>();
       
   356             members.add(element);
       
   357         }
       
   358 
       
   359         public boolean isEqual(ExecutableElement method) {
       
   360             for (Element member : members) {
       
   361                 if (member.getKind() != ElementKind.METHOD)
       
   362                     continue;
       
   363                 ExecutableElement thatMethod = (ExecutableElement) member;
       
   364                 if (utils.executableMembersEqual(method, thatMethod) &&
       
   365                         !utils.isSimpleOverride(thatMethod)) {
       
   366                     members.add(method);
       
   367                     return true;
       
   368                 }
       
   369             }
       
   370             return false;
       
   371         }
       
   372     }
       
   373 
       
   374     /**
       
   375      * A data structure that represents the class members for
       
   376      * a visible class.
       
   377      */
       
   378     private class ClassMembers {
       
   379 
       
   380         /**
       
   381          * The mapping class, whose inherited members are put in the
       
   382          * {@link #members} list.
       
   383          */
       
   384         private final TypeElement typeElement;
       
   385 
       
   386         /**
       
   387          * List of members from the mapping class.
       
   388          */
       
   389         private List<Element> members = null;
       
   390 
       
   391         /**
       
   392          * Level/Depth of inheritance.
       
   393          */
       
   394         private final String level;
       
   395 
       
   396         private ClassMembers(TypeElement mappingClass, String level) {
       
   397             this.typeElement = mappingClass;
       
   398             this.level = level;
       
   399             if (classMap.containsKey(mappingClass) &&
       
   400                         level.startsWith(classMap.get(mappingClass).level)) {
       
   401                 //Remove lower level class so that it can be replaced with
       
   402                 //same class found at higher level.
       
   403                 purgeMemberLevelMap(getClassMembers(mappingClass, false),
       
   404                     classMap.get(mappingClass).level);
       
   405                 classMap.remove(mappingClass);
       
   406                 visibleClasses.remove(mappingClass);
       
   407             }
       
   408             if (!classMap.containsKey(mappingClass)) {
       
   409                 classMap.put(mappingClass, this);
       
   410                 visibleClasses.add(mappingClass);
       
   411             }
       
   412         }
       
   413 
       
   414         private void build() {
       
   415             if (kind == Kind.CONSTRUCTORS) {
       
   416                 addMembers(typeElement);
       
   417             } else {
       
   418                 mapClass();
       
   419             }
       
   420         }
       
   421 
       
   422         private void mapClass() {
       
   423             addMembers(typeElement);
       
   424             List<? extends TypeMirror> interfaces = typeElement.getInterfaces();
       
   425             for (TypeMirror anInterface : interfaces) {
       
   426                 String locallevel = level + 1;
       
   427                 ClassMembers cm = new ClassMembers(utils.asTypeElement(anInterface), locallevel);
       
   428                 cm.mapClass();
       
   429             }
       
   430             if (utils.isClass(typeElement)) {
       
   431                 TypeElement superclass = utils.getSuperClass(typeElement);
       
   432                 if (!(superclass == null || typeElement.equals(superclass))) {
       
   433                     ClassMembers cm = new ClassMembers(superclass, level + "c");
       
   434                     cm.mapClass();
       
   435                 }
       
   436             }
       
   437         }
       
   438 
       
   439         /**
       
   440          * Get all the valid members from the mapping class. Get the list of
       
   441          * members for the class to be included into(ctii), also get the level
       
   442          * string for ctii. If mapping class member is not already in the
       
   443          * inherited member list and if it is visible in the ctii and not
       
   444          * overridden, put such a member in the inherited member list.
       
   445          * Adjust member-level-map, class-map.
       
   446          */
       
   447         private void addMembers(TypeElement fromClass) {
       
   448             List<Element> result = new ArrayList<>();
       
   449             for (Element element : getClassMembers(fromClass, true)) {
       
   450                 if (memberIsVisible(element)) {
       
   451                     if (!isOverridden(element, level)) {
       
   452                         if (!utils.isHidden(element)) {
       
   453                             result.add(element);
       
   454                         }
       
   455                     }
       
   456                 }
       
   457             }
       
   458             if (members != null) {
       
   459                 throw new AssertionError("members should not be null");
       
   460             }
       
   461             members = Collections.unmodifiableList(result);
       
   462             if (!members.isEmpty()) {
       
   463                 noVisibleMembers = false;
       
   464             }
       
   465             fillMemberLevelMap(getClassMembers(fromClass, false), level);
       
   466         }
       
   467 
       
   468         /**
       
   469          * Is given element visible in given typeElement in terms of inheritance? The given element
       
   470          * is visible in the given typeElement if it is public or protected and if it is
       
   471          * package-private if it's containing class is in the same package as the given typeElement.
       
   472          */
       
   473         private boolean memberIsVisible(Element element) {
       
   474             if (utils.getEnclosingTypeElement(element).equals(VisibleMemberMap.this.typeElement)) {
       
   475                 //Member is in class that we are finding visible members for.
       
   476                 //Of course it is visible.
       
   477                 return true;
       
   478             } else if (utils.isPrivate(element)) {
       
   479                 //Member is in super class or implemented interface.
       
   480                 //Private, so not inherited.
       
   481                 return false;
       
   482             } else if (utils.isPackagePrivate(element)) {
       
   483                 //Member is package private.  Only return true if its class is in
       
   484                 //same package.
       
   485                 return utils.containingPackage(element).equals(utils.containingPackage(VisibleMemberMap.this.typeElement));
       
   486             } else {
       
   487                 //Public members are always inherited.
       
   488                 return true;
       
   489             }
       
   490         }
       
   491 
       
   492         /**
       
   493          * Return all available class members.
       
   494          */
       
   495         private List<? extends Element> getClassMembers(TypeElement te, boolean filter) {
       
   496             if (utils.isEnum(te) && kind == Kind.CONSTRUCTORS) {
       
   497                 //If any of these rules are hit, return empty array because
       
   498                 //we don't document these members ever.
       
   499                 return Collections.emptyList();
       
   500             }
       
   501             List<? extends Element> list;
       
   502             switch (kind) {
       
   503                 case ANNOTATION_TYPE_FIELDS:
       
   504                     list = (filter)
       
   505                             ? utils.getAnnotationFields(te)
       
   506                             : utils.getAnnotationFieldsUnfiltered(te);
       
   507                     break;
       
   508                 case ANNOTATION_TYPE_MEMBER_OPTIONAL:
       
   509                     list = utils.isAnnotationType(te)
       
   510                             ? filterAnnotations(te, false)
       
   511                             : Collections.emptyList();
       
   512                     break;
       
   513                 case ANNOTATION_TYPE_MEMBER_REQUIRED:
       
   514                     list = utils.isAnnotationType(te)
       
   515                             ? filterAnnotations(te, true)
       
   516                             : Collections.emptyList();
       
   517                     break;
       
   518                 case INNER_CLASSES:
       
   519                     List<TypeElement> xlist = filter
       
   520                             ? utils.getInnerClasses(te)
       
   521                             : utils.getInnerClassesUnfiltered(te);
       
   522                     list = new ArrayList<>(xlist);
       
   523                     break;
       
   524                 case ENUM_CONSTANTS:
       
   525                     list = utils.getEnumConstants(te);
       
   526                     break;
       
   527                 case FIELDS:
       
   528                     if (filter) {
       
   529                         list = utils.isAnnotationType(te)
       
   530                                 ? utils.getAnnotationFields(te)
       
   531                                 : utils.getFields(te);
       
   532                     } else {
       
   533                         list = utils.isAnnotationType(te)
       
   534                                 ? utils.getAnnotationFieldsUnfiltered(te)
       
   535                                 : utils.getFieldsUnfiltered(te);
       
   536                     }
       
   537                     break;
       
   538                 case CONSTRUCTORS:
       
   539                     list = utils.getConstructors(te);
       
   540                     break;
       
   541                 case METHODS:
       
   542                     list = filter ? utils.getMethods(te) : utils.getMethodsUnfiltered(te);
       
   543                     checkOnPropertiesTags(list);
       
   544                     break;
       
   545                 case PROPERTIES:
       
   546                     list = properties(te, filter);
       
   547                     break;
       
   548                 default:
       
   549                     list = Collections.emptyList();
       
   550             }
       
   551             // Deprected members should be excluded or not?
       
   552             if (configuration.nodeprecated) {
       
   553                 return utils.excludeDeprecatedMembers(list);
       
   554             }
       
   555             return list;
       
   556         }
       
   557 
       
   558         /**
       
   559          * Filter the annotation type members and return either the required
       
   560          * members or the optional members, depending on the value of the
       
   561          * required parameter.
       
   562          *
       
   563          * @param typeElement The annotation type to process.
       
   564          * @param required
       
   565          * @return the annotation type members and return either the required
       
   566          * members or the optional members, depending on the value of the
       
   567          * required parameter.
       
   568          */
       
   569         private List<Element> filterAnnotations(TypeElement typeElement, boolean required) {
       
   570             List<Element> members = utils.getAnnotationMethods(typeElement);
       
   571             List<Element> targetMembers = new ArrayList<>();
       
   572             for (Element member : members) {
       
   573                 ExecutableElement ee = (ExecutableElement)member;
       
   574                 if ((required && ee.getDefaultValue() == null)
       
   575                         || ((!required) && ee.getDefaultValue() != null)) {
       
   576                     targetMembers.add(member);
       
   577                 }
       
   578             }
       
   579             return targetMembers;
       
   580         }
       
   581 
       
   582         /**
       
   583          * Is member overridden? The member is overridden if it is found in the
       
   584          * same level hierarchy e.g. member at level "11" overrides member at
       
   585          * level "111".
       
   586          */
       
   587         private boolean isOverridden(Element element, String level) {
       
   588             Object key = getMemberKey(element);
       
   589             Map<?, String> memberLevelMap = (Map<?, String>) memberNameMap.get(key);
       
   590             if (memberLevelMap == null)
       
   591                 return false;
       
   592             for (String mappedlevel : memberLevelMap.values()) {
       
   593                 if (mappedlevel.equals(STARTLEVEL)
       
   594                         || (level.startsWith(mappedlevel)
       
   595                         && !level.equals(mappedlevel))) {
       
   596                     return true;
       
   597                 }
       
   598             }
       
   599             return false;
       
   600         }
       
   601 
       
   602         private List<Element> properties(final TypeElement typeElement, final boolean filter) {
       
   603             final List<ExecutableElement> allMethods = filter
       
   604                     ? utils.getMethods(typeElement)
       
   605                     : utils.getMethodsUnfiltered(typeElement);
       
   606             final List<VariableElement> allFields = utils.getFieldsUnfiltered(typeElement);
       
   607 
       
   608             if (propertiesCache.containsKey(typeElement)) {
       
   609                 return propertiesCache.get(typeElement);
       
   610             }
       
   611 
       
   612             final List<Element> result = new ArrayList<>();
       
   613 
       
   614             for (final Element propertyMethod : allMethods) {
       
   615                 ExecutableElement ee = (ExecutableElement)propertyMethod;
       
   616                 if (!isPropertyMethod(ee)) {
       
   617                     continue;
       
   618                 }
       
   619 
       
   620                 final ExecutableElement getter = getterForField(allMethods, ee);
       
   621                 final ExecutableElement setter = setterForField(allMethods, ee);
       
   622                 final VariableElement field = fieldForProperty(allFields, ee);
       
   623 
       
   624                 addToPropertiesMap(setter, getter, ee, field);
       
   625                 getterSetterMap.put(propertyMethod, new GetterSetter(getter, setter));
       
   626                 result.add(ee);
       
   627             }
       
   628             propertiesCache.put(typeElement, result);
       
   629             return result;
       
   630         }
       
   631 
       
   632         private void addToPropertiesMap(ExecutableElement setter,
       
   633                                         ExecutableElement getter,
       
   634                                         ExecutableElement propertyMethod,
       
   635                                         VariableElement field) {
       
   636             if (field == null || utils.getDocCommentTree(field) == null) {
       
   637                 addToPropertiesMap(setter, propertyMethod);
       
   638                 addToPropertiesMap(getter, propertyMethod);
       
   639                 addToPropertiesMap(propertyMethod, propertyMethod);
       
   640             } else {
       
   641                 addToPropertiesMap(getter, field);
       
   642                 addToPropertiesMap(setter, field);
       
   643                 addToPropertiesMap(propertyMethod, field);
       
   644             }
       
   645         }
       
   646 
       
   647         private void addToPropertiesMap(Element propertyMethod,
       
   648                                         Element commentSource) {
       
   649             if (null == propertyMethod || null == commentSource) {
       
   650                 return;
       
   651             }
       
   652             DocCommentTree docTree = utils.getDocCommentTree(propertyMethod);
       
   653 
       
   654             /* The second condition is required for the property buckets. In
       
   655              * this case the comment is at the property method (not at the field)
       
   656              * and it needs to be listed in the map.
       
   657              */
       
   658             if ((docTree == null) || propertyMethod.equals(commentSource)) {
       
   659                 classPropertiesMap.put(propertyMethod, commentSource);
       
   660             }
       
   661         }
       
   662 
       
   663         private ExecutableElement getterForField(List<ExecutableElement> methods,
       
   664                                          ExecutableElement propertyMethod) {
       
   665             final String propertyMethodName = utils.getSimpleName(propertyMethod);
       
   666             final String fieldName = propertyMethodName.substring(0,
       
   667                             propertyMethodName.lastIndexOf("Property"));
       
   668             final String fieldNameUppercased =
       
   669                     "" + Character.toUpperCase(fieldName.charAt(0))
       
   670                                             + fieldName.substring(1);
       
   671             final String getterNamePattern;
       
   672             final String fieldTypeName = propertyMethod.getReturnType().toString();
       
   673             if ("boolean".equals(fieldTypeName)
       
   674                     || fieldTypeName.endsWith("BooleanProperty")) {
       
   675                 getterNamePattern = "(is|get)" + fieldNameUppercased;
       
   676             } else {
       
   677                 getterNamePattern = "get" + fieldNameUppercased;
       
   678             }
       
   679 
       
   680             for (ExecutableElement method : methods) {
       
   681                 if (Pattern.matches(getterNamePattern, utils.getSimpleName(method))) {
       
   682                     if (method.getParameters().isEmpty() &&
       
   683                             utils.isPublic(method) || utils.isProtected(method)) {
       
   684                         return method;
       
   685                     }
       
   686                 }
       
   687             }
       
   688             return null;
       
   689         }
       
   690 
       
   691         private ExecutableElement setterForField(List<ExecutableElement> methods,
       
   692                                          ExecutableElement propertyMethod) {
       
   693             final String propertyMethodName = utils.getSimpleName(propertyMethod);
       
   694             final String fieldName =
       
   695                     propertyMethodName.substring(0,
       
   696                             propertyMethodName.lastIndexOf("Property"));
       
   697             final String fieldNameUppercased =
       
   698                     "" + Character.toUpperCase(fieldName.charAt(0))
       
   699                                              + fieldName.substring(1);
       
   700             final String setter = "set" + fieldNameUppercased;
       
   701 
       
   702             for (ExecutableElement method : methods) {
       
   703                 if (setter.equals(utils.getSimpleName(method))) {
       
   704                     if (method.getParameters().size() == 1
       
   705                             && method.getReturnType().getKind() == TypeKind.VOID
       
   706                             && (utils.isPublic(method) || utils.isProtected(method))) {
       
   707                         return method;
       
   708                     }
       
   709                 }
       
   710             }
       
   711             return null;
       
   712         }
       
   713 
       
   714         private VariableElement fieldForProperty(List<VariableElement> fields, ExecutableElement property) {
       
   715 
       
   716             for (VariableElement field : fields) {
       
   717                 final String fieldName = utils.getSimpleName(field);
       
   718                 final String propertyName = fieldName + "Property";
       
   719                 if (propertyName.equals(utils.getSimpleName(property))) {
       
   720                     return field;
       
   721                 }
       
   722             }
       
   723             return null;
       
   724         }
       
   725 
       
   726         private boolean isPropertyMethod(ExecutableElement method) {
       
   727             if (!configuration.javafx) {
       
   728                return false;
       
   729             }
       
   730             if (!utils.getSimpleName(method).endsWith("Property")) {
       
   731                 return false;
       
   732             }
       
   733 
       
   734             if (!memberIsVisible(method)) {
       
   735                 return false;
       
   736             }
       
   737 
       
   738             if (GETTERSETTERPATTERN.matcher(utils.getSimpleName(method)).matches()) {
       
   739                 return false;
       
   740             }
       
   741             if (!method.getTypeParameters().isEmpty()) {
       
   742                 return false;
       
   743             }
       
   744             return method.getParameters().isEmpty()
       
   745                     && method.getReturnType().getKind() != TypeKind.VOID;
       
   746         }
       
   747 
       
   748         private void checkOnPropertiesTags(List<? extends Element> members) {
       
   749             for (Element e: members) {
       
   750                 ExecutableElement ee = (ExecutableElement)e;
       
   751                 if (utils.isIncluded(ee)) {
       
   752                     CommentHelper ch = utils.getCommentHelper(ee);
       
   753                     for (DocTree tree: utils.getBlockTags(ee)) {
       
   754                         String tagName = ch.getTagName(tree);
       
   755                         if (tagName.equals("@propertySetter")
       
   756                                 || tagName.equals("@propertyGetter")
       
   757                                 || tagName.equals("@propertyDescription")) {
       
   758                             if (!isPropertyGetterOrSetter(members, ee)) {
       
   759                                 messages.warning(ch.getDocTreePath(tree),
       
   760                                         "doclet.javafx_tag_misuse");
       
   761                             }
       
   762                             break;
       
   763                         }
       
   764                     }
       
   765                 }
       
   766             }
       
   767         }
       
   768 
       
   769         private boolean isPropertyGetterOrSetter(List<? extends Element> members,
       
   770                                                  ExecutableElement method) {
       
   771             String propertyName = utils.propertyName(method);
       
   772             if (!propertyName.isEmpty()) {
       
   773                 String propertyMethodName = propertyName + "Property";
       
   774                 for (Element member: members) {
       
   775                     if (utils.getSimpleName(member).equals(propertyMethodName)) {
       
   776                         return true;
       
   777                     }
       
   778                 }
       
   779             }
       
   780             return false;
       
   781         }
       
   782     }
       
   783 
       
   784     public class GetterSetter {
       
   785         private final Element getter;
       
   786         private final Element setter;
       
   787 
       
   788         public GetterSetter(Element getter, Element setter) {
       
   789             this.getter = getter;
       
   790             this.setter = setter;
       
   791         }
       
   792 
       
   793         public Element getGetter() {
       
   794             return getter;
       
   795         }
       
   796 
       
   797         public Element getSetter() {
       
   798             return setter;
       
   799         }
       
   800     }
       
   801 
       
   802     /**
       
   803      * Return true if this map has no visible members.
       
   804      *
       
   805      * @return true if this map has no visible members.
       
   806      */
       
   807     public boolean noVisibleMembers() {
       
   808         return noVisibleMembers;
       
   809     }
       
   810 
       
   811     private ClassMember getClassMember(ExecutableElement member) {
       
   812         for (Object key : memberNameMap.keySet()) {
       
   813             if (key instanceof String) {
       
   814                 continue;
       
   815             }
       
   816             if (((ClassMember) key).isEqual(member)) {
       
   817                 return (ClassMember) key;
       
   818             }
       
   819         }
       
   820         return new ClassMember(member);
       
   821     }
       
   822 
       
   823     /**
       
   824      * Return the key to the member map for the given member.
       
   825      */
       
   826     private Object getMemberKey(Element element) {
       
   827         if (utils.isConstructor(element)) {
       
   828             return utils.getSimpleName(element) + utils.flatSignature((ExecutableElement)element);
       
   829         } else if (utils.isMethod(element)) {
       
   830             return getClassMember((ExecutableElement) element);
       
   831         } else if (utils.isField(element) || utils.isEnumConstant(element) || utils.isAnnotationType(element)) {
       
   832             return utils.getSimpleName(element);
       
   833         } else { // it's a class or interface
       
   834             String classOrIntName = utils.getSimpleName(element);
       
   835             //Strip off the containing class name because we only want the member name.
       
   836             classOrIntName = classOrIntName.indexOf('.') != 0
       
   837                     ? classOrIntName.substring(classOrIntName.lastIndexOf('.'))
       
   838                     : classOrIntName;
       
   839             return "clint" + classOrIntName;
       
   840         }
       
   841     }
       
   842 }