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