src/share/classes/com/sun/tools/doclets/internal/toolkit/util/VisibleMemberMap.java

Sun, 24 Feb 2013 11:36:58 -0800

author
jjg
date
Sun, 24 Feb 2013 11:36:58 -0800
changeset 1606
ccbe7ffdd867
parent 1359
25e14ad23cef
child 2035
a2a5ad0853ed
permissions
-rw-r--r--

7112427: The doclet needs to be able to generate JavaFX documentation.
Reviewed-by: jjg
Contributed-by: jan.valenta@oracle.com

     1 /*
     2  * Copyright (c) 1999, 2013, 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  */
    26 package com.sun.tools.doclets.internal.toolkit.util;
    28 import java.util.*;
    29 import java.util.regex.Pattern;
    31 import com.sun.javadoc.*;
    32 import com.sun.tools.doclets.internal.toolkit.*;
    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 {
    51     private boolean noVisibleMembers = true;
    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_MEMBER_OPTIONAL = 5;
    59     public static final int ANNOTATION_TYPE_MEMBER_REQUIRED = 6;
    60     public static final int PROPERTIES      = 7;
    62     /**
    63      * The total number of member types is {@value}.
    64      */
    65     public static final int NUM_MEMBER_TYPES = 8;
    67     public static final String STARTLEVEL = "start";
    69     /**
    70      * List of ClassDoc objects for which ClassMembers objects are built.
    71      */
    72     private final List<ClassDoc> visibleClasses = new ArrayList<ClassDoc>();
    74     /**
    75      * Map for each member name on to a map which contains members with same
    76      * name-signature. The mapped map will contain mapping for each MemberDoc
    77      * onto it's respecive level string.
    78      */
    79     private final Map<Object,Map<ProgramElementDoc,String>> memberNameMap = new HashMap<Object,Map<ProgramElementDoc,String>>();
    81     /**
    82      * Map of class and it's ClassMembers object.
    83      */
    84     private final Map<ClassDoc,ClassMembers> classMap = new HashMap<ClassDoc,ClassMembers>();
    86     /**
    87      * Type whose visible members are requested.  This is the leaf of
    88      * the class tree being mapped.
    89      */
    90     private final ClassDoc classdoc;
    92     /**
    93      * Member kind: InnerClasses/Fields/Methods?
    94      */
    95     private final int kind;
    97     /**
    98      * The configuration this VisibleMemberMap was created with.
    99      */
   100     private final Configuration configuration;
   102     private static final Map<ClassDoc, ProgramElementDoc[]> propertiesCache =
   103             new HashMap<ClassDoc, ProgramElementDoc[]>();
   104     private static final Map<ProgramElementDoc, ProgramElementDoc> classPropertiesMap =
   105             new HashMap<ProgramElementDoc, ProgramElementDoc>();
   106     private static final Map<ProgramElementDoc, GetterSetter> getterSetterMap =
   107             new HashMap<ProgramElementDoc, GetterSetter>();
   109     /**
   110      * Construct a VisibleMemberMap of the given type for the given
   111      * class.
   112      *
   113      * @param classdoc the class whose members are being mapped.
   114      * @param kind the kind of member that is being mapped.
   115      * @param configuration the configuration to use to construct this
   116      * VisibleMemberMap. If the field configuration.nodeprecated is true the
   117      * deprecated members are excluded from the map. If the field
   118      * configuration.javafx is true the JavaFX features are used.
   119      */
   120     public VisibleMemberMap(ClassDoc classdoc,
   121                             int kind,
   122                             Configuration configuration) {
   123         this.classdoc = classdoc;
   124         this.kind = kind;
   125         this.configuration = configuration;
   126         new ClassMembers(classdoc, STARTLEVEL).build();
   127     }
   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     }
   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     }
   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     }
   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     }
   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<ProgramElementDoc>();
   175         for (Iterator<ClassDoc> iter = visibleClasses.iterator(); iter.hasNext(); ) {
   176             ClassDoc currentClass = iter.next();
   177             if (currentClass != classdoc &&
   178                 currentClass.isPackagePrivate() &&
   179                 !Util.isLinkable(currentClass, configuration)) {
   180                 // Document these members in the child class because
   181                 // the parent is inaccessible.
   182                 results.addAll(getMembersFor(currentClass));
   183             }
   184         }
   185         return results;
   186     }
   188     /**
   189      * Return the visible members of the class being mapped.  Also append at the
   190      * end of the list members that are inherited by inaccessible parents. We
   191      * document these members in the child because the parent is not documented.
   192      *
   193      * @param configuration the current configuration of the doclet.
   194      */
   195     public List<ProgramElementDoc> getLeafClassMembers(Configuration configuration) {
   196         List<ProgramElementDoc> result = getMembersFor(classdoc);
   197         result.addAll(getInheritedPackagePrivateMethods(configuration));
   198         return result;
   199     }
   201     /**
   202      * Retrn the list of members for the given class.
   203      *
   204      * @param cd the class to retrieve the list of visible members for.
   205      *
   206      * @return the list of members for the given class.
   207      */
   208     public List<ProgramElementDoc> getMembersFor(ClassDoc cd) {
   209         ClassMembers clmembers = classMap.get(cd);
   210         if (clmembers == null) {
   211             return new ArrayList<ProgramElementDoc>();
   212         }
   213         return clmembers.getMembers();
   214     }
   216     /**
   217      * Sort the given mixed list of classes and interfaces to a list of
   218      * classes followed by interfaces traversed. Don't sort alphabetically.
   219      */
   220     private void sort(List<ClassDoc> list) {
   221         List<ClassDoc> classes = new ArrayList<ClassDoc>();
   222         List<ClassDoc> interfaces = new ArrayList<ClassDoc>();
   223         for (int i = 0; i < list.size(); i++) {
   224             ClassDoc cd = list.get(i);
   225             if (cd.isClass()) {
   226                 classes.add(cd);
   227             } else {
   228                 interfaces.add(cd);
   229             }
   230         }
   231         list.clear();
   232         list.addAll(classes);
   233         list.addAll(interfaces);
   234     }
   236     private void fillMemberLevelMap(List<ProgramElementDoc> list, String level) {
   237         for (int i = 0; i < list.size(); i++) {
   238             Object key = getMemberKey(list.get(i));
   239             Map<ProgramElementDoc,String> memberLevelMap = memberNameMap.get(key);
   240             if (memberLevelMap == null) {
   241                 memberLevelMap = new HashMap<ProgramElementDoc,String>();
   242                 memberNameMap.put(key, memberLevelMap);
   243             }
   244             memberLevelMap.put(list.get(i), level);
   245         }
   246     }
   248     private void purgeMemberLevelMap(List<ProgramElementDoc> list, String level) {
   249         for (int i = 0; i < list.size(); i++) {
   250             Object key = getMemberKey(list.get(i));
   251             Map<ProgramElementDoc, String> memberLevelMap = memberNameMap.get(key);
   252             if (level.equals(memberLevelMap.get(list.get(i))))
   253                 memberLevelMap.remove(list.get(i));
   254         }
   255     }
   257     /**
   258      * Represents a class member.  We should be able to just use a
   259      * ProgramElementDoc instead of this class, but that doesn't take
   260      * type variables in consideration when comparing.
   261      */
   262     private class ClassMember {
   263         private Set<ProgramElementDoc> members;
   265         public ClassMember(ProgramElementDoc programElementDoc) {
   266             members = new HashSet<ProgramElementDoc>();
   267             members.add(programElementDoc);
   268         }
   270         public void addMember(ProgramElementDoc programElementDoc) {
   271             members.add(programElementDoc);
   272         }
   274         public boolean isEqual(MethodDoc member) {
   275             for (Iterator<ProgramElementDoc> iter = members.iterator(); iter.hasNext(); ) {
   276                 MethodDoc member2 = (MethodDoc) iter.next();
   277                 if (Util.executableMembersEqual(member, member2)) {
   278                     members.add(member);
   279                         return true;
   280                 }
   281             }
   282             return false;
   283         }
   284     }
   286     /**
   287      * A data structure that represents the class members for
   288      * a visible class.
   289      */
   290     private class ClassMembers {
   292         /**
   293          * The mapping class, whose inherited members are put in the
   294          * {@link #members} list.
   295          */
   296         private ClassDoc mappingClass;
   298         /**
   299          * List of inherited members from the mapping class.
   300          */
   301         private List<ProgramElementDoc> members = new ArrayList<ProgramElementDoc>();
   303         /**
   304          * Level/Depth of inheritance.
   305          */
   306         private String level;
   308         /**
   309          * Return list of inherited members from mapping class.
   310          *
   311          * @return List Inherited members.
   312          */
   313         public List<ProgramElementDoc> getMembers() {
   314             return members;
   315         }
   317         private ClassMembers(ClassDoc mappingClass, String level) {
   318             this.mappingClass = mappingClass;
   319             this.level = level;
   320             if (classMap.containsKey(mappingClass) &&
   321                         level.startsWith(classMap.get(mappingClass).level)) {
   322                 //Remove lower level class so that it can be replaced with
   323                 //same class found at higher level.
   324                 purgeMemberLevelMap(getClassMembers(mappingClass, false),
   325                     classMap.get(mappingClass).level);
   326                 classMap.remove(mappingClass);
   327                 visibleClasses.remove(mappingClass);
   328             }
   329             if (!classMap.containsKey(mappingClass)) {
   330                 classMap.put(mappingClass, this);
   331                 visibleClasses.add(mappingClass);
   332             }
   334         }
   336         private void build() {
   337             if (kind == CONSTRUCTORS) {
   338                 addMembers(mappingClass);
   339             } else {
   340                 mapClass();
   341             }
   342         }
   344         private void mapClass() {
   345             addMembers(mappingClass);
   346             ClassDoc[] interfaces = mappingClass.interfaces();
   347             for (int i = 0; i < interfaces.length; i++) {
   348                 String locallevel = level + 1;
   349                 ClassMembers cm = new ClassMembers(interfaces[i], locallevel);
   350                 cm.mapClass();
   351             }
   352             if (mappingClass.isClass()) {
   353                 ClassDoc superclass = mappingClass.superclass();
   354                 if (!(superclass == null || mappingClass.equals(superclass))) {
   355                     ClassMembers cm = new ClassMembers(superclass,
   356                                                        level + "c");
   357                     cm.mapClass();
   358                 }
   359             }
   360         }
   362         /**
   363          * Get all the valid members from the mapping class. Get the list of
   364          * members for the class to be included into(ctii), also get the level
   365          * string for ctii. If mapping class member is not already in the
   366          * inherited member list and if it is visible in the ctii and not
   367          * overridden, put such a member in the inherited member list.
   368          * Adjust member-level-map, class-map.
   369          */
   370         private void addMembers(ClassDoc fromClass) {
   371             List<ProgramElementDoc> cdmembers = getClassMembers(fromClass, true);
   372             List<ProgramElementDoc> incllist = new ArrayList<ProgramElementDoc>();
   373             for (int i = 0; i < cdmembers.size(); i++) {
   374                 ProgramElementDoc pgmelem = cdmembers.get(i);
   375                 if (!found(members, pgmelem) &&
   376                     memberIsVisible(pgmelem) &&
   377                     !isOverridden(pgmelem, level) &&
   378                     !isTreatedAsPrivate(pgmelem)) {
   379                         incllist.add(pgmelem);
   380                 }
   381             }
   382             if (incllist.size() > 0) {
   383                 noVisibleMembers = false;
   384             }
   385             members.addAll(incllist);
   386             fillMemberLevelMap(getClassMembers(fromClass, false), level);
   387         }
   389         private boolean isTreatedAsPrivate(ProgramElementDoc pgmelem) {
   390             if (!configuration.javafx) {
   391                 return false;
   392             }
   394             Tag[] aspTags = pgmelem.tags("@treatAsPrivate");
   395             boolean result = (aspTags != null) && (aspTags.length > 0);
   396             return result;
   397         }
   399         /**
   400          * Is given doc item visible in given classdoc in terms fo inheritance?
   401          * The given doc item is visible in the given classdoc if it is public
   402          * or protected and if it is package-private if it's containing class
   403          * is in the same package as the given classdoc.
   404          */
   405         private boolean memberIsVisible(ProgramElementDoc pgmdoc) {
   406             if (pgmdoc.containingClass().equals(classdoc)) {
   407                 //Member is in class that we are finding visible members for.
   408                 //Of course it is visible.
   409                 return true;
   410             } else if (pgmdoc.isPrivate()) {
   411                 //Member is in super class or implemented interface.
   412                 //Private, so not inherited.
   413                 return false;
   414             } else if (pgmdoc.isPackagePrivate()) {
   415                 //Member is package private.  Only return true if its class is in
   416                 //same package.
   417                 return pgmdoc.containingClass().containingPackage().equals(
   418                     classdoc.containingPackage());
   419             } else {
   420                 //Public members are always inherited.
   421                 return true;
   422             }
   423         }
   425         /**
   426          * Return all available class members.
   427          */
   428         private List<ProgramElementDoc> getClassMembers(ClassDoc cd, boolean filter) {
   429             if (cd.isEnum() && kind == CONSTRUCTORS) {
   430                 //If any of these rules are hit, return empty array because
   431                 //we don't document these members ever.
   432                 return Arrays.asList(new ProgramElementDoc[] {});
   433             }
   434             ProgramElementDoc[] members = null;
   435             switch (kind) {
   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 Util.excludeDeprecatedMembersAsList(members);
   471             }
   472             return Arrays.asList(members);
   473         }
   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<AnnotationTypeElementDoc>();
   490             for (int i = 0; i < members.length; i++) {
   491                 if ((required && members[i].defaultValue() == null) ||
   492                      ((!required) && members[i].defaultValue() != null)){
   493                     targetMembers.add(members[i]);
   494                 }
   495             }
   496             return targetMembers.toArray(new AnnotationTypeElementDoc[]{});
   497         }
   499         private boolean found(List<ProgramElementDoc> list, ProgramElementDoc elem) {
   500             for (int i = 0; i < list.size(); i++) {
   501                 ProgramElementDoc pgmelem = list.get(i);
   502                 if (Util.matches(pgmelem, elem)) {
   503                     return true;
   504                 }
   505             }
   506             return false;
   507         }
   510         /**
   511          * Is member overridden? The member is overridden if it is found in the
   512          * same level hierarchy e.g. member at level "11" overrides member at
   513          * level "111".
   514          */
   515         private boolean isOverridden(ProgramElementDoc pgmdoc, String level) {
   516             Map<?,String> memberLevelMap = (Map<?,String>) memberNameMap.get(getMemberKey(pgmdoc));
   517             if (memberLevelMap == null)
   518                 return false;
   519             String mappedlevel = null;
   520             Iterator<String> iterator = memberLevelMap.values().iterator();
   521             while (iterator.hasNext()) {
   522                 mappedlevel = iterator.next();
   523                 if (mappedlevel.equals(STARTLEVEL) ||
   524                     (level.startsWith(mappedlevel) &&
   525                      !level.equals(mappedlevel))) {
   526                     return true;
   527                 }
   528             }
   529             return false;
   530         }
   532         private ProgramElementDoc[] properties(final ClassDoc cd, final boolean filter) {
   533             final MethodDoc[] allMethods = cd.methods(filter);
   534             final FieldDoc[] allFields = cd.fields(false);
   536             if (propertiesCache.containsKey(cd)) {
   537                 return propertiesCache.get(cd);
   538             }
   540             final List<MethodDoc> result = new ArrayList<MethodDoc>();
   542             for (final MethodDoc propertyMethod : allMethods) {
   544                 if (!isPropertyMethod(propertyMethod)) {
   545                     continue;
   546                 }
   548                 final MethodDoc getter = getterForField(allMethods, propertyMethod);
   549                 final MethodDoc setter = setterForField(allMethods, propertyMethod);
   550                 final FieldDoc field = fieldForProperty(allFields, propertyMethod);
   552                 addToPropertiesMap(setter, getter, propertyMethod, field);
   553                 getterSetterMap.put(propertyMethod, new GetterSetter(getter, setter));
   554                 result.add(propertyMethod);
   555             }
   556             final ProgramElementDoc[] resultAray =
   557                     result.toArray(new ProgramElementDoc[result.size()]);
   558             propertiesCache.put(cd, resultAray);
   559             return resultAray;
   560         }
   562         private void addToPropertiesMap(MethodDoc setter,
   563                                         MethodDoc getter,
   564                                         MethodDoc propertyMethod,
   565                                         FieldDoc field) {
   566             if ((field == null)
   567                     || (field.getRawCommentText() == null)
   568                     || field.getRawCommentText().length() == 0) {
   569                 addToPropertiesMap(setter, propertyMethod);
   570                 addToPropertiesMap(getter, propertyMethod);
   571                 addToPropertiesMap(propertyMethod, propertyMethod);
   572             } else {
   573                 addToPropertiesMap(getter, field);
   574                 addToPropertiesMap(setter, field);
   575                 addToPropertiesMap(propertyMethod, field);
   576             }
   577         }
   579         private void addToPropertiesMap(ProgramElementDoc propertyMethod,
   580                                         ProgramElementDoc commentSource) {
   581             if (null == propertyMethod || null == commentSource) {
   582                 return;
   583             }
   584             final String methodRawCommentText = propertyMethod.getRawCommentText();
   586             /* The second condition is required for the property buckets. In
   587              * this case the comment is at the property method (not at the field)
   588              * and it needs to be listed in the map.
   589              */
   590             if ((null == methodRawCommentText || 0 == methodRawCommentText.length())
   591                     || propertyMethod.equals(commentSource)) {
   592                 classPropertiesMap.put(propertyMethod, commentSource);
   593             }
   594         }
   596         private MethodDoc getterForField(MethodDoc[] methods,
   597                                          MethodDoc propertyMethod) {
   598             final String propertyMethodName = propertyMethod.name();
   599             final String fieldName =
   600                     propertyMethodName.substring(0,
   601                             propertyMethodName.lastIndexOf("Property"));
   602             final String fieldNameUppercased =
   603                     "" + Character.toUpperCase(fieldName.charAt(0))
   604                                             + fieldName.substring(1);
   605             final String getterNamePattern;
   606             final String fieldTypeName = propertyMethod.returnType().toString();
   607             if ("boolean".equals(fieldTypeName)
   608                     || fieldTypeName.endsWith("BooleanProperty")) {
   609                 getterNamePattern = "(is|get)" + fieldNameUppercased;
   610             } else {
   611                 getterNamePattern = "get" + fieldNameUppercased;
   612             }
   614             for (MethodDoc methodDoc : methods) {
   615                 if (Pattern.matches(getterNamePattern, methodDoc.name())) {
   616                     if (0 == methodDoc.parameters().length
   617                             && (methodDoc.isPublic() || methodDoc.isProtected())) {
   618                         return methodDoc;
   619                     }
   620                 }
   621             }
   622             return null;
   623         }
   625         private MethodDoc setterForField(MethodDoc[] methods,
   626                                          MethodDoc propertyMethod) {
   627             final String propertyMethodName = propertyMethod.name();
   628             final String fieldName =
   629                     propertyMethodName.substring(0,
   630                             propertyMethodName.lastIndexOf("Property"));
   631             final String fieldNameUppercased =
   632                     "" + Character.toUpperCase(fieldName.charAt(0))
   633                                              + fieldName.substring(1);
   634             final String setter = "set" + fieldNameUppercased;
   636             for (MethodDoc methodDoc : methods) {
   637                 if (setter.equals(methodDoc.name())) {
   638                     if (1 == methodDoc.parameters().length
   639                             && "void".equals(methodDoc.returnType().simpleTypeName())
   640                             && (methodDoc.isPublic() || methodDoc.isProtected())) {
   641                         return methodDoc;
   642                     }
   643                 }
   644             }
   645             return null;
   646         }
   648         private FieldDoc fieldForProperty(FieldDoc[] fields, MethodDoc property) {
   650             for (FieldDoc field : fields) {
   651                 final String fieldName = field.name();
   652                 final String propertyName = fieldName + "Property";
   653                 if (propertyName.equals(property.name())) {
   654                     return field;
   655                 }
   656             }
   657             return null;
   658         }
   660         // properties aren't named setA* or getA*
   661         private final Pattern pattern = Pattern.compile("[sg]et\\p{Upper}.*");
   662         private boolean isPropertyMethod(MethodDoc method) {
   663             if (!method.name().endsWith("Property")) {
   664                 return false;
   665             }
   667             if (! memberIsVisible(method)) {
   668                 return false;
   669             }
   671             if (pattern.matcher(method.name()).matches()) {
   672                 return false;
   673             }
   675             return 0 == method.parameters().length
   676                     && !"void".equals(method.returnType().simpleTypeName());
   677         }
   679         private void checkOnPropertiesTags(MethodDoc[] members) {
   680             for (MethodDoc methodDoc: members) {
   681                 if (methodDoc.isIncluded()) {
   682                     for (Tag tag: methodDoc.tags()) {
   683                         String tagName = tag.name();
   684                         if (tagName.equals("@propertySetter")
   685                                 || tagName.equals("@propertyGetter")
   686                                 || tagName.equals("@propertyDescription")) {
   687                             if (!isPropertyGetterOrSetter(members, methodDoc)) {
   688                                 configuration.message.warning(tag.position(),
   689                                         "doclet.javafx_tag_misuse");
   690                             }
   691                             break;
   692                         }
   693                     }
   694                 }
   695             }
   696         }
   698         private boolean isPropertyGetterOrSetter(MethodDoc[] members,
   699                                                  MethodDoc methodDoc) {
   700             boolean found = false;
   701             String propertyName = Util.propertyNameFromMethodName(methodDoc.name());
   702             if (!propertyName.isEmpty()) {
   703                 String propertyMethodName = propertyName + "Property";
   704                 for (MethodDoc member: members) {
   705                     if (member.name().equals(propertyMethodName)) {
   706                         found = true;
   707                         break;
   708                     }
   709                 }
   710             }
   711             return found;
   712         }
   713     }
   715     private class GetterSetter {
   716         private final ProgramElementDoc getter;
   717         private final ProgramElementDoc setter;
   719         public GetterSetter(ProgramElementDoc getter, ProgramElementDoc setter) {
   720             this.getter = getter;
   721             this.setter = setter;
   722         }
   724         public ProgramElementDoc getGetter() {
   725             return getter;
   726         }
   728         public ProgramElementDoc getSetter() {
   729             return setter;
   730         }
   731     }
   733     /**
   734      * Return true if this map has no visible members.
   735      *
   736      * @return true if this map has no visible members.
   737      */
   738     public boolean noVisibleMembers() {
   739         return noVisibleMembers;
   740     }
   742     private ClassMember getClassMember(MethodDoc member) {
   743         for (Iterator<?> iter = memberNameMap.keySet().iterator(); iter.hasNext();) {
   744             Object key = iter.next();
   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     }
   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 }

mercurial