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