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