Thu, 15 Nov 2012 19:54:20 -0800
8002079: update DocFile to use a JavaFileManager
Reviewed-by: darcy
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 }