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