aoqi@0: /* robm@3006: * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. Oracle designates this aoqi@0: * particular file as subject to the "Classpath" exception as provided aoqi@0: * by Oracle in the LICENSE file that accompanied this code. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: package com.sun.tools.doclets.internal.toolkit.util; aoqi@0: aoqi@0: import java.util.*; aoqi@0: import java.util.regex.Pattern; aoqi@0: aoqi@0: import com.sun.javadoc.*; aoqi@0: import com.sun.tools.doclets.internal.toolkit.*; aoqi@0: aoqi@0: /** aoqi@0: * A data structure that encapsulates the visible members of a particular aoqi@0: * type for a given class tree. To use this data structor, you must specify aoqi@0: * the type of member you are interested in (nested class, field, constructor aoqi@0: * or method) and the leaf of the class tree. The data structure will map aoqi@0: * all visible members in the leaf and classes above the leaf in the tree. aoqi@0: * aoqi@0: *

This is NOT part of any supported API. aoqi@0: * If you write code that depends on this, you do so at your own risk. aoqi@0: * This code and its internal interfaces are subject to change or aoqi@0: * deletion without notice. aoqi@0: * aoqi@0: * @author Atul M Dambalkar aoqi@0: * @author Jamie Ho (rewrite) aoqi@0: */ aoqi@0: public class VisibleMemberMap { aoqi@0: aoqi@0: private boolean noVisibleMembers = true; aoqi@0: aoqi@0: public static final int INNERCLASSES = 0; aoqi@0: public static final int ENUM_CONSTANTS = 1; aoqi@0: public static final int FIELDS = 2; aoqi@0: public static final int CONSTRUCTORS = 3; aoqi@0: public static final int METHODS = 4; aoqi@0: public static final int ANNOTATION_TYPE_FIELDS = 5; aoqi@0: public static final int ANNOTATION_TYPE_MEMBER_OPTIONAL = 6; aoqi@0: public static final int ANNOTATION_TYPE_MEMBER_REQUIRED = 7; aoqi@0: public static final int PROPERTIES = 8; aoqi@0: aoqi@0: /** aoqi@0: * The total number of member types is {@value}. aoqi@0: */ aoqi@0: public static final int NUM_MEMBER_TYPES = 9; aoqi@0: aoqi@0: public static final String STARTLEVEL = "start"; aoqi@0: aoqi@0: /** aoqi@0: * List of ClassDoc objects for which ClassMembers objects are built. aoqi@0: */ aoqi@0: private final List visibleClasses = new ArrayList(); aoqi@0: aoqi@0: /** aoqi@0: * Map for each member name on to a map which contains members with same aoqi@0: * name-signature. The mapped map will contain mapping for each MemberDoc aoqi@0: * onto it's respecive level string. aoqi@0: */ aoqi@0: private final Map> memberNameMap = new HashMap>(); aoqi@0: aoqi@0: /** aoqi@0: * Map of class and it's ClassMembers object. aoqi@0: */ aoqi@0: private final Map classMap = new HashMap(); aoqi@0: aoqi@0: /** aoqi@0: * Type whose visible members are requested. This is the leaf of aoqi@0: * the class tree being mapped. aoqi@0: */ aoqi@0: private final ClassDoc classdoc; aoqi@0: aoqi@0: /** aoqi@0: * Member kind: InnerClasses/Fields/Methods? aoqi@0: */ aoqi@0: private final int kind; aoqi@0: aoqi@0: /** aoqi@0: * The configuration this VisibleMemberMap was created with. aoqi@0: */ aoqi@0: private final Configuration configuration; aoqi@0: aoqi@0: private static final Map propertiesCache = aoqi@0: new HashMap(); aoqi@0: private static final Map classPropertiesMap = aoqi@0: new HashMap(); aoqi@0: private static final Map getterSetterMap = aoqi@0: new HashMap(); aoqi@0: aoqi@0: /** aoqi@0: * Construct a VisibleMemberMap of the given type for the given aoqi@0: * class. aoqi@0: * aoqi@0: * @param classdoc the class whose members are being mapped. aoqi@0: * @param kind the kind of member that is being mapped. aoqi@0: * @param configuration the configuration to use to construct this aoqi@0: * VisibleMemberMap. If the field configuration.nodeprecated is true the aoqi@0: * deprecated members are excluded from the map. If the field aoqi@0: * configuration.javafx is true the JavaFX features are used. aoqi@0: */ aoqi@0: public VisibleMemberMap(ClassDoc classdoc, aoqi@0: int kind, aoqi@0: Configuration configuration) { aoqi@0: this.classdoc = classdoc; aoqi@0: this.kind = kind; aoqi@0: this.configuration = configuration; aoqi@0: new ClassMembers(classdoc, STARTLEVEL).build(); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Return the list of visible classes in this map. aoqi@0: * aoqi@0: * @return the list of visible classes in this map. aoqi@0: */ aoqi@0: public List getVisibleClassesList() { aoqi@0: sort(visibleClasses); aoqi@0: return visibleClasses; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Returns the property field documentation belonging to the given member. aoqi@0: * @param ped the member for which the property documentation is needed. aoqi@0: * @return the property field documentation, null if there is none. aoqi@0: */ aoqi@0: public ProgramElementDoc getPropertyMemberDoc(ProgramElementDoc ped) { aoqi@0: return classPropertiesMap.get(ped); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Returns the getter documentation belonging to the given property method. aoqi@0: * @param propertyMethod the method for which the getter is needed. aoqi@0: * @return the getter documentation, null if there is none. aoqi@0: */ aoqi@0: public ProgramElementDoc getGetterForProperty(ProgramElementDoc propertyMethod) { aoqi@0: return getterSetterMap.get(propertyMethod).getGetter(); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Returns the setter documentation belonging to the given property method. aoqi@0: * @param propertyMethod the method for which the setter is needed. aoqi@0: * @return the setter documentation, null if there is none. aoqi@0: */ aoqi@0: public ProgramElementDoc getSetterForProperty(ProgramElementDoc propertyMethod) { aoqi@0: return getterSetterMap.get(propertyMethod).getSetter(); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Return the package private members inherited by the class. Only return aoqi@0: * if parent is package private and not documented. aoqi@0: * aoqi@0: * @param configuration the current configuration of the doclet. aoqi@0: * @return the package private members inherited by the class. aoqi@0: */ aoqi@0: private List getInheritedPackagePrivateMethods(Configuration configuration) { aoqi@0: List results = new ArrayList(); aoqi@0: for (Iterator iter = visibleClasses.iterator(); iter.hasNext(); ) { aoqi@0: ClassDoc currentClass = iter.next(); aoqi@0: if (currentClass != classdoc && aoqi@0: currentClass.isPackagePrivate() && aoqi@0: !Util.isLinkable(currentClass, configuration)) { aoqi@0: // Document these members in the child class because aoqi@0: // the parent is inaccessible. aoqi@0: results.addAll(getMembersFor(currentClass)); aoqi@0: } aoqi@0: } aoqi@0: return results; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Return the visible members of the class being mapped. Also append at the aoqi@0: * end of the list members that are inherited by inaccessible parents. We aoqi@0: * document these members in the child because the parent is not documented. aoqi@0: * aoqi@0: * @param configuration the current configuration of the doclet. aoqi@0: */ aoqi@0: public List getLeafClassMembers(Configuration configuration) { aoqi@0: List result = getMembersFor(classdoc); aoqi@0: result.addAll(getInheritedPackagePrivateMethods(configuration)); aoqi@0: return result; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Retrn the list of members for the given class. aoqi@0: * aoqi@0: * @param cd the class to retrieve the list of visible members for. aoqi@0: * aoqi@0: * @return the list of members for the given class. aoqi@0: */ aoqi@0: public List getMembersFor(ClassDoc cd) { aoqi@0: ClassMembers clmembers = classMap.get(cd); aoqi@0: if (clmembers == null) { aoqi@0: return new ArrayList(); aoqi@0: } aoqi@0: return clmembers.getMembers(); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Sort the given mixed list of classes and interfaces to a list of aoqi@0: * classes followed by interfaces traversed. Don't sort alphabetically. aoqi@0: */ aoqi@0: private void sort(List list) { aoqi@0: List classes = new ArrayList(); aoqi@0: List interfaces = new ArrayList(); aoqi@0: for (int i = 0; i < list.size(); i++) { aoqi@0: ClassDoc cd = list.get(i); aoqi@0: if (cd.isClass()) { aoqi@0: classes.add(cd); aoqi@0: } else { aoqi@0: interfaces.add(cd); aoqi@0: } aoqi@0: } aoqi@0: list.clear(); aoqi@0: list.addAll(classes); aoqi@0: list.addAll(interfaces); aoqi@0: } aoqi@0: aoqi@0: private void fillMemberLevelMap(List list, String level) { aoqi@0: for (int i = 0; i < list.size(); i++) { aoqi@0: Object key = getMemberKey(list.get(i)); aoqi@0: Map memberLevelMap = memberNameMap.get(key); aoqi@0: if (memberLevelMap == null) { aoqi@0: memberLevelMap = new HashMap(); aoqi@0: memberNameMap.put(key, memberLevelMap); aoqi@0: } aoqi@0: memberLevelMap.put(list.get(i), level); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: private void purgeMemberLevelMap(List list, String level) { aoqi@0: for (int i = 0; i < list.size(); i++) { aoqi@0: Object key = getMemberKey(list.get(i)); aoqi@0: Map memberLevelMap = memberNameMap.get(key); robm@3006: if (memberLevelMap != null && level.equals(memberLevelMap.get(list.get(i)))) aoqi@0: memberLevelMap.remove(list.get(i)); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Represents a class member. We should be able to just use a aoqi@0: * ProgramElementDoc instead of this class, but that doesn't take aoqi@0: * type variables in consideration when comparing. aoqi@0: */ aoqi@0: private class ClassMember { aoqi@0: private Set members; aoqi@0: aoqi@0: public ClassMember(ProgramElementDoc programElementDoc) { aoqi@0: members = new HashSet(); aoqi@0: members.add(programElementDoc); aoqi@0: } aoqi@0: aoqi@0: public void addMember(ProgramElementDoc programElementDoc) { aoqi@0: members.add(programElementDoc); aoqi@0: } aoqi@0: aoqi@0: public boolean isEqual(MethodDoc member) { aoqi@0: for (Iterator iter = members.iterator(); iter.hasNext(); ) { aoqi@0: MethodDoc member2 = (MethodDoc) iter.next(); aoqi@0: if (Util.executableMembersEqual(member, member2)) { aoqi@0: members.add(member); aoqi@0: return true; aoqi@0: } aoqi@0: } aoqi@0: return false; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * A data structure that represents the class members for aoqi@0: * a visible class. aoqi@0: */ aoqi@0: private class ClassMembers { aoqi@0: aoqi@0: /** aoqi@0: * The mapping class, whose inherited members are put in the aoqi@0: * {@link #members} list. aoqi@0: */ aoqi@0: private ClassDoc mappingClass; aoqi@0: aoqi@0: /** aoqi@0: * List of inherited members from the mapping class. aoqi@0: */ aoqi@0: private List members = new ArrayList(); aoqi@0: aoqi@0: /** aoqi@0: * Level/Depth of inheritance. aoqi@0: */ aoqi@0: private String level; aoqi@0: aoqi@0: /** aoqi@0: * Return list of inherited members from mapping class. aoqi@0: * aoqi@0: * @return List Inherited members. aoqi@0: */ aoqi@0: public List getMembers() { aoqi@0: return members; aoqi@0: } aoqi@0: aoqi@0: private ClassMembers(ClassDoc mappingClass, String level) { aoqi@0: this.mappingClass = mappingClass; aoqi@0: this.level = level; aoqi@0: if (classMap.containsKey(mappingClass) && aoqi@0: level.startsWith(classMap.get(mappingClass).level)) { aoqi@0: //Remove lower level class so that it can be replaced with aoqi@0: //same class found at higher level. aoqi@0: purgeMemberLevelMap(getClassMembers(mappingClass, false), aoqi@0: classMap.get(mappingClass).level); aoqi@0: classMap.remove(mappingClass); aoqi@0: visibleClasses.remove(mappingClass); aoqi@0: } aoqi@0: if (!classMap.containsKey(mappingClass)) { aoqi@0: classMap.put(mappingClass, this); aoqi@0: visibleClasses.add(mappingClass); aoqi@0: } aoqi@0: aoqi@0: } aoqi@0: aoqi@0: private void build() { aoqi@0: if (kind == CONSTRUCTORS) { aoqi@0: addMembers(mappingClass); aoqi@0: } else { aoqi@0: mapClass(); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: private void mapClass() { aoqi@0: addMembers(mappingClass); aoqi@0: ClassDoc[] interfaces = mappingClass.interfaces(); aoqi@0: for (int i = 0; i < interfaces.length; i++) { aoqi@0: String locallevel = level + 1; aoqi@0: ClassMembers cm = new ClassMembers(interfaces[i], locallevel); aoqi@0: cm.mapClass(); aoqi@0: } aoqi@0: if (mappingClass.isClass()) { aoqi@0: ClassDoc superclass = mappingClass.superclass(); aoqi@0: if (!(superclass == null || mappingClass.equals(superclass))) { aoqi@0: ClassMembers cm = new ClassMembers(superclass, aoqi@0: level + "c"); aoqi@0: cm.mapClass(); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Get all the valid members from the mapping class. Get the list of aoqi@0: * members for the class to be included into(ctii), also get the level aoqi@0: * string for ctii. If mapping class member is not already in the aoqi@0: * inherited member list and if it is visible in the ctii and not aoqi@0: * overridden, put such a member in the inherited member list. aoqi@0: * Adjust member-level-map, class-map. aoqi@0: */ aoqi@0: private void addMembers(ClassDoc fromClass) { aoqi@0: List cdmembers = getClassMembers(fromClass, true); aoqi@0: List incllist = new ArrayList(); aoqi@0: for (int i = 0; i < cdmembers.size(); i++) { aoqi@0: ProgramElementDoc pgmelem = cdmembers.get(i); aoqi@0: if (!found(members, pgmelem) && aoqi@0: memberIsVisible(pgmelem) && aoqi@0: !isOverridden(pgmelem, level) && aoqi@0: !isTreatedAsPrivate(pgmelem)) { aoqi@0: incllist.add(pgmelem); aoqi@0: } aoqi@0: } aoqi@0: if (incllist.size() > 0) { aoqi@0: noVisibleMembers = false; aoqi@0: } aoqi@0: members.addAll(incllist); aoqi@0: fillMemberLevelMap(getClassMembers(fromClass, false), level); aoqi@0: } aoqi@0: aoqi@0: private boolean isTreatedAsPrivate(ProgramElementDoc pgmelem) { aoqi@0: if (!configuration.javafx) { aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: Tag[] aspTags = pgmelem.tags("@treatAsPrivate"); aoqi@0: boolean result = (aspTags != null) && (aspTags.length > 0); aoqi@0: return result; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Is given doc item visible in given classdoc in terms fo inheritance? aoqi@0: * The given doc item is visible in the given classdoc if it is public aoqi@0: * or protected and if it is package-private if it's containing class aoqi@0: * is in the same package as the given classdoc. aoqi@0: */ aoqi@0: private boolean memberIsVisible(ProgramElementDoc pgmdoc) { aoqi@0: if (pgmdoc.containingClass().equals(classdoc)) { aoqi@0: //Member is in class that we are finding visible members for. aoqi@0: //Of course it is visible. aoqi@0: return true; aoqi@0: } else if (pgmdoc.isPrivate()) { aoqi@0: //Member is in super class or implemented interface. aoqi@0: //Private, so not inherited. aoqi@0: return false; aoqi@0: } else if (pgmdoc.isPackagePrivate()) { aoqi@0: //Member is package private. Only return true if its class is in aoqi@0: //same package. aoqi@0: return pgmdoc.containingClass().containingPackage().equals( aoqi@0: classdoc.containingPackage()); aoqi@0: } else { aoqi@0: //Public members are always inherited. aoqi@0: return true; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Return all available class members. aoqi@0: */ aoqi@0: private List getClassMembers(ClassDoc cd, boolean filter) { aoqi@0: if (cd.isEnum() && kind == CONSTRUCTORS) { aoqi@0: //If any of these rules are hit, return empty array because aoqi@0: //we don't document these members ever. aoqi@0: return Arrays.asList(new ProgramElementDoc[] {}); aoqi@0: } aoqi@0: ProgramElementDoc[] members = null; aoqi@0: switch (kind) { aoqi@0: case ANNOTATION_TYPE_FIELDS: aoqi@0: members = cd.fields(filter); aoqi@0: break; aoqi@0: case ANNOTATION_TYPE_MEMBER_OPTIONAL: aoqi@0: members = cd.isAnnotationType() ? aoqi@0: filter((AnnotationTypeDoc) cd, false) : aoqi@0: new AnnotationTypeElementDoc[] {}; aoqi@0: break; aoqi@0: case ANNOTATION_TYPE_MEMBER_REQUIRED: aoqi@0: members = cd.isAnnotationType() ? aoqi@0: filter((AnnotationTypeDoc) cd, true) : aoqi@0: new AnnotationTypeElementDoc[] {}; aoqi@0: break; aoqi@0: case INNERCLASSES: aoqi@0: members = cd.innerClasses(filter); aoqi@0: break; aoqi@0: case ENUM_CONSTANTS: aoqi@0: members = cd.enumConstants(); aoqi@0: break; aoqi@0: case FIELDS: aoqi@0: members = cd.fields(filter); aoqi@0: break; aoqi@0: case CONSTRUCTORS: aoqi@0: members = cd.constructors(); aoqi@0: break; aoqi@0: case METHODS: aoqi@0: members = cd.methods(filter); aoqi@0: checkOnPropertiesTags((MethodDoc[])members); aoqi@0: break; aoqi@0: case PROPERTIES: aoqi@0: members = properties(cd, filter); aoqi@0: break; aoqi@0: default: aoqi@0: members = new ProgramElementDoc[0]; aoqi@0: } aoqi@0: // Deprected members should be excluded or not? aoqi@0: if (configuration.nodeprecated) { aoqi@0: return Util.excludeDeprecatedMembersAsList(members); aoqi@0: } aoqi@0: return Arrays.asList(members); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Filter the annotation type members and return either the required aoqi@0: * members or the optional members, depending on the value of the aoqi@0: * required parameter. aoqi@0: * aoqi@0: * @param doc The annotation type to process. aoqi@0: * @param required aoqi@0: * @return the annotation type members and return either the required aoqi@0: * members or the optional members, depending on the value of the aoqi@0: * required parameter. aoqi@0: */ aoqi@0: private AnnotationTypeElementDoc[] filter(AnnotationTypeDoc doc, aoqi@0: boolean required) { aoqi@0: AnnotationTypeElementDoc[] members = doc.elements(); aoqi@0: List targetMembers = new ArrayList(); aoqi@0: for (int i = 0; i < members.length; i++) { aoqi@0: if ((required && members[i].defaultValue() == null) || aoqi@0: ((!required) && members[i].defaultValue() != null)){ aoqi@0: targetMembers.add(members[i]); aoqi@0: } aoqi@0: } aoqi@0: return targetMembers.toArray(new AnnotationTypeElementDoc[]{}); aoqi@0: } aoqi@0: aoqi@0: private boolean found(List list, ProgramElementDoc elem) { aoqi@0: for (int i = 0; i < list.size(); i++) { aoqi@0: ProgramElementDoc pgmelem = list.get(i); aoqi@0: if (Util.matches(pgmelem, elem)) { aoqi@0: return true; aoqi@0: } aoqi@0: } aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: aoqi@0: /** aoqi@0: * Is member overridden? The member is overridden if it is found in the aoqi@0: * same level hierarchy e.g. member at level "11" overrides member at aoqi@0: * level "111". aoqi@0: */ aoqi@0: private boolean isOverridden(ProgramElementDoc pgmdoc, String level) { aoqi@0: Map memberLevelMap = (Map) memberNameMap.get(getMemberKey(pgmdoc)); aoqi@0: if (memberLevelMap == null) aoqi@0: return false; aoqi@0: String mappedlevel = null; aoqi@0: Iterator iterator = memberLevelMap.values().iterator(); aoqi@0: while (iterator.hasNext()) { aoqi@0: mappedlevel = iterator.next(); aoqi@0: if (mappedlevel.equals(STARTLEVEL) || aoqi@0: (level.startsWith(mappedlevel) && aoqi@0: !level.equals(mappedlevel))) { aoqi@0: return true; aoqi@0: } aoqi@0: } aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: private ProgramElementDoc[] properties(final ClassDoc cd, final boolean filter) { aoqi@0: final MethodDoc[] allMethods = cd.methods(filter); aoqi@0: final FieldDoc[] allFields = cd.fields(false); aoqi@0: aoqi@0: if (propertiesCache.containsKey(cd)) { aoqi@0: return propertiesCache.get(cd); aoqi@0: } aoqi@0: aoqi@0: final List result = new ArrayList(); aoqi@0: aoqi@0: for (final MethodDoc propertyMethod : allMethods) { aoqi@0: aoqi@0: if (!isPropertyMethod(propertyMethod)) { aoqi@0: continue; aoqi@0: } aoqi@0: aoqi@0: final MethodDoc getter = getterForField(allMethods, propertyMethod); aoqi@0: final MethodDoc setter = setterForField(allMethods, propertyMethod); aoqi@0: final FieldDoc field = fieldForProperty(allFields, propertyMethod); aoqi@0: aoqi@0: addToPropertiesMap(setter, getter, propertyMethod, field); aoqi@0: getterSetterMap.put(propertyMethod, new GetterSetter(getter, setter)); aoqi@0: result.add(propertyMethod); aoqi@0: } aoqi@0: final ProgramElementDoc[] resultAray = aoqi@0: result.toArray(new ProgramElementDoc[result.size()]); aoqi@0: propertiesCache.put(cd, resultAray); aoqi@0: return resultAray; aoqi@0: } aoqi@0: aoqi@0: private void addToPropertiesMap(MethodDoc setter, aoqi@0: MethodDoc getter, aoqi@0: MethodDoc propertyMethod, aoqi@0: FieldDoc field) { aoqi@0: if ((field == null) aoqi@0: || (field.getRawCommentText() == null) aoqi@0: || field.getRawCommentText().length() == 0) { aoqi@0: addToPropertiesMap(setter, propertyMethod); aoqi@0: addToPropertiesMap(getter, propertyMethod); aoqi@0: addToPropertiesMap(propertyMethod, propertyMethod); aoqi@0: } else { aoqi@0: addToPropertiesMap(getter, field); aoqi@0: addToPropertiesMap(setter, field); aoqi@0: addToPropertiesMap(propertyMethod, field); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: private void addToPropertiesMap(ProgramElementDoc propertyMethod, aoqi@0: ProgramElementDoc commentSource) { aoqi@0: if (null == propertyMethod || null == commentSource) { aoqi@0: return; aoqi@0: } aoqi@0: final String methodRawCommentText = propertyMethod.getRawCommentText(); aoqi@0: aoqi@0: /* The second condition is required for the property buckets. In aoqi@0: * this case the comment is at the property method (not at the field) aoqi@0: * and it needs to be listed in the map. aoqi@0: */ aoqi@0: if ((null == methodRawCommentText || 0 == methodRawCommentText.length()) aoqi@0: || propertyMethod.equals(commentSource)) { aoqi@0: classPropertiesMap.put(propertyMethod, commentSource); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: private MethodDoc getterForField(MethodDoc[] methods, aoqi@0: MethodDoc propertyMethod) { aoqi@0: final String propertyMethodName = propertyMethod.name(); aoqi@0: final String fieldName = aoqi@0: propertyMethodName.substring(0, aoqi@0: propertyMethodName.lastIndexOf("Property")); aoqi@0: final String fieldNameUppercased = aoqi@0: "" + Character.toUpperCase(fieldName.charAt(0)) aoqi@0: + fieldName.substring(1); aoqi@0: final String getterNamePattern; aoqi@0: final String fieldTypeName = propertyMethod.returnType().toString(); aoqi@0: if ("boolean".equals(fieldTypeName) aoqi@0: || fieldTypeName.endsWith("BooleanProperty")) { aoqi@0: getterNamePattern = "(is|get)" + fieldNameUppercased; aoqi@0: } else { aoqi@0: getterNamePattern = "get" + fieldNameUppercased; aoqi@0: } aoqi@0: aoqi@0: for (MethodDoc methodDoc : methods) { aoqi@0: if (Pattern.matches(getterNamePattern, methodDoc.name())) { aoqi@0: if (0 == methodDoc.parameters().length aoqi@0: && (methodDoc.isPublic() || methodDoc.isProtected())) { aoqi@0: return methodDoc; aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: return null; aoqi@0: } aoqi@0: aoqi@0: private MethodDoc setterForField(MethodDoc[] methods, aoqi@0: MethodDoc propertyMethod) { aoqi@0: final String propertyMethodName = propertyMethod.name(); aoqi@0: final String fieldName = aoqi@0: propertyMethodName.substring(0, aoqi@0: propertyMethodName.lastIndexOf("Property")); aoqi@0: final String fieldNameUppercased = aoqi@0: "" + Character.toUpperCase(fieldName.charAt(0)) aoqi@0: + fieldName.substring(1); aoqi@0: final String setter = "set" + fieldNameUppercased; aoqi@0: aoqi@0: for (MethodDoc methodDoc : methods) { aoqi@0: if (setter.equals(methodDoc.name())) { aoqi@0: if (1 == methodDoc.parameters().length aoqi@0: && "void".equals(methodDoc.returnType().simpleTypeName()) aoqi@0: && (methodDoc.isPublic() || methodDoc.isProtected())) { aoqi@0: return methodDoc; aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: return null; aoqi@0: } aoqi@0: aoqi@0: private FieldDoc fieldForProperty(FieldDoc[] fields, MethodDoc property) { aoqi@0: aoqi@0: for (FieldDoc field : fields) { aoqi@0: final String fieldName = field.name(); aoqi@0: final String propertyName = fieldName + "Property"; aoqi@0: if (propertyName.equals(property.name())) { aoqi@0: return field; aoqi@0: } aoqi@0: } aoqi@0: return null; aoqi@0: } aoqi@0: aoqi@0: // properties aren't named setA* or getA* aoqi@0: private final Pattern pattern = Pattern.compile("[sg]et\\p{Upper}.*"); aoqi@0: private boolean isPropertyMethod(MethodDoc method) { sgehwolf@3640: if (!configuration.javafx) { sgehwolf@3640: return false; sgehwolf@3640: } aoqi@0: if (!method.name().endsWith("Property")) { aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: if (! memberIsVisible(method)) { aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: if (pattern.matcher(method.name()).matches()) { aoqi@0: return false; aoqi@0: } sgehwolf@3640: if (method.typeParameters().length > 0) { sgehwolf@3640: return false; sgehwolf@3640: } aoqi@0: return 0 == method.parameters().length aoqi@0: && !"void".equals(method.returnType().simpleTypeName()); aoqi@0: } aoqi@0: aoqi@0: private void checkOnPropertiesTags(MethodDoc[] members) { aoqi@0: for (MethodDoc methodDoc: members) { aoqi@0: if (methodDoc.isIncluded()) { aoqi@0: for (Tag tag: methodDoc.tags()) { aoqi@0: String tagName = tag.name(); aoqi@0: if (tagName.equals("@propertySetter") aoqi@0: || tagName.equals("@propertyGetter") aoqi@0: || tagName.equals("@propertyDescription")) { aoqi@0: if (!isPropertyGetterOrSetter(members, methodDoc)) { aoqi@0: configuration.message.warning(tag.position(), aoqi@0: "doclet.javafx_tag_misuse"); aoqi@0: } aoqi@0: break; aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: private boolean isPropertyGetterOrSetter(MethodDoc[] members, aoqi@0: MethodDoc methodDoc) { aoqi@0: boolean found = false; aoqi@0: String propertyName = Util.propertyNameFromMethodName(configuration, methodDoc.name()); aoqi@0: if (!propertyName.isEmpty()) { aoqi@0: String propertyMethodName = propertyName + "Property"; aoqi@0: for (MethodDoc member: members) { aoqi@0: if (member.name().equals(propertyMethodName)) { aoqi@0: found = true; aoqi@0: break; aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: return found; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: private class GetterSetter { aoqi@0: private final ProgramElementDoc getter; aoqi@0: private final ProgramElementDoc setter; aoqi@0: aoqi@0: public GetterSetter(ProgramElementDoc getter, ProgramElementDoc setter) { aoqi@0: this.getter = getter; aoqi@0: this.setter = setter; aoqi@0: } aoqi@0: aoqi@0: public ProgramElementDoc getGetter() { aoqi@0: return getter; aoqi@0: } aoqi@0: aoqi@0: public ProgramElementDoc getSetter() { aoqi@0: return setter; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Return true if this map has no visible members. aoqi@0: * aoqi@0: * @return true if this map has no visible members. aoqi@0: */ aoqi@0: public boolean noVisibleMembers() { aoqi@0: return noVisibleMembers; aoqi@0: } aoqi@0: aoqi@0: private ClassMember getClassMember(MethodDoc member) { aoqi@0: for (Iterator iter = memberNameMap.keySet().iterator(); iter.hasNext();) { aoqi@0: Object key = iter.next(); aoqi@0: if (key instanceof String) { aoqi@0: continue; aoqi@0: } else if (((ClassMember) key).isEqual(member)) { aoqi@0: return (ClassMember) key; aoqi@0: } aoqi@0: } aoqi@0: return new ClassMember(member); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Return the key to the member map for the given member. aoqi@0: */ aoqi@0: private Object getMemberKey(ProgramElementDoc doc) { aoqi@0: if (doc.isConstructor()) { aoqi@0: return doc.name() + ((ExecutableMemberDoc)doc).signature(); aoqi@0: } else if (doc.isMethod()) { aoqi@0: return getClassMember((MethodDoc) doc); aoqi@0: } else if (doc.isField() || doc.isEnumConstant() || doc.isAnnotationTypeElement()) { aoqi@0: return doc.name(); aoqi@0: } else { // it's a class or interface aoqi@0: String classOrIntName = doc.name(); aoqi@0: //Strip off the containing class name because we only want the member name. aoqi@0: classOrIntName = classOrIntName.indexOf('.') != 0 ? classOrIntName.substring(classOrIntName.lastIndexOf('.'), classOrIntName.length()) : classOrIntName; aoqi@0: return "clint" + classOrIntName; aoqi@0: } aoqi@0: } aoqi@0: }