Mon, 02 May 2011 02:13:02 -0700
6492694: @deprecated tag doesn't work in package-info files.
Reviewed-by: jjg
1 /*
2 * Copyright (c) 1999, 2008, 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 com.sun.javadoc.*;
29 import com.sun.tools.doclets.internal.toolkit.*;
30 import java.util.*;
32 /**
33 * A data structure that encapsulates the visible members of a particular
34 * type for a given class tree. To use this data structor, you must specify
35 * the type of member you are interested in (nested class, field, constructor
36 * or method) and the leaf of the class tree. The data structure will map
37 * all visible members in the leaf and classes above the leaf in the tree.
38 *
39 * This code is not part of an API.
40 * It is implementation that is subject to change.
41 * Do not use it as an API
42 *
43 * @author Atul M Dambalkar
44 * @author Jamie Ho (rewrite)
45 */
46 public class VisibleMemberMap {
48 private boolean noVisibleMembers = true;
50 public static final int INNERCLASSES = 0;
51 public static final int ENUM_CONSTANTS = 1;
52 public static final int FIELDS = 2;
53 public static final int CONSTRUCTORS = 3;
54 public static final int METHODS = 4;
55 public static final int ANNOTATION_TYPE_MEMBER_OPTIONAL = 5;
56 public static final int ANNOTATION_TYPE_MEMBER_REQUIRED = 6;
58 /**
59 * The total number of member types is {@value}.
60 */
61 public static final int NUM_MEMBER_TYPES = 7;
63 public static final String STARTLEVEL = "start";
65 /**
66 * List of ClassDoc objects for which ClassMembers objects are built.
67 */
68 private final List<ClassDoc> visibleClasses = new ArrayList<ClassDoc>();
70 /**
71 * Map for each member name on to a map which contains members with same
72 * name-signature. The mapped map will contain mapping for each MemberDoc
73 * onto it's respecive level string.
74 */
75 private final Map<Object,Map<ProgramElementDoc,String>> memberNameMap = new HashMap<Object,Map<ProgramElementDoc,String>>();
77 /**
78 * Map of class and it's ClassMembers object.
79 */
80 private final Map<ClassDoc,ClassMembers> classMap = new HashMap<ClassDoc,ClassMembers>();
82 /**
83 * Type whose visible members are requested. This is the leaf of
84 * the class tree being mapped.
85 */
86 private final ClassDoc classdoc;
88 /**
89 * Member kind: InnerClasses/Fields/Methods?
90 */
91 private final int kind;
93 /**
94 * Deprected members should be excluded or not?
95 */
96 private final boolean nodepr;
98 /**
99 * 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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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;
223 public ClassMember(ProgramElementDoc programElementDoc) {
224 members = new HashSet<ProgramElementDoc>();
225 members.add(programElementDoc);
226 }
228 public void addMember(ProgramElementDoc programElementDoc) {
229 members.add(programElementDoc);
230 }
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 }
244 /**
245 * A data structure that represents the class members for
246 * a visible class.
247 */
248 private class ClassMembers {
250 /**
251 * The mapping class, whose inherited members are put in the
252 * {@link #members} list.
253 */
254 private ClassDoc mappingClass;
256 /**
257 * List of inherited members from the mapping class.
258 */
259 private List<ProgramElementDoc> members = new ArrayList<ProgramElementDoc>();
261 /**
262 * Level/Depth of inheritance.
263 */
264 private String level;
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 }
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 }
292 }
294 private void build() {
295 if (kind == CONSTRUCTORS) {
296 addMembers(mappingClass);
297 } else {
298 mapClass();
299 }
300 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }