• 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 
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     ArrayList<AnnotationInstanceInfo> showAnnos = getShowAnnotationsIncludeOuters();
1238     AnnotationInstanceInfo.makeLinkListHDF(
1239       data,
1240       "class.showAnnotations",
1241       showAnnos.toArray(new AnnotationInstanceInfo[showAnnos.size()]));
1242 
1243     // class info
1244     String kind = kind();
1245     if (kind != null) {
1246       data.setValue("class.kind", kind);
1247     }
1248     data.setValue("class.since", getSince());
1249     if (isDeprecated()) {
1250       data.setValue("class.deprecatedsince", getDeprecatedSince());
1251     }
1252     setFederatedReferences(data, "class");
1253 
1254     // the containing package -- note that this can be passed to type_link,
1255     // but it also contains the list of all of the packages
1256     containingPackage().makeClassLinkListHDF(data, "class.package");
1257 
1258     // inheritance hierarchy
1259     List<ClassTypePair> ctplist = superClassesWithTypes();
1260     n = ctplist.size();
1261     for (i = 0; i < ctplist.size(); i++) {
1262       // go in reverse order
1263       ClassTypePair ctp = ctplist.get(n - i - 1);
1264       if (ctp.classInfo().checkLevel()) {
1265         ctp.typeInfo().makeQualifiedHDF(data, "class.inheritance." + i + ".class");
1266         ctp.typeInfo().makeHDF(data, "class.inheritance." + i + ".short_class");
1267         j = 0;
1268         for (ClassTypePair t : ctp.classInfo().interfacesWithTypes()) {
1269           t.typeInfo().makeHDF(data, "class.inheritance." + i + ".interfaces." + j);
1270           j++;
1271         }
1272       }
1273     }
1274 
1275     // class description
1276     TagInfo.makeHDF(data, "class.descr", inlineTags());
1277     TagInfo.makeHDF(data, "class.seeAlso", comment().seeTags());
1278     TagInfo.makeHDF(data, "class.deprecated", deprecatedTags());
1279 
1280     // known subclasses
1281     TreeMap<String, ClassInfo> direct = new TreeMap<String, ClassInfo>();
1282     TreeMap<String, ClassInfo> indirect = new TreeMap<String, ClassInfo>();
1283     ClassInfo[] all = Converter.rootClasses();
1284     for (ClassInfo cl : all) {
1285       if (cl.superclass() != null && cl.superclass().equals(this)) {
1286         direct.put(cl.name(), cl);
1287       } else if (cl.isDerivedFrom(this)) {
1288         indirect.put(cl.name(), cl);
1289       }
1290     }
1291     // direct
1292     i = 0;
1293     for (ClassInfo cl : direct.values()) {
1294       if (cl.checkLevel()) {
1295         cl.makeShortDescrHDF(data, "class.subclasses.direct." + i);
1296       }
1297       i++;
1298     }
1299     // indirect
1300     i = 0;
1301     for (ClassInfo cl : indirect.values()) {
1302       if (cl.checkLevel()) {
1303         cl.makeShortDescrHDF(data, "class.subclasses.indirect." + i);
1304       }
1305       i++;
1306     }
1307 
1308     // hide special cases
1309     if ("java.lang.Object".equals(qualified) || "java.io.Serializable".equals(qualified)) {
1310       data.setValue("class.subclasses.hidden", "1");
1311     } else {
1312       data.setValue("class.subclasses.hidden", "0");
1313     }
1314 
1315     // nested classes
1316     i = 0;
1317     for (ClassInfo inner : inners) {
1318       if (inner.checkLevel()) {
1319         inner.makeShortDescrHDF(data, "class.inners." + i);
1320       }
1321       i++;
1322     }
1323 
1324     // enum constants
1325     i = 0;
1326     for (FieldInfo field : enumConstants) {
1327       field.makeHDF(data, "class.enumConstants." + i);
1328       i++;
1329     }
1330 
1331     // constants
1332     i = 0;
1333     for (FieldInfo field : fields) {
1334       if (field.isConstant()) {
1335         field.makeHDF(data, "class.constants." + i);
1336         i++;
1337       }
1338     }
1339 
1340     // fields
1341     i = 0;
1342     for (FieldInfo field : fields) {
1343       if (!field.isConstant()) {
1344         field.makeHDF(data, "class.fields." + i);
1345         i++;
1346       }
1347     }
1348 
1349     // public constructors
1350     i = 0;
1351     for (MethodInfo ctor : ctors) {
1352       if (ctor.isPublic()) {
1353         ctor.makeHDF(data, "class.ctors.public." + i);
1354         i++;
1355       }
1356     }
1357 
1358     // protected constructors
1359     if (Doclava.checkLevel(Doclava.SHOW_PROTECTED)) {
1360       i = 0;
1361       for (MethodInfo ctor : ctors) {
1362         if (ctor.isProtected()) {
1363           ctor.makeHDF(data, "class.ctors.protected." + i);
1364           i++;
1365         }
1366       }
1367     }
1368 
1369     // package private constructors
1370     if (Doclava.checkLevel(Doclava.SHOW_PACKAGE)) {
1371       i = 0;
1372       for (MethodInfo ctor : ctors) {
1373         if (ctor.isPackagePrivate()) {
1374           ctor.makeHDF(data, "class.ctors.package." + i);
1375           i++;
1376         }
1377       }
1378     }
1379 
1380     // private constructors
1381     if (Doclava.checkLevel(Doclava.SHOW_PRIVATE)) {
1382       i = 0;
1383       for (MethodInfo ctor : ctors) {
1384         if (ctor.isPrivate()) {
1385           ctor.makeHDF(data, "class.ctors.private." + i);
1386           i++;
1387         }
1388       }
1389     }
1390 
1391     // public methods
1392     i = 0;
1393     for (MethodInfo method : methods) {
1394       if (method.isPublic()) {
1395         method.makeHDF(data, "class.methods.public." + i);
1396         i++;
1397       }
1398     }
1399 
1400     // protected methods
1401     if (Doclava.checkLevel(Doclava.SHOW_PROTECTED)) {
1402       i = 0;
1403       for (MethodInfo method : methods) {
1404         if (method.isProtected()) {
1405           method.makeHDF(data, "class.methods.protected." + i);
1406           i++;
1407         }
1408       }
1409     }
1410 
1411     // package private methods
1412     if (Doclava.checkLevel(Doclava.SHOW_PACKAGE)) {
1413       i = 0;
1414       for (MethodInfo method : methods) {
1415         if (method.isPackagePrivate()) {
1416           method.makeHDF(data, "class.methods.package." + i);
1417           i++;
1418         }
1419       }
1420     }
1421 
1422     // private methods
1423     if (Doclava.checkLevel(Doclava.SHOW_PRIVATE)) {
1424       i = 0;
1425       for (MethodInfo method : methods) {
1426         if (method.isPrivate()) {
1427           method.makeHDF(data, "class.methods.private." + i);
1428           i++;
1429         }
1430       }
1431     }
1432 
1433     // xml attributes
1434     i = 0;
1435     for (AttributeInfo attr : selfAttributes) {
1436       if (attr.checkLevel()) {
1437         attr.makeHDF(data, "class.attrs." + i);
1438         i++;
1439       }
1440     }
1441 
1442     // inherited methods
1443     Iterator<ClassTypePair> superclassesItr = superClassesWithTypes().iterator();
1444     superclassesItr.next(); // skip the first one, which is the current class
1445     ClassTypePair superCtp;
1446     i = 0;
1447     while (superclassesItr.hasNext()) {
1448       superCtp = superclassesItr.next();
1449       if (superCtp.classInfo().checkLevel()) {
1450         makeInheritedHDF(data, i, superCtp);
1451         i++;
1452       }
1453     }
1454     Iterator<ClassTypePair> interfacesItr = allInterfacesWithTypes().iterator();
1455     while (interfacesItr.hasNext()) {
1456       superCtp = interfacesItr.next();
1457       if (superCtp.classInfo().checkLevel()) {
1458         makeInheritedHDF(data, i, superCtp);
1459         i++;
1460       }
1461     }
1462   }
1463 
makeInheritedHDF(Data data, int index, ClassTypePair ctp)1464   private static void makeInheritedHDF(Data data, int index, ClassTypePair ctp) {
1465     int i;
1466 
1467     String base = "class.inherited." + index;
1468     data.setValue(base + ".qualified", ctp.classInfo().qualifiedName());
1469     if (ctp.classInfo().checkLevel()) {
1470       data.setValue(base + ".link", ctp.classInfo().htmlPage());
1471     }
1472     String kind = ctp.classInfo().kind();
1473     if (kind != null) {
1474       data.setValue(base + ".kind", kind);
1475     }
1476 
1477     if (ctp.classInfo().mIsIncluded) {
1478       data.setValue(base + ".included", "true");
1479     } else {
1480       Doclava.federationTagger.tagAll(new ClassInfo[] {ctp.classInfo()});
1481       if (!ctp.classInfo().getFederatedReferences().isEmpty()) {
1482         FederatedSite site = ctp.classInfo().getFederatedReferences().iterator().next();
1483         data.setValue(base + ".link", site.linkFor(ctp.classInfo().htmlPage()));
1484         data.setValue(base + ".federated", site.name());
1485       }
1486     }
1487 
1488     // xml attributes
1489     i = 0;
1490     for (AttributeInfo attr : ctp.classInfo().selfAttributes()) {
1491       attr.makeHDF(data, base + ".attrs." + i);
1492       i++;
1493     }
1494 
1495     // methods
1496     i = 0;
1497     for (MethodInfo method : ctp.classInfo().selfMethods()) {
1498       method.makeHDF(data, base + ".methods." + i, ctp.getTypeArgumentMapping());
1499       i++;
1500     }
1501 
1502     // fields
1503     i = 0;
1504     for (FieldInfo field : ctp.classInfo().selfFields()) {
1505       if (!field.isConstant()) {
1506         field.makeHDF(data, base + ".fields." + i);
1507         i++;
1508       }
1509     }
1510 
1511     // constants
1512     i = 0;
1513     for (FieldInfo field : ctp.classInfo().selfFields()) {
1514       if (field.isConstant()) {
1515         field.makeHDF(data, base + ".constants." + i);
1516         i++;
1517       }
1518     }
1519   }
1520 
1521   @Override
isHidden()1522   public boolean isHidden() {
1523     if (mHidden == null) {
1524       mHidden = isHiddenImpl();
1525     }
1526 
1527     return mHidden;
1528   }
1529 
1530   /**
1531    * @return true if the containing package has @hide comment, or an ancestor
1532    * class of this class is hidden, or this class has @hide comment.
1533    */
isHiddenImpl()1534   public boolean isHiddenImpl() {
1535     ClassInfo cl = this;
1536     while (cl != null) {
1537       if (cl.hasShowAnnotation()) {
1538         return false;
1539       }
1540       PackageInfo pkg = cl.containingPackage();
1541       if (pkg != null && pkg.hasHideComment()) {
1542         return true;
1543       }
1544       if (cl.comment().isHidden()) {
1545         return true;
1546       }
1547       cl = cl.containingClass();
1548     }
1549     return false;
1550   }
1551 
1552   @Override
isRemoved()1553   public boolean isRemoved() {
1554     if (mRemoved == null) {
1555       mRemoved = isRemovedImpl();
1556     }
1557 
1558     return mRemoved;
1559   }
1560 
1561   /**
1562    * @return true if the containing package has @removed comment, or an ancestor
1563    * class of this class is removed, or this class has @removed comment.
1564    */
isRemovedImpl()1565   public boolean isRemovedImpl() {
1566     ClassInfo cl = this;
1567     while (cl != null) {
1568       if (cl.hasShowAnnotation()) {
1569         return false;
1570       }
1571       PackageInfo pkg = cl.containingPackage();
1572       if (pkg != null && pkg.hasRemovedComment()) {
1573         return true;
1574       }
1575       if (cl.comment().isRemoved()) {
1576         return true;
1577       }
1578       cl = cl.containingClass();
1579     }
1580     return false;
1581   }
1582 
1583   @Override
isHiddenOrRemoved()1584   public boolean isHiddenOrRemoved() {
1585     return isHidden() || isRemoved();
1586   }
1587 
hasShowAnnotation()1588   public boolean hasShowAnnotation() {
1589     return mShowAnnotations.size() > 0;
1590   }
1591 
showAnnotations()1592   public ArrayList<AnnotationInstanceInfo> showAnnotations() {
1593     return mShowAnnotations;
1594   }
1595 
getShowAnnotationsIncludeOuters()1596   public ArrayList<AnnotationInstanceInfo> getShowAnnotationsIncludeOuters() {
1597     ArrayList<AnnotationInstanceInfo> allAnnotations = new ArrayList<AnnotationInstanceInfo>();
1598     ClassInfo cl = this;
1599     while (cl != null) {
1600       if (cl.showAnnotations() != null) {
1601         // Don't allow duplicates into the merged list
1602         for (AnnotationInstanceInfo newAii : cl.showAnnotations()) {
1603           boolean addIt = true;
1604           for (AnnotationInstanceInfo existingAii : allAnnotations) {
1605             if (existingAii.type().name() == newAii.type().name()) {
1606               addIt = false;
1607               break;
1608             }
1609           }
1610           if (addIt) {
1611             allAnnotations.add(newAii);
1612           }
1613         }
1614       }
1615       cl = cl.containingClass();
1616     }
1617     return allAnnotations;
1618   }
1619 
matchMethod(ArrayList<MethodInfo> methods, String name, String[] params, String[] dimensions, boolean varargs)1620   private MethodInfo matchMethod(ArrayList<MethodInfo> methods, String name, String[] params,
1621       String[] dimensions, boolean varargs) {
1622     for (MethodInfo method : methods) {
1623       if (method.name().equals(name)) {
1624         if (params == null) {
1625           return method;
1626         } else {
1627           if (method.matchesParams(params, dimensions, varargs)) {
1628             return method;
1629           }
1630         }
1631       }
1632     }
1633     return null;
1634   }
1635 
findMethod(String name, String[] params, String[] dimensions, boolean varargs)1636   public MethodInfo findMethod(String name, String[] params, String[] dimensions, boolean varargs) {
1637     // first look on our class, and our superclasses
1638 
1639     // for methods
1640     MethodInfo rv;
1641     rv = matchMethod(methods(), name, params, dimensions, varargs);
1642 
1643     if (rv != null) {
1644       return rv;
1645     }
1646 
1647     // for constructors
1648     rv = matchMethod(constructors(), name, params, dimensions, varargs);
1649     if (rv != null) {
1650       return rv;
1651     }
1652 
1653     // then recursively look at our containing class
1654     ClassInfo containing = containingClass();
1655     if (containing != null) {
1656       return containing.findMethod(name, params, dimensions, varargs);
1657     }
1658 
1659     return null;
1660   }
1661 
supportsMethod(MethodInfo method)1662   public boolean supportsMethod(MethodInfo method) {
1663     for (MethodInfo m : methods()) {
1664       if (m.getHashableName().equals(method.getHashableName())) {
1665         return true;
1666       }
1667     }
1668     return false;
1669   }
1670 
searchInnerClasses(String[] nameParts, int index)1671   private ClassInfo searchInnerClasses(String[] nameParts, int index) {
1672     String part = nameParts[index];
1673 
1674     ArrayList<ClassInfo> inners = mInnerClasses;
1675     for (ClassInfo in : inners) {
1676       String[] innerParts = in.nameParts();
1677       if (part.equals(innerParts[innerParts.length - 1])) {
1678         if (index == nameParts.length - 1) {
1679           return in;
1680         } else {
1681           return in.searchInnerClasses(nameParts, index + 1);
1682         }
1683       }
1684     }
1685     return null;
1686   }
1687 
extendedFindClass(String className)1688   public ClassInfo extendedFindClass(String className) {
1689     // ClassDoc.findClass has this bug that we're working around here:
1690     // If you have a class PackageManager with an inner class PackageInfo
1691     // and you call it with "PackageInfo" it doesn't find it.
1692     return searchInnerClasses(className.split("\\."), 0);
1693   }
1694 
findClass(String className)1695   public ClassInfo findClass(String className) {
1696     return Converter.obtainClass(mClass.findClass(className));
1697   }
1698 
findInnerClass(String className)1699   public ClassInfo findInnerClass(String className) {
1700     // ClassDoc.findClass won't find inner classes. To deal with that,
1701     // we try what they gave us first, but if that didn't work, then
1702     // we see if there are any periods in className, and start searching
1703     // from there.
1704     String[] nodes = className.split("\\.");
1705     ClassDoc cl = mClass;
1706 
1707     int N = nodes.length;
1708     for (int i = 0; i < N; ++i) {
1709       final String n = nodes[i];
1710       if (n.isEmpty() && i == 0) {
1711         // We skip over an empty classname component if it's at location 0. This is
1712         // to deal with names like ".Inner". java7 will return a bogus ClassInfo when
1713         // we call "findClass("") and the next iteration of the loop will throw a
1714         // runtime exception.
1715         continue;
1716       }
1717 
1718       cl = cl.findClass(n);
1719       if (cl == null) {
1720         return null;
1721       }
1722     }
1723 
1724     return Converter.obtainClass(cl);
1725   }
1726 
findField(String name)1727   public FieldInfo findField(String name) {
1728     // first look on our class, and our superclasses
1729     for (FieldInfo f : fields()) {
1730       if (f.name().equals(name)) {
1731         return f;
1732       }
1733     }
1734 
1735     // then look at our enum constants (these are really fields, maybe
1736     // they should be mixed into fields(). not sure)
1737     for (FieldInfo f : enumConstants()) {
1738       if (f.name().equals(name)) {
1739         return f;
1740       }
1741     }
1742 
1743     // then recursively look at our containing class
1744     ClassInfo containing = containingClass();
1745     if (containing != null) {
1746       return containing.findField(name);
1747     }
1748 
1749     return null;
1750   }
1751 
sortByName(ClassInfo[] classes)1752   public static ClassInfo[] sortByName(ClassInfo[] classes) {
1753     int i;
1754     Sorter[] sorted = new Sorter[classes.length];
1755     for (i = 0; i < sorted.length; i++) {
1756       ClassInfo cl = classes[i];
1757       sorted[i] = new Sorter(cl.name(), cl);
1758     }
1759 
1760     Arrays.sort(sorted);
1761 
1762     ClassInfo[] rv = new ClassInfo[classes.length];
1763     for (i = 0; i < rv.length; i++) {
1764       rv[i] = (ClassInfo) sorted[i].data;
1765     }
1766 
1767     return rv;
1768   }
1769 
equals(ClassInfo that)1770   public boolean equals(ClassInfo that) {
1771     if (that != null) {
1772       return this.qualifiedName().equals(that.qualifiedName());
1773     } else {
1774       return false;
1775     }
1776   }
1777 
setNonWrittenConstructors(ArrayList<MethodInfo> nonWritten)1778   public void setNonWrittenConstructors(ArrayList<MethodInfo> nonWritten) {
1779     mNonWrittenConstructors = nonWritten;
1780   }
1781 
getNonWrittenConstructors()1782   public ArrayList<MethodInfo> getNonWrittenConstructors() {
1783     return mNonWrittenConstructors;
1784   }
1785 
kind()1786   public String kind() {
1787     if (isOrdinaryClass()) {
1788       return "class";
1789     } else if (isInterface()) {
1790       return "interface";
1791     } else if (isEnum()) {
1792       return "enum";
1793     } else if (isError()) {
1794       return "class";
1795     } else if (isException()) {
1796       return "class";
1797     } else if (isAnnotation()) {
1798       return "@interface";
1799     }
1800     return null;
1801   }
1802 
scope()1803   public String scope() {
1804     if (isPublic()) {
1805       return "public";
1806     } else if (isProtected()) {
1807       return "protected";
1808     } else if (isPackagePrivate()) {
1809       return "";
1810     } else if (isPrivate()) {
1811       return "private";
1812     } else {
1813       throw new RuntimeException("invalid scope for object " + this);
1814     }
1815   }
1816 
setHiddenMethods(ArrayList<MethodInfo> mInfo)1817   public void setHiddenMethods(ArrayList<MethodInfo> mInfo) {
1818     mHiddenMethods = mInfo;
1819   }
1820 
getHiddenMethods()1821   public ArrayList<MethodInfo> getHiddenMethods() {
1822     return mHiddenMethods;
1823   }
1824 
1825   @Override
toString()1826   public String toString() {
1827     return this.qualifiedName();
1828   }
1829 
setReasonIncluded(String reason)1830   public void setReasonIncluded(String reason) {
1831     mReasonIncluded = reason;
1832   }
1833 
getReasonIncluded()1834   public String getReasonIncluded() {
1835     return mReasonIncluded;
1836   }
1837 
1838   private ClassDoc mClass;
1839 
1840   // ctor
1841   private boolean mIsPublic;
1842   private boolean mIsProtected;
1843   private boolean mIsPackagePrivate;
1844   private boolean mIsPrivate;
1845   private boolean mIsStatic;
1846   private boolean mIsInterface;
1847   private boolean mIsAbstract;
1848   private boolean mIsOrdinaryClass;
1849   private boolean mIsException;
1850   private boolean mIsError;
1851   private boolean mIsEnum;
1852   private boolean mIsAnnotation;
1853   private boolean mIsFinal;
1854   private boolean mIsIncluded;
1855   private String mName;
1856   private String mQualifiedName;
1857   private String mQualifiedTypeName;
1858   private boolean mIsPrimitive;
1859   private TypeInfo mTypeInfo;
1860   private String[] mNameParts;
1861 
1862   // init
1863   private ArrayList<ClassInfo> mRealInterfaces = new ArrayList<ClassInfo>();
1864   private ArrayList<ClassInfo> mInterfaces;
1865   private ArrayList<TypeInfo> mRealInterfaceTypes;
1866   private ArrayList<ClassInfo> mInnerClasses;
1867   // mAllConstructors will not contain *all* constructors. Only the constructors that pass
1868   // checkLevel. @see {@link Converter#convertMethods(ConstructorDoc[])}
1869   private ArrayList<MethodInfo> mAllConstructors = new ArrayList<MethodInfo>();
1870   // mAllSelfMethods will not contain *all* self methods. Only the methods that pass
1871   // checkLevel. @see {@link Converter#convertMethods(MethodDoc[])}
1872   private ArrayList<MethodInfo> mAllSelfMethods = new ArrayList<MethodInfo>();
1873   private ArrayList<MethodInfo> mAnnotationElements = new ArrayList<MethodInfo>(); // if this class is an annotation
1874   private ArrayList<FieldInfo> mAllSelfFields = new ArrayList<FieldInfo>();
1875   private ArrayList<FieldInfo> mEnumConstants = new ArrayList<FieldInfo>();
1876   private PackageInfo mContainingPackage;
1877   private ClassInfo mContainingClass;
1878   private ClassInfo mRealSuperclass;
1879   private TypeInfo mRealSuperclassType;
1880   private ClassInfo mSuperclass;
1881   private ArrayList<AnnotationInstanceInfo> mAnnotations;
1882   private ArrayList<AnnotationInstanceInfo> mShowAnnotations;
1883   private boolean mSuperclassInit;
1884   private boolean mDeprecatedKnown;
1885 
1886   // lazy
1887   private ArrayList<ClassTypePair> mSuperclassesWithTypes;
1888   private ArrayList<ClassTypePair> mInterfacesWithTypes;
1889   private ArrayList<ClassTypePair> mAllInterfacesWithTypes;
1890   private ArrayList<MethodInfo> mConstructors;
1891   private ArrayList<ClassInfo> mRealInnerClasses;
1892   private ArrayList<MethodInfo> mSelfMethods;
1893   private ArrayList<FieldInfo> mSelfFields;
1894   private ArrayList<AttributeInfo> mSelfAttributes;
1895   private ArrayList<MethodInfo> mMethods;
1896   private ArrayList<FieldInfo> mFields;
1897   private ArrayList<TypeInfo> mTypeParameters;
1898   private ArrayList<MethodInfo> mHiddenMethods;
1899   private Boolean mHidden = null;
1900   private Boolean mRemoved = null;
1901   private Boolean mCheckLevel = null;
1902   private String mReasonIncluded;
1903   private ArrayList<MethodInfo> mNonWrittenConstructors;
1904   private boolean mIsDeprecated;
1905 
1906   // TODO: Temporary members from apicheck migration.
1907   private HashMap<String, MethodInfo> mApiCheckConstructors = new HashMap<String, MethodInfo>();
1908   private HashMap<String, MethodInfo> mApiCheckMethods = new HashMap<String, MethodInfo>();
1909   private HashMap<String, FieldInfo> mApiCheckFields = new HashMap<String, FieldInfo>();
1910   private HashMap<String, FieldInfo> mApiCheckEnumConstants = new HashMap<String, FieldInfo>();
1911 
1912   // Resolutions
1913   private ArrayList<Resolution> mResolutions;
1914 
1915   private List<MethodInfo> mRemovedConstructors; // immutable after you set its value.
1916   // @removed self methods that do not override any parent methods
1917   private List<MethodInfo> mRemovedSelfMethods; // immutable after you set its value.
1918   private List<MethodInfo> mRemovedMethods; // immutable after you set its value.
1919   private List<FieldInfo> mRemovedSelfFields; // immutable after you set its value.
1920   private List<FieldInfo> mRemovedEnumConstants; // immutable after you set its value.
1921 
1922   /**
1923    * Returns true if {@code cl} implements the interface {@code iface} either by either being that
1924    * interface, implementing that interface or extending a type that implements the interface.
1925    */
implementsInterface(String iface)1926   public boolean implementsInterface(String iface) {
1927     if (qualifiedName().equals(iface)) {
1928       return true;
1929     }
1930     for (ClassInfo clImplements : realInterfaces()) {
1931       if (clImplements.implementsInterface(iface)) {
1932         return true;
1933       }
1934     }
1935     if (mSuperclass != null && mSuperclass.implementsInterface(iface)) {
1936       return true;
1937     }
1938     return false;
1939   }
1940 
1941   /**
1942    * Returns true if {@code this} extends the class {@code ext}.
1943    */
extendsClass(String cl)1944   public boolean extendsClass(String cl) {
1945     if (qualifiedName().equals(cl)) {
1946       return true;
1947     }
1948     if (mSuperclass != null && mSuperclass.extendsClass(cl)) {
1949       return true;
1950     }
1951     return false;
1952   }
1953 
1954   /**
1955    * Returns true if {@code this} is assignable to cl
1956    */
isAssignableTo(String cl)1957   public boolean isAssignableTo(String cl) {
1958     return implementsInterface(cl) || extendsClass(cl);
1959   }
1960 
addInterface(ClassInfo iface)1961   public void addInterface(ClassInfo iface) {
1962     mRealInterfaces.add(iface);
1963   }
1964 
addConstructor(MethodInfo ctor)1965   public void addConstructor(MethodInfo ctor) {
1966     mApiCheckConstructors.put(ctor.getHashableName(), ctor);
1967 
1968     mAllConstructors.add(ctor);
1969     mConstructors = null; // flush this, hopefully it hasn't been used yet.
1970   }
1971 
addField(FieldInfo field)1972   public void addField(FieldInfo field) {
1973     mApiCheckFields.put(field.name(), field);
1974 
1975     mAllSelfFields.add(field);
1976 
1977     mSelfFields = null; // flush this, hopefully it hasn't been used yet.
1978   }
1979 
addEnumConstant(FieldInfo field)1980   public void addEnumConstant(FieldInfo field) {
1981     mApiCheckEnumConstants.put(field.name(), field);
1982 
1983     mEnumConstants.add(field);
1984   }
1985 
setSuperClass(ClassInfo superclass)1986   public void setSuperClass(ClassInfo superclass) {
1987     mRealSuperclass = superclass;
1988     mSuperclass = superclass;
1989   }
1990 
allConstructorsMap()1991   public Map<String, MethodInfo> allConstructorsMap() {
1992     return mApiCheckConstructors;
1993   }
1994 
allFields()1995   public Map<String, FieldInfo> allFields() {
1996     return mApiCheckFields;
1997   }
1998 
allEnums()1999   public Map<String, FieldInfo> allEnums() {
2000     return mApiCheckEnumConstants;
2001   }
2002 
2003   /**
2004    * Returns all methods defined directly in this class. For a list of all
2005    * methods supported by this class, see {@link #methods()}.
2006    */
allMethods()2007   public Map<String, MethodInfo> allMethods() {
2008     return mApiCheckMethods;
2009   }
2010 
2011   /**
2012    * Returns the class hierarchy for this class, starting with this class.
2013    */
hierarchy()2014   public Iterable<ClassInfo> hierarchy() {
2015     List<ClassInfo> result = new ArrayList<ClassInfo>(4);
2016     for (ClassInfo c = this; c != null; c = c.mSuperclass) {
2017       result.add(c);
2018     }
2019     return result;
2020   }
2021 
superclassName()2022   public String superclassName() {
2023     if (mSuperclass == null) {
2024       if (mQualifiedName.equals("java.lang.Object")) {
2025         return null;
2026       }
2027       throw new UnsupportedOperationException("Superclass not set for " + qualifiedName());
2028     }
2029     return mSuperclass.mQualifiedName;
2030   }
2031 
setAnnotations(ArrayList<AnnotationInstanceInfo> annotations)2032   public void setAnnotations(ArrayList<AnnotationInstanceInfo> annotations) {
2033     mAnnotations = annotations;
2034   }
2035 
isConsistent(ClassInfo cl)2036   public boolean isConsistent(ClassInfo cl) {
2037     boolean consistent = true;
2038 
2039     if (isInterface() != cl.isInterface()) {
2040       Errors.error(Errors.CHANGED_CLASS, cl.position(), "Class " + cl.qualifiedName()
2041           + " changed class/interface declaration");
2042       consistent = false;
2043     }
2044     for (ClassInfo iface : mRealInterfaces) {
2045       if (!cl.implementsInterface(iface.mQualifiedName)) {
2046         Errors.error(Errors.REMOVED_INTERFACE, cl.position(), "Class " + qualifiedName()
2047             + " no longer implements " + iface);
2048       }
2049     }
2050     for (ClassInfo iface : cl.mRealInterfaces) {
2051       if (!implementsInterface(iface.mQualifiedName)) {
2052         Errors.error(Errors.ADDED_INTERFACE, cl.position(), "Added interface " + iface
2053             + " to class " + qualifiedName());
2054         consistent = false;
2055       }
2056     }
2057 
2058     for (MethodInfo mInfo : mApiCheckMethods.values()) {
2059       if (cl.mApiCheckMethods.containsKey(mInfo.getHashableName())) {
2060         if (!mInfo.isConsistent(cl.mApiCheckMethods.get(mInfo.getHashableName()))) {
2061           consistent = false;
2062         }
2063       } else {
2064         /*
2065          * This class formerly provided this method directly, and now does not. Check our ancestry
2066          * to see if there's an inherited version that still fulfills the API requirement.
2067          */
2068         MethodInfo mi = ClassInfo.overriddenMethod(mInfo, cl);
2069         if (mi == null) {
2070           mi = ClassInfo.interfaceMethod(mInfo, cl);
2071         }
2072         if (mi == null) {
2073           Errors.error(Errors.REMOVED_METHOD, mInfo.position(), "Removed public method "
2074               + mInfo.qualifiedName());
2075           consistent = false;
2076         }
2077       }
2078     }
2079     for (MethodInfo mInfo : cl.mApiCheckMethods.values()) {
2080       if (!mApiCheckMethods.containsKey(mInfo.getHashableName())) {
2081         /*
2082          * Similarly to the above, do not fail if this "new" method is really an override of an
2083          * existing superclass method.
2084          */
2085         MethodInfo mi = ClassInfo.overriddenMethod(mInfo, this);
2086         if (mi == null) {
2087           Errors.error(Errors.ADDED_METHOD, mInfo.position(), "Added public method "
2088               + mInfo.qualifiedName());
2089           consistent = false;
2090         }
2091       }
2092     }
2093 
2094     for (MethodInfo mInfo : mApiCheckConstructors.values()) {
2095       if (cl.mApiCheckConstructors.containsKey(mInfo.getHashableName())) {
2096         if (!mInfo.isConsistent(cl.mApiCheckConstructors.get(mInfo.getHashableName()))) {
2097           consistent = false;
2098         }
2099       } else {
2100         Errors.error(Errors.REMOVED_METHOD, mInfo.position(), "Removed public constructor "
2101             + mInfo.prettySignature());
2102         consistent = false;
2103       }
2104     }
2105     for (MethodInfo mInfo : cl.mApiCheckConstructors.values()) {
2106       if (!mApiCheckConstructors.containsKey(mInfo.getHashableName())) {
2107         Errors.error(Errors.ADDED_METHOD, mInfo.position(), "Added public constructor "
2108             + mInfo.prettySignature());
2109         consistent = false;
2110       }
2111     }
2112 
2113     for (FieldInfo mInfo : mApiCheckFields.values()) {
2114       if (cl.mApiCheckFields.containsKey(mInfo.name())) {
2115         if (!mInfo.isConsistent(cl.mApiCheckFields.get(mInfo.name()))) {
2116           consistent = false;
2117         }
2118       } else {
2119         Errors.error(Errors.REMOVED_FIELD, mInfo.position(), "Removed field "
2120             + mInfo.qualifiedName());
2121         consistent = false;
2122       }
2123     }
2124     for (FieldInfo mInfo : cl.mApiCheckFields.values()) {
2125       if (!mApiCheckFields.containsKey(mInfo.name())) {
2126         Errors.error(Errors.ADDED_FIELD, mInfo.position(), "Added public field "
2127             + mInfo.qualifiedName());
2128         consistent = false;
2129       }
2130     }
2131 
2132     for (FieldInfo info : mApiCheckEnumConstants.values()) {
2133       if (cl.mApiCheckEnumConstants.containsKey(info.name())) {
2134         if (!info.isConsistent(cl.mApiCheckEnumConstants.get(info.name()))) {
2135           consistent = false;
2136         }
2137       } else {
2138         Errors.error(Errors.REMOVED_FIELD, info.position(), "Removed enum constant "
2139             + info.qualifiedName());
2140         consistent = false;
2141       }
2142     }
2143     for (FieldInfo info : cl.mApiCheckEnumConstants.values()) {
2144       if (!mApiCheckEnumConstants.containsKey(info.name())) {
2145         Errors.error(Errors.ADDED_FIELD, info.position(), "Added enum constant "
2146             + info.qualifiedName());
2147         consistent = false;
2148       }
2149     }
2150 
2151     if (mIsAbstract != cl.mIsAbstract) {
2152       consistent = false;
2153       Errors.error(Errors.CHANGED_ABSTRACT, cl.position(), "Class " + cl.qualifiedName()
2154           + " changed abstract qualifier");
2155     }
2156 
2157     if (!mIsFinal && cl.mIsFinal) {
2158       /*
2159        * It is safe to make a class final if it did not previously have any public
2160        * constructors because it was impossible for an application to create a subclass.
2161        */
2162       if (mApiCheckConstructors.isEmpty()) {
2163         consistent = false;
2164         Errors.error(Errors.ADDED_FINAL_UNINSTANTIABLE, cl.position(),
2165             "Class " + cl.qualifiedName() + " added final qualifier but "
2166             + "was previously uninstantiable and therefore could not be subclassed");
2167       } else {
2168         consistent = false;
2169         Errors.error(Errors.ADDED_FINAL, cl.position(), "Class " + cl.qualifiedName()
2170             + " added final qualifier");
2171       }
2172     } else if (mIsFinal && !cl.mIsFinal) {
2173       consistent = false;
2174       Errors.error(Errors.REMOVED_FINAL, cl.position(), "Class " + cl.qualifiedName()
2175           + " removed final qualifier");
2176     }
2177 
2178     if (mIsStatic != cl.mIsStatic) {
2179       consistent = false;
2180       Errors.error(Errors.CHANGED_STATIC, cl.position(), "Class " + cl.qualifiedName()
2181           + " changed static qualifier");
2182     }
2183 
2184     if (!scope().equals(cl.scope())) {
2185       consistent = false;
2186       Errors.error(Errors.CHANGED_SCOPE, cl.position(), "Class " + cl.qualifiedName()
2187           + " scope changed from " + scope() + " to " + cl.scope());
2188     }
2189 
2190     if (!isDeprecated() == cl.isDeprecated()) {
2191       consistent = false;
2192       Errors.error(Errors.CHANGED_DEPRECATED, cl.position(), "Class " + cl.qualifiedName()
2193           + " has changed deprecation state");
2194     }
2195 
2196     if (superclassName() != null) { // java.lang.Object can't have a superclass.
2197       if (!cl.extendsClass(superclassName())) {
2198         consistent = false;
2199         Errors.error(Errors.CHANGED_SUPERCLASS, cl.position(), "Class " + qualifiedName()
2200             + " superclass changed from " + superclassName() + " to " + cl.superclassName());
2201       }
2202     }
2203 
2204     return consistent;
2205   }
2206 
2207   // Find a superclass implementation of the given method based on the methods in mApiCheckMethods.
overriddenMethod(MethodInfo candidate, ClassInfo newClassObj)2208   public static MethodInfo overriddenMethod(MethodInfo candidate, ClassInfo newClassObj) {
2209     if (newClassObj == null) {
2210       return null;
2211     }
2212     for (MethodInfo mi : newClassObj.mApiCheckMethods.values()) {
2213       if (mi.matches(candidate)) {
2214         // found it
2215         return mi;
2216       }
2217     }
2218 
2219     // not found here. recursively search ancestors
2220     return ClassInfo.overriddenMethod(candidate, newClassObj.mSuperclass);
2221   }
2222 
2223   // Find a superinterface declaration of the given method.
interfaceMethod(MethodInfo candidate, ClassInfo newClassObj)2224   public static MethodInfo interfaceMethod(MethodInfo candidate, ClassInfo newClassObj) {
2225     if (newClassObj == null) {
2226       return null;
2227     }
2228     for (ClassInfo interfaceInfo : newClassObj.interfaces()) {
2229       for (MethodInfo mi : interfaceInfo.mApiCheckMethods.values()) {
2230         if (mi.matches(candidate)) {
2231           return mi;
2232         }
2233       }
2234     }
2235     return ClassInfo.interfaceMethod(candidate, newClassObj.mSuperclass);
2236   }
2237 
hasConstructor(MethodInfo constructor)2238   public boolean hasConstructor(MethodInfo constructor) {
2239     String name = constructor.getHashableName();
2240     for (MethodInfo ctor : mApiCheckConstructors.values()) {
2241       if (name.equals(ctor.getHashableName())) {
2242         return true;
2243       }
2244     }
2245     return false;
2246   }
2247 
setTypeInfo(TypeInfo typeInfo)2248   public void setTypeInfo(TypeInfo typeInfo) {
2249     mTypeInfo = typeInfo;
2250   }
2251 
type()2252   public TypeInfo type() {
2253       return mTypeInfo;
2254   }
2255 
addInnerClass(ClassInfo innerClass)2256   public void addInnerClass(ClassInfo innerClass) {
2257       if (mInnerClasses == null) {
2258           mInnerClasses = new ArrayList<ClassInfo>();
2259       }
2260 
2261       mInnerClasses.add(innerClass);
2262   }
2263 
setContainingClass(ClassInfo containingClass)2264   public void setContainingClass(ClassInfo containingClass) {
2265       mContainingClass = containingClass;
2266   }
2267 
setSuperclassType(TypeInfo superclassType)2268   public void setSuperclassType(TypeInfo superclassType) {
2269       mRealSuperclassType = superclassType;
2270   }
2271 
printResolutions()2272   public void printResolutions() {
2273       if (mResolutions == null || mResolutions.isEmpty()) {
2274           return;
2275       }
2276 
2277       System.out.println("Resolutions for Class " + mName + ":");
2278 
2279       for (Resolution r : mResolutions) {
2280           System.out.println(r);
2281       }
2282   }
2283 
addResolution(Resolution resolution)2284   public void addResolution(Resolution resolution) {
2285       if (mResolutions == null) {
2286           mResolutions = new ArrayList<Resolution>();
2287       }
2288 
2289       mResolutions.add(resolution);
2290   }
2291 
resolveResolutions()2292   public boolean resolveResolutions() {
2293       ArrayList<Resolution> resolutions = mResolutions;
2294       mResolutions = new ArrayList<Resolution>();
2295 
2296       boolean allResolved = true;
2297       for (Resolution resolution : resolutions) {
2298           StringBuilder qualifiedClassName = new StringBuilder();
2299           InfoBuilder.resolveQualifiedName(resolution.getValue(), qualifiedClassName,
2300                   resolution.getInfoBuilder());
2301 
2302           // if we still couldn't resolve it, save it for the next pass
2303           if ("".equals(qualifiedClassName.toString())) {
2304               mResolutions.add(resolution);
2305               allResolved = false;
2306           } else if ("superclassQualifiedName".equals(resolution.getVariable())) {
2307               setSuperClass(InfoBuilder.Caches.obtainClass(qualifiedClassName.toString()));
2308           } else if ("interfaceQualifiedName".equals(resolution.getVariable())) {
2309               addInterface(InfoBuilder.Caches.obtainClass(qualifiedClassName.toString()));
2310           }
2311       }
2312 
2313       return allResolved;
2314   }
2315 }
2316