langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/util/VisibleMemberMap.java
changeset 10 06bc494ca11e
child 868 d0f233085cbb
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/share/classes/com/sun/tools/doclets/internal/toolkit/util/VisibleMemberMap.java	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,515 @@
+/*
+ * Copyright 1999-2005 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package com.sun.tools.doclets.internal.toolkit.util;
+
+import com.sun.javadoc.*;
+import com.sun.tools.doclets.internal.toolkit.*;
+import java.util.*;
+
+/**
+ * A data structure that encapsulates the visible members of a particular
+ * type for a given class tree.  To use this data structor, you must specify
+ * the type of member you are interested in (nested class, field, constructor
+ * or method) and the leaf of the class tree.  The data structure will map
+ * all visible members in the leaf and classes above the leaf in the tree.
+ *
+ * This code is not part of an API.
+ * It is implementation that is subject to change.
+ * Do not use it as an API
+ *
+ * @author Atul M Dambalkar
+ * @author Jamie Ho (rewrite)
+ */
+public class VisibleMemberMap {
+
+    private boolean noVisibleMembers = true;
+
+    public static final int INNERCLASSES    = 0;
+    public static final int ENUM_CONSTANTS  = 1;
+    public static final int FIELDS          = 2;
+    public static final int CONSTRUCTORS    = 3;
+    public static final int METHODS         = 4;
+    public static final int ANNOTATION_TYPE_MEMBER_OPTIONAL = 5;
+    public static final int ANNOTATION_TYPE_MEMBER_REQUIRED = 6;
+
+    /**
+     * The total number of member types is {@value}.
+     */
+    public static final int NUM_MEMBER_TYPES = 7;
+
+    public static final String STARTLEVEL = "start";
+
+    /**
+     * List of ClassDoc objects for which ClassMembers objects are built.
+     */
+    private final List visibleClasses = new ArrayList();
+
+    /**
+     * Map for each member name on to a map which contains members with same
+     * name-signature. The mapped map will contain mapping for each MemberDoc
+     * onto it's respecive level string.
+     */
+    private final Map memberNameMap = new HashMap();
+
+    /**
+     * Map of class and it's ClassMembers object.
+     */
+    private final Map classMap = new HashMap();
+
+    /**
+     * Type whose visible members are requested.  This is the leaf of
+     * the class tree being mapped.
+     */
+    private final ClassDoc classdoc;
+
+    /**
+     * Member kind: InnerClasses/Fields/Methods?
+     */
+    private final int kind;
+
+    /**
+     * Deprected members should be excluded or not?
+     */
+    private final boolean nodepr;
+
+    /**
+     * Construct a VisibleMemberMap of the given type for the given
+     * class.  If nodepr is true, exclude the deprecated members from
+     * the map.
+     *
+     * @param classdoc the class whose members are being mapped.
+     * @param kind the kind of member that is being mapped.
+     * @param nodepr if true, exclude the deprecated members from the map.
+     */
+    public VisibleMemberMap(ClassDoc classdoc, int kind, boolean nodepr) {
+        this.classdoc = classdoc;
+        this.nodepr = nodepr;
+        this.kind = kind;
+        new ClassMembers(classdoc, STARTLEVEL).build();
+    }
+
+    /**
+     * Return the list of visible classes in this map.
+     *
+     * @return the list of visible classes in this map.
+     */
+    public List getVisibleClassesList() {
+        sort(visibleClasses);
+        return visibleClasses;
+    }
+
+    /**
+     * Return the package private members inherited by the class.  Only return
+     * if parent is package private and not documented.
+     *
+     * @param configuation the current configuration of the doclet.
+     * @return the package private members inherited by the class.
+     */
+    private List getInheritedPackagePrivateMethods(Configuration configuration) {
+        List results = new ArrayList();
+        for (Iterator iter = visibleClasses.iterator(); iter.hasNext(); ) {
+            ClassDoc currentClass = (ClassDoc) iter.next();
+            if (currentClass != classdoc &&
+                currentClass.isPackagePrivate() &&
+                !Util.isLinkable(currentClass, configuration)) {
+                // Document these members in the child class because
+                // the parent is inaccessible.
+                results.addAll(getMembersFor(currentClass));
+            }
+        }
+        return results;
+    }
+
+    /**
+     * Return the visible members of the class being mapped.  Also append at the
+     * end of the list members that are inherited by inaccessible parents. We
+     * document these members in the child because the parent is not documented.
+     *
+     * @param configuation the current configuration of the doclet.
+     */
+    public List getLeafClassMembers(Configuration configuration) {
+        List result = getMembersFor(classdoc);
+        result.addAll(getInheritedPackagePrivateMethods(configuration));
+        return result;
+    }
+
+    /**
+     * Retrn the list of members for the given class.
+     *
+     * @param cd the class to retrieve the list of visible members for.
+     *
+     * @return the list of members for the given class.
+     */
+    public List getMembersFor(ClassDoc cd) {
+        ClassMembers clmembers = (ClassMembers)(classMap.get(cd));
+        if (clmembers == null) {
+            return new ArrayList();
+        }
+        return clmembers.getMembers();
+    }
+
+    /**
+     * Sort the given mixed list of classes and interfaces to a list of
+     * classes followed by interfaces traversed. Don't sort alphabetically.
+     */
+    private void sort(List list) {
+        List classes = new ArrayList();
+        List interfaces = new ArrayList();
+        for (int i = 0; i < list.size(); i++) {
+            ClassDoc cd = (ClassDoc)list.get(i);
+            if (cd.isClass()) {
+                classes.add(cd);
+            } else {
+                interfaces.add(cd);
+            }
+        }
+        list.clear();
+        list.addAll(classes);
+        list.addAll(interfaces);
+    }
+
+    private void fillMemberLevelMap(List list, String level) {
+        for (int i = 0; i < list.size(); i++) {
+            Object key = getMemberKey((ProgramElementDoc)list.get(i));
+            Map memberLevelMap = (Map) memberNameMap.get(key);
+            if (memberLevelMap == null) {
+                memberLevelMap = new HashMap();
+                memberNameMap.put(key, memberLevelMap);
+            }
+            memberLevelMap.put(list.get(i), level);
+        }
+    }
+
+    private void purgeMemberLevelMap(List list, String level) {
+        for (int i = 0; i < list.size(); i++) {
+            Object key = getMemberKey((ProgramElementDoc)list.get(i));
+            Map memberLevelMap = (Map) memberNameMap.get(key);
+            if (level.equals(memberLevelMap.get(list.get(i))))
+                memberLevelMap.remove(list.get(i));
+        }
+    }
+
+    /**
+     * Represents a class member.  We should be able to just use a
+     * ProgramElementDoc instead of this class, but that doesn't take
+     * type variables in consideration when comparing.
+     */
+    private class ClassMember {
+        private Set members;
+
+        public ClassMember(ProgramElementDoc programElementDoc) {
+            members = new HashSet();
+            members.add(programElementDoc);
+        }
+
+        public void addMember(ProgramElementDoc programElementDoc) {
+            members.add(programElementDoc);
+        }
+
+        public boolean isEqual(MethodDoc member) {
+            for (Iterator iter = members.iterator(); iter.hasNext(); ) {
+                MethodDoc member2 = (MethodDoc) iter.next();
+                if (Util.executableMembersEqual(member, member2)) {
+                    members.add(member);
+                        return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    /**
+     * A data structure that represents the class members for
+     * a visible class.
+     */
+    private class ClassMembers {
+
+        /**
+         * The mapping class, whose inherited members are put in the
+         * {@link #members} list.
+         */
+        private ClassDoc mappingClass;
+
+        /**
+         * List of inherited members from the mapping class.
+         */
+        private List members = new ArrayList();
+
+        /**
+         * Level/Depth of inheritance.
+         */
+        private String level;
+
+        /**
+         * Return list of inherited members from mapping class.
+         *
+         * @return List Inherited members.
+         */
+        public List getMembers() {
+            return members;
+        }
+
+        private ClassMembers(ClassDoc mappingClass, String level) {
+            this.mappingClass = mappingClass;
+            this.level = level;
+            if (classMap.containsKey(mappingClass) &&
+                        level.startsWith(((ClassMembers) classMap.get(mappingClass)).level)) {
+                //Remove lower level class so that it can be replaced with
+                //same class found at higher level.
+                purgeMemberLevelMap(getClassMembers(mappingClass, false),
+                    ((ClassMembers) classMap.get(mappingClass)).level);
+                classMap.remove(mappingClass);
+                visibleClasses.remove(mappingClass);
+            }
+            if (!classMap.containsKey(mappingClass)) {
+                classMap.put(mappingClass, this);
+                visibleClasses.add(mappingClass);
+            }
+
+        }
+
+        private void build() {
+            if (kind == CONSTRUCTORS) {
+                addMembers(mappingClass);
+            } else {
+                mapClass();
+            }
+        }
+
+        private void mapClass() {
+            addMembers(mappingClass);
+            ClassDoc[] interfaces = mappingClass.interfaces();
+            for (int i = 0; i < interfaces.length; i++) {
+                String locallevel = level + 1;
+                ClassMembers cm = new ClassMembers(interfaces[i], locallevel);
+                cm.mapClass();
+            }
+            if (mappingClass.isClass()) {
+                ClassDoc superclass = mappingClass.superclass();
+                if (!(superclass == null || mappingClass.equals(superclass))) {
+                    ClassMembers cm = new ClassMembers(superclass,
+                                                       level + "c");
+                    cm.mapClass();
+                }
+            }
+        }
+
+        /**
+         * Get all the valid members from the mapping class. Get the list of
+         * members for the class to be included into(ctii), also get the level
+         * string for ctii. If mapping class member is not already in the
+         * inherited member list and if it is visible in the ctii and not
+         * overridden, put such a member in the inherited member list.
+         * Adjust member-level-map, class-map.
+         */
+        private void addMembers(ClassDoc fromClass) {
+            List cdmembers = getClassMembers(fromClass, true);
+            List incllist = new ArrayList();
+            for (int i = 0; i < cdmembers.size(); i++) {
+                ProgramElementDoc pgmelem =
+                    (ProgramElementDoc)(cdmembers.get(i));
+                if (!found(members, pgmelem) &&
+                    memberIsVisible(pgmelem) &&
+                    !isOverridden(pgmelem, level)) {
+                    incllist.add(pgmelem);
+                }
+            }
+            if (incllist.size() > 0) {
+                noVisibleMembers = false;
+            }
+            members.addAll(incllist);
+            fillMemberLevelMap(getClassMembers(fromClass, false), level);
+        }
+
+        /**
+         * Is given doc item visible in given classdoc in terms fo inheritance?
+         * The given doc item is visible in the given classdoc if it is public
+         * or protected and if it is package-private if it's containing class
+         * is in the same package as the given classdoc.
+         */
+        private boolean memberIsVisible(ProgramElementDoc pgmdoc) {
+            if (pgmdoc.containingClass().equals(classdoc)) {
+                //Member is in class that we are finding visible members for.
+                //Of course it is visible.
+                return true;
+            } else if (pgmdoc.isPrivate()) {
+                //Member is in super class or implemented interface.
+                //Private, so not inherited.
+                return false;
+            } else if (pgmdoc.isPackagePrivate()) {
+                //Member is package private.  Only return true if its class is in
+                //same package.
+                return pgmdoc.containingClass().containingPackage().equals(
+                    classdoc.containingPackage());
+            } else {
+                //Public members are always inherited.
+                return true;
+            }
+        }
+
+        /**
+         * Return all available class members.
+         */
+        private List getClassMembers(ClassDoc cd, boolean filter) {
+            if (cd.isEnum() && kind == CONSTRUCTORS) {
+                //If any of these rules are hit, return empty array because
+                //we don't document these members ever.
+                return Arrays.asList(new ProgramElementDoc[] {});
+            }
+            ProgramElementDoc[] members = null;
+            switch (kind) {
+                case ANNOTATION_TYPE_MEMBER_OPTIONAL:
+                    members = cd.isAnnotationType() ?
+                        filter((AnnotationTypeDoc) cd, false) :
+                        new AnnotationTypeElementDoc[] {};
+                    break;
+                case ANNOTATION_TYPE_MEMBER_REQUIRED:
+                    members = cd.isAnnotationType() ?
+                        filter((AnnotationTypeDoc) cd, true) :
+                        new AnnotationTypeElementDoc[] {};
+                    break;
+                case INNERCLASSES:
+                    members = cd.innerClasses(filter);
+                    break;
+                case ENUM_CONSTANTS:
+                    members = cd.enumConstants();
+                    break;
+                case FIELDS:
+                    members = cd.fields(filter);
+                    break;
+                case CONSTRUCTORS:
+                    members = cd.constructors();
+                    break;
+                case METHODS:
+                    members = cd.methods(filter);
+                    break;
+                default:
+                    members = new ProgramElementDoc[0];
+            }
+            if (nodepr) {
+                return Util.excludeDeprecatedMembersAsList(members);
+            }
+            return Arrays.asList(members);
+        }
+
+        /**
+         * Filter the annotation type members and return either the required
+         * members or the optional members, depending on the value of the
+         * required parameter.
+         *
+         * @param doc The annotation type to process.
+         * @param required
+         * @return the annotation type members and return either the required
+         * members or the optional members, depending on the value of the
+         * required parameter.
+         */
+        private AnnotationTypeElementDoc[] filter(AnnotationTypeDoc doc,
+            boolean required) {
+            AnnotationTypeElementDoc[] members = ((AnnotationTypeDoc) doc).elements();
+            List targetMembers = new ArrayList();
+            for (int i = 0; i < members.length; i++) {
+                if ((required && members[i].defaultValue() == null) ||
+                     ((!required) && members[i].defaultValue() != null)){
+                    targetMembers.add(members[i]);
+                }
+            }
+            return (AnnotationTypeElementDoc[])
+                targetMembers.toArray(new AnnotationTypeElementDoc[]{});
+        }
+
+        private boolean found(List list, ProgramElementDoc elem) {
+            for (int i = 0; i < list.size(); i++) {
+                ProgramElementDoc pgmelem = (ProgramElementDoc)list.get(i);
+                if (Util.matches(pgmelem, elem)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+
+        /**
+         * Is member overridden? The member is overridden if it is found in the
+         * same level hierarchy e.g. member at level "11" overrides member at
+         * level "111".
+         */
+        private boolean isOverridden(ProgramElementDoc pgmdoc, String level) {
+            Map memberLevelMap = (Map) memberNameMap.get(getMemberKey(pgmdoc));
+            if (memberLevelMap == null)
+                return false;
+            String mappedlevel = null;
+            Iterator iterator = memberLevelMap.values().iterator();
+            while (iterator.hasNext()) {
+                mappedlevel = (String)(iterator.next());
+                if (mappedlevel.equals(STARTLEVEL) ||
+                    (level.startsWith(mappedlevel) &&
+                     !level.equals(mappedlevel))) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Return true if this map has no visible members.
+     *
+     * @return true if this map has no visible members.
+     */
+    public boolean noVisibleMembers() {
+        return noVisibleMembers;
+    }
+
+    private ClassMember getClassMember(MethodDoc member) {
+        for (Iterator iter = memberNameMap.keySet().iterator(); iter.hasNext();) {
+            Object key = iter.next();
+            if (key instanceof String) {
+                continue;
+            } else if (((ClassMember) key).isEqual(member)) {
+                return (ClassMember) key;
+            }
+        }
+        return new ClassMember(member);
+    }
+
+    /**
+     * Return the key to the member map for the given member.
+     */
+    private Object getMemberKey(ProgramElementDoc doc) {
+        if (doc.isConstructor()) {
+            return doc.name() + ((ExecutableMemberDoc)doc).signature();
+        } else if (doc.isMethod()) {
+            return getClassMember((MethodDoc) doc);
+        } else if (doc.isField() || doc.isEnumConstant() || doc.isAnnotationTypeElement()) {
+            return doc.name();
+        } else { // it's a class or interface
+            String classOrIntName = doc.name();
+            //Strip off the containing class name because we only want the member name.
+            classOrIntName = classOrIntName.indexOf('.') != 0 ? classOrIntName.substring(classOrIntName.lastIndexOf('.'), classOrIntName.length()) : classOrIntName;
+            return "clint" + classOrIntName;
+        }
+    }
+}