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