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