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

Mon, 15 Oct 2012 17:07:55 -0700

author
jjg
date
Mon, 15 Oct 2012 17:07:55 -0700
changeset 1364
8db45b13526e
parent 1359
25e14ad23cef
child 1606
ccbe7ffdd867
permissions
-rw-r--r--

8000666: javadoc should write directly to Writer instead of composing strings
Reviewed-by: bpatel

     1 /*
     2  * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Oracle in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    24  */
    26 package com.sun.tools.doclets.internal.toolkit.util;
    28 import java.util.*;
    30 import com.sun.javadoc.*;
    31 import com.sun.tools.doclets.internal.toolkit.*;
    33 /**
    34  * A data structure that encapsulates the visible members of a particular
    35  * type for a given class tree.  To use this data structor, you must specify
    36  * the type of member you are interested in (nested class, field, constructor
    37  * or method) and the leaf of the class tree.  The data structure will map
    38  * all visible members in the leaf and classes above the leaf in the tree.
    39  *
    40  *  <p><b>This is NOT part of any supported API.
    41  *  If you write code that depends on this, you do so at your own risk.
    42  *  This code and its internal interfaces are subject to change or
    43  *  deletion without notice.</b>
    44  *
    45  * @author Atul M Dambalkar
    46  * @author Jamie Ho (rewrite)
    47  */
    48 public class VisibleMemberMap {
    50     private boolean noVisibleMembers = true;
    52     public static final int INNERCLASSES    = 0;
    53     public static final int ENUM_CONSTANTS  = 1;
    54     public static final int FIELDS          = 2;
    55     public static final int CONSTRUCTORS    = 3;
    56     public static final int METHODS         = 4;
    57     public static final int ANNOTATION_TYPE_MEMBER_OPTIONAL = 5;
    58     public static final int ANNOTATION_TYPE_MEMBER_REQUIRED = 6;
    60     /**
    61      * The total number of member types is {@value}.
    62      */
    63     public static final int NUM_MEMBER_TYPES = 7;
    65     public static final String STARTLEVEL = "start";
    67     /**
    68      * List of ClassDoc objects for which ClassMembers objects are built.
    69      */
    70     private final List<ClassDoc> visibleClasses = new ArrayList<ClassDoc>();
    72     /**
    73      * Map for each member name on to a map which contains members with same
    74      * name-signature. The mapped map will contain mapping for each MemberDoc
    75      * onto it's respecive level string.
    76      */
    77     private final Map<Object,Map<ProgramElementDoc,String>> memberNameMap = new HashMap<Object,Map<ProgramElementDoc,String>>();
    79     /**
    80      * Map of class and it's ClassMembers object.
    81      */
    82     private final Map<ClassDoc,ClassMembers> classMap = new HashMap<ClassDoc,ClassMembers>();
    84     /**
    85      * Type whose visible members are requested.  This is the leaf of
    86      * the class tree being mapped.
    87      */
    88     private final ClassDoc classdoc;
    90     /**
    91      * Member kind: InnerClasses/Fields/Methods?
    92      */
    93     private final int kind;
    95     /**
    96      * Deprected members should be excluded or not?
    97      */
    98     private final boolean nodepr;
   100     /**
   101      * Construct a VisibleMemberMap of the given type for the given
   102      * class.  If nodepr is true, exclude the deprecated members from
   103      * the map.
   104      *
   105      * @param classdoc the class whose members are being mapped.
   106      * @param kind the kind of member that is being mapped.
   107      * @param nodepr if true, exclude the deprecated members from the map.
   108      */
   109     public VisibleMemberMap(ClassDoc classdoc, int kind, boolean nodepr) {
   110         this.classdoc = classdoc;
   111         this.nodepr = nodepr;
   112         this.kind = kind;
   113         new ClassMembers(classdoc, STARTLEVEL).build();
   114     }
   116     /**
   117      * Return the list of visible classes in this map.
   118      *
   119      * @return the list of visible classes in this map.
   120      */
   121     public List<ClassDoc> getVisibleClassesList() {
   122         sort(visibleClasses);
   123         return visibleClasses;
   124     }
   126     /**
   127      * Return the package private members inherited by the class.  Only return
   128      * if parent is package private and not documented.
   129      *
   130      * @param configuration the current configuration of the doclet.
   131      * @return the package private members inherited by the class.
   132      */
   133     private List<ProgramElementDoc> getInheritedPackagePrivateMethods(Configuration configuration) {
   134         List<ProgramElementDoc> results = new ArrayList<ProgramElementDoc>();
   135         for (Iterator<ClassDoc> iter = visibleClasses.iterator(); iter.hasNext(); ) {
   136             ClassDoc currentClass = iter.next();
   137             if (currentClass != classdoc &&
   138                 currentClass.isPackagePrivate() &&
   139                 !Util.isLinkable(currentClass, configuration)) {
   140                 // Document these members in the child class because
   141                 // the parent is inaccessible.
   142                 results.addAll(getMembersFor(currentClass));
   143             }
   144         }
   145         return results;
   146     }
   148     /**
   149      * Return the visible members of the class being mapped.  Also append at the
   150      * end of the list members that are inherited by inaccessible parents. We
   151      * document these members in the child because the parent is not documented.
   152      *
   153      * @param configuration the current configuration of the doclet.
   154      */
   155     public List<ProgramElementDoc> getLeafClassMembers(Configuration configuration) {
   156         List<ProgramElementDoc> result = getMembersFor(classdoc);
   157         result.addAll(getInheritedPackagePrivateMethods(configuration));
   158         return result;
   159     }
   161     /**
   162      * Retrn the list of members for the given class.
   163      *
   164      * @param cd the class to retrieve the list of visible members for.
   165      *
   166      * @return the list of members for the given class.
   167      */
   168     public List<ProgramElementDoc> getMembersFor(ClassDoc cd) {
   169         ClassMembers clmembers = classMap.get(cd);
   170         if (clmembers == null) {
   171             return new ArrayList<ProgramElementDoc>();
   172         }
   173         return clmembers.getMembers();
   174     }
   176     /**
   177      * Sort the given mixed list of classes and interfaces to a list of
   178      * classes followed by interfaces traversed. Don't sort alphabetically.
   179      */
   180     private void sort(List<ClassDoc> list) {
   181         List<ClassDoc> classes = new ArrayList<ClassDoc>();
   182         List<ClassDoc> interfaces = new ArrayList<ClassDoc>();
   183         for (int i = 0; i < list.size(); i++) {
   184             ClassDoc cd = list.get(i);
   185             if (cd.isClass()) {
   186                 classes.add(cd);
   187             } else {
   188                 interfaces.add(cd);
   189             }
   190         }
   191         list.clear();
   192         list.addAll(classes);
   193         list.addAll(interfaces);
   194     }
   196     private void fillMemberLevelMap(List<ProgramElementDoc> list, String level) {
   197         for (int i = 0; i < list.size(); i++) {
   198             Object key = getMemberKey(list.get(i));
   199             Map<ProgramElementDoc,String> memberLevelMap = memberNameMap.get(key);
   200             if (memberLevelMap == null) {
   201                 memberLevelMap = new HashMap<ProgramElementDoc,String>();
   202                 memberNameMap.put(key, memberLevelMap);
   203             }
   204             memberLevelMap.put(list.get(i), level);
   205         }
   206     }
   208     private void purgeMemberLevelMap(List<ProgramElementDoc> list, String level) {
   209         for (int i = 0; i < list.size(); i++) {
   210             Object key = getMemberKey(list.get(i));
   211             Map<ProgramElementDoc, String> memberLevelMap = memberNameMap.get(key);
   212             if (level.equals(memberLevelMap.get(list.get(i))))
   213                 memberLevelMap.remove(list.get(i));
   214         }
   215     }
   217     /**
   218      * Represents a class member.  We should be able to just use a
   219      * ProgramElementDoc instead of this class, but that doesn't take
   220      * type variables in consideration when comparing.
   221      */
   222     private class ClassMember {
   223         private Set<ProgramElementDoc> members;
   225         public ClassMember(ProgramElementDoc programElementDoc) {
   226             members = new HashSet<ProgramElementDoc>();
   227             members.add(programElementDoc);
   228         }
   230         public void addMember(ProgramElementDoc programElementDoc) {
   231             members.add(programElementDoc);
   232         }
   234         public boolean isEqual(MethodDoc member) {
   235             for (Iterator<ProgramElementDoc> iter = members.iterator(); iter.hasNext(); ) {
   236                 MethodDoc member2 = (MethodDoc) iter.next();
   237                 if (Util.executableMembersEqual(member, member2)) {
   238                     members.add(member);
   239                         return true;
   240                 }
   241             }
   242             return false;
   243         }
   244     }
   246     /**
   247      * A data structure that represents the class members for
   248      * a visible class.
   249      */
   250     private class ClassMembers {
   252         /**
   253          * The mapping class, whose inherited members are put in the
   254          * {@link #members} list.
   255          */
   256         private ClassDoc mappingClass;
   258         /**
   259          * List of inherited members from the mapping class.
   260          */
   261         private List<ProgramElementDoc> members = new ArrayList<ProgramElementDoc>();
   263         /**
   264          * Level/Depth of inheritance.
   265          */
   266         private String level;
   268         /**
   269          * Return list of inherited members from mapping class.
   270          *
   271          * @return List Inherited members.
   272          */
   273         public List<ProgramElementDoc> getMembers() {
   274             return members;
   275         }
   277         private ClassMembers(ClassDoc mappingClass, String level) {
   278             this.mappingClass = mappingClass;
   279             this.level = level;
   280             if (classMap.containsKey(mappingClass) &&
   281                         level.startsWith(classMap.get(mappingClass).level)) {
   282                 //Remove lower level class so that it can be replaced with
   283                 //same class found at higher level.
   284                 purgeMemberLevelMap(getClassMembers(mappingClass, false),
   285                     classMap.get(mappingClass).level);
   286                 classMap.remove(mappingClass);
   287                 visibleClasses.remove(mappingClass);
   288             }
   289             if (!classMap.containsKey(mappingClass)) {
   290                 classMap.put(mappingClass, this);
   291                 visibleClasses.add(mappingClass);
   292             }
   294         }
   296         private void build() {
   297             if (kind == CONSTRUCTORS) {
   298                 addMembers(mappingClass);
   299             } else {
   300                 mapClass();
   301             }
   302         }
   304         private void mapClass() {
   305             addMembers(mappingClass);
   306             ClassDoc[] interfaces = mappingClass.interfaces();
   307             for (int i = 0; i < interfaces.length; i++) {
   308                 String locallevel = level + 1;
   309                 ClassMembers cm = new ClassMembers(interfaces[i], locallevel);
   310                 cm.mapClass();
   311             }
   312             if (mappingClass.isClass()) {
   313                 ClassDoc superclass = mappingClass.superclass();
   314                 if (!(superclass == null || mappingClass.equals(superclass))) {
   315                     ClassMembers cm = new ClassMembers(superclass,
   316                                                        level + "c");
   317                     cm.mapClass();
   318                 }
   319             }
   320         }
   322         /**
   323          * Get all the valid members from the mapping class. Get the list of
   324          * members for the class to be included into(ctii), also get the level
   325          * string for ctii. If mapping class member is not already in the
   326          * inherited member list and if it is visible in the ctii and not
   327          * overridden, put such a member in the inherited member list.
   328          * Adjust member-level-map, class-map.
   329          */
   330         private void addMembers(ClassDoc fromClass) {
   331             List<ProgramElementDoc> cdmembers = getClassMembers(fromClass, true);
   332             List<ProgramElementDoc> incllist = new ArrayList<ProgramElementDoc>();
   333             for (int i = 0; i < cdmembers.size(); i++) {
   334                 ProgramElementDoc pgmelem = cdmembers.get(i);
   335                 if (!found(members, pgmelem) &&
   336                     memberIsVisible(pgmelem) &&
   337                     !isOverridden(pgmelem, level)) {
   338                     incllist.add(pgmelem);
   339                 }
   340             }
   341             if (incllist.size() > 0) {
   342                 noVisibleMembers = false;
   343             }
   344             members.addAll(incllist);
   345             fillMemberLevelMap(getClassMembers(fromClass, false), level);
   346         }
   348         /**
   349          * Is given doc item visible in given classdoc in terms fo inheritance?
   350          * The given doc item is visible in the given classdoc if it is public
   351          * or protected and if it is package-private if it's containing class
   352          * is in the same package as the given classdoc.
   353          */
   354         private boolean memberIsVisible(ProgramElementDoc pgmdoc) {
   355             if (pgmdoc.containingClass().equals(classdoc)) {
   356                 //Member is in class that we are finding visible members for.
   357                 //Of course it is visible.
   358                 return true;
   359             } else if (pgmdoc.isPrivate()) {
   360                 //Member is in super class or implemented interface.
   361                 //Private, so not inherited.
   362                 return false;
   363             } else if (pgmdoc.isPackagePrivate()) {
   364                 //Member is package private.  Only return true if its class is in
   365                 //same package.
   366                 return pgmdoc.containingClass().containingPackage().equals(
   367                     classdoc.containingPackage());
   368             } else {
   369                 //Public members are always inherited.
   370                 return true;
   371             }
   372         }
   374         /**
   375          * Return all available class members.
   376          */
   377         private List<ProgramElementDoc> getClassMembers(ClassDoc cd, boolean filter) {
   378             if (cd.isEnum() && kind == CONSTRUCTORS) {
   379                 //If any of these rules are hit, return empty array because
   380                 //we don't document these members ever.
   381                 return Arrays.asList(new ProgramElementDoc[] {});
   382             }
   383             ProgramElementDoc[] members = null;
   384             switch (kind) {
   385                 case ANNOTATION_TYPE_MEMBER_OPTIONAL:
   386                     members = cd.isAnnotationType() ?
   387                         filter((AnnotationTypeDoc) cd, false) :
   388                         new AnnotationTypeElementDoc[] {};
   389                     break;
   390                 case ANNOTATION_TYPE_MEMBER_REQUIRED:
   391                     members = cd.isAnnotationType() ?
   392                         filter((AnnotationTypeDoc) cd, true) :
   393                         new AnnotationTypeElementDoc[] {};
   394                     break;
   395                 case INNERCLASSES:
   396                     members = cd.innerClasses(filter);
   397                     break;
   398                 case ENUM_CONSTANTS:
   399                     members = cd.enumConstants();
   400                     break;
   401                 case FIELDS:
   402                     members = cd.fields(filter);
   403                     break;
   404                 case CONSTRUCTORS:
   405                     members = cd.constructors();
   406                     break;
   407                 case METHODS:
   408                     members = cd.methods(filter);
   409                     break;
   410                 default:
   411                     members = new ProgramElementDoc[0];
   412             }
   413             if (nodepr) {
   414                 return Util.excludeDeprecatedMembersAsList(members);
   415             }
   416             return Arrays.asList(members);
   417         }
   419         /**
   420          * Filter the annotation type members and return either the required
   421          * members or the optional members, depending on the value of the
   422          * required parameter.
   423          *
   424          * @param doc The annotation type to process.
   425          * @param required
   426          * @return the annotation type members and return either the required
   427          * members or the optional members, depending on the value of the
   428          * required parameter.
   429          */
   430         private AnnotationTypeElementDoc[] filter(AnnotationTypeDoc doc,
   431             boolean required) {
   432             AnnotationTypeElementDoc[] members = doc.elements();
   433             List<AnnotationTypeElementDoc> targetMembers = new ArrayList<AnnotationTypeElementDoc>();
   434             for (int i = 0; i < members.length; i++) {
   435                 if ((required && members[i].defaultValue() == null) ||
   436                      ((!required) && members[i].defaultValue() != null)){
   437                     targetMembers.add(members[i]);
   438                 }
   439             }
   440             return targetMembers.toArray(new AnnotationTypeElementDoc[]{});
   441         }
   443         private boolean found(List<ProgramElementDoc> list, ProgramElementDoc elem) {
   444             for (int i = 0; i < list.size(); i++) {
   445                 ProgramElementDoc pgmelem = list.get(i);
   446                 if (Util.matches(pgmelem, elem)) {
   447                     return true;
   448                 }
   449             }
   450             return false;
   451         }
   454         /**
   455          * Is member overridden? The member is overridden if it is found in the
   456          * same level hierarchy e.g. member at level "11" overrides member at
   457          * level "111".
   458          */
   459         private boolean isOverridden(ProgramElementDoc pgmdoc, String level) {
   460             Map<?,String> memberLevelMap = (Map<?,String>) memberNameMap.get(getMemberKey(pgmdoc));
   461             if (memberLevelMap == null)
   462                 return false;
   463             String mappedlevel = null;
   464             Iterator<String> iterator = memberLevelMap.values().iterator();
   465             while (iterator.hasNext()) {
   466                 mappedlevel = iterator.next();
   467                 if (mappedlevel.equals(STARTLEVEL) ||
   468                     (level.startsWith(mappedlevel) &&
   469                      !level.equals(mappedlevel))) {
   470                     return true;
   471                 }
   472             }
   473             return false;
   474         }
   475     }
   477     /**
   478      * Return true if this map has no visible members.
   479      *
   480      * @return true if this map has no visible members.
   481      */
   482     public boolean noVisibleMembers() {
   483         return noVisibleMembers;
   484     }
   486     private ClassMember getClassMember(MethodDoc member) {
   487         for (Iterator<?> iter = memberNameMap.keySet().iterator(); iter.hasNext();) {
   488             Object key = iter.next();
   489             if (key instanceof String) {
   490                 continue;
   491             } else if (((ClassMember) key).isEqual(member)) {
   492                 return (ClassMember) key;
   493             }
   494         }
   495         return new ClassMember(member);
   496     }
   498     /**
   499      * Return the key to the member map for the given member.
   500      */
   501     private Object getMemberKey(ProgramElementDoc doc) {
   502         if (doc.isConstructor()) {
   503             return doc.name() + ((ExecutableMemberDoc)doc).signature();
   504         } else if (doc.isMethod()) {
   505             return getClassMember((MethodDoc) doc);
   506         } else if (doc.isField() || doc.isEnumConstant() || doc.isAnnotationTypeElement()) {
   507             return doc.name();
   508         } else { // it's a class or interface
   509             String classOrIntName = doc.name();
   510             //Strip off the containing class name because we only want the member name.
   511             classOrIntName = classOrIntName.indexOf('.') != 0 ? classOrIntName.substring(classOrIntName.lastIndexOf('.'), classOrIntName.length()) : classOrIntName;
   512             return "clint" + classOrIntName;
   513         }
   514     }
   515 }

mercurial