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