duke@1: /* xdono@117: * Copyright 1999-2008 Sun Microsystems, Inc. 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 duke@1: * published by the Free Software Foundation. Sun designates this duke@1: * particular file as subject to the "Classpath" exception as provided duke@1: * by Sun 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: * duke@1: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, duke@1: * CA 95054 USA or visit www.sun.com if you need additional information or duke@1: * have any questions. duke@1: */ duke@1: duke@1: package com.sun.tools.doclets.internal.toolkit.util; duke@1: duke@1: import com.sun.javadoc.*; duke@1: import com.sun.tools.doclets.internal.toolkit.*; duke@1: import java.util.*; 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: * duke@1: * This code is not part of an API. duke@1: * It is implementation that is subject to change. duke@1: * Do not use it as an API 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; duke@1: public static final int ANNOTATION_TYPE_MEMBER_OPTIONAL = 5; duke@1: public static final int ANNOTATION_TYPE_MEMBER_REQUIRED = 6; duke@1: duke@1: /** duke@1: * The total number of member types is {@value}. duke@1: */ duke@1: public static final int NUM_MEMBER_TYPES = 7; 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: /** duke@1: * Deprected members should be excluded or not? duke@1: */ duke@1: private final boolean nodepr; duke@1: duke@1: /** duke@1: * Construct a VisibleMemberMap of the given type for the given duke@1: * class. If nodepr is true, exclude the deprecated members from duke@1: * the map. 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. duke@1: * @param nodepr if true, exclude the deprecated members from the map. duke@1: */ duke@1: public VisibleMemberMap(ClassDoc classdoc, int kind, boolean nodepr) { duke@1: this.classdoc = classdoc; duke@1: this.nodepr = nodepr; duke@1: this.kind = kind; 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: /** 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: * duke@1: * @param configuation 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: * duke@1: * @param configuation 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); duke@1: if (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) && duke@1: !isOverridden(pgmelem, level)) { duke@1: 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: 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) { 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); duke@1: break; duke@1: default: duke@1: members = new ProgramElementDoc[0]; duke@1: } duke@1: if (nodepr) { 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 memberLevelMap = (Map) 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: } 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: }