src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/VisibleMemberMap.java

Print this page




   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 com.sun.tools.doclets.internal.toolkit.util;
  27 
  28 import java.util.*;
  29 import java.util.regex.Pattern;
  30 
  31 import com.sun.javadoc.*;
  32 import com.sun.tools.doclets.internal.toolkit.*;




  33 





  34 /**
  35  * A data structure that encapsulates the visible members of a particular
  36  * type for a given class tree.  To use this data structor, you must specify
  37  * the type of member you are interested in (nested class, field, constructor
  38  * or method) and the leaf of the class tree.  The data structure will map
  39  * all visible members in the leaf and classes above the leaf in the tree.
  40  *
  41  *  <p><b>This is NOT part of any supported API.
  42  *  If you write code that depends on this, you do so at your own risk.
  43  *  This code and its internal interfaces are subject to change or
  44  *  deletion without notice.</b>
  45  *
  46  * @author Atul M Dambalkar
  47  * @author Jamie Ho (rewrite)
  48  */
  49 public class VisibleMemberMap {
  50 
  51     private boolean noVisibleMembers = true;
  52 
  53     public static final int INNERCLASSES    = 0;
  54     public static final int ENUM_CONSTANTS  = 1;
  55     public static final int FIELDS          = 2;
  56     public static final int CONSTRUCTORS    = 3;
  57     public static final int METHODS         = 4;
  58     public static final int ANNOTATION_TYPE_FIELDS = 5;
  59     public static final int ANNOTATION_TYPE_MEMBER_OPTIONAL = 6;
  60     public static final int ANNOTATION_TYPE_MEMBER_REQUIRED = 7;
  61     public static final int PROPERTIES      = 8;

  62 
  63     /**
  64      * The total number of member types is {@value}.
  65      */
  66     public static final int NUM_MEMBER_TYPES = 9;















  67 
  68     public static final String STARTLEVEL = "start";
  69 


  70     /**
  71      * List of ClassDoc objects for which ClassMembers objects are built.
  72      */
  73     private final List<ClassDoc> visibleClasses = new ArrayList<>();
  74 
  75     /**
  76      * Map for each member name on to a map which contains members with same
  77      * name-signature. The mapped map will contain mapping for each MemberDoc
  78      * onto it's respecive level string.
  79      */
  80     private final Map<Object,Map<ProgramElementDoc,String>> memberNameMap = new HashMap<>();
  81 
  82     /**
  83      * Map of class and it's ClassMembers object.
  84      */
  85     private final Map<ClassDoc,ClassMembers> classMap = new HashMap<>();
  86 
  87     /**
  88      * Type whose visible members are requested.  This is the leaf of
  89      * the class tree being mapped.
  90      */
  91     private final ClassDoc classdoc;
  92 
  93     /**
  94      * Member kind: InnerClasses/Fields/Methods?
  95      */
  96     private final int kind;
  97 
  98     /**
  99      * The configuration this VisibleMemberMap was created with.
 100      */
 101     private final Configuration configuration;
 102     private final Utils utils;

 103 
 104     private static final Map<ClassDoc, ProgramElementDoc[]> propertiesCache = new HashMap<>();
 105     private static final Map<ProgramElementDoc, ProgramElementDoc> classPropertiesMap = new HashMap<>();
 106     private static final Map<ProgramElementDoc, GetterSetter> getterSetterMap = new HashMap<>();
 107 
 108     /**
 109      * Construct a VisibleMemberMap of the given type for the given
 110      * class.
 111      *
 112      * @param classdoc the class whose members are being mapped.
 113      * @param kind the kind of member that is being mapped.
 114      * @param configuration the configuration to use to construct this
 115      * VisibleMemberMap. If the field configuration.nodeprecated is true the
 116      * deprecated members are excluded from the map. If the field
 117      * configuration.javafx is true the JavaFX features are used.
 118      */
 119     public VisibleMemberMap(ClassDoc classdoc,
 120                             int kind,
 121                             Configuration configuration) {
 122         this.classdoc = classdoc;
 123         this.kind = kind;
 124         this.configuration = configuration;
 125         this.utils = configuration.utils;
 126         new ClassMembers(classdoc, STARTLEVEL).build();


 127     }
 128 
 129     /**
 130      * Return the list of visible classes in this map.
 131      *
 132      * @return the list of visible classes in this map.
 133      */
 134     public List<ClassDoc> getVisibleClassesList() {
 135         sort(visibleClasses);
 136         return visibleClasses;

 137     }
 138 
 139     /**
 140      * Returns the property field documentation belonging to the given member.
 141      * @param ped the member for which the property documentation is needed.
 142      * @return the property field documentation, null if there is none.
 143      */
 144     public ProgramElementDoc getPropertyMemberDoc(ProgramElementDoc ped) {
 145         return classPropertiesMap.get(ped);
 146     }
 147 
 148     /**
 149      * Returns the getter documentation belonging to the given property method.
 150      * @param propertyMethod the method for which the getter is needed.
 151      * @return the getter documentation, null if there is none.
 152      */
 153     public ProgramElementDoc getGetterForProperty(ProgramElementDoc propertyMethod) {
 154         return getterSetterMap.get(propertyMethod).getGetter();
 155     }
 156 
 157     /**
 158      * Returns the setter documentation belonging to the given property method.
 159      * @param propertyMethod the method for which the setter is needed.
 160      * @return the setter documentation, null if there is none.
 161      */
 162     public ProgramElementDoc getSetterForProperty(ProgramElementDoc propertyMethod) {
 163         return getterSetterMap.get(propertyMethod).getSetter();
 164     }
 165 
 166     /**
 167      * Return the package private members inherited by the class.  Only return
 168      * if parent is package private and not documented.
 169      *
 170      * @param configuration the current configuration of the doclet.
 171      * @return the package private members inherited by the class.
 172      */
 173     private List<ProgramElementDoc> getInheritedPackagePrivateMethods(Configuration configuration) {
 174         List<ProgramElementDoc> results = new ArrayList<>();
 175         for (ClassDoc currentClass : visibleClasses) {
 176             if (currentClass != classdoc &&
 177                 currentClass.isPackagePrivate() &&
 178                 !utils.isLinkable(currentClass, configuration)) {
 179                 // Document these members in the child class because
 180                 // the parent is inaccessible.
 181                 results.addAll(getMembersFor(currentClass));
 182             }
 183         }
 184         return results;
 185     }
 186 
 187     /**
 188      * Return the visible members of the class being mapped.  Also append at the
 189      * end of the list members that are inherited by inaccessible parents. We
 190      * document these members in the child because the parent is not documented.
 191      *
 192      * @param configuration the current configuration of the doclet.
 193      */
 194     public List<ProgramElementDoc> getLeafClassMembers(Configuration configuration) {
 195         List<ProgramElementDoc> result = getMembersFor(classdoc);
 196         result.addAll(getInheritedPackagePrivateMethods(configuration));
 197         return result;
 198     }
 199 






 200     /**
 201      * Retrn the list of members for the given class.
 202      *
 203      * @param cd the class to retrieve the list of visible members for.
 204      *
 205      * @return the list of members for the given class.
 206      */
 207     public List<ProgramElementDoc> getMembersFor(ClassDoc cd) {
 208         ClassMembers clmembers = classMap.get(cd);
 209         if (clmembers == null) {
 210             return new ArrayList<>();
 211         }
 212         return clmembers.getMembers();


 213     }
 214 
 215     /**
 216      * Sort the given mixed list of classes and interfaces to a list of
 217      * classes followed by interfaces traversed. Don't sort alphabetically.
 218      */
 219     private void sort(List<ClassDoc> list) {
 220         List<ClassDoc> classes = new ArrayList<>();
 221         List<ClassDoc> interfaces = new ArrayList<>();
 222         for (ClassDoc cd : list) {
 223             if (cd.isClass()) {
 224                 classes.add(cd);
 225             }
 226             else {
 227                 interfaces.add(cd);

 228             }
 229         }
 230         list.clear();
 231         list.addAll(classes);
 232         list.addAll(interfaces);
 233     }
 234 
 235     private void fillMemberLevelMap(List<ProgramElementDoc> list, String level) {
 236         for (ProgramElementDoc element : list) {
 237             Object key = getMemberKey(element);
 238             Map<ProgramElementDoc, String> memberLevelMap = memberNameMap.get(key);
 239             if (memberLevelMap == null) {
 240                 memberLevelMap = new HashMap<>();
 241                 memberNameMap.put(key, memberLevelMap);
 242             }
 243             memberLevelMap.put(element, level);
 244         }
 245     }
 246 
 247     private void purgeMemberLevelMap(List<ProgramElementDoc> list, String level) {
 248         for (ProgramElementDoc element : list) {
 249             Object key = getMemberKey(element);
 250             Map<ProgramElementDoc, String> memberLevelMap = memberNameMap.get(key);
 251             if (memberLevelMap != null && level.equals(memberLevelMap.get(element)))
 252                 memberLevelMap.remove(element);
 253         }
 254     }
 255 
 256     /**
 257      * Represents a class member.  We should be able to just use a
 258      * ProgramElementDoc instead of this class, but that doesn't take
 259      * type variables in consideration when comparing.
 260      */
 261     private class ClassMember {
 262         private Set<ProgramElementDoc> members;
 263 
 264         public ClassMember(ProgramElementDoc programElementDoc) {
 265             members = new HashSet<>();
 266             members.add(programElementDoc);
 267         }
 268 
 269         public void addMember(ProgramElementDoc programElementDoc) {
 270             members.add(programElementDoc);
 271         }
 272 
 273         public boolean isEqual(MethodDoc member) {
 274             for (ProgramElementDoc element : members) {
 275                 if (utils.executableMembersEqual(member, (MethodDoc) element)) {
 276                     members.add(member);
 277                     return true;
 278                 }
 279             }
 280             return false;
 281         }
 282     }
 283 
 284     /**
 285      * A data structure that represents the class members for
 286      * a visible class.
 287      */
 288     private class ClassMembers {
 289 
 290         /**
 291          * The mapping class, whose inherited members are put in the
 292          * {@link #members} list.
 293          */
 294         private ClassDoc mappingClass;
 295 
 296         /**
 297          * List of inherited members from the mapping class.
 298          */
 299         private List<ProgramElementDoc> members = new ArrayList<>();
 300 
 301         /**
 302          * Level/Depth of inheritance.
 303          */
 304         private String level;
 305 
 306         /**
 307          * Return list of inherited members from mapping class.
 308          *
 309          * @return List Inherited members.
 310          */
 311         public List<ProgramElementDoc> getMembers() {
 312             return members;
 313         }
 314 
 315         private ClassMembers(ClassDoc mappingClass, String level) {
 316             this.mappingClass = mappingClass;
 317             this.level = level;
 318             if (classMap.containsKey(mappingClass) &&
 319                         level.startsWith(classMap.get(mappingClass).level)) {
 320                 //Remove lower level class so that it can be replaced with
 321                 //same class found at higher level.
 322                 purgeMemberLevelMap(getClassMembers(mappingClass, false),
 323                     classMap.get(mappingClass).level);
 324                 classMap.remove(mappingClass);
 325                 visibleClasses.remove(mappingClass);
 326             }
 327             if (!classMap.containsKey(mappingClass)) {
 328                 classMap.put(mappingClass, this);
 329                 visibleClasses.add(mappingClass);
 330             }
 331 
 332         }
 333 
 334         private void build() {
 335             if (kind == CONSTRUCTORS) {
 336                 addMembers(mappingClass);
 337             } else {
 338                 mapClass();
 339             }
 340         }
 341 
 342         private void mapClass() {
 343             addMembers(mappingClass);
 344             ClassDoc[] interfaces = mappingClass.interfaces();
 345             for (ClassDoc anInterface : interfaces) {
 346                 String locallevel = level + 1;
 347                 ClassMembers cm = new ClassMembers(anInterface, locallevel);
 348                 cm.mapClass();
 349             }
 350             if (mappingClass.isClass()) {
 351                 ClassDoc superclass = mappingClass.superclass();
 352                 if (!(superclass == null || mappingClass.equals(superclass))) {
 353                     ClassMembers cm = new ClassMembers(superclass,
 354                                                        level + "c");
 355                     cm.mapClass();
 356                 }
 357             }
 358         }
 359 
 360         /**
 361          * Get all the valid members from the mapping class. Get the list of
 362          * members for the class to be included into(ctii), also get the level
 363          * string for ctii. If mapping class member is not already in the
 364          * inherited member list and if it is visible in the ctii and not
 365          * overridden, put such a member in the inherited member list.
 366          * Adjust member-level-map, class-map.
 367          */
 368         private void addMembers(ClassDoc fromClass) {
 369             List<ProgramElementDoc> cdmembers = getClassMembers(fromClass, true);
 370             List<ProgramElementDoc> incllist = new ArrayList<>();
 371             for (ProgramElementDoc pgmelem : cdmembers) {
 372                 if (!found(members, pgmelem) &&
 373                     memberIsVisible(pgmelem) &&
 374                     !isOverridden(pgmelem, level) &&
 375                     !isTreatedAsPrivate(pgmelem)) {
 376                     incllist.add(pgmelem);
 377                 }
 378             }
 379             if (incllist.size() > 0) {



 380                 noVisibleMembers = false;
 381             }
 382             members.addAll(incllist);
 383             fillMemberLevelMap(getClassMembers(fromClass, false), level);
 384         }
 385 
 386         private boolean isTreatedAsPrivate(ProgramElementDoc pgmelem) {
 387             if (!configuration.javafx) {
 388                 return false;
 389             }
 390 
 391             Tag[] aspTags = pgmelem.tags("@treatAsPrivate");
 392             boolean result = (aspTags != null) && (aspTags.length > 0);
 393             return result;
 394         }
 395 
 396         /**
 397          * Is given doc item visible in given classdoc in terms fo inheritance?
 398          * The given doc item is visible in the given classdoc if it is public
 399          * or protected and if it is package-private if it's containing class
 400          * is in the same package as the given classdoc.
 401          */
 402         private boolean memberIsVisible(ProgramElementDoc pgmdoc) {
 403             if (pgmdoc.containingClass().equals(classdoc)) {
 404                 //Member is in class that we are finding visible members for.
 405                 //Of course it is visible.
 406                 return true;
 407             } else if (pgmdoc.isPrivate()) {
 408                 //Member is in super class or implemented interface.
 409                 //Private, so not inherited.
 410                 return false;
 411             } else if (pgmdoc.isPackagePrivate()) {
 412                 //Member is package private.  Only return true if its class is in
 413                 //same package.
 414                 return pgmdoc.containingClass().containingPackage().equals(
 415                     classdoc.containingPackage());
 416             } else {
 417                 //Public members are always inherited.
 418                 return true;
 419             }
 420         }
 421 
 422         /**
 423          * Return all available class members.
 424          */
 425         private List<ProgramElementDoc> getClassMembers(ClassDoc cd, boolean filter) {
 426             if (cd.isEnum() && kind == CONSTRUCTORS) {
 427                 //If any of these rules are hit, return empty array because
 428                 //we don't document these members ever.
 429                 return Arrays.asList(new ProgramElementDoc[] {});
 430             }
 431             ProgramElementDoc[] members = null;
 432             switch (kind) {
 433                 case ANNOTATION_TYPE_FIELDS:
 434                     members = cd.fields(filter);


 435                     break;
 436                 case ANNOTATION_TYPE_MEMBER_OPTIONAL:
 437                     members = cd.isAnnotationType() ?
 438                         filter((AnnotationTypeDoc) cd, false) :
 439                         new AnnotationTypeElementDoc[] {};
 440                     break;
 441                 case ANNOTATION_TYPE_MEMBER_REQUIRED:
 442                     members = cd.isAnnotationType() ?
 443                         filter((AnnotationTypeDoc) cd, true) :
 444                         new AnnotationTypeElementDoc[] {};
 445                     break;
 446                 case INNERCLASSES:
 447                     members = cd.innerClasses(filter);



 448                     break;
 449                 case ENUM_CONSTANTS:
 450                     members = cd.enumConstants();
 451                     break;
 452                 case FIELDS:
 453                     members = cd.fields(filter);








 454                     break;
 455                 case CONSTRUCTORS:
 456                     members = cd.constructors();
 457                     break;
 458                 case METHODS:
 459                     members = cd.methods(filter);
 460                     checkOnPropertiesTags((MethodDoc[])members);
 461                     break;
 462                 case PROPERTIES:
 463                     members = properties(cd, filter);
 464                     break;
 465                 default:
 466                     members = new ProgramElementDoc[0];
 467             }
 468             // Deprected members should be excluded or not?
 469             if (configuration.nodeprecated) {
 470                 return utils.excludeDeprecatedMembersAsList(members);
 471             }
 472             return Arrays.asList(members);
 473         }
 474 
 475         /**
 476          * Filter the annotation type members and return either the required
 477          * members or the optional members, depending on the value of the
 478          * required parameter.
 479          *
 480          * @param doc The annotation type to process.
 481          * @param required
 482          * @return the annotation type members and return either the required
 483          * members or the optional members, depending on the value of the
 484          * required parameter.
 485          */
 486         private AnnotationTypeElementDoc[] filter(AnnotationTypeDoc doc,
 487             boolean required) {
 488             AnnotationTypeElementDoc[] members = doc.elements();
 489             List<AnnotationTypeElementDoc> targetMembers = new ArrayList<>();
 490             for (AnnotationTypeElementDoc member : members) {
 491                 if ((required && member.defaultValue() == null) ||
 492                     ((!required) && member.defaultValue() != null)) {
 493                     targetMembers.add(member);
 494                 }
 495             }
 496             return targetMembers.toArray(new AnnotationTypeElementDoc[]{});
 497         }
 498 
 499         private boolean found(List<ProgramElementDoc> list, ProgramElementDoc elem) {
 500             for (ProgramElementDoc pgmelem : list) {
 501                 if (utils.matches(pgmelem, elem)) {
 502                     return true;
 503                 }
 504             }
 505             return false;
 506         }
 507 
 508 
 509         /**
 510          * Is member overridden? The member is overridden if it is found in the
 511          * same level hierarchy e.g. member at level "11" overrides member at
 512          * level "111".
 513          */
 514         private boolean isOverridden(ProgramElementDoc pgmdoc, String level) {
 515             Map<?,String> memberLevelMap = (Map<?,String>) memberNameMap.get(getMemberKey(pgmdoc));

 516             if (memberLevelMap == null)
 517                 return false;
 518             for (String mappedlevel : memberLevelMap.values()) {
 519                 if (mappedlevel.equals(STARTLEVEL) ||
 520                     (level.startsWith(mappedlevel) &&
 521                      !level.equals(mappedlevel))) {
 522                     return true;
 523                 }
 524             }
 525             return false;
 526         }
 527 
 528         private ProgramElementDoc[] properties(final ClassDoc cd, final boolean filter) {
 529             final MethodDoc[] allMethods = cd.methods(filter);
 530             final FieldDoc[] allFields = cd.fields(false);


 531 
 532             if (propertiesCache.containsKey(cd)) {
 533                 return propertiesCache.get(cd);
 534             }
 535 
 536             final List<MethodDoc> result = new ArrayList<>();
 537 
 538             for (final MethodDoc propertyMethod : allMethods) {
 539 
 540                 if (!isPropertyMethod(propertyMethod)) {
 541                     continue;
 542                 }
 543 
 544                 final MethodDoc getter = getterForField(allMethods, propertyMethod);
 545                 final MethodDoc setter = setterForField(allMethods, propertyMethod);
 546                 final FieldDoc field = fieldForProperty(allFields, propertyMethod);
 547 
 548                 addToPropertiesMap(setter, getter, propertyMethod, field);
 549                 getterSetterMap.put(propertyMethod, new GetterSetter(getter, setter));
 550                 result.add(propertyMethod);
 551             }
 552             final ProgramElementDoc[] resultAray =
 553                     result.toArray(new ProgramElementDoc[result.size()]);
 554             propertiesCache.put(cd, resultAray);
 555             return resultAray;
 556         }
 557 
 558         private void addToPropertiesMap(MethodDoc setter,
 559                                         MethodDoc getter,
 560                                         MethodDoc propertyMethod,
 561                                         FieldDoc field) {
 562             if ((field == null)
 563                     || (field.getRawCommentText() == null)
 564                     || field.getRawCommentText().length() == 0) {
 565                 addToPropertiesMap(setter, propertyMethod);
 566                 addToPropertiesMap(getter, propertyMethod);
 567                 addToPropertiesMap(propertyMethod, propertyMethod);
 568             } else {
 569                 addToPropertiesMap(getter, field);
 570                 addToPropertiesMap(setter, field);
 571                 addToPropertiesMap(propertyMethod, field);
 572             }
 573         }
 574 
 575         private void addToPropertiesMap(ProgramElementDoc propertyMethod,
 576                                         ProgramElementDoc commentSource) {
 577             if (null == propertyMethod || null == commentSource) {
 578                 return;
 579             }
 580             final String methodRawCommentText = propertyMethod.getRawCommentText();
 581 
 582             /* The second condition is required for the property buckets. In
 583              * this case the comment is at the property method (not at the field)
 584              * and it needs to be listed in the map.
 585              */
 586             if ((null == methodRawCommentText || 0 == methodRawCommentText.length())
 587                     || propertyMethod.equals(commentSource)) {
 588                 classPropertiesMap.put(propertyMethod, commentSource);
 589             }
 590         }
 591 
 592         private MethodDoc getterForField(MethodDoc[] methods,
 593                                          MethodDoc propertyMethod) {
 594             final String propertyMethodName = propertyMethod.name();
 595             final String fieldName =
 596                     propertyMethodName.substring(0,
 597                             propertyMethodName.lastIndexOf("Property"));
 598             final String fieldNameUppercased =
 599                     "" + Character.toUpperCase(fieldName.charAt(0))
 600                                             + fieldName.substring(1);
 601             final String getterNamePattern;
 602             final String fieldTypeName = propertyMethod.returnType().toString();
 603             if ("boolean".equals(fieldTypeName)
 604                     || fieldTypeName.endsWith("BooleanProperty")) {
 605                 getterNamePattern = "(is|get)" + fieldNameUppercased;
 606             } else {
 607                 getterNamePattern = "get" + fieldNameUppercased;
 608             }
 609 
 610             for (MethodDoc methodDoc : methods) {
 611                 if (Pattern.matches(getterNamePattern, methodDoc.name())) {
 612                     if (0 == methodDoc.parameters().length
 613                             && (methodDoc.isPublic() || methodDoc.isProtected())) {
 614                         return methodDoc;
 615                     }
 616                 }
 617             }
 618             return null;
 619         }
 620 
 621         private MethodDoc setterForField(MethodDoc[] methods,
 622                                          MethodDoc propertyMethod) {
 623             final String propertyMethodName = propertyMethod.name();
 624             final String fieldName =
 625                     propertyMethodName.substring(0,
 626                             propertyMethodName.lastIndexOf("Property"));
 627             final String fieldNameUppercased =
 628                     "" + Character.toUpperCase(fieldName.charAt(0))
 629                                              + fieldName.substring(1);
 630             final String setter = "set" + fieldNameUppercased;
 631 
 632             for (MethodDoc methodDoc : methods) {
 633                 if (setter.equals(methodDoc.name())) {
 634                     if (1 == methodDoc.parameters().length
 635                             && "void".equals(methodDoc.returnType().simpleTypeName())
 636                             && (methodDoc.isPublic() || methodDoc.isProtected())) {
 637                         return methodDoc;
 638                     }
 639                 }
 640             }
 641             return null;
 642         }
 643 
 644         private FieldDoc fieldForProperty(FieldDoc[] fields, MethodDoc property) {
 645 
 646             for (FieldDoc field : fields) {
 647                 final String fieldName = field.name();
 648                 final String propertyName = fieldName + "Property";
 649                 if (propertyName.equals(property.name())) {
 650                     return field;
 651                 }
 652             }
 653             return null;
 654         }
 655 
 656         // properties aren't named setA* or getA*
 657         private final Pattern pattern = Pattern.compile("[sg]et\\p{Upper}.*");
 658         private boolean isPropertyMethod(MethodDoc method) {
 659             if (!configuration.javafx) {
 660                return false;
 661             }
 662             if (!method.name().endsWith("Property")) {
 663                 return false;
 664             }
 665 
 666             if (! memberIsVisible(method)) {
 667                 return false;
 668             }
 669 
 670             if (pattern.matcher(method.name()).matches()) {
 671                 return false;
 672             }
 673             if (method.typeParameters().length > 0) {
 674                 return false;
 675             }
 676             return 0 == method.parameters().length
 677                     && !"void".equals(method.returnType().simpleTypeName());
 678         }
 679 
 680         private void checkOnPropertiesTags(MethodDoc[] members) {
 681             for (MethodDoc methodDoc: members) {
 682                 if (methodDoc.isIncluded()) {
 683                     for (Tag tag: methodDoc.tags()) {
 684                         String tagName = tag.name();


 685                         if (tagName.equals("@propertySetter")
 686                                 || tagName.equals("@propertyGetter")
 687                                 || tagName.equals("@propertyDescription")) {
 688                             if (!isPropertyGetterOrSetter(members, methodDoc)) {
 689                                 configuration.message.warning(tag.position(),
 690                                         "doclet.javafx_tag_misuse");
 691                             }
 692                             break;
 693                         }
 694                     }
 695                 }
 696             }
 697         }
 698 
 699         private boolean isPropertyGetterOrSetter(MethodDoc[] members,
 700                                                  MethodDoc methodDoc) {
 701             boolean found = false;
 702             String propertyName = utils.propertyNameFromMethodName(configuration, methodDoc.name());
 703             if (!propertyName.isEmpty()) {
 704                 String propertyMethodName = propertyName + "Property";
 705                 for (MethodDoc member: members) {
 706                     if (member.name().equals(propertyMethodName)) {
 707                         found = true;
 708                         break;
 709                     }
 710                 }
 711             }
 712             return found;
 713         }
 714     }
 715 
 716     private class GetterSetter {
 717         private final ProgramElementDoc getter;
 718         private final ProgramElementDoc setter;
 719 
 720         public GetterSetter(ProgramElementDoc getter, ProgramElementDoc setter) {
 721             this.getter = getter;
 722             this.setter = setter;
 723         }
 724 
 725         public ProgramElementDoc getGetter() {
 726             return getter;
 727         }
 728 
 729         public ProgramElementDoc getSetter() {
 730             return setter;
 731         }
 732     }
 733 
 734     /**
 735      * Return true if this map has no visible members.
 736      *
 737      * @return true if this map has no visible members.
 738      */
 739     public boolean noVisibleMembers() {
 740         return noVisibleMembers;
 741     }
 742 
 743     private ClassMember getClassMember(MethodDoc member) {
 744         for (Object key : memberNameMap.keySet()) {
 745             if (key instanceof String) {
 746                 continue;
 747             } else if (((ClassMember) key).isEqual(member)) {

 748                 return (ClassMember) key;
 749             }
 750         }
 751         return new ClassMember(member);
 752     }
 753 
 754     /**
 755      * Return the key to the member map for the given member.
 756      */
 757     private Object getMemberKey(ProgramElementDoc doc) {
 758         if (doc.isConstructor()) {
 759             return doc.name() + ((ExecutableMemberDoc)doc).signature();
 760         } else if (doc.isMethod()) {
 761             return getClassMember((MethodDoc) doc);
 762         } else if (doc.isField() || doc.isEnumConstant() || doc.isAnnotationTypeElement()) {
 763             return doc.name();
 764         } else { // it's a class or interface
 765             String classOrIntName = doc.name();
 766             //Strip off the containing class name because we only want the member name.
 767             classOrIntName = classOrIntName.indexOf('.') != 0 ? classOrIntName.substring(classOrIntName.lastIndexOf('.'), classOrIntName.length()) : classOrIntName;


 768             return "clint" + classOrIntName;
 769         }
 770     }
 771 }


   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.ExecutableElement;
  33 import javax.lang.model.element.TypeElement;
  34 import javax.lang.model.element.VariableElement;
  35 import javax.lang.model.type.TypeKind;
  36 import javax.lang.model.type.TypeMirror;
  37 
  38 import com.sun.source.doctree.DocCommentTree;
  39 import com.sun.source.doctree.DocTree;
  40 
  41 import jdk.javadoc.internal.doclets.toolkit.Configuration;
  42 
  43 /**
  44  * A data structure that encapsulates the visible members of a particular
  45  * type for a given class tree.  To use this data structure, you must specify
  46  * the type of member you are interested in (nested class, field, constructor
  47  * or method) and the leaf of the class tree.  The data structure will map
  48  * all visible members in the leaf and classes above the leaf in the tree.
  49  *
  50  *  <p><b>This is NOT part of any supported API.
  51  *  If you write code that depends on this, you do so at your own risk.
  52  *  This code and its internal interfaces are subject to change or
  53  *  deletion without notice.</b>
  54  *
  55  * @author Atul M Dambalkar
  56  * @author Jamie Ho (rewrite)
  57  */
  58 public class VisibleMemberMap {
  59 
  60     private boolean noVisibleMembers = true;
  61 
  62     public static enum Kind {
  63         INNER_CLASSES,
  64         ENUM_CONSTANTS,
  65         FIELDS,
  66         CONSTRUCTORS,
  67         METHODS,
  68         ANNOTATION_TYPE_FIELDS,
  69         ANNOTATION_TYPE_MEMBER_OPTIONAL,
  70         ANNOTATION_TYPE_MEMBER_REQUIRED,
  71         PROPERTIES;
  72 
  73         public static final EnumSet<Kind> summarySet = EnumSet.range(INNER_CLASSES, METHODS);
  74         public static final EnumSet<Kind> detailSet = EnumSet.range(ENUM_CONSTANTS, METHODS);
  75         public static String getNavLinkLabels(Kind kind) {
  76             switch (kind) {
  77                 case INNER_CLASSES:
  78                     return "doclet.navNested";
  79                 case ENUM_CONSTANTS:
  80                     return "doclet.navEnum";
  81                 case FIELDS:
  82                     return "doclet.navField";
  83                 case CONSTRUCTORS:
  84                     return "doclet.navConstructor";
  85                 case METHODS:
  86                     return "doclet.navMethod";
  87                 default:
  88                     throw new AssertionError("unknown kind:" + kind);
  89             }
  90         }
  91     }
  92 
  93     public static final String STARTLEVEL = "start";
  94 
  95     // properties aren't named setA* or getA*
  96     private static final Pattern GETTERSETTERPATTERN = Pattern.compile("[sg]et\\p{Upper}.*");
  97     /**
  98      * List of TypeElement objects for which ClassMembers objects are built.
  99      */
 100     private final Set<TypeElement> visibleClasses;
 101 
 102     /**
 103      * Map for each member name on to a map which contains members with same
 104      * name-signature. The mapped map will contain mapping for each MemberDoc
 105      * onto it's respecive level string.
 106      */
 107     private final Map<Object, Map<Element, String>> memberNameMap = new HashMap<>();
 108 
 109     /**
 110      * Map of class and it's ClassMembers object.
 111      */
 112     private final Map<TypeElement, ClassMembers> classMap = new HashMap<>();
 113 
 114     /**
 115      * Type whose visible members are requested.  This is the leaf of
 116      * the class tree being mapped.
 117      */
 118     private final TypeElement typeElement;
 119 
 120     /**
 121      * Member kind: InnerClasses/Fields/Methods?
 122      */
 123     private final Kind kind;
 124 
 125     /**
 126      * The configuration this VisibleMemberMap was created with.
 127      */
 128     private final Configuration configuration;
 129     private final Utils utils;
 130     private final Comparator<Element> comparator;
 131 
 132     private static final Map<TypeElement, List<Element>> propertiesCache = new HashMap<>();
 133     private static final Map<Element, Element> classPropertiesMap = new HashMap<>();
 134     private static final Map<Element, GetterSetter> getterSetterMap = new HashMap<>();
 135 
 136     /**
 137      * Construct a VisibleMemberMap of the given type for the given class.

 138      *
 139      * @param typeElement whose members are being mapped.
 140      * @param kind the kind of member that is being mapped.
 141      * @param configuration the configuration to use to construct this
 142      * VisibleMemberMap. If the field configuration.nodeprecated is true the
 143      * deprecated members are excluded from the map. If the field
 144      * configuration.javafx is true the JavaFX features are used.
 145      */
 146     public VisibleMemberMap(TypeElement typeElement,
 147                             Kind kind,
 148                             Configuration configuration) {
 149         this.typeElement = typeElement;
 150         this.kind = kind;
 151         this.configuration = configuration;
 152         this.utils = configuration.utils;
 153         comparator  = utils.makeGeneralPurposeComparator();
 154         visibleClasses = new LinkedHashSet<>();
 155         new ClassMembers(typeElement, STARTLEVEL).build();
 156     }
 157 
 158     /**
 159      * Return the list of visible classes in this map.
 160      *
 161      * @return the list of visible classes in this map.
 162      */
 163     public SortedSet<TypeElement> getVisibleClasses() {
 164         SortedSet<TypeElement> vClasses = new TreeSet<>(comparator);
 165         vClasses.addAll(visibleClasses);
 166         return vClasses;
 167     }
 168 
 169     /**
 170      * Returns the property field documentation belonging to the given member.
 171      * @param element the member for which the property documentation is needed.
 172      * @return the property field documentation, null if there is none.
 173      */
 174     public Element getPropertyMemberDoc(Element element) {
 175         return classPropertiesMap.get(element);
 176     }
 177 
 178     /**
 179      * Returns the getter documentation belonging to the given property method.
 180      * @param propertyMethod the method for which the getter is needed.
 181      * @return the getter documentation, null if there is none.
 182      */
 183     public Element getGetterForProperty(Element propertyMethod) {
 184         return getterSetterMap.get(propertyMethod).getGetter();
 185     }
 186 
 187     /**
 188      * Returns the setter documentation belonging to the given property method.
 189      * @param propertyMethod the method for which the setter is needed.
 190      * @return the setter documentation, null if there is none.
 191      */
 192     public Element getSetterForProperty(Element propertyMethod) {
 193         return getterSetterMap.get(propertyMethod).getSetter();
 194     }
 195 
 196     /**
 197      * Return the package private members inherited by the class.  Only return
 198      * if parent is package private and not documented.
 199      *

 200      * @return the package private members inherited by the class.
 201      */
 202     private List<Element> getInheritedPackagePrivateMethods() {
 203         List<Element> results = new ArrayList<>();
 204         for (TypeElement currentClass : visibleClasses) {
 205             if (currentClass != typeElement &&
 206                 utils.isPackagePrivate(currentClass) &&
 207                 !utils.isLinkable(currentClass)) {
 208                 // Document these members in the child class because
 209                 // the parent is inaccessible.
 210                 results.addAll(classMap.get(currentClass).members);
 211             }
 212         }
 213         return results;
 214     }
 215 
 216     /**
 217      * Return the visible members of the class being mapped.  Also append at the
 218      * end of the list members that are inherited by inaccessible parents. We
 219      * document these members in the child because the parent is not documented.
 220      *
 221      * @param configuration the current configuration of the doclet.
 222      */
 223     public SortedSet<Element> getLeafClassMembers() {
 224         SortedSet<Element> result = getMembersFor(typeElement);
 225         result.addAll(getInheritedPackagePrivateMethods());
 226         return result;
 227     }
 228 
 229     public Set<Element> getLeafClassMembersSourceOrder() {
 230         Set<Element> result = new LinkedHashSet<>(classMap.get(typeElement).members);
 231         result.addAll(getInheritedPackagePrivateMethods());
 232         return result;
 233     }
 234 
 235     /**
 236      * Retrn the list of members for the given class.
 237      *
 238      * @param typeElement the class to retrieve the list of visible members for.
 239      *
 240      * @return the list of members for the given class.
 241      */
 242     public SortedSet<Element> getMembersFor(TypeElement typeElement) {
 243         return asSortedSet(classMap.get(typeElement).members);


 244     }
 245 
 246     public boolean hasMembersFor(TypeElement typeElement) {
 247         return !classMap.get(typeElement).members.isEmpty();
 248     }
 249 
 250     private SortedSet<Element> asSortedSet(Collection<Element> in) {
 251         if (in == null) {
 252             return Collections.emptySortedSet();







 253         }
 254         TreeSet<Element> out = new TreeSet<>(comparator);
 255         out.addAll(in);
 256         return out;
 257     }





 258 
 259     private void fillMemberLevelMap(List<? extends Element> list, String level) {
 260         for (Element element : list) {
 261             Object key = getMemberKey(element);
 262             Map<Element, String> memberLevelMap = memberNameMap.get(key);
 263             if (memberLevelMap == null) {
 264                 memberLevelMap = new HashMap<>();
 265                 memberNameMap.put(key, memberLevelMap);
 266             }
 267             memberLevelMap.put(element, level);
 268         }
 269     }
 270 
 271     private void purgeMemberLevelMap(Iterable<? extends Element> list, String level) {
 272         for (Element element : list) {
 273             Object key = getMemberKey(element);
 274             Map<Element, String> memberLevelMap = memberNameMap.get(key);
 275             if (memberLevelMap != null && level.equals(memberLevelMap.get(element)))
 276                 memberLevelMap.remove(element);
 277         }
 278     }
 279 
 280     /**
 281      * Represents a class member.


 282      */
 283     private class ClassMember {
 284         private Set<Element> members;
 285 
 286         public ClassMember(Element element) {
 287             members = new HashSet<>();
 288             members.add(element);
 289         }
 290 
 291         public boolean isEqual(ExecutableElement member) {
 292             for (Element element : members) {
 293                 if (utils.executableMembersEqual(member, (ExecutableElement) element)) {




 294                     members.add(member);
 295                     return true;
 296                 }
 297             }
 298             return false;
 299         }
 300     }
 301 
 302     /**
 303      * A data structure that represents the class members for
 304      * a visible class.
 305      */
 306     private class ClassMembers {
 307 
 308         /**
 309          * The mapping class, whose inherited members are put in the
 310          * {@link #members} list.
 311          */
 312         private final TypeElement typeElement;
 313 
 314         /**
 315          * List of inherited members from the mapping class.
 316          */
 317         private Set<Element> members = new LinkedHashSet<>();
 318 
 319         /**
 320          * Level/Depth of inheritance.
 321          */
 322         private final String level;
 323 
 324         private ClassMembers(TypeElement mappingClass, String level) {
 325             this.typeElement = mappingClass;









 326             this.level = level;
 327             if (classMap.containsKey(mappingClass) &&
 328                         level.startsWith(classMap.get(mappingClass).level)) {
 329                 //Remove lower level class so that it can be replaced with
 330                 //same class found at higher level.
 331                 purgeMemberLevelMap(getClassMembers(mappingClass, false),
 332                     classMap.get(mappingClass).level);
 333                 classMap.remove(mappingClass);
 334                 visibleClasses.remove(mappingClass);
 335             }
 336             if (!classMap.containsKey(mappingClass)) {
 337                 classMap.put(mappingClass, this);
 338                 visibleClasses.add(mappingClass);
 339             }

 340         }
 341 
 342         private void build() {
 343             if (kind == Kind.CONSTRUCTORS) {
 344                 addMembers(typeElement);
 345             } else {
 346                 mapClass();
 347             }
 348         }
 349 
 350         private void mapClass() {
 351             addMembers(typeElement);
 352             List<? extends TypeMirror> interfaces = typeElement.getInterfaces();
 353             for (TypeMirror anInterface : interfaces) {
 354                 String locallevel = level + 1;
 355                 ClassMembers cm = new ClassMembers(utils.asTypeElement(anInterface), locallevel);
 356                 cm.mapClass();
 357             }
 358             if (utils.isClass(typeElement)) {
 359                 TypeElement superclass = utils.getSuperClass(typeElement);
 360                 if (!(superclass == null || typeElement.equals(superclass))) {
 361                     ClassMembers cm = new ClassMembers(superclass, level + "c");

 362                     cm.mapClass();
 363                 }
 364             }
 365         }
 366 
 367         /**
 368          * Get all the valid members from the mapping class. Get the list of
 369          * members for the class to be included into(ctii), also get the level
 370          * string for ctii. If mapping class member is not already in the
 371          * inherited member list and if it is visible in the ctii and not
 372          * overridden, put such a member in the inherited member list.
 373          * Adjust member-level-map, class-map.
 374          */
 375         private void addMembers(TypeElement fromClass) {
 376             List<? extends Element> classMembers = getClassMembers(fromClass, true);
 377             List<Element> incllist = new ArrayList<>();
 378             for (Element element : classMembers) {
 379                 if (!found(members, element)) {
 380                     if (memberIsVisible(element)) {
 381                         if (!isOverridden(element, level)) {
 382                             if (!isTreatedAsPrivate(element)) {
 383                                 incllist.add(element);
 384                             }
 385                         }
 386                     }
 387                 }
 388             }
 389             if (!incllist.isEmpty()) {
 390                 noVisibleMembers = false;
 391             }
 392             members.addAll(incllist);
 393             fillMemberLevelMap(getClassMembers(fromClass, false), level);
 394         }
 395 
 396         private boolean isTreatedAsPrivate(Element pgmelem) {
 397             if (!configuration.javafx) {
 398                 return false;
 399             }
 400 
 401             List<? extends DocTree> aspTags = utils.getBlockTags(pgmelem, "@treatAsPrivate");
 402             boolean result = (aspTags != null) && (!aspTags.isEmpty());
 403             return result;
 404         }
 405 
 406         /**
 407          * Is given element visible in given typeElement in terms of inheritance? The given element
 408          * is visible in the given typeElement if it is public or protected and if it is
 409          * package-private if it's containing class is in the same package as the given typeElement.

 410          */
 411         private boolean memberIsVisible(Element element) {
 412             if (utils.getEnclosingTypeElement(element).equals(VisibleMemberMap.this.typeElement)) {
 413                 //Member is in class that we are finding visible members for.
 414                 //Of course it is visible.
 415                 return true;
 416             } else if (utils.isPrivate(element)) {
 417                 //Member is in super class or implemented interface.
 418                 //Private, so not inherited.
 419                 return false;
 420             } else if (utils.isPackagePrivate(element)) {
 421                 //Member is package private.  Only return true if its class is in
 422                 //same package.
 423                 return utils.containingPackage(element).equals(utils.containingPackage(VisibleMemberMap.this.typeElement));

 424             } else {
 425                 //Public members are always inherited.
 426                 return true;
 427             }
 428         }
 429 
 430         /**
 431          * Return all available class members.
 432          */
 433         private List<? extends Element> getClassMembers(TypeElement te, boolean filter) {
 434             if (utils.isEnum(te) && kind == Kind.CONSTRUCTORS) {
 435                 //If any of these rules are hit, return empty array because
 436                 //we don't document these members ever.
 437                 return Collections.emptyList();
 438             }
 439             List<? extends Element> list;
 440             switch (kind) {
 441                 case ANNOTATION_TYPE_FIELDS:
 442                     list = (filter)
 443                             ? utils.getAnnotationFields(te)
 444                             : utils.getAnnotationFieldsUnfiltered(te);
 445                     break;
 446                 case ANNOTATION_TYPE_MEMBER_OPTIONAL:
 447                     list = utils.isAnnotationType(te)
 448                             ? filterAnnotations(te, false)
 449                             : Collections.emptyList();
 450                     break;
 451                 case ANNOTATION_TYPE_MEMBER_REQUIRED:
 452                     list = utils.isAnnotationType(te)
 453                             ? filterAnnotations(te, true)
 454                             : Collections.emptyList();
 455                     break;
 456                 case INNER_CLASSES:
 457                     List<TypeElement> xlist = filter
 458                             ? utils.getInnerClasses(te)
 459                             : utils.getInnerClassesUnfiltered(te);
 460                     list = new ArrayList<>(xlist);
 461                     break;
 462                 case ENUM_CONSTANTS:
 463                     list = utils.getEnumConstants(te);
 464                     break;
 465                 case FIELDS:
 466                     if (filter) {
 467                         list = utils.isAnnotationType(te)
 468                                 ? utils.getAnnotationFields(te)
 469                                 : utils.getFields(te);
 470                     } else {
 471                         list = utils.isAnnotationType(te)
 472                                 ? utils.getAnnotationFieldsUnfiltered(te)
 473                                 : utils.getFieldsUnfiltered(te);
 474                     }
 475                     break;
 476                 case CONSTRUCTORS:
 477                     list = utils.getConstructors(te);
 478                     break;
 479                 case METHODS:
 480                     list = filter ? utils.getMethods(te) : utils.getMethodsUnfiltered(te);
 481                     checkOnPropertiesTags(list);
 482                     break;
 483                 case PROPERTIES:
 484                     list = properties(te, filter);
 485                     break;
 486                 default:
 487                     list = Collections.emptyList();
 488             }
 489             // Deprected members should be excluded or not?
 490             if (configuration.nodeprecated) {
 491                 return utils.excludeDeprecatedMembers(list);
 492             }
 493             return list;
 494         }
 495 
 496         /**
 497          * Filter the annotation type members and return either the required
 498          * members or the optional members, depending on the value of the
 499          * required parameter.
 500          *
 501          * @param typeElement The annotation type to process.
 502          * @param required
 503          * @return the annotation type members and return either the required
 504          * members or the optional members, depending on the value of the
 505          * required parameter.
 506          */
 507         private List<Element> filterAnnotations(TypeElement typeElement, boolean required) {
 508             List<Element> members = utils.getAnnotationMethods(typeElement);
 509             List<Element> targetMembers = new ArrayList<>();
 510             for (Element member : members) {
 511                 ExecutableElement ee = (ExecutableElement)member;
 512                 if ((required && ee.getDefaultValue() == null)
 513                         || ((!required) && ee.getDefaultValue() != null)) {
 514                     targetMembers.add(member);
 515                 }
 516             }
 517             return targetMembers;
 518         }
 519 
 520         private boolean found(Iterable<Element> list, Element elem) {
 521             for (Element pgmelem : list) {
 522                 if (utils.matches(pgmelem, elem)) {
 523                     return true;
 524                 }
 525             }
 526             return false;
 527         }
 528 
 529 
 530         /**
 531          * Is member overridden? The member is overridden if it is found in the
 532          * same level hierarchy e.g. member at level "11" overrides member at
 533          * level "111".
 534          */
 535         private boolean isOverridden(Element element, String level) {
 536             Object key = getMemberKey(element);
 537             Map<?, String> memberLevelMap = (Map<?, String>) memberNameMap.get(key);
 538             if (memberLevelMap == null)
 539                 return false;
 540             for (String mappedlevel : memberLevelMap.values()) {
 541                 if (mappedlevel.equals(STARTLEVEL)
 542                         || (level.startsWith(mappedlevel)
 543                         && !level.equals(mappedlevel))) {
 544                     return true;
 545                 }
 546             }
 547             return false;
 548         }
 549 
 550         private List<Element> properties(final TypeElement typeElement, final boolean filter) {
 551             final List<ExecutableElement> allMethods = filter
 552                     ? utils.getMethods(typeElement)
 553                     : utils.getMethodsUnfiltered(typeElement);
 554             final List<VariableElement> allFields = utils.getFieldsUnfiltered(typeElement);
 555 
 556             if (propertiesCache.containsKey(typeElement)) {
 557                 return propertiesCache.get(typeElement);
 558             }
 559 
 560             final List<Element> result = new ArrayList<>();
 561 
 562             for (final Element propertyMethod : allMethods) {
 563                 ExecutableElement ee = (ExecutableElement)propertyMethod;
 564                 if (!isPropertyMethod(ee)) {
 565                     continue;
 566                 }
 567 
 568                 final ExecutableElement getter = getterForField(allMethods, ee);
 569                 final ExecutableElement setter = setterForField(allMethods, ee);
 570                 final VariableElement field = fieldForProperty(allFields, ee);
 571 
 572                 addToPropertiesMap(setter, getter, ee, field);
 573                 getterSetterMap.put(propertyMethod, new GetterSetter(getter, setter));
 574                 result.add(ee);
 575             }
 576             propertiesCache.put(typeElement, result);
 577             return result;


 578         }
 579 
 580         private void addToPropertiesMap(ExecutableElement setter,
 581                                         ExecutableElement getter,
 582                                         ExecutableElement propertyMethod,
 583                                         VariableElement field) {
 584             if (field == null || utils.getDocCommentTree(field) == null) {


 585                 addToPropertiesMap(setter, propertyMethod);
 586                 addToPropertiesMap(getter, propertyMethod);
 587                 addToPropertiesMap(propertyMethod, propertyMethod);
 588             } else {
 589                 addToPropertiesMap(getter, field);
 590                 addToPropertiesMap(setter, field);
 591                 addToPropertiesMap(propertyMethod, field);
 592             }
 593         }
 594 
 595         private void addToPropertiesMap(Element propertyMethod,
 596                                         Element commentSource) {
 597             if (null == propertyMethod || null == commentSource) {
 598                 return;
 599             }
 600             DocCommentTree docTree = utils.getDocCommentTree(propertyMethod);
 601 
 602             /* The second condition is required for the property buckets. In
 603              * this case the comment is at the property method (not at the field)
 604              * and it needs to be listed in the map.
 605              */
 606             if ((docTree == null) || propertyMethod.equals(commentSource)) {

 607                 classPropertiesMap.put(propertyMethod, commentSource);
 608             }
 609         }
 610 
 611         private ExecutableElement getterForField(List<ExecutableElement> methods,
 612                                          ExecutableElement propertyMethod) {
 613             final String propertyMethodName = utils.getSimpleName(propertyMethod);
 614             final String fieldName = propertyMethodName.substring(0,

 615                             propertyMethodName.lastIndexOf("Property"));
 616             final String fieldNameUppercased =
 617                     "" + Character.toUpperCase(fieldName.charAt(0))
 618                                             + fieldName.substring(1);
 619             final String getterNamePattern;
 620             final String fieldTypeName = propertyMethod.getReturnType().toString();
 621             if ("boolean".equals(fieldTypeName)
 622                     || fieldTypeName.endsWith("BooleanProperty")) {
 623                 getterNamePattern = "(is|get)" + fieldNameUppercased;
 624             } else {
 625                 getterNamePattern = "get" + fieldNameUppercased;
 626             }
 627 
 628             for (ExecutableElement method : methods) {
 629                 if (Pattern.matches(getterNamePattern, utils.getSimpleName(method))) {
 630                     if (method.getParameters().isEmpty() &&
 631                             utils.isPublic(method) || utils.isProtected(method)) {
 632                         return method;
 633                     }
 634                 }
 635             }
 636             return null;
 637         }
 638 
 639         private ExecutableElement setterForField(List<ExecutableElement> methods,
 640                                          ExecutableElement propertyMethod) {
 641             final String propertyMethodName = utils.getSimpleName(propertyMethod);
 642             final String fieldName =
 643                     propertyMethodName.substring(0,
 644                             propertyMethodName.lastIndexOf("Property"));
 645             final String fieldNameUppercased =
 646                     "" + Character.toUpperCase(fieldName.charAt(0))
 647                                              + fieldName.substring(1);
 648             final String setter = "set" + fieldNameUppercased;
 649 
 650             for (ExecutableElement method : methods) {
 651                 if (setter.equals(utils.getSimpleName(method))) {
 652                     if (method.getParameters().size() == 1
 653                             && method.getReturnType().getKind() == TypeKind.VOID
 654                             && (utils.isPublic(method) || utils.isProtected(method))) {
 655                         return method;
 656                     }
 657                 }
 658             }
 659             return null;
 660         }
 661 
 662         private VariableElement fieldForProperty(List<VariableElement> fields, ExecutableElement property) {
 663 
 664             for (VariableElement field : fields) {
 665                 final String fieldName = utils.getSimpleName(field);
 666                 final String propertyName = fieldName + "Property";
 667                 if (propertyName.equals(utils.getSimpleName(property))) {
 668                     return field;
 669                 }
 670             }
 671             return null;
 672         }
 673 
 674         private boolean isPropertyMethod(ExecutableElement method) {


 675             if (!configuration.javafx) {
 676                return false;
 677             }
 678             if (!utils.getSimpleName(method).endsWith("Property")) {
 679                 return false;
 680             }
 681 
 682             if (!memberIsVisible(method)) {
 683                 return false;
 684             }
 685 
 686             if (GETTERSETTERPATTERN.matcher(utils.getSimpleName(method)).matches()) {
 687                 return false;
 688             }
 689             if (!method.getTypeParameters().isEmpty()) {
 690                 return false;
 691             }
 692             return method.getParameters().isEmpty()
 693                     && method.getReturnType().getKind() != TypeKind.VOID;
 694         }
 695 
 696         private void checkOnPropertiesTags(List<? extends Element> members) {
 697             for (Element e: members) {
 698                 ExecutableElement ee = (ExecutableElement)e;
 699                 if (utils.isIncluded(ee)) {
 700                     CommentHelper ch = utils.getCommentHelper(ee);
 701                     for (DocTree tree: utils.getBlockTags(ee)) {
 702                         String tagName = ch.getTagName(tree);
 703                         if (tagName.equals("@propertySetter")
 704                                 || tagName.equals("@propertyGetter")
 705                                 || tagName.equals("@propertyDescription")) {
 706                             if (!isPropertyGetterOrSetter(members, ee)) {
 707                                 configuration.message.warning(ch.getDocTreePath(tree),
 708                                         "doclet.javafx_tag_misuse");
 709                             }
 710                             break;
 711                         }
 712                     }
 713                 }
 714             }
 715         }
 716 
 717         private boolean isPropertyGetterOrSetter(List<? extends Element> members,
 718                                                  ExecutableElement method) {
 719             String propertyName = utils.propertyName(method);

 720             if (!propertyName.isEmpty()) {
 721                 String propertyMethodName = propertyName + "Property";
 722                 for (Element member: members) {
 723                     if (utils.getSimpleName(member).equals(propertyMethodName)) {
 724                         return true;

 725                     }
 726                 }
 727             }
 728             return false;
 729         }
 730     }
 731 
 732     private class GetterSetter {
 733         private final Element getter;
 734         private final Element setter;
 735 
 736         public GetterSetter(Element getter, Element setter) {
 737             this.getter = getter;
 738             this.setter = setter;
 739         }
 740 
 741         public Element getGetter() {
 742             return getter;
 743         }
 744 
 745         public Element getSetter() {
 746             return setter;
 747         }
 748     }
 749 
 750     /**
 751      * Return true if this map has no visible members.
 752      *
 753      * @return true if this map has no visible members.
 754      */
 755     public boolean noVisibleMembers() {
 756         return noVisibleMembers;
 757     }
 758 
 759     private ClassMember getClassMember(ExecutableElement member) {
 760         for (Object key : memberNameMap.keySet()) {
 761             if (key instanceof String) {
 762                 continue;
 763             }
 764             if (((ClassMember) key).isEqual(member)) {
 765                 return (ClassMember) key;
 766             }
 767         }
 768         return new ClassMember(member);
 769     }
 770 
 771     /**
 772      * Return the key to the member map for the given member.
 773      */
 774     private Object getMemberKey(Element element) {
 775         if (utils.isConstructor(element)) {
 776             return utils.getSimpleName(element) + utils.flatSignature((ExecutableElement)element);
 777         } else if (utils.isMethod(element)) {
 778             return getClassMember((ExecutableElement) element);
 779         } else if (utils.isField(element) || utils.isEnumConstant(element) || utils.isAnnotationType(element)) {
 780             return utils.getSimpleName(element);
 781         } else { // it's a class or interface
 782             String classOrIntName = utils.getSimpleName(element);
 783             //Strip off the containing class name because we only want the member name.
 784             classOrIntName = classOrIntName.indexOf('.') != 0
 785                     ? classOrIntName.substring(classOrIntName.lastIndexOf('.'))
 786                     : classOrIntName;
 787             return "clint" + classOrIntName;
 788         }
 789     }
 790 }