001 /*
002 * Copyright 1999-2008 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<ClassDoc> visibleClasses = new ArrayList<ClassDoc>();
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<Object,Map<ProgramElementDoc,String>> memberNameMap = new HashMap<Object,Map<ProgramElementDoc,String>>();
076
077 /**
078 * Map of class and it's ClassMembers object.
079 */
080 private final Map<ClassDoc,ClassMembers> classMap = new HashMap<ClassDoc,ClassMembers>();
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<ClassDoc> 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<ProgramElementDoc> getInheritedPackagePrivateMethods(Configuration configuration) {
132 List<ProgramElementDoc> results = new ArrayList<ProgramElementDoc>();
133 for (Iterator<ClassDoc> iter = visibleClasses.iterator(); iter.hasNext(); ) {
134 ClassDoc currentClass = iter.next();
135 if (currentClass != classdoc &&
136 currentClass.isPackagePrivate() &&
137 !Util.isLinkable(currentClass, configuration)) {
138 // Document these members in the child class because
139 // the parent is inaccessible.
140 results.addAll(getMembersFor(currentClass));
141 }
142 }
143 return results;
144 }
145
146 /**
147 * Return the visible members of the class being mapped. Also append at the
148 * end of the list members that are inherited by inaccessible parents. We
149 * document these members in the child because the parent is not documented.
150 *
151 * @param configuation the current configuration of the doclet.
152 */
153 public List<ProgramElementDoc> getLeafClassMembers(Configuration configuration) {
154 List<ProgramElementDoc> result = getMembersFor(classdoc);
155 result.addAll(getInheritedPackagePrivateMethods(configuration));
156 return result;
157 }
158
159 /**
160 * Retrn the list of members for the given class.
161 *
162 * @param cd the class to retrieve the list of visible members for.
163 *
164 * @return the list of members for the given class.
165 */
166 public List<ProgramElementDoc> getMembersFor(ClassDoc cd) {
167 ClassMembers clmembers = classMap.get(cd);
168 if (clmembers == null) {
169 return new ArrayList<ProgramElementDoc>();
170 }
171 return clmembers.getMembers();
172 }
173
174 /**
175 * Sort the given mixed list of classes and interfaces to a list of
176 * classes followed by interfaces traversed. Don't sort alphabetically.
177 */
178 private void sort(List<ClassDoc> list) {
179 List<ClassDoc> classes = new ArrayList<ClassDoc>();
180 List<ClassDoc> interfaces = new ArrayList<ClassDoc>();
181 for (int i = 0; i < list.size(); i++) {
182 ClassDoc cd = list.get(i);
183 if (cd.isClass()) {
184 classes.add(cd);
185 } else {
186 interfaces.add(cd);
187 }
188 }
189 list.clear();
190 list.addAll(classes);
191 list.addAll(interfaces);
192 }
193
194 private void fillMemberLevelMap(List<ProgramElementDoc> list, String level) {
195 for (int i = 0; i < list.size(); i++) {
196 Object key = getMemberKey(list.get(i));
197 Map<ProgramElementDoc,String> memberLevelMap = memberNameMap.get(key);
198 if (memberLevelMap == null) {
199 memberLevelMap = new HashMap<ProgramElementDoc,String>();
200 memberNameMap.put(key, memberLevelMap);
201 }
202 memberLevelMap.put(list.get(i), level);
203 }
204 }
205
206 private void purgeMemberLevelMap(List<ProgramElementDoc> list, String level) {
207 for (int i = 0; i < list.size(); i++) {
208 Object key = getMemberKey(list.get(i));
209 Map<ProgramElementDoc, String> memberLevelMap = memberNameMap.get(key);
210 if (level.equals(memberLevelMap.get(list.get(i))))
211 memberLevelMap.remove(list.get(i));
212 }
213 }
214
215 /**
216 * Represents a class member. We should be able to just use a
217 * ProgramElementDoc instead of this class, but that doesn't take
218 * type variables in consideration when comparing.
219 */
220 private class ClassMember {
221 private Set<ProgramElementDoc> members;
222
223 public ClassMember(ProgramElementDoc programElementDoc) {
224 members = new HashSet<ProgramElementDoc>();
225 members.add(programElementDoc);
226 }
227
228 public void addMember(ProgramElementDoc programElementDoc) {
229 members.add(programElementDoc);
230 }
231
232 public boolean isEqual(MethodDoc member) {
233 for (Iterator<ProgramElementDoc> iter = members.iterator(); iter.hasNext(); ) {
234 MethodDoc member2 = (MethodDoc) iter.next();
235 if (Util.executableMembersEqual(member, member2)) {
236 members.add(member);
237 return true;
238 }
239 }
240 return false;
241 }
242 }
243
244 /**
245 * A data structure that represents the class members for
246 * a visible class.
247 */
248 private class ClassMembers {
249
250 /**
251 * The mapping class, whose inherited members are put in the
252 * {@link #members} list.
253 */
254 private ClassDoc mappingClass;
255
256 /**
257 * List of inherited members from the mapping class.
258 */
259 private List<ProgramElementDoc> members = new ArrayList<ProgramElementDoc>();
260
261 /**
262 * Level/Depth of inheritance.
263 */
264 private String level;
265
266 /**
267 * Return list of inherited members from mapping class.
268 *
269 * @return List Inherited members.
270 */
271 public List<ProgramElementDoc> getMembers() {
272 return members;
273 }
274
275 private ClassMembers(ClassDoc mappingClass, String level) {
276 this.mappingClass = mappingClass;
277 this.level = level;
278 if (classMap.containsKey(mappingClass) &&
279 level.startsWith(classMap.get(mappingClass).level)) {
280 //Remove lower level class so that it can be replaced with
281 //same class found at higher level.
282 purgeMemberLevelMap(getClassMembers(mappingClass, false),
283 classMap.get(mappingClass).level);
284 classMap.remove(mappingClass);
285 visibleClasses.remove(mappingClass);
286 }
287 if (!classMap.containsKey(mappingClass)) {
288 classMap.put(mappingClass, this);
289 visibleClasses.add(mappingClass);
290 }
291
292 }
293
294 private void build() {
295 if (kind == CONSTRUCTORS) {
296 addMembers(mappingClass);
297 } else {
298 mapClass();
299 }
300 }
301
302 private void mapClass() {
303 addMembers(mappingClass);
304 ClassDoc[] interfaces = mappingClass.interfaces();
305 for (int i = 0; i < interfaces.length; i++) {
306 String locallevel = level + 1;
307 ClassMembers cm = new ClassMembers(interfaces[i], locallevel);
308 cm.mapClass();
309 }
310 if (mappingClass.isClass()) {
311 ClassDoc superclass = mappingClass.superclass();
312 if (!(superclass == null || mappingClass.equals(superclass))) {
313 ClassMembers cm = new ClassMembers(superclass,
314 level + "c");
315 cm.mapClass();
316 }
317 }
318 }
319
320 /**
321 * Get all the valid members from the mapping class. Get the list of
322 * members for the class to be included into(ctii), also get the level
323 * string for ctii. If mapping class member is not already in the
324 * inherited member list and if it is visible in the ctii and not
325 * overridden, put such a member in the inherited member list.
326 * Adjust member-level-map, class-map.
327 */
328 private void addMembers(ClassDoc fromClass) {
329 List<ProgramElementDoc> cdmembers = getClassMembers(fromClass, true);
330 List<ProgramElementDoc> incllist = new ArrayList<ProgramElementDoc>();
331 for (int i = 0; i < cdmembers.size(); i++) {
332 ProgramElementDoc pgmelem = cdmembers.get(i);
333 if (!found(members, pgmelem) &&
334 memberIsVisible(pgmelem) &&
335 !isOverridden(pgmelem, level)) {
336 incllist.add(pgmelem);
337 }
338 }
339 if (incllist.size() > 0) {
340 noVisibleMembers = false;
341 }
342 members.addAll(incllist);
343 fillMemberLevelMap(getClassMembers(fromClass, false), level);
344 }
345
346 /**
347 * Is given doc item visible in given classdoc in terms fo inheritance?
348 * The given doc item is visible in the given classdoc if it is public
349 * or protected and if it is package-private if it's containing class
350 * is in the same package as the given classdoc.
351 */
352 private boolean memberIsVisible(ProgramElementDoc pgmdoc) {
353 if (pgmdoc.containingClass().equals(classdoc)) {
354 //Member is in class that we are finding visible members for.
355 //Of course it is visible.
356 return true;
357 } else if (pgmdoc.isPrivate()) {
358 //Member is in super class or implemented interface.
359 //Private, so not inherited.
360 return false;
361 } else if (pgmdoc.isPackagePrivate()) {
362 //Member is package private. Only return true if its class is in
363 //same package.
364 return pgmdoc.containingClass().containingPackage().equals(
365 classdoc.containingPackage());
366 } else {
367 //Public members are always inherited.
368 return true;
369 }
370 }
371
372 /**
373 * Return all available class members.
374 */
375 private List<ProgramElementDoc> getClassMembers(ClassDoc cd, boolean filter) {
376 if (cd.isEnum() && kind == CONSTRUCTORS) {
377 //If any of these rules are hit, return empty array because
378 //we don't document these members ever.
379 return Arrays.asList(new ProgramElementDoc[] {});
380 }
381 ProgramElementDoc[] members = null;
382 switch (kind) {
383 case ANNOTATION_TYPE_MEMBER_OPTIONAL:
384 members = cd.isAnnotationType() ?
385 filter((AnnotationTypeDoc) cd, false) :
386 new AnnotationTypeElementDoc[] {};
387 break;
388 case ANNOTATION_TYPE_MEMBER_REQUIRED:
389 members = cd.isAnnotationType() ?
390 filter((AnnotationTypeDoc) cd, true) :
391 new AnnotationTypeElementDoc[] {};
392 break;
393 case INNERCLASSES:
394 members = cd.innerClasses(filter);
395 break;
396 case ENUM_CONSTANTS:
397 members = cd.enumConstants();
398 break;
399 case FIELDS:
400 members = cd.fields(filter);
401 break;
402 case CONSTRUCTORS:
403 members = cd.constructors();
404 break;
405 case METHODS:
406 members = cd.methods(filter);
407 break;
408 default:
409 members = new ProgramElementDoc[0];
410 }
411 if (nodepr) {
412 return Util.excludeDeprecatedMembersAsList(members);
413 }
414 return Arrays.asList(members);
415 }
416
417 /**
418 * Filter the annotation type members and return either the required
419 * members or the optional members, depending on the value of the
420 * required parameter.
421 *
422 * @param doc The annotation type to process.
423 * @param required
424 * @return 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 private AnnotationTypeElementDoc[] filter(AnnotationTypeDoc doc,
429 boolean required) {
430 AnnotationTypeElementDoc[] members = doc.elements();
431 List<AnnotationTypeElementDoc> targetMembers = new ArrayList<AnnotationTypeElementDoc>();
432 for (int i = 0; i < members.length; i++) {
433 if ((required && members[i].defaultValue() == null) ||
434 ((!required) && members[i].defaultValue() != null)){
435 targetMembers.add(members[i]);
436 }
437 }
438 return targetMembers.toArray(new AnnotationTypeElementDoc[]{});
439 }
440
441 private boolean found(List<ProgramElementDoc> list, ProgramElementDoc elem) {
442 for (int i = 0; i < list.size(); i++) {
443 ProgramElementDoc pgmelem = list.get(i);
444 if (Util.matches(pgmelem, elem)) {
445 return true;
446 }
447 }
448 return false;
449 }
450
451
452 /**
453 * Is member overridden? The member is overridden if it is found in the
454 * same level hierarchy e.g. member at level "11" overrides member at
455 * level "111".
456 */
457 private boolean isOverridden(ProgramElementDoc pgmdoc, String level) {
458 Map<?,String> memberLevelMap = (Map<?,String>) memberNameMap.get(getMemberKey(pgmdoc));
459 if (memberLevelMap == null)
460 return false;
461 String mappedlevel = null;
462 Iterator<String> iterator = memberLevelMap.values().iterator();
463 while (iterator.hasNext()) {
464 mappedlevel = iterator.next();
465 if (mappedlevel.equals(STARTLEVEL) ||
466 (level.startsWith(mappedlevel) &&
467 !level.equals(mappedlevel))) {
468 return true;
469 }
470 }
471 return false;
472 }
473 }
474
475 /**
476 * Return true if this map has no visible members.
477 *
478 * @return true if this map has no visible members.
479 */
480 public boolean noVisibleMembers() {
481 return noVisibleMembers;
482 }
483
484 private ClassMember getClassMember(MethodDoc member) {
485 for (Iterator<?> iter = memberNameMap.keySet().iterator(); iter.hasNext();) {
486 Object key = iter.next();
487 if (key instanceof String) {
488 continue;
489 } else if (((ClassMember) key).isEqual(member)) {
490 return (ClassMember) key;
491 }
492 }
493 return new ClassMember(member);
494 }
495
496 /**
497 * Return the key to the member map for the given member.
498 */
499 private Object getMemberKey(ProgramElementDoc doc) {
500 if (doc.isConstructor()) {
501 return doc.name() + ((ExecutableMemberDoc)doc).signature();
502 } else if (doc.isMethod()) {
503 return getClassMember((MethodDoc) doc);
504 } else if (doc.isField() || doc.isEnumConstant() || doc.isAnnotationTypeElement()) {
505 return doc.name();
506 } else { // it's a class or interface
507 String classOrIntName = doc.name();
508 //Strip off the containing class name because we only want the member name.
509 classOrIntName = classOrIntName.indexOf('.') != 0 ? classOrIntName.substring(classOrIntName.lastIndexOf('.'), classOrIntName.length()) : classOrIntName;
510 return "clint" + classOrIntName;
511 }
512 }
513 }