Tue, 09 Oct 2012 19:10:00 -0700
8000663: clean up langtools imports
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 * This code is not part of an API.
41 * It is implementation that is subject to change.
42 * Do not use it as an API
43 *
44 * @author Atul M Dambalkar
45 * @author Jamie Ho (rewrite)
46 */
47 public class VisibleMemberMap {
49 private boolean noVisibleMembers = true;
51 public static final int INNERCLASSES = 0;
52 public static final int ENUM_CONSTANTS = 1;
53 public static final int FIELDS = 2;
54 public static final int CONSTRUCTORS = 3;
55 public static final int METHODS = 4;
56 public static final int ANNOTATION_TYPE_MEMBER_OPTIONAL = 5;
57 public static final int ANNOTATION_TYPE_MEMBER_REQUIRED = 6;
59 /**
60 * The total number of member types is {@value}.
61 */
62 public static final int NUM_MEMBER_TYPES = 7;
64 public static final String STARTLEVEL = "start";
66 /**
67 * List of ClassDoc objects for which ClassMembers objects are built.
68 */
69 private final List<ClassDoc> visibleClasses = new ArrayList<ClassDoc>();
71 /**
72 * Map for each member name on to a map which contains members with same
73 * name-signature. The mapped map will contain mapping for each MemberDoc
74 * onto it's respecive level string.
75 */
76 private final Map<Object,Map<ProgramElementDoc,String>> memberNameMap = new HashMap<Object,Map<ProgramElementDoc,String>>();
78 /**
79 * Map of class and it's ClassMembers object.
80 */
81 private final Map<ClassDoc,ClassMembers> classMap = new HashMap<ClassDoc,ClassMembers>();
83 /**
84 * Type whose visible members are requested. This is the leaf of
85 * the class tree being mapped.
86 */
87 private final ClassDoc classdoc;
89 /**
90 * Member kind: InnerClasses/Fields/Methods?
91 */
92 private final int kind;
94 /**
95 * Deprected members should be excluded or not?
96 */
97 private final boolean nodepr;
99 /**
100 * Construct a VisibleMemberMap of the given type for the given
101 * class. If nodepr is true, exclude the deprecated members from
102 * the map.
103 *
104 * @param classdoc the class whose members are being mapped.
105 * @param kind the kind of member that is being mapped.
106 * @param nodepr if true, exclude the deprecated members from the map.
107 */
108 public VisibleMemberMap(ClassDoc classdoc, int kind, boolean nodepr) {
109 this.classdoc = classdoc;
110 this.nodepr = nodepr;
111 this.kind = kind;
112 new ClassMembers(classdoc, STARTLEVEL).build();
113 }
115 /**
116 * Return the list of visible classes in this map.
117 *
118 * @return the list of visible classes in this map.
119 */
120 public List<ClassDoc> getVisibleClassesList() {
121 sort(visibleClasses);
122 return visibleClasses;
123 }
125 /**
126 * Return the package private members inherited by the class. Only return
127 * if parent is package private and not documented.
128 *
129 * @param configuation the current configuration of the doclet.
130 * @return the package private members inherited by the class.
131 */
132 private List<ProgramElementDoc> getInheritedPackagePrivateMethods(Configuration configuration) {
133 List<ProgramElementDoc> results = new ArrayList<ProgramElementDoc>();
134 for (Iterator<ClassDoc> iter = visibleClasses.iterator(); iter.hasNext(); ) {
135 ClassDoc currentClass = 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 }
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<ProgramElementDoc> getLeafClassMembers(Configuration configuration) {
155 List<ProgramElementDoc> result = getMembersFor(classdoc);
156 result.addAll(getInheritedPackagePrivateMethods(configuration));
157 return result;
158 }
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<ProgramElementDoc> getMembersFor(ClassDoc cd) {
168 ClassMembers clmembers = classMap.get(cd);
169 if (clmembers == null) {
170 return new ArrayList<ProgramElementDoc>();
171 }
172 return clmembers.getMembers();
173 }
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<ClassDoc> list) {
180 List<ClassDoc> classes = new ArrayList<ClassDoc>();
181 List<ClassDoc> interfaces = new ArrayList<ClassDoc>();
182 for (int i = 0; i < list.size(); i++) {
183 ClassDoc cd = 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 }
195 private void fillMemberLevelMap(List<ProgramElementDoc> list, String level) {
196 for (int i = 0; i < list.size(); i++) {
197 Object key = getMemberKey(list.get(i));
198 Map<ProgramElementDoc,String> memberLevelMap = memberNameMap.get(key);
199 if (memberLevelMap == null) {
200 memberLevelMap = new HashMap<ProgramElementDoc,String>();
201 memberNameMap.put(key, memberLevelMap);
202 }
203 memberLevelMap.put(list.get(i), level);
204 }
205 }
207 private void purgeMemberLevelMap(List<ProgramElementDoc> list, String level) {
208 for (int i = 0; i < list.size(); i++) {
209 Object key = getMemberKey(list.get(i));
210 Map<ProgramElementDoc, String> memberLevelMap = memberNameMap.get(key);
211 if (level.equals(memberLevelMap.get(list.get(i))))
212 memberLevelMap.remove(list.get(i));
213 }
214 }
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<ProgramElementDoc> members;
224 public ClassMember(ProgramElementDoc programElementDoc) {
225 members = new HashSet<ProgramElementDoc>();
226 members.add(programElementDoc);
227 }
229 public void addMember(ProgramElementDoc programElementDoc) {
230 members.add(programElementDoc);
231 }
233 public boolean isEqual(MethodDoc member) {
234 for (Iterator<ProgramElementDoc> 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 }
245 /**
246 * A data structure that represents the class members for
247 * a visible class.
248 */
249 private class ClassMembers {
251 /**
252 * The mapping class, whose inherited members are put in the
253 * {@link #members} list.
254 */
255 private ClassDoc mappingClass;
257 /**
258 * List of inherited members from the mapping class.
259 */
260 private List<ProgramElementDoc> members = new ArrayList<ProgramElementDoc>();
262 /**
263 * Level/Depth of inheritance.
264 */
265 private String level;
267 /**
268 * Return list of inherited members from mapping class.
269 *
270 * @return List Inherited members.
271 */
272 public List<ProgramElementDoc> getMembers() {
273 return members;
274 }
276 private ClassMembers(ClassDoc mappingClass, String level) {
277 this.mappingClass = mappingClass;
278 this.level = level;
279 if (classMap.containsKey(mappingClass) &&
280 level.startsWith(classMap.get(mappingClass).level)) {
281 //Remove lower level class so that it can be replaced with
282 //same class found at higher level.
283 purgeMemberLevelMap(getClassMembers(mappingClass, false),
284 classMap.get(mappingClass).level);
285 classMap.remove(mappingClass);
286 visibleClasses.remove(mappingClass);
287 }
288 if (!classMap.containsKey(mappingClass)) {
289 classMap.put(mappingClass, this);
290 visibleClasses.add(mappingClass);
291 }
293 }
295 private void build() {
296 if (kind == CONSTRUCTORS) {
297 addMembers(mappingClass);
298 } else {
299 mapClass();
300 }
301 }
303 private void mapClass() {
304 addMembers(mappingClass);
305 ClassDoc[] interfaces = mappingClass.interfaces();
306 for (int i = 0; i < interfaces.length; i++) {
307 String locallevel = level + 1;
308 ClassMembers cm = new ClassMembers(interfaces[i], locallevel);
309 cm.mapClass();
310 }
311 if (mappingClass.isClass()) {
312 ClassDoc superclass = mappingClass.superclass();
313 if (!(superclass == null || mappingClass.equals(superclass))) {
314 ClassMembers cm = new ClassMembers(superclass,
315 level + "c");
316 cm.mapClass();
317 }
318 }
319 }
321 /**
322 * Get all the valid members from the mapping class. Get the list of
323 * members for the class to be included into(ctii), also get the level
324 * string for ctii. If mapping class member is not already in the
325 * inherited member list and if it is visible in the ctii and not
326 * overridden, put such a member in the inherited member list.
327 * Adjust member-level-map, class-map.
328 */
329 private void addMembers(ClassDoc fromClass) {
330 List<ProgramElementDoc> cdmembers = getClassMembers(fromClass, true);
331 List<ProgramElementDoc> incllist = new ArrayList<ProgramElementDoc>();
332 for (int i = 0; i < cdmembers.size(); i++) {
333 ProgramElementDoc pgmelem = cdmembers.get(i);
334 if (!found(members, pgmelem) &&
335 memberIsVisible(pgmelem) &&
336 !isOverridden(pgmelem, level)) {
337 incllist.add(pgmelem);
338 }
339 }
340 if (incllist.size() > 0) {
341 noVisibleMembers = false;
342 }
343 members.addAll(incllist);
344 fillMemberLevelMap(getClassMembers(fromClass, false), level);
345 }
347 /**
348 * Is given doc item visible in given classdoc in terms fo inheritance?
349 * The given doc item is visible in the given classdoc if it is public
350 * or protected and if it is package-private if it's containing class
351 * is in the same package as the given classdoc.
352 */
353 private boolean memberIsVisible(ProgramElementDoc pgmdoc) {
354 if (pgmdoc.containingClass().equals(classdoc)) {
355 //Member is in class that we are finding visible members for.
356 //Of course it is visible.
357 return true;
358 } else if (pgmdoc.isPrivate()) {
359 //Member is in super class or implemented interface.
360 //Private, so not inherited.
361 return false;
362 } else if (pgmdoc.isPackagePrivate()) {
363 //Member is package private. Only return true if its class is in
364 //same package.
365 return pgmdoc.containingClass().containingPackage().equals(
366 classdoc.containingPackage());
367 } else {
368 //Public members are always inherited.
369 return true;
370 }
371 }
373 /**
374 * Return all available class members.
375 */
376 private List<ProgramElementDoc> getClassMembers(ClassDoc cd, boolean filter) {
377 if (cd.isEnum() && kind == CONSTRUCTORS) {
378 //If any of these rules are hit, return empty array because
379 //we don't document these members ever.
380 return Arrays.asList(new ProgramElementDoc[] {});
381 }
382 ProgramElementDoc[] members = null;
383 switch (kind) {
384 case ANNOTATION_TYPE_MEMBER_OPTIONAL:
385 members = cd.isAnnotationType() ?
386 filter((AnnotationTypeDoc) cd, false) :
387 new AnnotationTypeElementDoc[] {};
388 break;
389 case ANNOTATION_TYPE_MEMBER_REQUIRED:
390 members = cd.isAnnotationType() ?
391 filter((AnnotationTypeDoc) cd, true) :
392 new AnnotationTypeElementDoc[] {};
393 break;
394 case INNERCLASSES:
395 members = cd.innerClasses(filter);
396 break;
397 case ENUM_CONSTANTS:
398 members = cd.enumConstants();
399 break;
400 case FIELDS:
401 members = cd.fields(filter);
402 break;
403 case CONSTRUCTORS:
404 members = cd.constructors();
405 break;
406 case METHODS:
407 members = cd.methods(filter);
408 break;
409 default:
410 members = new ProgramElementDoc[0];
411 }
412 if (nodepr) {
413 return Util.excludeDeprecatedMembersAsList(members);
414 }
415 return Arrays.asList(members);
416 }
418 /**
419 * Filter the annotation type members and return either the required
420 * members or the optional members, depending on the value of the
421 * required parameter.
422 *
423 * @param doc The annotation type to process.
424 * @param required
425 * @return the annotation type members and return either the required
426 * members or the optional members, depending on the value of the
427 * required parameter.
428 */
429 private AnnotationTypeElementDoc[] filter(AnnotationTypeDoc doc,
430 boolean required) {
431 AnnotationTypeElementDoc[] members = doc.elements();
432 List<AnnotationTypeElementDoc> targetMembers = new ArrayList<AnnotationTypeElementDoc>();
433 for (int i = 0; i < members.length; i++) {
434 if ((required && members[i].defaultValue() == null) ||
435 ((!required) && members[i].defaultValue() != null)){
436 targetMembers.add(members[i]);
437 }
438 }
439 return targetMembers.toArray(new AnnotationTypeElementDoc[]{});
440 }
442 private boolean found(List<ProgramElementDoc> list, ProgramElementDoc elem) {
443 for (int i = 0; i < list.size(); i++) {
444 ProgramElementDoc pgmelem = list.get(i);
445 if (Util.matches(pgmelem, elem)) {
446 return true;
447 }
448 }
449 return false;
450 }
453 /**
454 * Is member overridden? The member is overridden if it is found in the
455 * same level hierarchy e.g. member at level "11" overrides member at
456 * level "111".
457 */
458 private boolean isOverridden(ProgramElementDoc pgmdoc, String level) {
459 Map<?,String> memberLevelMap = (Map<?,String>) memberNameMap.get(getMemberKey(pgmdoc));
460 if (memberLevelMap == null)
461 return false;
462 String mappedlevel = null;
463 Iterator<String> iterator = memberLevelMap.values().iterator();
464 while (iterator.hasNext()) {
465 mappedlevel = iterator.next();
466 if (mappedlevel.equals(STARTLEVEL) ||
467 (level.startsWith(mappedlevel) &&
468 !level.equals(mappedlevel))) {
469 return true;
470 }
471 }
472 return false;
473 }
474 }
476 /**
477 * Return true if this map has no visible members.
478 *
479 * @return true if this map has no visible members.
480 */
481 public boolean noVisibleMembers() {
482 return noVisibleMembers;
483 }
485 private ClassMember getClassMember(MethodDoc member) {
486 for (Iterator<?> iter = memberNameMap.keySet().iterator(); iter.hasNext();) {
487 Object key = iter.next();
488 if (key instanceof String) {
489 continue;
490 } else if (((ClassMember) key).isEqual(member)) {
491 return (ClassMember) key;
492 }
493 }
494 return new ClassMember(member);
495 }
497 /**
498 * Return the key to the member map for the given member.
499 */
500 private Object getMemberKey(ProgramElementDoc doc) {
501 if (doc.isConstructor()) {
502 return doc.name() + ((ExecutableMemberDoc)doc).signature();
503 } else if (doc.isMethod()) {
504 return getClassMember((MethodDoc) doc);
505 } else if (doc.isField() || doc.isEnumConstant() || doc.isAnnotationTypeElement()) {
506 return doc.name();
507 } else { // it's a class or interface
508 String classOrIntName = doc.name();
509 //Strip off the containing class name because we only want the member name.
510 classOrIntName = classOrIntName.indexOf('.') != 0 ? classOrIntName.substring(classOrIntName.lastIndexOf('.'), classOrIntName.length()) : classOrIntName;
511 return "clint" + classOrIntName;
512 }
513 }
514 }