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