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