• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.doclava;
18 
19 import com.google.clearsilver.jsilver.data.Data;
20 import com.sun.javadoc.ClassDoc;
21 
22 import java.util.ArrayDeque;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.Collections;
26 import java.util.Comparator;
27 import java.util.HashMap;
28 import java.util.HashSet;
29 import java.util.Iterator;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Queue;
33 import java.util.Set;
34 import java.util.TreeMap;
35 
36 public class ClassInfo extends DocInfo implements ContainerInfo, Comparable, Scoped, Resolvable {
37 
38   /**
39    * Contains a ClassInfo and a TypeInfo.
40    * <p>
41    * This is used to match a ClassInfo, which doesn't keep track of its type parameters
42    * and a type which does.
43    */
44   private class ClassTypePair {
45     private final ClassInfo mClassInfo;
46     private final TypeInfo mTypeInfo;
47 
ClassTypePair(ClassInfo cl, TypeInfo t)48     public ClassTypePair(ClassInfo cl, TypeInfo t) {
49       mClassInfo = cl;
50       mTypeInfo = t;
51     }
52 
classInfo()53     public ClassInfo classInfo() {
54       return mClassInfo;
55     }
56 
typeInfo()57     public TypeInfo typeInfo() {
58       return mTypeInfo;
59     }
60 
getTypeArgumentMapping()61     public Map<String, TypeInfo> getTypeArgumentMapping() {
62       return TypeInfo.getTypeArgumentMapping(classInfo(), typeInfo());
63     }
64   }
65 
66   public static final Comparator<ClassInfo> comparator = new Comparator<ClassInfo>() {
67     public int compare(ClassInfo a, ClassInfo b) {
68       return a.name().compareTo(b.name());
69     }
70   };
71 
72   public static final Comparator<ClassInfo> qualifiedComparator = new Comparator<ClassInfo>() {
73     public int compare(ClassInfo a, ClassInfo b) {
74       return a.qualifiedName().compareTo(b.qualifiedName());
75     }
76   };
77 
78   /**
79    * Constructs a stub representation of a class.
80    */
ClassInfo(String qualifiedName)81   public ClassInfo(String qualifiedName) {
82     super("", SourcePositionInfo.UNKNOWN);
83     mQualifiedName = qualifiedName;
84     if (qualifiedName.lastIndexOf('.') != -1) {
85       mName = qualifiedName.substring(qualifiedName.lastIndexOf('.') + 1);
86     } else {
87       mName = qualifiedName;
88     }
89   }
90 
ClassInfo(ClassDoc cl, String rawCommentText, SourcePositionInfo position, boolean isPublic, boolean isProtected, boolean isPackagePrivate, boolean isPrivate, boolean isStatic, boolean isInterface, boolean isAbstract, boolean isOrdinaryClass, boolean isException, boolean isError, boolean isEnum, boolean isAnnotation, boolean isFinal, boolean isIncluded, String name, String qualifiedName, String qualifiedTypeName, boolean isPrimitive)91   public ClassInfo(ClassDoc cl, String rawCommentText, SourcePositionInfo position,
92           boolean isPublic, boolean isProtected, boolean isPackagePrivate, boolean isPrivate,
93           boolean isStatic, boolean isInterface, boolean isAbstract, boolean isOrdinaryClass,
94           boolean isException, boolean isError, boolean isEnum, boolean isAnnotation, boolean isFinal,
95           boolean isIncluded, String name, String qualifiedName, String qualifiedTypeName,
96           boolean isPrimitive) {
97       super(rawCommentText, position);
98 
99       initialize(rawCommentText, position,
100               isPublic, isProtected, isPackagePrivate, isPrivate,
101               isStatic, isInterface, isAbstract, isOrdinaryClass,
102               isException, isError, isEnum, isAnnotation, isFinal,
103               isIncluded, qualifiedTypeName, isPrimitive, null);
104 
105       mName = name;
106       mQualifiedName = qualifiedName;
107       mNameParts = name.split("\\.");
108       mClass = cl;
109   }
110 
initialize(String rawCommentText, SourcePositionInfo position, boolean isPublic, boolean isProtected, boolean isPackagePrivate, boolean isPrivate, boolean isStatic, boolean isInterface, boolean isAbstract, boolean isOrdinaryClass, boolean isException, boolean isError, boolean isEnum, boolean isAnnotation, boolean isFinal, boolean isIncluded, String qualifiedTypeName, boolean isPrimitive, ArrayList<AnnotationInstanceInfo> annotations)111   public void initialize(String rawCommentText, SourcePositionInfo position,
112           boolean isPublic, boolean isProtected, boolean isPackagePrivate, boolean isPrivate,
113           boolean isStatic, boolean isInterface, boolean isAbstract, boolean isOrdinaryClass,
114           boolean isException, boolean isError, boolean isEnum, boolean isAnnotation, boolean isFinal,
115           boolean isIncluded, String qualifiedTypeName, boolean isPrimitive, ArrayList<AnnotationInstanceInfo> annotations) {
116 
117     // calls
118     setPosition(position);
119     setRawCommentText(rawCommentText);
120     mIsPublic = isPublic;
121     mIsProtected = isProtected;
122     mIsPackagePrivate = isPackagePrivate;
123     mIsPrivate = isPrivate;
124     mIsStatic = isStatic;
125     mIsInterface = isInterface;
126     mIsAbstract = isAbstract;
127     mIsOrdinaryClass = isOrdinaryClass;
128     mIsException = isException;
129     mIsError = isError;
130     mIsEnum = isEnum;
131     mIsAnnotation = isAnnotation;
132     mIsFinal = isFinal;
133     mIsIncluded = isIncluded;
134     mQualifiedTypeName = qualifiedTypeName;
135     mIsPrimitive = isPrimitive;
136     mAnnotations = annotations;
137     mShowAnnotations = AnnotationInstanceInfo.getShowAnnotationsIntersection(annotations);
138   }
139 
init(TypeInfo typeInfo, ArrayList<ClassInfo> interfaces, ArrayList<TypeInfo> interfaceTypes, ArrayList<ClassInfo> innerClasses, ArrayList<MethodInfo> constructors, ArrayList<MethodInfo> methods, ArrayList<MethodInfo> annotationElements, ArrayList<FieldInfo> fields, ArrayList<FieldInfo> enumConstants, PackageInfo containingPackage, ClassInfo containingClass, ClassInfo superclass, TypeInfo superclassType, ArrayList<AnnotationInstanceInfo> annotations)140   public void init(TypeInfo typeInfo, ArrayList<ClassInfo> interfaces,
141           ArrayList<TypeInfo> interfaceTypes, ArrayList<ClassInfo> innerClasses,
142           ArrayList<MethodInfo> constructors, ArrayList<MethodInfo> methods,
143           ArrayList<MethodInfo> annotationElements, ArrayList<FieldInfo> fields,
144           ArrayList<FieldInfo> enumConstants, PackageInfo containingPackage,
145           ClassInfo containingClass, ClassInfo superclass,
146       TypeInfo superclassType, ArrayList<AnnotationInstanceInfo> annotations) {
147     mTypeInfo = typeInfo;
148     mRealInterfaces = new ArrayList<ClassInfo>(interfaces);
149     mRealInterfaceTypes = interfaceTypes;
150     mInnerClasses = innerClasses;
151     // mAllConstructors will not contain *all* constructors. Only the constructors that pass
152     // checkLevel. @see {@link Converter#convertMethods(ConstructorDoc[])}
153     mAllConstructors = constructors;
154     // mAllSelfMethods will not contain *all* self methods. Only the methods that pass
155     // checkLevel. @see {@link Converter#convertMethods(MethodDoc[])}
156     mAllSelfMethods = methods;
157     mAnnotationElements = annotationElements;
158     // mAllSelfFields will not contain *all* self fields. Only the fields that pass
159     // checkLevel. @see {@link Converter#convetFields(FieldDoc[])}
160     mAllSelfFields = fields;
161     // mEnumConstants will not contain *all* enum constants. Only the enums that pass
162     // checkLevel. @see {@link Converter#convetFields(FieldDoc[])}
163     mEnumConstants = enumConstants;
164     mContainingPackage = containingPackage;
165     mContainingClass = containingClass;
166     mRealSuperclass = superclass;
167     mRealSuperclassType = superclassType;
168     mAnnotations = annotations;
169     mShowAnnotations = AnnotationInstanceInfo.getShowAnnotationsIntersection(annotations);
170 
171     // after providing new methods and new superclass info,clear any cached
172     // lists of self + superclass methods, ctors, etc.
173     mSuperclassInit = false;
174     mConstructors = null;
175     mMethods = null;
176     mSelfMethods = null;
177     mFields = null;
178     mSelfFields = null;
179     mSelfAttributes = null;
180     mDeprecatedKnown = false;
181     mSuperclassesWithTypes = null;
182     mInterfacesWithTypes = null;
183     mAllInterfacesWithTypes = null;
184 
185     Collections.sort(mEnumConstants, FieldInfo.comparator);
186     Collections.sort(mInnerClasses, ClassInfo.comparator);
187   }
188 
init2()189   public void init2() {
190     // calling this here forces the AttrTagInfo objects to be linked to the AttribtueInfo
191     // objects
192     selfAttributes();
193   }
194 
init3(ArrayList<TypeInfo> types, ArrayList<ClassInfo> realInnerClasses)195   public void init3(ArrayList<TypeInfo> types, ArrayList<ClassInfo> realInnerClasses) {
196     mTypeParameters = types;
197     mRealInnerClasses = realInnerClasses;
198   }
199 
getRealInnerClasses()200   public ArrayList<ClassInfo> getRealInnerClasses() {
201     return mRealInnerClasses;
202   }
203 
getTypeParameters()204   public ArrayList<TypeInfo> getTypeParameters() {
205     return mTypeParameters;
206   }
207 
208   /**
209    * @return true if this class needs to be shown in api txt, based on the
210    * hidden/removed status of the class and the show level setting in doclava.
211    */
checkLevel()212   public boolean checkLevel() {
213     if (mCheckLevel == null) {
214       mCheckLevel = Doclava.checkLevel(mIsPublic, mIsProtected, mIsPackagePrivate, mIsPrivate,
215           isHiddenOrRemoved());
216     }
217 
218     return mCheckLevel;
219   }
220 
compareTo(Object that)221   public int compareTo(Object that) {
222     if (that instanceof ClassInfo) {
223       return mQualifiedName.compareTo(((ClassInfo) that).mQualifiedName);
224     } else {
225       return this.hashCode() - that.hashCode();
226     }
227   }
228 
229   @Override
parent()230   public ContainerInfo parent() {
231     return this;
232   }
233 
isPublic()234   public boolean isPublic() {
235     return mIsPublic;
236   }
237 
isProtected()238   public boolean isProtected() {
239     return mIsProtected;
240   }
241 
isPackagePrivate()242   public boolean isPackagePrivate() {
243     return mIsPackagePrivate;
244   }
245 
isPrivate()246   public boolean isPrivate() {
247     return mIsPrivate;
248   }
249 
isStatic()250   public boolean isStatic() {
251     return mIsStatic;
252   }
253 
isInterface()254   public boolean isInterface() {
255     return mIsInterface;
256   }
257 
isAbstract()258   public boolean isAbstract() {
259     return mIsAbstract;
260   }
261 
containingPackage()262   public PackageInfo containingPackage() {
263     return mContainingPackage;
264   }
265 
containingClass()266   public ClassInfo containingClass() {
267     return mContainingClass;
268   }
269 
isOrdinaryClass()270   public boolean isOrdinaryClass() {
271     return mIsOrdinaryClass;
272   }
273 
isException()274   public boolean isException() {
275     return mIsException;
276   }
277 
isError()278   public boolean isError() {
279     return mIsError;
280   }
281 
isEnum()282   public boolean isEnum() {
283     return mIsEnum;
284   }
285 
isAnnotation()286   public boolean isAnnotation() {
287     return mIsAnnotation;
288   }
289 
isFinal()290   public boolean isFinal() {
291     return mIsFinal;
292   }
293 
isEffectivelyFinal()294   public boolean isEffectivelyFinal() {
295     return mIsFinal || mApiCheckConstructors.isEmpty();
296   }
297 
isIncluded()298   public boolean isIncluded() {
299     return mIsIncluded;
300   }
301 
typeVariables()302   public HashSet<String> typeVariables() {
303     HashSet<String> result = TypeInfo.typeVariables(mTypeInfo.typeArguments());
304     ClassInfo cl = containingClass();
305     while (cl != null) {
306       ArrayList<TypeInfo> types = cl.asTypeInfo().typeArguments();
307       if (types != null) {
308         TypeInfo.typeVariables(types, result);
309       }
310       cl = cl.containingClass();
311     }
312     return result;
313   }
314 
getTypeParameter(String qualifiedTypeName)315   public TypeInfo getTypeParameter(String qualifiedTypeName) {
316       List<TypeInfo> parameters = mTypeInfo.typeArguments();
317       if (parameters == null) {
318           return null;
319       }
320       for (TypeInfo parameter : parameters) {
321           if (parameter.qualifiedTypeName().equals(qualifiedTypeName)) {
322               return parameter;
323           }
324       }
325       return null;
326   }
327 
328   /**
329    * List of only direct interface's classes, without worrying about type param mapping.
330    * This can't be lazy loaded, because its overloads depend on changing type parameters
331    * passed in from the callers.
332    */
justMyInterfacesWithTypes()333   private List<ClassTypePair> justMyInterfacesWithTypes() {
334     return justMyInterfacesWithTypes(Collections.<String, TypeInfo>emptyMap());
335   }
336 
337   /**
338    * List of only direct interface's classes and their parameterized types.
339    * This can't be lazy loaded, because of the passed in typeArgumentsMap.
340    */
justMyInterfacesWithTypes(Map<String, TypeInfo> typeArgumentsMap)341   private List<ClassTypePair> justMyInterfacesWithTypes(Map<String, TypeInfo> typeArgumentsMap) {
342     if (mRealInterfaces == null || mRealInterfaceTypes == null) {
343       return Collections.<ClassTypePair>emptyList();
344     }
345 
346     List<ClassTypePair> list = new ArrayList<ClassTypePair>();
347     for (int i = 0; i < mRealInterfaces.size(); i++) {
348       ClassInfo iface = mRealInterfaces.get(i);
349       TypeInfo type = mRealInterfaceTypes.get(i);
350       if (iface != null && type != null) {
351         type = type.getTypeWithArguments(typeArgumentsMap);
352         if (iface.checkLevel()) {
353           list.add(new ClassTypePair(iface, type));
354         } else {
355           // add the interface's interfaces
356           Map<String, TypeInfo> map = TypeInfo.getTypeArgumentMapping(iface.asTypeInfo(), type);
357           list.addAll(iface.justMyInterfacesWithTypes(map));
358         }
359       }
360     }
361     return list;
362   }
363 
364   /**
365    * List of only direct interface's classes, and any hidden superclass's direct interfaces
366    * between this class and the first visible superclass and those interface class's parameterized types.
367    */
interfacesWithTypes()368   private ArrayList<ClassTypePair> interfacesWithTypes() {
369     if (mInterfacesWithTypes == null) {
370       mInterfacesWithTypes = new ArrayList<ClassTypePair>();
371 
372       Iterator<ClassTypePair> itr = superClassesWithTypes().iterator();
373       // skip the first one, which is this class
374       itr.next();
375       while (itr.hasNext()) {
376         ClassTypePair ctp = itr.next();
377         if (ctp.classInfo().checkLevel()) {
378           break;
379         } else {
380           // fill mInterfacesWithTypes from the hidden superclass
381           mInterfacesWithTypes.addAll(
382               ctp.classInfo().justMyInterfacesWithTypes(ctp.getTypeArgumentMapping()));
383         }
384       }
385       mInterfacesWithTypes.addAll(
386           justMyInterfacesWithTypes());
387     }
388     return mInterfacesWithTypes;
389   }
390 
391   /**
392    * List of all interface's classes reachable in this class's inheritance hierarchy
393    * and those interface class's parameterized types.
394    */
allInterfacesWithTypes()395   private ArrayList<ClassTypePair> allInterfacesWithTypes() {
396     if (mAllInterfacesWithTypes == null) {
397         mAllInterfacesWithTypes = new ArrayList<ClassTypePair>();
398         Queue<ClassTypePair> toParse = new ArrayDeque<ClassTypePair>();
399         Set<String> visited = new HashSet<String>();
400 
401         Iterator<ClassTypePair> itr = superClassesWithTypes().iterator();
402         // skip the first one, which is this class
403         itr.next();
404         while (itr.hasNext()) {
405           ClassTypePair ctp = itr.next();
406           toParse.addAll(
407               ctp.classInfo().justMyInterfacesWithTypes(ctp.getTypeArgumentMapping()));
408         }
409         toParse.addAll(justMyInterfacesWithTypes());
410         while (!toParse.isEmpty()) {
411           ClassTypePair ctp = toParse.remove();
412           if (!visited.contains(ctp.typeInfo().fullName())) {
413             mAllInterfacesWithTypes.add(ctp);
414             visited.add(ctp.typeInfo().fullName());
415             toParse.addAll(ctp.classInfo().justMyInterfacesWithTypes(ctp.getTypeArgumentMapping()));
416           }
417         }
418     }
419     return mAllInterfacesWithTypes;
420   }
421 
422   /**
423    * A list of ClassTypePairs that contain all superclasses
424    * and their corresponding types. The types will have type parameters
425    * cascaded upwards so they match, if any classes along the way set them.
426    * The list includes the current class, and is an ascending order up the
427    * heirarchy tree.
428    * */
superClassesWithTypes()429   private ArrayList<ClassTypePair> superClassesWithTypes() {
430     if (mSuperclassesWithTypes == null) {
431       mSuperclassesWithTypes = new ArrayList<ClassTypePair>();
432 
433       ClassTypePair lastCtp = new ClassTypePair(this, this.asTypeInfo());
434       mSuperclassesWithTypes.add(lastCtp);
435 
436       Map<String, TypeInfo> typeArgumentsMap;
437       ClassInfo superclass = mRealSuperclass;
438       TypeInfo supertype = mRealSuperclassType;
439       TypeInfo nextType;
440       while (superclass != null && supertype != null) {
441         typeArgumentsMap = lastCtp.getTypeArgumentMapping();
442         lastCtp = new ClassTypePair(superclass, supertype.getTypeWithArguments(typeArgumentsMap));
443         mSuperclassesWithTypes.add(lastCtp);
444 
445         supertype = superclass.mRealSuperclassType;
446         superclass = superclass.mRealSuperclass;
447       }
448     }
449     return mSuperclassesWithTypes;
450   }
451 
gatherHiddenInterfaces(ClassInfo cl, HashSet<ClassInfo> interfaces)452   private static void gatherHiddenInterfaces(ClassInfo cl, HashSet<ClassInfo> interfaces) {
453     for (ClassInfo iface : cl.mRealInterfaces) {
454       if (iface.checkLevel()) {
455         interfaces.add(iface);
456       } else {
457         gatherHiddenInterfaces(iface, interfaces);
458       }
459     }
460   }
461 
interfaces()462   public ArrayList<ClassInfo> interfaces() {
463     if (mInterfaces == null) {
464       if (checkLevel()) {
465         HashSet<ClassInfo> interfaces = new HashSet<ClassInfo>();
466         ClassInfo superclass = mRealSuperclass;
467         while (superclass != null && !superclass.checkLevel()) {
468           gatherHiddenInterfaces(superclass, interfaces);
469           superclass = superclass.mRealSuperclass;
470         }
471         gatherHiddenInterfaces(this, interfaces);
472         mInterfaces = new ArrayList<ClassInfo>(interfaces);
473       } else {
474         // put something here in case someone uses it
475         mInterfaces = new ArrayList<ClassInfo>(mRealInterfaces);
476       }
477       Collections.sort(mInterfaces, ClassInfo.qualifiedComparator);
478     }
479     return mInterfaces;
480   }
481 
realInterfaces()482   public ArrayList<ClassInfo> realInterfaces() {
483     return mRealInterfaces;
484   }
485 
realInterfaceTypes()486   ArrayList<TypeInfo> realInterfaceTypes() {
487     return mRealInterfaceTypes;
488   }
489 
addInterfaceType(TypeInfo type)490   public void addInterfaceType(TypeInfo type) {
491       if (mRealInterfaceTypes == null) {
492           mRealInterfaceTypes = new ArrayList<TypeInfo>();
493       }
494 
495       mRealInterfaceTypes.add(type);
496   }
497 
name()498   public String name() {
499     return mName;
500   }
501 
nameParts()502   public String[] nameParts() {
503     return mNameParts;
504   }
505 
leafName()506   public String leafName() {
507     return mNameParts[mNameParts.length - 1];
508   }
509 
qualifiedName()510   public String qualifiedName() {
511     return mQualifiedName;
512   }
513 
qualifiedTypeName()514   public String qualifiedTypeName() {
515     return mQualifiedTypeName;
516   }
517 
isPrimitive()518   public boolean isPrimitive() {
519     return mIsPrimitive;
520   }
521 
allConstructors()522   public ArrayList<MethodInfo> allConstructors() {
523     return mAllConstructors;
524   }
525 
constructors()526   public ArrayList<MethodInfo> constructors() {
527     if (mConstructors == null) {
528       if (mAllConstructors == null) {
529         return new ArrayList<MethodInfo>();
530       }
531 
532       mConstructors = new ArrayList<MethodInfo>();
533       for (MethodInfo m : mAllConstructors) {
534         if (!m.isHiddenOrRemoved()) {
535             mConstructors.add(m);
536         }
537       }
538 
539       Collections.sort(mConstructors, MethodInfo.comparator);
540     }
541     return mConstructors;
542   }
543 
innerClasses()544   public ArrayList<ClassInfo> innerClasses() {
545     return mInnerClasses;
546   }
547 
inlineTags()548   public TagInfo[] inlineTags() {
549     return comment().tags();
550   }
551 
firstSentenceTags()552   public TagInfo[] firstSentenceTags() {
553     return comment().briefTags();
554   }
555 
setDeprecated(boolean deprecated)556   public void setDeprecated(boolean deprecated) {
557     mDeprecatedKnown = true;
558     mIsDeprecated = deprecated;
559   }
560 
isDeprecated()561   public boolean isDeprecated() {
562     if (!mDeprecatedKnown) {
563       boolean commentDeprecated = comment().isDeprecated();
564       boolean annotationDeprecated = false;
565       for (AnnotationInstanceInfo annotation : annotations()) {
566         if (annotation.type().qualifiedName().equals("java.lang.Deprecated")) {
567           annotationDeprecated = true;
568           break;
569         }
570       }
571 
572       // Check to see that the JavaDoc contains @deprecated AND the method is marked as @Deprecated.
573       // Otherwise, warn.
574       // Note: We only do this for "included" classes (i.e. those we have source code for); we do
575       // not have comments for classes from .class files but we do know whether a class is marked
576       // as @Deprecated.
577       if (isIncluded() && !isHiddenOrRemoved() && commentDeprecated != annotationDeprecated) {
578         Errors.error(Errors.DEPRECATION_MISMATCH, position(), "Class " + qualifiedName()
579             + ": @Deprecated annotation (" + (annotationDeprecated ? "" : "not ")
580             + "present) and @deprecated doc tag (" + (commentDeprecated ? "" : "not ")
581             + "present) do not match");
582       }
583 
584       mIsDeprecated = commentDeprecated | annotationDeprecated;
585       mDeprecatedKnown = true;
586     }
587     return mIsDeprecated;
588   }
589 
deprecatedTags()590   public TagInfo[] deprecatedTags() {
591     // Should we also do the interfaces?
592     return comment().deprecatedTags();
593   }
594 
methods()595   public ArrayList<MethodInfo> methods() {
596       if (mMethods == null) {
597           TreeMap<String, MethodInfo> all = new TreeMap<String, MethodInfo>();
598 
599           ArrayList<ClassInfo> interfaces = interfaces();
600           for (ClassInfo iface : interfaces) {
601             if (iface != null) {
602               for (MethodInfo method : iface.methods()) {
603                 all.put(method.getHashableName(), method);
604               }
605             }
606           }
607 
608           ClassInfo superclass = superclass();
609           if (superclass != null) {
610             for (MethodInfo method : superclass.methods()) {
611                 all.put(method.getHashableName(), method);
612             }
613           }
614 
615           for (MethodInfo method : selfMethods()) {
616               all.put(method.getHashableName(), method);
617           }
618 
619           mMethods = new ArrayList<MethodInfo>(all.values());
620           Collections.sort(mMethods, MethodInfo.comparator);
621       }
622     return mMethods;
623   }
624 
annotationElements()625   public ArrayList<MethodInfo> annotationElements() {
626     return mAnnotationElements;
627   }
628 
annotations()629   public ArrayList<AnnotationInstanceInfo> annotations() {
630     return mAnnotations;
631   }
632 
addFields(ClassInfo cl, TreeMap<String, FieldInfo> all)633   private static void addFields(ClassInfo cl, TreeMap<String, FieldInfo> all) {
634     for (FieldInfo field : cl.fields()) {
635         all.put(field.name(), field);
636     }
637   }
638 
fields()639   public ArrayList<FieldInfo> fields() {
640     if (mFields == null) {
641       TreeMap<String, FieldInfo> all = new TreeMap<String, FieldInfo>();
642 
643       for (ClassInfo iface : interfaces()) {
644         addFields(iface, all);
645       }
646 
647       ClassInfo superclass = superclass();
648       if (superclass != null) {
649         addFields(superclass, all);
650       }
651 
652       for (FieldInfo field : selfFields()) {
653         if (!field.isHiddenOrRemoved()) {
654             all.put(field.name(), field);
655         }
656       }
657 
658       for (FieldInfo enumConst : mEnumConstants) {
659         if (!enumConst.isHiddenOrRemoved()) {
660             all.put(enumConst.name(), enumConst);
661         }
662       }
663 
664       mFields = new ArrayList<FieldInfo>(all.values());
665     }
666     return mFields;
667   }
668 
gatherFields(ClassInfo owner, ClassInfo cl, HashMap<String, FieldInfo> fields)669   public void gatherFields(ClassInfo owner, ClassInfo cl, HashMap<String, FieldInfo> fields) {
670     for (FieldInfo f : cl.selfFields()) {
671       if (f.checkLevel()) {
672         fields.put(f.name(), f.cloneForClass(owner));
673       }
674     }
675   }
676 
selfFields()677   public ArrayList<FieldInfo> selfFields() {
678     if (mSelfFields == null) {
679         HashMap<String, FieldInfo> fields = new HashMap<String, FieldInfo>();
680       // our hidden parents
681       if (mRealSuperclass != null && !mRealSuperclass.checkLevel()) {
682         gatherFields(this, mRealSuperclass, fields);
683       }
684       for (ClassInfo iface : mRealInterfaces) {
685         if (!iface.checkLevel()) {
686           gatherFields(this, iface, fields);
687         }
688       }
689 
690       for (FieldInfo f : mAllSelfFields) {
691           if (!f.isHiddenOrRemoved()) {
692               fields.put(f.name(), f);
693           }
694       }
695 
696       mSelfFields = new ArrayList<FieldInfo>(fields.values());
697       Collections.sort(mSelfFields, FieldInfo.comparator);
698     }
699     return mSelfFields;
700   }
701 
allSelfFields()702   public ArrayList<FieldInfo> allSelfFields() {
703     return mAllSelfFields;
704   }
705 
gatherMethods(ClassInfo owner, ClassTypePair ctp, HashMap<String, MethodInfo> methods)706   private void gatherMethods(ClassInfo owner, ClassTypePair ctp, HashMap<String, MethodInfo> methods) {
707     for (MethodInfo m : ctp.classInfo().selfMethods()) {
708       if (m.checkLevel()) {
709         methods.put(m.name() + m.signature(), m.cloneForClass(owner, ctp.getTypeArgumentMapping()));
710       }
711     }
712   }
713 
selfMethods()714   public ArrayList<MethodInfo> selfMethods() {
715     if (mSelfMethods == null) {
716         HashMap<String, MethodInfo> methods = new HashMap<String, MethodInfo>();
717       // our hidden parents
718       for (ClassTypePair ctp : superClassesWithTypes()) {
719         // this class is included in this list, so skip it!
720         if (ctp.classInfo() != this) {
721           if (ctp.classInfo().checkLevel()) {
722             break;
723           }
724           gatherMethods(this, ctp, methods);
725         }
726       }
727       for (ClassTypePair ctp : justMyInterfacesWithTypes(Collections.<String, TypeInfo>emptyMap())) {
728         if (!ctp.classInfo().checkLevel()) {
729           gatherMethods(this, ctp, methods);
730         }
731       }
732       // mine
733       if (mAllSelfMethods != null) {
734         for (MethodInfo m : mAllSelfMethods) {
735           if (m.checkLevel()) {
736             methods.put(m.name() + m.signature(), m);
737           }
738         }
739       }
740 
741       for (MethodInfo mi : annotationElements()) {
742         if (!mi.isHiddenOrRemoved()) {
743           // add annotation element as a field
744           methods.put(mi.name() + mi.signature(), mi);
745         }
746       }
747 
748       // sort it
749       mSelfMethods = new ArrayList<MethodInfo>(methods.values());
750       Collections.sort(mSelfMethods, MethodInfo.comparator);
751     }
752     return mSelfMethods;
753   }
754 
allSelfMethods()755   public ArrayList<MethodInfo> allSelfMethods() {
756     return mAllSelfMethods;
757   }
758 
759   /**
760    * @param removedMethods the removed methods regardless of access levels.
761    */
setRemovedMethods(List<MethodInfo> removedMethods)762   public void setRemovedMethods(List<MethodInfo> removedMethods) {
763     Collections.sort(removedMethods, MethodInfo.comparator);
764     mRemovedMethods = Collections.unmodifiableList(removedMethods);
765   }
766 
setExhaustiveConstructors(List<MethodInfo> constructors)767   public void setExhaustiveConstructors(List<MethodInfo> constructors) {
768     mExhaustiveConstructors = constructors;
769   }
770 
setExhaustiveMethods(List<MethodInfo> methods)771   public void setExhaustiveMethods(List<MethodInfo> methods) {
772     mExhaustiveMethods = methods;
773   }
774 
setExhaustiveEnumConstants(List<FieldInfo> enumConstants)775   public void setExhaustiveEnumConstants(List<FieldInfo> enumConstants) {
776     mExhaustiveEnumConstants = enumConstants;
777   }
778 
setExhaustiveFields(List<FieldInfo> fields)779   public void setExhaustiveFields(List<FieldInfo> fields) {
780     mExhaustiveFields = fields;
781   }
782 
783   /**
784    * @return all methods that are marked as removed, regardless of access levels.
785    * The returned list is sorted and unmodifiable.
786    */
getRemovedMethods()787   public List<MethodInfo> getRemovedMethods() {
788     return mRemovedMethods;
789   }
790 
getExhaustiveConstructors()791   public List<MethodInfo> getExhaustiveConstructors() {
792     return mExhaustiveConstructors;
793   }
794 
getExhaustiveMethods()795   public List<MethodInfo> getExhaustiveMethods() {
796     return mExhaustiveMethods;
797   }
798 
getExhaustiveEnumConstants()799   public List<FieldInfo> getExhaustiveEnumConstants() {
800     return mExhaustiveEnumConstants;
801   }
802 
getExhaustiveFields()803   public List<FieldInfo> getExhaustiveFields() {
804     return mExhaustiveFields;
805   }
806 
addMethod(MethodInfo method)807   public void addMethod(MethodInfo method) {
808     mApiCheckMethods.put(method.getHashableName(), method);
809 
810     mAllSelfMethods.add(method);
811     mSelfMethods = null; // flush this, hopefully it hasn't been used yet.
812   }
813 
addAnnotationElement(MethodInfo method)814   public void addAnnotationElement(MethodInfo method) {
815     mAnnotationElements.add(method);
816   }
817 
818   // Called by PackageInfo when a ClassInfo is added to a package.
819   // This is needed because ApiCheck uses PackageInfo.addClass
820   // rather than using setContainingPackage to dispatch to the
821   // appropriate method. TODO: move ApiCheck away from addClass.
setPackage(PackageInfo pkg)822   void setPackage(PackageInfo pkg) {
823     mContainingPackage = pkg;
824   }
825 
setContainingPackage(PackageInfo pkg)826   public void setContainingPackage(PackageInfo pkg) {
827     mContainingPackage = pkg;
828 
829     if (mContainingPackage != null) {
830         if (mIsEnum) {
831             mContainingPackage.addEnum(this);
832         } else if (mIsInterface) {
833             mContainingPackage.addInterface(this);
834         } else {
835             mContainingPackage.addOrdinaryClass(this);
836         }
837     }
838   }
839 
selfAttributes()840   public ArrayList<AttributeInfo> selfAttributes() {
841     if (mSelfAttributes == null) {
842       TreeMap<FieldInfo, AttributeInfo> attrs = new TreeMap<FieldInfo, AttributeInfo>();
843 
844       // the ones in the class comment won't have any methods
845       for (AttrTagInfo tag : comment().attrTags()) {
846         FieldInfo field = tag.reference();
847         if (field != null) {
848           AttributeInfo attr = attrs.get(field);
849           if (attr == null) {
850             attr = new AttributeInfo(this, field);
851             attrs.put(field, attr);
852           }
853           tag.setAttribute(attr);
854         }
855       }
856 
857       // in the methods
858       for (MethodInfo m : selfMethods()) {
859         for (AttrTagInfo tag : m.comment().attrTags()) {
860           FieldInfo field = tag.reference();
861           if (field != null) {
862             AttributeInfo attr = attrs.get(field);
863             if (attr == null) {
864               attr = new AttributeInfo(this, field);
865               attrs.put(field, attr);
866             }
867             tag.setAttribute(attr);
868             attr.methods.add(m);
869           }
870         }
871       }
872 
873       // constructors too
874       for (MethodInfo m : constructors()) {
875         for (AttrTagInfo tag : m.comment().attrTags()) {
876           FieldInfo field = tag.reference();
877           if (field != null) {
878             AttributeInfo attr = attrs.get(field);
879             if (attr == null) {
880               attr = new AttributeInfo(this, field);
881               attrs.put(field, attr);
882             }
883             tag.setAttribute(attr);
884             attr.methods.add(m);
885           }
886         }
887       }
888 
889       mSelfAttributes = new ArrayList<AttributeInfo>(attrs.values());
890       Collections.sort(mSelfAttributes, AttributeInfo.comparator);
891     }
892     return mSelfAttributes;
893   }
894 
enumConstants()895   public ArrayList<FieldInfo> enumConstants() {
896     return mEnumConstants;
897   }
898 
superclass()899   public ClassInfo superclass() {
900     if (!mSuperclassInit) {
901       if (this.checkLevel()) {
902         // rearrange our little inheritance hierarchy, because we need to hide classes that
903         // don't pass checkLevel
904         ClassInfo superclass = mRealSuperclass;
905         while (superclass != null && !superclass.checkLevel()) {
906           superclass = superclass.mRealSuperclass;
907         }
908         mSuperclass = superclass;
909       } else {
910         mSuperclass = mRealSuperclass;
911       }
912     }
913     return mSuperclass;
914   }
915 
realSuperclass()916   public ClassInfo realSuperclass() {
917     return mRealSuperclass;
918   }
919 
920   /**
921    * always the real superclass, not the collapsed one we get through superclass(), also has the
922    * type parameter info if it's generic.
923    */
superclassType()924   public TypeInfo superclassType() {
925     return mRealSuperclassType;
926   }
927 
asTypeInfo()928   public TypeInfo asTypeInfo() {
929     return mTypeInfo;
930   }
931 
interfaceTypes()932   ArrayList<TypeInfo> interfaceTypes() {
933       ArrayList<TypeInfo> types = new ArrayList<TypeInfo>();
934       for (ClassInfo iface : interfaces()) {
935           types.add(iface.asTypeInfo());
936       }
937       return types;
938   }
939 
htmlPage()940   public String htmlPage() {
941     String s = containingPackage().name();
942     s = s.replace('.', '/');
943     s += '/';
944     s += name();
945     s += ".html";
946     s = Doclava.javadocDir + s;
947     return s;
948   }
949 
950   /** Even indirectly */
isDerivedFrom(ClassInfo cl)951   public boolean isDerivedFrom(ClassInfo cl) {
952     return isDerivedFrom(cl.qualifiedName());
953   }
954 
955   /** Even indirectly */
isDerivedFrom(String qualifiedName)956   public boolean isDerivedFrom(String qualifiedName) {
957     ClassInfo dad = this.superclass();
958     if (dad != null) {
959       if (dad.mQualifiedName.equals(qualifiedName)) {
960         return true;
961       } else {
962         if (dad.isDerivedFrom(qualifiedName)) {
963           return true;
964         }
965       }
966     }
967     for (ClassInfo iface : interfaces()) {
968       if (iface.mQualifiedName.equals(qualifiedName)) {
969         return true;
970       } else {
971         if (iface.isDerivedFrom(qualifiedName)) {
972           return true;
973         }
974       }
975     }
976     return false;
977   }
978 
makeKeywordEntries(List<KeywordEntry> keywords)979   public void makeKeywordEntries(List<KeywordEntry> keywords) {
980     if (!checkLevel()) {
981       return;
982     }
983 
984     String htmlPage = htmlPage();
985     String qualifiedName = qualifiedName();
986 
987     keywords.add(new KeywordEntry(name(), htmlPage, "class in " + containingPackage().name()));
988 
989     ArrayList<FieldInfo> fields = selfFields();
990     //ArrayList<FieldInfo> enumConstants = enumConstants();
991     ArrayList<MethodInfo> ctors = constructors();
992     ArrayList<MethodInfo> methods = selfMethods();
993 
994     // enum constants
995     for (FieldInfo field : enumConstants()) {
996       if (field.checkLevel()) {
997         keywords.add(new KeywordEntry(field.name(), htmlPage + "#" + field.anchor(),
998             "enum constant in " + qualifiedName));
999       }
1000     }
1001 
1002     // constants
1003     for (FieldInfo field : fields) {
1004       if (field.isConstant() && field.checkLevel()) {
1005         keywords.add(new KeywordEntry(field.name(), htmlPage + "#" + field.anchor(), "constant in "
1006             + qualifiedName));
1007       }
1008     }
1009 
1010     // fields
1011     for (FieldInfo field : fields) {
1012       if (!field.isConstant() && field.checkLevel()) {
1013         keywords.add(new KeywordEntry(field.name(), htmlPage + "#" + field.anchor(), "field in "
1014             + qualifiedName));
1015       }
1016     }
1017 
1018     // public constructors
1019     for (MethodInfo m : ctors) {
1020       if (m.isPublic() && m.checkLevel()) {
1021         keywords.add(new KeywordEntry(m.prettySignature(), htmlPage + "#" + m.anchor(),
1022             "constructor in " + qualifiedName));
1023       }
1024     }
1025 
1026     // protected constructors
1027     if (Doclava.checkLevel(Doclava.SHOW_PROTECTED)) {
1028       for (MethodInfo m : ctors) {
1029         if (m.isProtected() && m.checkLevel()) {
1030           keywords.add(new KeywordEntry(m.prettySignature(),
1031               htmlPage + "#" + m.anchor(), "constructor in " + qualifiedName));
1032         }
1033       }
1034     }
1035 
1036     // package private constructors
1037     if (Doclava.checkLevel(Doclava.SHOW_PACKAGE)) {
1038       for (MethodInfo m : ctors) {
1039         if (m.isPackagePrivate() && m.checkLevel()) {
1040           keywords.add(new KeywordEntry(m.prettySignature(),
1041               htmlPage + "#" + m.anchor(), "constructor in " + qualifiedName));
1042         }
1043       }
1044     }
1045 
1046     // private constructors
1047     if (Doclava.checkLevel(Doclava.SHOW_PRIVATE)) {
1048       for (MethodInfo m : ctors) {
1049         if (m.isPrivate() && m.checkLevel()) {
1050           keywords.add(new KeywordEntry(m.name() + m.prettySignature(),
1051               htmlPage + "#" + m.anchor(), "constructor in " + qualifiedName));
1052         }
1053       }
1054     }
1055 
1056     // public methods
1057     for (MethodInfo m : methods) {
1058       if (m.isPublic() && m.checkLevel()) {
1059         keywords.add(new KeywordEntry(m.name() + m.prettySignature(), htmlPage + "#" + m.anchor(),
1060             "method in " + qualifiedName));
1061       }
1062     }
1063 
1064     // protected methods
1065     if (Doclava.checkLevel(Doclava.SHOW_PROTECTED)) {
1066       for (MethodInfo m : methods) {
1067         if (m.isProtected() && m.checkLevel()) {
1068           keywords.add(new KeywordEntry(m.name() + m.prettySignature(),
1069               htmlPage + "#" + m.anchor(), "method in " + qualifiedName));
1070         }
1071       }
1072     }
1073 
1074     // package private methods
1075     if (Doclava.checkLevel(Doclava.SHOW_PACKAGE)) {
1076       for (MethodInfo m : methods) {
1077         if (m.isPackagePrivate() && m.checkLevel()) {
1078           keywords.add(new KeywordEntry(m.name() + m.prettySignature(),
1079               htmlPage + "#" + m.anchor(), "method in " + qualifiedName));
1080         }
1081       }
1082     }
1083 
1084     // private methods
1085     if (Doclava.checkLevel(Doclava.SHOW_PRIVATE)) {
1086       for (MethodInfo m : methods) {
1087         if (m.isPrivate() && m.checkLevel()) {
1088           keywords.add(new KeywordEntry(m.name() + m.prettySignature(),
1089               htmlPage + "#" + m.anchor(), "method in " + qualifiedName));
1090         }
1091       }
1092     }
1093   }
1094 
makeLink(Data data, String base)1095   public void makeLink(Data data, String base) {
1096     data.setValue(base + ".label", this.name());
1097     if (!this.isPrimitive() && this.isIncluded() && this.checkLevel()) {
1098       data.setValue(base + ".link", this.htmlPage());
1099     }
1100   }
1101 
makeLinkListHDF(Data data, String base, ClassInfo[] classes)1102   public static void makeLinkListHDF(Data data, String base, ClassInfo[] classes) {
1103     final int N = classes.length;
1104     for (int i = 0; i < N; i++) {
1105       ClassInfo cl = classes[i];
1106       if (cl.checkLevel()) {
1107         cl.asTypeInfo().makeHDF(data, base + "." + i);
1108       }
1109     }
1110   }
1111 
1112   /**
1113    * Used in lists of this class (packages, nested classes, known subclasses)
1114    */
makeShortDescrHDF(Data data, String base)1115   public void makeShortDescrHDF(Data data, String base) {
1116     mTypeInfo.makeHDF(data, base + ".type");
1117     data.setValue(base + ".kind", this.kind());
1118     TagInfo.makeHDF(data, base + ".shortDescr", this.firstSentenceTags());
1119     TagInfo.makeHDF(data, base + ".deprecated", deprecatedTags());
1120     data.setValue(base + ".since", getSince());
1121     if (isDeprecated()) {
1122       data.setValue(base + ".deprecatedsince", getDeprecatedSince());
1123     }
1124 
1125     ArrayList<AnnotationInstanceInfo> showAnnos = getShowAnnotationsIncludeOuters();
1126     AnnotationInstanceInfo.makeLinkListHDF(
1127       data,
1128       base + ".showAnnotations",
1129       showAnnos.toArray(new AnnotationInstanceInfo[showAnnos.size()]));
1130 
1131     setFederatedReferences(data, base);
1132   }
1133 
1134   /**
1135    * Turns into the main class page
1136    */
makeHDF(Data data)1137   public void makeHDF(Data data) {
1138     int i, j, n;
1139     String name = name();
1140     String qualified = qualifiedName();
1141     ArrayList<AttributeInfo> selfAttributes = selfAttributes();
1142     ArrayList<MethodInfo> methods = selfMethods();
1143     ArrayList<FieldInfo> fields = selfFields();
1144     ArrayList<FieldInfo> enumConstants = enumConstants();
1145     ArrayList<MethodInfo> ctors = constructors();
1146     ArrayList<ClassInfo> inners = innerClasses();
1147 
1148     // class name
1149     mTypeInfo.makeHDF(data, "class.type");
1150     mTypeInfo.makeQualifiedHDF(data, "class.qualifiedType");
1151     data.setValue("class.name", name);
1152     data.setValue("class.qualified", qualified);
1153     if (isProtected()) {
1154       data.setValue("class.scope", "protected");
1155     } else if (isPublic()) {
1156       data.setValue("class.scope", "public");
1157     }
1158     if (isStatic()) {
1159       data.setValue("class.static", "static");
1160     }
1161     if (isFinal()) {
1162       data.setValue("class.final", "final");
1163     }
1164     if (isAbstract() && !isInterface()) {
1165       data.setValue("class.abstract", "abstract");
1166     }
1167 
1168     int numAnnotationDocumentation = 0;
1169     for (AnnotationInstanceInfo aii : annotations()) {
1170       String annotationDocumentation = Doclava.getDocumentationStringForAnnotation(
1171           aii.type().qualifiedName());
1172       if (annotationDocumentation != null) {
1173         data.setValue("class.annotationdocumentation." + numAnnotationDocumentation + ".text",
1174             annotationDocumentation);
1175         numAnnotationDocumentation++;
1176       }
1177     }
1178 
1179     ArrayList<AnnotationInstanceInfo> showAnnos = getShowAnnotationsIncludeOuters();
1180     AnnotationInstanceInfo.makeLinkListHDF(
1181       data,
1182       "class.showAnnotations",
1183       showAnnos.toArray(new AnnotationInstanceInfo[showAnnos.size()]));
1184 
1185     // class info
1186     String kind = kind();
1187     if (kind != null) {
1188       data.setValue("class.kind", kind);
1189     }
1190     data.setValue("class.since", getSince());
1191     if (isDeprecated()) {
1192       data.setValue("class.deprecatedsince", getDeprecatedSince());
1193     }
1194     setFederatedReferences(data, "class");
1195 
1196     // the containing package -- note that this can be passed to type_link,
1197     // but it also contains the list of all of the packages
1198     containingPackage().makeClassLinkListHDF(data, "class.package");
1199 
1200     // inheritance hierarchy
1201     List<ClassTypePair> ctplist = superClassesWithTypes();
1202     n = ctplist.size();
1203     for (i = 0; i < ctplist.size(); i++) {
1204       // go in reverse order
1205       ClassTypePair ctp = ctplist.get(n - i - 1);
1206       if (ctp.classInfo().checkLevel()) {
1207         ctp.typeInfo().makeQualifiedHDF(data, "class.inheritance." + i + ".class");
1208         ctp.typeInfo().makeHDF(data, "class.inheritance." + i + ".short_class");
1209         j = 0;
1210         for (ClassTypePair t : ctp.classInfo().interfacesWithTypes()) {
1211           t.typeInfo().makeHDF(data, "class.inheritance." + i + ".interfaces." + j);
1212           j++;
1213         }
1214       }
1215     }
1216 
1217     // class description
1218     TagInfo.makeHDF(data, "class.descr", inlineTags());
1219     TagInfo.makeHDF(data, "class.descrAux", Doclava.auxSource.classAuxTags(this));
1220     TagInfo.makeHDF(data, "class.seeAlso", comment().seeTags());
1221     TagInfo.makeHDF(data, "class.deprecated", deprecatedTags());
1222 
1223     // known subclasses
1224     TreeMap<String, ClassInfo> direct = new TreeMap<String, ClassInfo>();
1225     TreeMap<String, ClassInfo> indirect = new TreeMap<String, ClassInfo>();
1226     ClassInfo[] all = Converter.rootClasses();
1227     for (ClassInfo cl : all) {
1228       if (cl.superclass() != null && cl.superclass().equals(this)) {
1229         direct.put(cl.name(), cl);
1230       } else if (cl.isDerivedFrom(this)) {
1231         indirect.put(cl.name(), cl);
1232       }
1233     }
1234     // direct
1235     i = 0;
1236     for (ClassInfo cl : direct.values()) {
1237       if (cl.checkLevel()) {
1238         cl.makeShortDescrHDF(data, "class.subclasses.direct." + i);
1239       }
1240       i++;
1241     }
1242     // indirect
1243     i = 0;
1244     for (ClassInfo cl : indirect.values()) {
1245       if (cl.checkLevel()) {
1246         cl.makeShortDescrHDF(data, "class.subclasses.indirect." + i);
1247       }
1248       i++;
1249     }
1250 
1251     // hide special cases
1252     if ("java.lang.Object".equals(qualified) || "java.io.Serializable".equals(qualified)) {
1253       data.setValue("class.subclasses.hidden", "1");
1254     } else {
1255       data.setValue("class.subclasses.hidden", "0");
1256     }
1257 
1258     // nested classes
1259     i = 0;
1260     for (ClassInfo inner : inners) {
1261       if (inner.checkLevel()) {
1262         inner.makeShortDescrHDF(data, "class.inners." + i);
1263       }
1264       i++;
1265     }
1266 
1267     // enum constants
1268     i = 0;
1269     for (FieldInfo field : enumConstants) {
1270       field.makeHDF(data, "class.enumConstants." + i);
1271       i++;
1272     }
1273 
1274     // constants
1275     i = 0;
1276     for (FieldInfo field : fields) {
1277       if (field.isConstant()) {
1278         field.makeHDF(data, "class.constants." + i);
1279         i++;
1280       }
1281     }
1282 
1283     // fields
1284     i = 0;
1285     for (FieldInfo field : fields) {
1286       if (!field.isConstant()) {
1287         field.makeHDF(data, "class.fields." + i);
1288         i++;
1289       }
1290     }
1291 
1292     // public constructors
1293     i = 0;
1294     for (MethodInfo ctor : ctors) {
1295       if (ctor.isPublic()) {
1296         ctor.makeHDF(data, "class.ctors.public." + i);
1297         i++;
1298       }
1299     }
1300 
1301     // protected constructors
1302     if (Doclava.checkLevel(Doclava.SHOW_PROTECTED)) {
1303       i = 0;
1304       for (MethodInfo ctor : ctors) {
1305         if (ctor.isProtected()) {
1306           ctor.makeHDF(data, "class.ctors.protected." + i);
1307           i++;
1308         }
1309       }
1310     }
1311 
1312     // package private constructors
1313     if (Doclava.checkLevel(Doclava.SHOW_PACKAGE)) {
1314       i = 0;
1315       for (MethodInfo ctor : ctors) {
1316         if (ctor.isPackagePrivate()) {
1317           ctor.makeHDF(data, "class.ctors.package." + i);
1318           i++;
1319         }
1320       }
1321     }
1322 
1323     // private constructors
1324     if (Doclava.checkLevel(Doclava.SHOW_PRIVATE)) {
1325       i = 0;
1326       for (MethodInfo ctor : ctors) {
1327         if (ctor.isPrivate()) {
1328           ctor.makeHDF(data, "class.ctors.private." + i);
1329           i++;
1330         }
1331       }
1332     }
1333 
1334     // public methods
1335     i = 0;
1336     for (MethodInfo method : methods) {
1337       if (method.isPublic()) {
1338         method.makeHDF(data, "class.methods.public." + i);
1339         i++;
1340       }
1341     }
1342 
1343     // protected methods
1344     if (Doclava.checkLevel(Doclava.SHOW_PROTECTED)) {
1345       i = 0;
1346       for (MethodInfo method : methods) {
1347         if (method.isProtected()) {
1348           method.makeHDF(data, "class.methods.protected." + i);
1349           i++;
1350         }
1351       }
1352     }
1353 
1354     // package private methods
1355     if (Doclava.checkLevel(Doclava.SHOW_PACKAGE)) {
1356       i = 0;
1357       for (MethodInfo method : methods) {
1358         if (method.isPackagePrivate()) {
1359           method.makeHDF(data, "class.methods.package." + i);
1360           i++;
1361         }
1362       }
1363     }
1364 
1365     // private methods
1366     if (Doclava.checkLevel(Doclava.SHOW_PRIVATE)) {
1367       i = 0;
1368       for (MethodInfo method : methods) {
1369         if (method.isPrivate()) {
1370           method.makeHDF(data, "class.methods.private." + i);
1371           i++;
1372         }
1373       }
1374     }
1375 
1376     // xml attributes
1377     i = 0;
1378     for (AttributeInfo attr : selfAttributes) {
1379       if (attr.checkLevel()) {
1380         attr.makeHDF(data, "class.attrs." + i);
1381         i++;
1382       }
1383     }
1384 
1385     // inherited methods
1386     Iterator<ClassTypePair> superclassesItr = superClassesWithTypes().iterator();
1387     superclassesItr.next(); // skip the first one, which is the current class
1388     ClassTypePair superCtp;
1389     i = 0;
1390     while (superclassesItr.hasNext()) {
1391       superCtp = superclassesItr.next();
1392       if (superCtp.classInfo().checkLevel()) {
1393         makeInheritedHDF(data, i, superCtp);
1394         i++;
1395       }
1396     }
1397     Iterator<ClassTypePair> interfacesItr = allInterfacesWithTypes().iterator();
1398     while (interfacesItr.hasNext()) {
1399       superCtp = interfacesItr.next();
1400       if (superCtp.classInfo().checkLevel()) {
1401         makeInheritedHDF(data, i, superCtp);
1402         i++;
1403       }
1404     }
1405   }
1406 
makeInheritedHDF(Data data, int index, ClassTypePair ctp)1407   private static void makeInheritedHDF(Data data, int index, ClassTypePair ctp) {
1408     int i;
1409 
1410     String base = "class.inherited." + index;
1411     data.setValue(base + ".qualified", ctp.classInfo().qualifiedName());
1412     if (ctp.classInfo().checkLevel()) {
1413       data.setValue(base + ".link", ctp.classInfo().htmlPage());
1414     }
1415     String kind = ctp.classInfo().kind();
1416     if (kind != null) {
1417       data.setValue(base + ".kind", kind);
1418     }
1419 
1420     if (ctp.classInfo().mIsIncluded) {
1421       data.setValue(base + ".included", "true");
1422     } else {
1423       Doclava.federationTagger.tagAll(new ClassInfo[] {ctp.classInfo()});
1424       if (!ctp.classInfo().getFederatedReferences().isEmpty()) {
1425         FederatedSite site = ctp.classInfo().getFederatedReferences().iterator().next();
1426         data.setValue(base + ".link", site.linkFor(ctp.classInfo().htmlPage()));
1427         data.setValue(base + ".federated", site.name());
1428       }
1429     }
1430 
1431     // xml attributes
1432     i = 0;
1433     for (AttributeInfo attr : ctp.classInfo().selfAttributes()) {
1434       attr.makeHDF(data, base + ".attrs." + i);
1435       i++;
1436     }
1437 
1438     // methods
1439     i = 0;
1440     for (MethodInfo method : ctp.classInfo().selfMethods()) {
1441       method.makeHDF(data, base + ".methods." + i, ctp.getTypeArgumentMapping());
1442       i++;
1443     }
1444 
1445     // fields
1446     i = 0;
1447     for (FieldInfo field : ctp.classInfo().selfFields()) {
1448       if (!field.isConstant()) {
1449         field.makeHDF(data, base + ".fields." + i);
1450         i++;
1451       }
1452     }
1453 
1454     // constants
1455     i = 0;
1456     for (FieldInfo field : ctp.classInfo().selfFields()) {
1457       if (field.isConstant()) {
1458         field.makeHDF(data, base + ".constants." + i);
1459         i++;
1460       }
1461     }
1462   }
1463 
1464   @Override
isHidden()1465   public boolean isHidden() {
1466     if (mHidden == null) {
1467       mHidden = isHiddenImpl();
1468     }
1469 
1470     return mHidden;
1471   }
1472 
1473   /**
1474    * @return true if the containing package has @hide comment, or an ancestor
1475    * class of this class is hidden, or this class has @hide comment.
1476    */
isHiddenImpl()1477   public boolean isHiddenImpl() {
1478     ClassInfo cl = this;
1479     while (cl != null) {
1480       if (cl.hasShowAnnotation()) {
1481         return false;
1482       }
1483       PackageInfo pkg = cl.containingPackage();
1484       if (pkg != null && pkg.hasHideComment()) {
1485         return true;
1486       }
1487       if (cl.comment().isHidden()) {
1488         return true;
1489       }
1490       cl = cl.containingClass();
1491     }
1492     return false;
1493   }
1494 
1495   @Override
isRemoved()1496   public boolean isRemoved() {
1497     if (mRemoved == null) {
1498       mRemoved = isRemovedImpl();
1499     }
1500 
1501     return mRemoved;
1502   }
1503 
1504   /**
1505    * @return true if the containing package has @removed comment, or an ancestor
1506    * class of this class is removed, or this class has @removed comment.
1507    */
isRemovedImpl()1508   public boolean isRemovedImpl() {
1509     ClassInfo cl = this;
1510     while (cl != null) {
1511       if (cl.hasShowAnnotation()) {
1512         return false;
1513       }
1514       PackageInfo pkg = cl.containingPackage();
1515       if (pkg != null && pkg.hasRemovedComment()) {
1516         return true;
1517       }
1518       if (cl.comment().isRemoved()) {
1519         return true;
1520       }
1521       cl = cl.containingClass();
1522     }
1523     return false;
1524   }
1525 
1526   @Override
isHiddenOrRemoved()1527   public boolean isHiddenOrRemoved() {
1528     return isHidden() || isRemoved();
1529   }
1530 
hasShowAnnotation()1531   public boolean hasShowAnnotation() {
1532     return mShowAnnotations != null && mShowAnnotations.size() > 0;
1533   }
1534 
showAnnotations()1535   public ArrayList<AnnotationInstanceInfo> showAnnotations() {
1536     return mShowAnnotations;
1537   }
1538 
getShowAnnotationsIncludeOuters()1539   public ArrayList<AnnotationInstanceInfo> getShowAnnotationsIncludeOuters() {
1540     ArrayList<AnnotationInstanceInfo> allAnnotations = new ArrayList<AnnotationInstanceInfo>();
1541     ClassInfo cl = this;
1542     while (cl != null) {
1543       if (cl.showAnnotations() != null) {
1544         // Don't allow duplicates into the merged list
1545         for (AnnotationInstanceInfo newAii : cl.showAnnotations()) {
1546           boolean addIt = true;
1547           for (AnnotationInstanceInfo existingAii : allAnnotations) {
1548             if (existingAii.type().name() == newAii.type().name()) {
1549               addIt = false;
1550               break;
1551             }
1552           }
1553           if (addIt) {
1554             allAnnotations.add(newAii);
1555           }
1556         }
1557       }
1558       cl = cl.containingClass();
1559     }
1560     return allAnnotations;
1561   }
1562 
matchMethod(ArrayList<MethodInfo> methods, String name, String[] params, String[] dimensions, boolean varargs)1563   private MethodInfo matchMethod(ArrayList<MethodInfo> methods, String name, String[] params,
1564       String[] dimensions, boolean varargs) {
1565     for (MethodInfo method : methods) {
1566       if (method.name().equals(name)) {
1567         if (params == null) {
1568           return method;
1569         } else {
1570           if (method.matchesParams(params, dimensions, varargs)) {
1571             return method;
1572           }
1573         }
1574       }
1575     }
1576     return null;
1577   }
1578 
findMethod(String name, String[] params, String[] dimensions, boolean varargs)1579   public MethodInfo findMethod(String name, String[] params, String[] dimensions, boolean varargs) {
1580     // first look on our class, and our superclasses
1581 
1582     // for methods
1583     MethodInfo rv;
1584     rv = matchMethod(methods(), name, params, dimensions, varargs);
1585 
1586     if (rv != null) {
1587       return rv;
1588     }
1589 
1590     // for constructors
1591     rv = matchMethod(constructors(), name, params, dimensions, varargs);
1592     if (rv != null) {
1593       return rv;
1594     }
1595 
1596     // then recursively look at our containing class
1597     ClassInfo containing = containingClass();
1598     if (containing != null) {
1599       return containing.findMethod(name, params, dimensions, varargs);
1600     }
1601 
1602     return null;
1603   }
1604 
supportsMethod(MethodInfo method)1605   public boolean supportsMethod(MethodInfo method) {
1606     for (MethodInfo m : methods()) {
1607       if (m.getHashableName().equals(method.getHashableName())) {
1608         return true;
1609       }
1610     }
1611     return false;
1612   }
1613 
searchInnerClasses(String[] nameParts, int index)1614   private ClassInfo searchInnerClasses(String[] nameParts, int index) {
1615     String part = nameParts[index];
1616 
1617     ArrayList<ClassInfo> inners = mInnerClasses;
1618     for (ClassInfo in : inners) {
1619       String[] innerParts = in.nameParts();
1620       if (part.equals(innerParts[innerParts.length - 1])) {
1621         if (index == nameParts.length - 1) {
1622           return in;
1623         } else {
1624           return in.searchInnerClasses(nameParts, index + 1);
1625         }
1626       }
1627     }
1628     return null;
1629   }
1630 
extendedFindClass(String className)1631   public ClassInfo extendedFindClass(String className) {
1632     // ClassDoc.findClass has this bug that we're working around here:
1633     // If you have a class PackageManager with an inner class PackageInfo
1634     // and you call it with "PackageInfo" it doesn't find it.
1635     return searchInnerClasses(className.split("\\."), 0);
1636   }
1637 
findClass(String className)1638   public ClassInfo findClass(String className) {
1639     return Converter.obtainClass(mClass.findClass(className));
1640   }
1641 
findInnerClass(String className)1642   public ClassInfo findInnerClass(String className) {
1643     // ClassDoc.findClass won't find inner classes. To deal with that,
1644     // we try what they gave us first, but if that didn't work, then
1645     // we see if there are any periods in className, and start searching
1646     // from there.
1647     String[] nodes = className.split("\\.");
1648     ClassDoc cl = mClass;
1649 
1650     int N = nodes.length;
1651     for (int i = 0; i < N; ++i) {
1652       final String n = nodes[i];
1653       if (n.isEmpty() && i == 0) {
1654         // We skip over an empty classname component if it's at location 0. This is
1655         // to deal with names like ".Inner". java7 will return a bogus ClassInfo when
1656         // we call "findClass("") and the next iteration of the loop will throw a
1657         // runtime exception.
1658         continue;
1659       }
1660 
1661       cl = cl.findClass(n);
1662       if (cl == null) {
1663         return null;
1664       }
1665     }
1666 
1667     return Converter.obtainClass(cl);
1668   }
1669 
findField(String name)1670   public FieldInfo findField(String name) {
1671     // first look on our class, and our superclasses
1672     for (FieldInfo f : fields()) {
1673       if (f.name().equals(name)) {
1674         return f;
1675       }
1676     }
1677 
1678     // then look at our enum constants (these are really fields, maybe
1679     // they should be mixed into fields(). not sure)
1680     for (FieldInfo f : enumConstants()) {
1681       if (f.name().equals(name)) {
1682         return f;
1683       }
1684     }
1685 
1686     // then recursively look at our containing class
1687     ClassInfo containing = containingClass();
1688     if (containing != null) {
1689       return containing.findField(name);
1690     }
1691 
1692     return null;
1693   }
1694 
sortByName(ClassInfo[] classes)1695   public static ClassInfo[] sortByName(ClassInfo[] classes) {
1696     int i;
1697     Sorter[] sorted = new Sorter[classes.length];
1698     for (i = 0; i < sorted.length; i++) {
1699       ClassInfo cl = classes[i];
1700       sorted[i] = new Sorter(cl.name(), cl);
1701     }
1702 
1703     Arrays.sort(sorted);
1704 
1705     ClassInfo[] rv = new ClassInfo[classes.length];
1706     for (i = 0; i < rv.length; i++) {
1707       rv[i] = (ClassInfo) sorted[i].data;
1708     }
1709 
1710     return rv;
1711   }
1712 
equals(ClassInfo that)1713   public boolean equals(ClassInfo that) {
1714     if (that != null) {
1715       return this.qualifiedName().equals(that.qualifiedName());
1716     } else {
1717       return false;
1718     }
1719   }
1720 
setNonWrittenConstructors(ArrayList<MethodInfo> nonWritten)1721   public void setNonWrittenConstructors(ArrayList<MethodInfo> nonWritten) {
1722     mNonWrittenConstructors = nonWritten;
1723   }
1724 
getNonWrittenConstructors()1725   public ArrayList<MethodInfo> getNonWrittenConstructors() {
1726     return mNonWrittenConstructors;
1727   }
1728 
kind()1729   public String kind() {
1730     if (isOrdinaryClass()) {
1731       return "class";
1732     } else if (isInterface()) {
1733       return "interface";
1734     } else if (isEnum()) {
1735       return "enum";
1736     } else if (isError()) {
1737       return "class";
1738     } else if (isException()) {
1739       return "class";
1740     } else if (isAnnotation()) {
1741       return "@interface";
1742     }
1743     return null;
1744   }
1745 
scope()1746   public String scope() {
1747     if (isPublic()) {
1748       return "public";
1749     } else if (isProtected()) {
1750       return "protected";
1751     } else if (isPackagePrivate()) {
1752       return "";
1753     } else if (isPrivate()) {
1754       return "private";
1755     } else {
1756       throw new RuntimeException("invalid scope for object " + this);
1757     }
1758   }
1759 
setHiddenMethods(ArrayList<MethodInfo> mInfo)1760   public void setHiddenMethods(ArrayList<MethodInfo> mInfo) {
1761     mHiddenMethods = mInfo;
1762   }
1763 
getHiddenMethods()1764   public ArrayList<MethodInfo> getHiddenMethods() {
1765     return mHiddenMethods;
1766   }
1767 
1768   @Override
toString()1769   public String toString() {
1770     return this.qualifiedName();
1771   }
1772 
setReasonIncluded(String reason)1773   public void setReasonIncluded(String reason) {
1774     mReasonIncluded = reason;
1775   }
1776 
getReasonIncluded()1777   public String getReasonIncluded() {
1778     return mReasonIncluded;
1779   }
1780 
1781   private ClassDoc mClass;
1782 
1783   // ctor
1784   private boolean mIsPublic;
1785   private boolean mIsProtected;
1786   private boolean mIsPackagePrivate;
1787   private boolean mIsPrivate;
1788   private boolean mIsStatic;
1789   private boolean mIsInterface;
1790   private boolean mIsAbstract;
1791   private boolean mIsOrdinaryClass;
1792   private boolean mIsException;
1793   private boolean mIsError;
1794   private boolean mIsEnum;
1795   private boolean mIsAnnotation;
1796   private boolean mIsFinal;
1797   private boolean mIsIncluded;
1798   private String mName;
1799   private String mQualifiedName;
1800   private String mQualifiedTypeName;
1801   private boolean mIsPrimitive;
1802   private TypeInfo mTypeInfo;
1803   private String[] mNameParts;
1804 
1805   // init
1806   private ArrayList<ClassInfo> mRealInterfaces = new ArrayList<ClassInfo>();
1807   private ArrayList<ClassInfo> mInterfaces;
1808   private ArrayList<TypeInfo> mRealInterfaceTypes;
1809   private ArrayList<ClassInfo> mInnerClasses;
1810   // mAllConstructors will not contain *all* constructors. Only the constructors that pass
1811   // checkLevel. @see {@link Converter#convertMethods(ConstructorDoc[])}
1812   private ArrayList<MethodInfo> mAllConstructors = new ArrayList<MethodInfo>();
1813   // mAllSelfMethods will not contain *all* self methods. Only the methods that pass
1814   // checkLevel. @see {@link Converter#convertMethods(MethodDoc[])}
1815   private ArrayList<MethodInfo> mAllSelfMethods = new ArrayList<MethodInfo>();
1816   private ArrayList<MethodInfo> mAnnotationElements = new ArrayList<MethodInfo>(); // if this class is an annotation
1817   private ArrayList<FieldInfo> mAllSelfFields = new ArrayList<FieldInfo>();
1818   private ArrayList<FieldInfo> mEnumConstants = new ArrayList<FieldInfo>();
1819   private PackageInfo mContainingPackage;
1820   private ClassInfo mContainingClass;
1821   private ClassInfo mRealSuperclass;
1822   private TypeInfo mRealSuperclassType;
1823   private ClassInfo mSuperclass;
1824   private ArrayList<AnnotationInstanceInfo> mAnnotations;
1825   private ArrayList<AnnotationInstanceInfo> mShowAnnotations;
1826   private boolean mSuperclassInit;
1827   private boolean mDeprecatedKnown;
1828 
1829   // lazy
1830   private ArrayList<ClassTypePair> mSuperclassesWithTypes;
1831   private ArrayList<ClassTypePair> mInterfacesWithTypes;
1832   private ArrayList<ClassTypePair> mAllInterfacesWithTypes;
1833   private ArrayList<MethodInfo> mConstructors;
1834   private ArrayList<ClassInfo> mRealInnerClasses;
1835   private ArrayList<MethodInfo> mSelfMethods;
1836   private ArrayList<FieldInfo> mSelfFields;
1837   private ArrayList<AttributeInfo> mSelfAttributes;
1838   private ArrayList<MethodInfo> mMethods;
1839   private ArrayList<FieldInfo> mFields;
1840   private ArrayList<TypeInfo> mTypeParameters;
1841   private ArrayList<MethodInfo> mHiddenMethods;
1842   private Boolean mHidden = null;
1843   private Boolean mRemoved = null;
1844   private Boolean mCheckLevel = null;
1845   private String mReasonIncluded;
1846   private ArrayList<MethodInfo> mNonWrittenConstructors;
1847   private boolean mIsDeprecated;
1848 
1849   // TODO: Temporary members from apicheck migration.
1850   private HashMap<String, MethodInfo> mApiCheckConstructors = new HashMap<String, MethodInfo>();
1851   private HashMap<String, MethodInfo> mApiCheckMethods = new HashMap<String, MethodInfo>();
1852   private HashMap<String, FieldInfo> mApiCheckFields = new HashMap<String, FieldInfo>();
1853   private HashMap<String, FieldInfo> mApiCheckEnumConstants = new HashMap<String, FieldInfo>();
1854 
1855   // Resolutions
1856   private ArrayList<Resolution> mResolutions;
1857 
1858   private List<MethodInfo> mRemovedMethods; // immutable after you set its value.
1859 
1860   private List<MethodInfo> mExhaustiveConstructors; // immutable after you set its value.
1861   private List<MethodInfo> mExhaustiveMethods; // immutable after you set its value.
1862   private List<FieldInfo> mExhaustiveEnumConstants; // immutable after you set its value.
1863   private List<FieldInfo> mExhaustiveFields; // immutable after you set its value.
1864 
1865   /**
1866    * Returns true if {@code cl} implements the interface {@code iface} either by either being that
1867    * interface, implementing that interface or extending a type that implements the interface.
1868    */
implementsInterface(String iface)1869   public boolean implementsInterface(String iface) {
1870     if (qualifiedName().equals(iface)) {
1871       return true;
1872     }
1873     for (ClassInfo clImplements : realInterfaces()) {
1874       if (clImplements.implementsInterface(iface)) {
1875         return true;
1876       }
1877     }
1878     if (mSuperclass != null && mSuperclass.implementsInterface(iface)) {
1879       return true;
1880     }
1881     return false;
1882   }
1883 
1884   /**
1885    * Returns true if {@code this} extends the class {@code ext}.
1886    */
extendsClass(String cl)1887   public boolean extendsClass(String cl) {
1888     if (qualifiedName().equals(cl)) {
1889       return true;
1890     }
1891     if (mSuperclass != null && mSuperclass.extendsClass(cl)) {
1892       return true;
1893     }
1894     return false;
1895   }
1896 
1897   /**
1898    * Returns true if {@code this} is assignable to cl
1899    */
isAssignableTo(String cl)1900   public boolean isAssignableTo(String cl) {
1901     return implementsInterface(cl) || extendsClass(cl);
1902   }
1903 
addInterface(ClassInfo iface)1904   public void addInterface(ClassInfo iface) {
1905     mRealInterfaces.add(iface);
1906   }
1907 
addConstructor(MethodInfo ctor)1908   public void addConstructor(MethodInfo ctor) {
1909     mApiCheckConstructors.put(ctor.getHashableName(), ctor);
1910 
1911     mAllConstructors.add(ctor);
1912     mConstructors = null; // flush this, hopefully it hasn't been used yet.
1913   }
1914 
addField(FieldInfo field)1915   public void addField(FieldInfo field) {
1916     mApiCheckFields.put(field.name(), field);
1917 
1918     mAllSelfFields.add(field);
1919 
1920     mSelfFields = null; // flush this, hopefully it hasn't been used yet.
1921   }
1922 
addEnumConstant(FieldInfo field)1923   public void addEnumConstant(FieldInfo field) {
1924     mApiCheckEnumConstants.put(field.name(), field);
1925 
1926     mEnumConstants.add(field);
1927   }
1928 
setSuperClass(ClassInfo superclass)1929   public void setSuperClass(ClassInfo superclass) {
1930     mRealSuperclass = superclass;
1931     mSuperclass = superclass;
1932   }
1933 
allConstructorsMap()1934   public Map<String, MethodInfo> allConstructorsMap() {
1935     return mApiCheckConstructors;
1936   }
1937 
allFields()1938   public Map<String, FieldInfo> allFields() {
1939     return mApiCheckFields;
1940   }
1941 
allEnums()1942   public Map<String, FieldInfo> allEnums() {
1943     return mApiCheckEnumConstants;
1944   }
1945 
1946   /**
1947    * Returns all methods defined directly in this class. For a list of all
1948    * methods supported by this class, see {@link #methods()}.
1949    */
allMethods()1950   public Map<String, MethodInfo> allMethods() {
1951     return mApiCheckMethods;
1952   }
1953 
1954   /**
1955    * Returns the class hierarchy for this class, starting with this class.
1956    */
hierarchy()1957   public Iterable<ClassInfo> hierarchy() {
1958     List<ClassInfo> result = new ArrayList<ClassInfo>(4);
1959     for (ClassInfo c = this; c != null; c = c.mSuperclass) {
1960       result.add(c);
1961     }
1962     return result;
1963   }
1964 
superclassName()1965   public String superclassName() {
1966     if (mSuperclass == null) {
1967       if (mQualifiedName.equals("java.lang.Object")) {
1968         return null;
1969       }
1970       throw new UnsupportedOperationException("Superclass not set for " + qualifiedName());
1971     }
1972     return mSuperclass.mQualifiedName;
1973   }
1974 
setAnnotations(ArrayList<AnnotationInstanceInfo> annotations)1975   public void setAnnotations(ArrayList<AnnotationInstanceInfo> annotations) {
1976     mAnnotations = annotations;
1977   }
1978 
isConsistent(ClassInfo cl)1979   public boolean isConsistent(ClassInfo cl) {
1980     return isConsistent(cl, null, null);
1981   }
1982 
isConsistent(ClassInfo cl, List<MethodInfo> newCtors, List<MethodInfo> newMethods)1983   public boolean isConsistent(ClassInfo cl, List<MethodInfo> newCtors, List<MethodInfo> newMethods) {
1984     boolean consistent = true;
1985     boolean diffMode = (newCtors != null) && (newMethods != null);
1986 
1987     if (isInterface() != cl.isInterface()) {
1988       Errors.error(Errors.CHANGED_CLASS, cl.position(), "Class " + cl.qualifiedName()
1989           + " changed class/interface declaration");
1990       consistent = false;
1991     }
1992     for (ClassInfo iface : mRealInterfaces) {
1993       if (!cl.implementsInterface(iface.mQualifiedName)) {
1994         Errors.error(Errors.REMOVED_INTERFACE, cl.position(), "Class " + qualifiedName()
1995             + " no longer implements " + iface);
1996       }
1997     }
1998     for (ClassInfo iface : cl.mRealInterfaces) {
1999       if (!implementsInterface(iface.mQualifiedName)) {
2000         Errors.error(Errors.ADDED_INTERFACE, cl.position(), "Added interface " + iface
2001             + " to class " + qualifiedName());
2002         consistent = false;
2003       }
2004     }
2005 
2006     for (MethodInfo mInfo : mApiCheckMethods.values()) {
2007       if (cl.mApiCheckMethods.containsKey(mInfo.getHashableName())) {
2008         if (!mInfo.isConsistent(cl.mApiCheckMethods.get(mInfo.getHashableName()))) {
2009           consistent = false;
2010         }
2011       } else {
2012         /*
2013          * This class formerly provided this method directly, and now does not. Check our ancestry
2014          * to see if there's an inherited version that still fulfills the API requirement.
2015          */
2016         MethodInfo mi = ClassInfo.overriddenMethod(mInfo, cl);
2017         if (mi == null) {
2018           mi = ClassInfo.interfaceMethod(mInfo, cl);
2019         }
2020         if (mi == null) {
2021           if (mInfo.isDeprecated()) {
2022             Errors.error(Errors.REMOVED_DEPRECATED_METHOD, mInfo.position(),
2023                 "Removed deprecated public method " + mInfo.prettyQualifiedSignature());
2024           } else {
2025             Errors.error(Errors.REMOVED_METHOD, mInfo.position(),
2026                 "Removed public method " + mInfo.prettyQualifiedSignature());
2027           }
2028           consistent = false;
2029         }
2030       }
2031     }
2032     for (MethodInfo mInfo : cl.mApiCheckMethods.values()) {
2033       if (!mApiCheckMethods.containsKey(mInfo.getHashableName())) {
2034         /*
2035          * Similarly to the above, do not fail if this "new" method is really an override of an
2036          * existing superclass method.
2037          * But we should fail if this is overriding an abstract method, because method's
2038          * abstractness affects how users use it. See also Stubs.methodIsOverride().
2039          */
2040         MethodInfo mi = ClassInfo.overriddenMethod(mInfo, this);
2041         if (mi == null ||
2042             mi.isAbstract() != mInfo.isAbstract()) {
2043           Errors.error(Errors.ADDED_METHOD, mInfo.position(), "Added public method "
2044               + mInfo.prettyQualifiedSignature());
2045           if (diffMode) {
2046             newMethods.add(mInfo);
2047           }
2048           consistent = false;
2049         }
2050       }
2051     }
2052     if (diffMode) {
2053       Collections.sort(newMethods, MethodInfo.comparator);
2054     }
2055 
2056     for (MethodInfo mInfo : mApiCheckConstructors.values()) {
2057       if (cl.mApiCheckConstructors.containsKey(mInfo.getHashableName())) {
2058         if (!mInfo.isConsistent(cl.mApiCheckConstructors.get(mInfo.getHashableName()))) {
2059           consistent = false;
2060         }
2061       } else {
2062         if (mInfo.isDeprecated()) {
2063           Errors.error(Errors.REMOVED_DEPRECATED_METHOD, mInfo.position(),
2064               "Removed deprecated public constructor " + mInfo.prettyQualifiedSignature());
2065         } else {
2066           Errors.error(Errors.REMOVED_METHOD, mInfo.position(),
2067               "Removed public constructor " + mInfo.prettyQualifiedSignature());
2068         }
2069         consistent = false;
2070       }
2071     }
2072     for (MethodInfo mInfo : cl.mApiCheckConstructors.values()) {
2073       if (!mApiCheckConstructors.containsKey(mInfo.getHashableName())) {
2074         Errors.error(Errors.ADDED_METHOD, mInfo.position(), "Added public constructor "
2075             + mInfo.prettyQualifiedSignature());
2076         if (diffMode) {
2077           newCtors.add(mInfo);
2078         }
2079         consistent = false;
2080       }
2081     }
2082     if (diffMode) {
2083       Collections.sort(newCtors, MethodInfo.comparator);
2084     }
2085 
2086     for (FieldInfo mInfo : mApiCheckFields.values()) {
2087       if (cl.mApiCheckFields.containsKey(mInfo.name())) {
2088         if (!mInfo.isConsistent(cl.mApiCheckFields.get(mInfo.name()))) {
2089           consistent = false;
2090         }
2091       } else {
2092         if (mInfo.isDeprecated()) {
2093           Errors.error(Errors.REMOVED_DEPRECATED_FIELD, mInfo.position(),
2094               "Removed deprecated field " + mInfo.qualifiedName());
2095         } else {
2096           Errors.error(Errors.REMOVED_FIELD, mInfo.position(),
2097               "Removed field " + mInfo.qualifiedName());
2098         }
2099         consistent = false;
2100       }
2101     }
2102     for (FieldInfo mInfo : cl.mApiCheckFields.values()) {
2103       if (!mApiCheckFields.containsKey(mInfo.name())) {
2104         Errors.error(Errors.ADDED_FIELD, mInfo.position(), "Added public field "
2105             + mInfo.qualifiedName());
2106         consistent = false;
2107       }
2108     }
2109 
2110     for (FieldInfo info : mApiCheckEnumConstants.values()) {
2111       if (cl.mApiCheckEnumConstants.containsKey(info.name())) {
2112         if (!info.isConsistent(cl.mApiCheckEnumConstants.get(info.name()))) {
2113           consistent = false;
2114         }
2115       } else {
2116         if (info.isDeprecated()) {
2117           Errors.error(Errors.REMOVED_DEPRECATED_FIELD, info.position(),
2118               "Removed deprecated enum constant " + info.qualifiedName());
2119         } else {
2120           Errors.error(Errors.REMOVED_FIELD, info.position(),
2121               "Removed enum constant " + info.qualifiedName());
2122         }
2123         consistent = false;
2124       }
2125     }
2126     for (FieldInfo info : cl.mApiCheckEnumConstants.values()) {
2127       if (!mApiCheckEnumConstants.containsKey(info.name())) {
2128         Errors.error(Errors.ADDED_FIELD, info.position(), "Added enum constant "
2129             + info.qualifiedName());
2130         consistent = false;
2131       }
2132     }
2133 
2134     if (mIsAbstract != cl.mIsAbstract) {
2135       consistent = false;
2136       Errors.error(Errors.CHANGED_ABSTRACT, cl.position(), "Class " + cl.qualifiedName()
2137           + " changed abstract qualifier");
2138     }
2139 
2140     if (!mIsFinal && cl.mIsFinal) {
2141       /*
2142        * It is safe to make a class final if it did not previously have any public
2143        * constructors because it was impossible for an application to create a subclass.
2144        */
2145       if (mApiCheckConstructors.isEmpty()) {
2146         consistent = false;
2147         Errors.error(Errors.ADDED_FINAL_UNINSTANTIABLE, cl.position(),
2148             "Class " + cl.qualifiedName() + " added final qualifier but "
2149             + "was previously uninstantiable and therefore could not be subclassed");
2150       } else {
2151         consistent = false;
2152         Errors.error(Errors.ADDED_FINAL, cl.position(), "Class " + cl.qualifiedName()
2153             + " added final qualifier");
2154       }
2155     } else if (mIsFinal && !cl.mIsFinal) {
2156       consistent = false;
2157       Errors.error(Errors.REMOVED_FINAL, cl.position(), "Class " + cl.qualifiedName()
2158           + " removed final qualifier");
2159     }
2160 
2161     if (mIsStatic != cl.mIsStatic) {
2162       consistent = false;
2163       Errors.error(Errors.CHANGED_STATIC, cl.position(), "Class " + cl.qualifiedName()
2164           + " changed static qualifier");
2165     }
2166 
2167     if (!scope().equals(cl.scope())) {
2168       consistent = false;
2169       Errors.error(Errors.CHANGED_SCOPE, cl.position(), "Class " + cl.qualifiedName()
2170           + " scope changed from " + scope() + " to " + cl.scope());
2171     }
2172 
2173     if (!isDeprecated() == cl.isDeprecated()) {
2174       consistent = false;
2175       Errors.error(Errors.CHANGED_DEPRECATED, cl.position(), "Class " + cl.qualifiedName()
2176           + " has changed deprecation state " + isDeprecated() + " --> " + cl.isDeprecated());
2177     }
2178 
2179     if (superclassName() != null) { // java.lang.Object can't have a superclass.
2180       if (!cl.extendsClass(superclassName())) {
2181         consistent = false;
2182         Errors.error(Errors.CHANGED_SUPERCLASS, cl.position(), "Class " + qualifiedName()
2183             + " superclass changed from " + superclassName() + " to " + cl.superclassName());
2184       }
2185     }
2186 
2187     if (hasTypeParameters() && cl.hasTypeParameters()) {
2188       ArrayList<TypeInfo> oldParams = typeParameters();
2189       ArrayList<TypeInfo> newParams = cl.typeParameters();
2190       if (oldParams.size() != newParams.size()) {
2191         consistent = false;
2192         Errors.error(Errors.CHANGED_TYPE, cl.position(), "Class " + qualifiedName()
2193             + " changed number of type parameters from " + oldParams.size()
2194             + " to " + newParams.size());
2195       }
2196     }
2197 
2198     return consistent;
2199   }
2200 
2201   // Find a superclass implementation of the given method based on the methods in mApiCheckMethods.
overriddenMethod(MethodInfo candidate, ClassInfo newClassObj)2202   public static MethodInfo overriddenMethod(MethodInfo candidate, ClassInfo newClassObj) {
2203     if (newClassObj == null) {
2204       return null;
2205     }
2206     for (MethodInfo mi : newClassObj.mApiCheckMethods.values()) {
2207       if (mi.matches(candidate)) {
2208         // found it
2209         return mi;
2210       }
2211     }
2212 
2213     // not found here. recursively search ancestors
2214     return ClassInfo.overriddenMethod(candidate, newClassObj.mSuperclass);
2215   }
2216 
2217   // Find a superinterface declaration of the given method.
interfaceMethod(MethodInfo candidate, ClassInfo newClassObj)2218   public static MethodInfo interfaceMethod(MethodInfo candidate, ClassInfo newClassObj) {
2219     if (newClassObj == null) {
2220       return null;
2221     }
2222     for (ClassInfo interfaceInfo : newClassObj.interfaces()) {
2223       for (MethodInfo mi : interfaceInfo.mApiCheckMethods.values()) {
2224         if (mi.matches(candidate)) {
2225           return mi;
2226         }
2227       }
2228     }
2229     return ClassInfo.interfaceMethod(candidate, newClassObj.mSuperclass);
2230   }
2231 
hasConstructor(MethodInfo constructor)2232   public boolean hasConstructor(MethodInfo constructor) {
2233     String name = constructor.getHashableName();
2234     for (MethodInfo ctor : mApiCheckConstructors.values()) {
2235       if (name.equals(ctor.getHashableName())) {
2236         return true;
2237       }
2238     }
2239     return false;
2240   }
2241 
setTypeInfo(TypeInfo typeInfo)2242   public void setTypeInfo(TypeInfo typeInfo) {
2243     mTypeInfo = typeInfo;
2244   }
2245 
type()2246   public TypeInfo type() {
2247       return mTypeInfo;
2248   }
2249 
hasTypeParameters()2250   public boolean hasTypeParameters() {
2251       if (mTypeInfo != null && mTypeInfo.typeArguments() != null) {
2252           return !mTypeInfo.typeArguments().isEmpty();
2253       }
2254       return false;
2255   }
2256 
typeParameters()2257   public ArrayList<TypeInfo> typeParameters() {
2258       if (hasTypeParameters()) {
2259           return mTypeInfo.typeArguments();
2260       }
2261       return null;
2262   }
2263 
addInnerClass(ClassInfo innerClass)2264   public void addInnerClass(ClassInfo innerClass) {
2265       if (mInnerClasses == null) {
2266           mInnerClasses = new ArrayList<ClassInfo>();
2267       }
2268 
2269       mInnerClasses.add(innerClass);
2270   }
2271 
setContainingClass(ClassInfo containingClass)2272   public void setContainingClass(ClassInfo containingClass) {
2273       mContainingClass = containingClass;
2274   }
2275 
setSuperclassType(TypeInfo superclassType)2276   public void setSuperclassType(TypeInfo superclassType) {
2277       mRealSuperclassType = superclassType;
2278   }
2279 
printResolutions()2280   public void printResolutions() {
2281       if (mResolutions == null || mResolutions.isEmpty()) {
2282           return;
2283       }
2284 
2285       System.out.println("Resolutions for Class " + mName + ":");
2286 
2287       for (Resolution r : mResolutions) {
2288           System.out.println(r);
2289       }
2290   }
2291 
addResolution(Resolution resolution)2292   public void addResolution(Resolution resolution) {
2293       if (mResolutions == null) {
2294           mResolutions = new ArrayList<Resolution>();
2295       }
2296 
2297       mResolutions.add(resolution);
2298   }
2299 
resolveResolutions()2300   public boolean resolveResolutions() {
2301       ArrayList<Resolution> resolutions = mResolutions;
2302       mResolutions = new ArrayList<Resolution>();
2303 
2304       boolean allResolved = true;
2305       for (Resolution resolution : resolutions) {
2306           StringBuilder qualifiedClassName = new StringBuilder();
2307           InfoBuilder.resolveQualifiedName(resolution.getValue(), qualifiedClassName,
2308                   resolution.getInfoBuilder());
2309 
2310           // if we still couldn't resolve it, save it for the next pass
2311           if ("".equals(qualifiedClassName.toString())) {
2312               mResolutions.add(resolution);
2313               allResolved = false;
2314           } else if ("superclassQualifiedName".equals(resolution.getVariable())) {
2315               setSuperClass(InfoBuilder.Caches.obtainClass(qualifiedClassName.toString()));
2316           } else if ("interfaceQualifiedName".equals(resolution.getVariable())) {
2317               addInterface(InfoBuilder.Caches.obtainClass(qualifiedClassName.toString()));
2318           }
2319       }
2320 
2321       return allResolved;
2322   }
2323 }
2324