001: /*
002: * Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.tools.doclets.internal.toolkit.util;
027:
028: import com.sun.javadoc.*;
029: import com.sun.tools.doclets.internal.toolkit.*;
030: import java.util.*;
031:
032: /**
033: * A data structure that encapsulates the visible members of a particular
034: * type for a given class tree. To use this data structor, you must specify
035: * the type of member you are interested in (nested class, field, constructor
036: * or method) and the leaf of the class tree. The data structure will map
037: * all visible members in the leaf and classes above the leaf in the tree.
038: *
039: * This code is not part of an API.
040: * It is implementation that is subject to change.
041: * Do not use it as an API
042: *
043: * @author Atul M Dambalkar
044: * @author Jamie Ho (rewrite)
045: */
046: public class VisibleMemberMap {
047:
048: private boolean noVisibleMembers = true;
049:
050: public static final int INNERCLASSES = 0;
051: public static final int ENUM_CONSTANTS = 1;
052: public static final int FIELDS = 2;
053: public static final int CONSTRUCTORS = 3;
054: public static final int METHODS = 4;
055: public static final int ANNOTATION_TYPE_MEMBER_OPTIONAL = 5;
056: public static final int ANNOTATION_TYPE_MEMBER_REQUIRED = 6;
057:
058: /**
059: * The total number of member types is {@value}.
060: */
061: public static final int NUM_MEMBER_TYPES = 7;
062:
063: public static final String STARTLEVEL = "start";
064:
065: /**
066: * List of ClassDoc objects for which ClassMembers objects are built.
067: */
068: private final List visibleClasses = new ArrayList();
069:
070: /**
071: * Map for each member name on to a map which contains members with same
072: * name-signature. The mapped map will contain mapping for each MemberDoc
073: * onto it's respecive level string.
074: */
075: private final Map memberNameMap = new HashMap();
076:
077: /**
078: * Map of class and it's ClassMembers object.
079: */
080: private final Map classMap = new HashMap();
081:
082: /**
083: * Type whose visible members are requested. This is the leaf of
084: * the class tree being mapped.
085: */
086: private final ClassDoc classdoc;
087:
088: /**
089: * Member kind: InnerClasses/Fields/Methods?
090: */
091: private final int kind;
092:
093: /**
094: * Deprected members should be excluded or not?
095: */
096: private final boolean nodepr;
097:
098: /**
099: * Construct a VisibleMemberMap of the given type for the given
100: * class. If nodepr is true, exclude the deprecated members from
101: * the map.
102: *
103: * @param classdoc the class whose members are being mapped.
104: * @param kind the kind of member that is being mapped.
105: * @param nodepr if true, exclude the deprecated members from the map.
106: */
107: public VisibleMemberMap(ClassDoc classdoc, int kind, boolean nodepr) {
108: this .classdoc = classdoc;
109: this .nodepr = nodepr;
110: this .kind = kind;
111: new ClassMembers(classdoc, STARTLEVEL).build();
112: }
113:
114: /**
115: * Return the list of visible classes in this map.
116: *
117: * @return the list of visible classes in this map.
118: */
119: public List getVisibleClassesList() {
120: sort(visibleClasses);
121: return visibleClasses;
122: }
123:
124: /**
125: * Return the package private members inherited by the class. Only return
126: * if parent is package private and not documented.
127: *
128: * @param configuation the current configuration of the doclet.
129: * @return the package private members inherited by the class.
130: */
131: private List getInheritedPackagePrivateMethods(
132: Configuration configuration) {
133: List results = new ArrayList();
134: for (Iterator iter = visibleClasses.iterator(); iter.hasNext();) {
135: ClassDoc currentClass = (ClassDoc) iter.next();
136: if (currentClass != classdoc
137: && currentClass.isPackagePrivate()
138: && !Util.isLinkable(currentClass, configuration)) {
139: // Document these members in the child class because
140: // the parent is inaccessible.
141: results.addAll(getMembersFor(currentClass));
142: }
143: }
144: return results;
145: }
146:
147: /**
148: * Return the visible members of the class being mapped. Also append at the
149: * end of the list members that are inherited by inaccessible parents. We
150: * document these members in the child because the parent is not documented.
151: *
152: * @param configuation the current configuration of the doclet.
153: */
154: public List getLeafClassMembers(Configuration configuration) {
155: List result = getMembersFor(classdoc);
156: result.addAll(getInheritedPackagePrivateMethods(configuration));
157: return result;
158: }
159:
160: /**
161: * Retrn the list of members for the given class.
162: *
163: * @param cd the class to retrieve the list of visible members for.
164: *
165: * @return the list of members for the given class.
166: */
167: public List getMembersFor(ClassDoc cd) {
168: ClassMembers clmembers = (ClassMembers) (classMap.get(cd));
169: if (clmembers == null) {
170: return new ArrayList();
171: }
172: return clmembers.getMembers();
173: }
174:
175: /**
176: * Sort the given mixed list of classes and interfaces to a list of
177: * classes followed by interfaces traversed. Don't sort alphabetically.
178: */
179: private void sort(List list) {
180: List classes = new ArrayList();
181: List interfaces = new ArrayList();
182: for (int i = 0; i < list.size(); i++) {
183: ClassDoc cd = (ClassDoc) list.get(i);
184: if (cd.isClass()) {
185: classes.add(cd);
186: } else {
187: interfaces.add(cd);
188: }
189: }
190: list.clear();
191: list.addAll(classes);
192: list.addAll(interfaces);
193: }
194:
195: private void fillMemberLevelMap(List list, String level) {
196: for (int i = 0; i < list.size(); i++) {
197: Object key = getMemberKey((ProgramElementDoc) list.get(i));
198: Map memberLevelMap = (Map) memberNameMap.get(key);
199: if (memberLevelMap == null) {
200: memberLevelMap = new HashMap();
201: memberNameMap.put(key, memberLevelMap);
202: }
203: memberLevelMap.put(list.get(i), level);
204: }
205: }
206:
207: private void purgeMemberLevelMap(List list, String level) {
208: for (int i = 0; i < list.size(); i++) {
209: Object key = getMemberKey((ProgramElementDoc) list.get(i));
210: Map memberLevelMap = (Map) memberNameMap.get(key);
211: if (level.equals(memberLevelMap.get(list.get(i))))
212: memberLevelMap.remove(list.get(i));
213: }
214: }
215:
216: /**
217: * Represents a class member. We should be able to just use a
218: * ProgramElementDoc instead of this class, but that doesn't take
219: * type variables in consideration when comparing.
220: */
221: private class ClassMember {
222: private Set members;
223:
224: public ClassMember(ProgramElementDoc programElementDoc) {
225: members = new HashSet();
226: members.add(programElementDoc);
227: }
228:
229: public void addMember(ProgramElementDoc programElementDoc) {
230: members.add(programElementDoc);
231: }
232:
233: public boolean isEqual(MethodDoc member) {
234: for (Iterator iter = members.iterator(); iter.hasNext();) {
235: MethodDoc member2 = (MethodDoc) iter.next();
236: if (Util.executableMembersEqual(member, member2)) {
237: members.add(member);
238: return true;
239: }
240: }
241: return false;
242: }
243: }
244:
245: /**
246: * A data structure that represents the class members for
247: * a visible class.
248: */
249: private class ClassMembers {
250:
251: /**
252: * The mapping class, whose inherited members are put in the
253: * {@link #members} list.
254: */
255: private ClassDoc mappingClass;
256:
257: /**
258: * List of inherited members from the mapping class.
259: */
260: private List members = new ArrayList();
261:
262: /**
263: * Level/Depth of inheritance.
264: */
265: private String level;
266:
267: /**
268: * Return list of inherited members from mapping class.
269: *
270: * @return List Inherited members.
271: */
272: public List getMembers() {
273: return members;
274: }
275:
276: private ClassMembers(ClassDoc mappingClass, String level) {
277: this .mappingClass = mappingClass;
278: this .level = level;
279: if (classMap.containsKey(mappingClass)
280: && level.startsWith(((ClassMembers) classMap
281: .get(mappingClass)).level)) {
282: //Remove lower level class so that it can be replaced with
283: //same class found at higher level.
284: purgeMemberLevelMap(
285: getClassMembers(mappingClass, false),
286: ((ClassMembers) classMap.get(mappingClass)).level);
287: classMap.remove(mappingClass);
288: visibleClasses.remove(mappingClass);
289: }
290: if (!classMap.containsKey(mappingClass)) {
291: classMap.put(mappingClass, this );
292: visibleClasses.add(mappingClass);
293: }
294:
295: }
296:
297: private void build() {
298: if (kind == CONSTRUCTORS) {
299: addMembers(mappingClass);
300: } else {
301: mapClass();
302: }
303: }
304:
305: private void mapClass() {
306: addMembers(mappingClass);
307: ClassDoc[] interfaces = mappingClass.interfaces();
308: for (int i = 0; i < interfaces.length; i++) {
309: String locallevel = level + 1;
310: ClassMembers cm = new ClassMembers(interfaces[i],
311: locallevel);
312: cm.mapClass();
313: }
314: if (mappingClass.isClass()) {
315: ClassDoc super class = mappingClass.super class();
316: if (!(super class == null || mappingClass
317: .equals(super class))) {
318: ClassMembers cm = new ClassMembers(super class,
319: level + "c");
320: cm.mapClass();
321: }
322: }
323: }
324:
325: /**
326: * Get all the valid members from the mapping class. Get the list of
327: * members for the class to be included into(ctii), also get the level
328: * string for ctii. If mapping class member is not already in the
329: * inherited member list and if it is visible in the ctii and not
330: * overridden, put such a member in the inherited member list.
331: * Adjust member-level-map, class-map.
332: */
333: private void addMembers(ClassDoc fromClass) {
334: List cdmembers = getClassMembers(fromClass, true);
335: List incllist = new ArrayList();
336: for (int i = 0; i < cdmembers.size(); i++) {
337: ProgramElementDoc pgmelem = (ProgramElementDoc) (cdmembers
338: .get(i));
339: if (!found(members, pgmelem)
340: && memberIsVisible(pgmelem)
341: && !isOverridden(pgmelem, level)) {
342: incllist.add(pgmelem);
343: }
344: }
345: if (incllist.size() > 0) {
346: noVisibleMembers = false;
347: }
348: members.addAll(incllist);
349: fillMemberLevelMap(getClassMembers(fromClass, false), level);
350: }
351:
352: /**
353: * Is given doc item visible in given classdoc in terms fo inheritance?
354: * The given doc item is visible in the given classdoc if it is public
355: * or protected and if it is package-private if it's containing class
356: * is in the same package as the given classdoc.
357: */
358: private boolean memberIsVisible(ProgramElementDoc pgmdoc) {
359: if (pgmdoc.containingClass().equals(classdoc)) {
360: //Member is in class that we are finding visible members for.
361: //Of course it is visible.
362: return true;
363: } else if (pgmdoc.isPrivate()) {
364: //Member is in super class or implemented interface.
365: //Private, so not inherited.
366: return false;
367: } else if (pgmdoc.isPackagePrivate()) {
368: //Member is package private. Only return true if its class is in
369: //same package.
370: return pgmdoc.containingClass().containingPackage()
371: .equals(classdoc.containingPackage());
372: } else {
373: //Public members are always inherited.
374: return true;
375: }
376: }
377:
378: /**
379: * Return all available class members.
380: */
381: private List getClassMembers(ClassDoc cd, boolean filter) {
382: if (cd.isEnum() && kind == CONSTRUCTORS) {
383: //If any of these rules are hit, return empty array because
384: //we don't document these members ever.
385: return Arrays.asList(new ProgramElementDoc[] {});
386: }
387: ProgramElementDoc[] members = null;
388: switch (kind) {
389: case ANNOTATION_TYPE_MEMBER_OPTIONAL:
390: members = cd.isAnnotationType() ? filter(
391: (AnnotationTypeDoc) cd, false)
392: : new AnnotationTypeElementDoc[] {};
393: break;
394: case ANNOTATION_TYPE_MEMBER_REQUIRED:
395: members = cd.isAnnotationType() ? filter(
396: (AnnotationTypeDoc) cd, true)
397: : new AnnotationTypeElementDoc[] {};
398: break;
399: case INNERCLASSES:
400: members = cd.innerClasses(filter);
401: break;
402: case ENUM_CONSTANTS:
403: members = cd.enumConstants();
404: break;
405: case FIELDS:
406: members = cd.fields(filter);
407: break;
408: case CONSTRUCTORS:
409: members = cd.constructors();
410: break;
411: case METHODS:
412: members = cd.methods(filter);
413: break;
414: default:
415: members = new ProgramElementDoc[0];
416: }
417: if (nodepr) {
418: return Util.excludeDeprecatedMembersAsList(members);
419: }
420: return Arrays.asList(members);
421: }
422:
423: /**
424: * Filter the annotation type members and return either the required
425: * members or the optional members, depending on the value of the
426: * required parameter.
427: *
428: * @param doc The annotation type to process.
429: * @param required
430: * @return the annotation type members and return either the required
431: * members or the optional members, depending on the value of the
432: * required parameter.
433: */
434: private AnnotationTypeElementDoc[] filter(
435: AnnotationTypeDoc doc, boolean required) {
436: AnnotationTypeElementDoc[] members = ((AnnotationTypeDoc) doc)
437: .elements();
438: List targetMembers = new ArrayList();
439: for (int i = 0; i < members.length; i++) {
440: if ((required && members[i].defaultValue() == null)
441: || ((!required) && members[i].defaultValue() != null)) {
442: targetMembers.add(members[i]);
443: }
444: }
445: return (AnnotationTypeElementDoc[]) targetMembers
446: .toArray(new AnnotationTypeElementDoc[] {});
447: }
448:
449: private boolean found(List list, ProgramElementDoc elem) {
450: for (int i = 0; i < list.size(); i++) {
451: ProgramElementDoc pgmelem = (ProgramElementDoc) list
452: .get(i);
453: if (Util.matches(pgmelem, elem)) {
454: return true;
455: }
456: }
457: return false;
458: }
459:
460: /**
461: * Is member overridden? The member is overridden if it is found in the
462: * same level hierarchy e.g. member at level "11" overrides member at
463: * level "111".
464: */
465: private boolean isOverridden(ProgramElementDoc pgmdoc,
466: String level) {
467: Map memberLevelMap = (Map) memberNameMap
468: .get(getMemberKey(pgmdoc));
469: if (memberLevelMap == null)
470: return false;
471: String mappedlevel = null;
472: Iterator iterator = memberLevelMap.values().iterator();
473: while (iterator.hasNext()) {
474: mappedlevel = (String) (iterator.next());
475: if (mappedlevel.equals(STARTLEVEL)
476: || (level.startsWith(mappedlevel) && !level
477: .equals(mappedlevel))) {
478: return true;
479: }
480: }
481: return false;
482: }
483: }
484:
485: /**
486: * Return true if this map has no visible members.
487: *
488: * @return true if this map has no visible members.
489: */
490: public boolean noVisibleMembers() {
491: return noVisibleMembers;
492: }
493:
494: private ClassMember getClassMember(MethodDoc member) {
495: for (Iterator iter = memberNameMap.keySet().iterator(); iter
496: .hasNext();) {
497: Object key = iter.next();
498: if (key instanceof String) {
499: continue;
500: } else if (((ClassMember) key).isEqual(member)) {
501: return (ClassMember) key;
502: }
503: }
504: return new ClassMember(member);
505: }
506:
507: /**
508: * Return the key to the member map for the given member.
509: */
510: private Object getMemberKey(ProgramElementDoc doc) {
511: if (doc.isConstructor()) {
512: return doc.name() + ((ExecutableMemberDoc) doc).signature();
513: } else if (doc.isMethod()) {
514: return getClassMember((MethodDoc) doc);
515: } else if (doc.isField() || doc.isEnumConstant()
516: || doc.isAnnotationTypeElement()) {
517: return doc.name();
518: } else { // it's a class or interface
519: String classOrIntName = doc.name();
520: //Strip off the containing class name because we only want the member name.
521: classOrIntName = classOrIntName.indexOf('.') != 0 ? classOrIntName
522: .substring(classOrIntName.lastIndexOf('.'),
523: classOrIntName.length())
524: : classOrIntName;
525: return "clint" + classOrIntName;
526: }
527: }
528: }
|