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       mFields = new ArrayList<FieldInfo>(all.values());
    471     }
    472     return mFields;
    473   }
    474 
    475   public void gatherFields(ClassInfo owner, ClassInfo cl, HashMap<String, FieldInfo> fields) {
    476     for (FieldInfo f : cl.selfFields()) {
    477       if (f.checkLevel()) {
    478         fields.put(f.name(), f.cloneForClass(owner));
    479       }
    480     }
    481   }
    482 
    483   public ArrayList<FieldInfo> selfFields() {
    484     if (mSelfFields == null) {
    485         HashMap<String, FieldInfo> fields = new HashMap<String, FieldInfo>();
    486       // our hidden parents
    487       if (mRealSuperclass != null && !mRealSuperclass.checkLevel()) {
    488         gatherFields(this, mRealSuperclass, fields);
    489       }
    490       for (ClassInfo iface : mRealInterfaces) {
    491         if (!iface.checkLevel()) {
    492           gatherFields(this, iface, fields);
    493         }
    494       }
    495 
    496       for (FieldInfo f : mAllSelfFields) {
    497           if (!f.isHidden()) {
    498               fields.put(f.name(), f);
    499           }
    500       }
    501 
    502       mSelfFields = new ArrayList<FieldInfo>(fields.values());
    503       Collections.sort(mSelfFields, FieldInfo.comparator);
    504     }
    505     return mSelfFields;
    506   }
    507 
    508   public ArrayList<FieldInfo> allSelfFields() {
    509     return mAllSelfFields;
    510   }
    511 
    512   private void gatherMethods(ClassInfo owner, ClassInfo cl, HashMap<String, MethodInfo> methods) {
    513     for (MethodInfo m : cl.selfMethods()) {
    514       if (m.checkLevel()) {
    515         methods.put(m.name() + m.signature(), m.cloneForClass(owner));
    516       }
    517     }
    518   }
    519 
    520   public ArrayList<MethodInfo> selfMethods() {
    521     if (mSelfMethods == null) {
    522         HashMap<String, MethodInfo> methods = new HashMap<String, MethodInfo>();
    523       // our hidden parents
    524       if (mRealSuperclass != null && !mRealSuperclass.checkLevel()) {
    525         gatherMethods(this, mRealSuperclass, methods);
    526       }
    527       for (ClassInfo iface : mRealInterfaces) {
    528         if (!iface.checkLevel()) {
    529           gatherMethods(this, iface, methods);
    530         }
    531       }
    532       // mine
    533       if (mAllSelfMethods != null) {
    534         for (MethodInfo m : mAllSelfMethods) {
    535           if (m.checkLevel()) {
    536               methods.put(m.name() + m.signature(), m);
    537           }
    538         }
    539       }
    540 
    541       // sort it
    542       mSelfMethods = new ArrayList<MethodInfo>(methods.values());
    543       Collections.sort(mSelfMethods, MethodInfo.comparator);
    544     }
    545     return mSelfMethods;
    546   }
    547 
    548   public ArrayList<MethodInfo> allSelfMethods() {
    549     return mAllSelfMethods;
    550   }
    551 
    552   public void addMethod(MethodInfo method) {
    553     mApiCheckMethods.put(method.getHashableName(), method);
    554 
    555     mAllSelfMethods.add(method);
    556     mSelfMethods = null; // flush this, hopefully it hasn't been used yet.
    557   }
    558 
    559   public void addAnnotationElement(MethodInfo method) {
    560       mAnnotationElements.add(method);
    561   }
    562 
    563   public void setContainingPackage(PackageInfo pkg) {
    564     mContainingPackage = pkg;
    565 
    566     if (mContainingPackage != null) {
    567         if (mIsEnum) {
    568             mContainingPackage.addEnum(this);
    569         } else if (mIsInterface) {
    570             mContainingPackage.addInterface(this);
    571         } else {
    572             mContainingPackage.addOrdinaryClass(this);
    573         }
    574     }
    575   }
    576 
    577   public ArrayList<AttributeInfo> selfAttributes() {
    578     if (mSelfAttributes == null) {
    579       TreeMap<FieldInfo, AttributeInfo> attrs = new TreeMap<FieldInfo, AttributeInfo>();
    580 
    581       // the ones in the class comment won't have any methods
    582       for (AttrTagInfo tag : comment().attrTags()) {
    583         FieldInfo field = tag.reference();
    584         if (field != null) {
    585           AttributeInfo attr = attrs.get(field);
    586           if (attr == null) {
    587             attr = new AttributeInfo(this, field);
    588             attrs.put(field, attr);
    589           }
    590           tag.setAttribute(attr);
    591         }
    592       }
    593 
    594       // in the methods
    595       for (MethodInfo m : selfMethods()) {
    596         for (AttrTagInfo tag : m.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             attr.methods.add(m);
    606           }
    607         }
    608       }
    609 
    610       // constructors too
    611       for (MethodInfo m : constructors()) {
    612         for (AttrTagInfo tag : m.comment().attrTags()) {
    613           FieldInfo field = tag.reference();
    614           if (field != null) {
    615             AttributeInfo attr = attrs.get(field);
    616             if (attr == null) {
    617               attr = new AttributeInfo(this, field);
    618               attrs.put(field, attr);
    619             }
    620             tag.setAttribute(attr);
    621             attr.methods.add(m);
    622           }
    623         }
    624       }
    625 
    626       mSelfAttributes = new ArrayList<AttributeInfo>(attrs.values());
    627       Collections.sort(mSelfAttributes, AttributeInfo.comparator);
    628     }
    629     return mSelfAttributes;
    630   }
    631 
    632   public ArrayList<FieldInfo> enumConstants() {
    633     return mEnumConstants;
    634   }
    635 
    636   public ClassInfo superclass() {
    637     if (!mSuperclassInit) {
    638       if (this.checkLevel()) {
    639         // rearrange our little inheritance hierarchy, because we need to hide classes that
    640         // don't pass checkLevel
    641         ClassInfo superclass = mRealSuperclass;
    642         while (superclass != null && !superclass.checkLevel()) {
    643           superclass = superclass.mRealSuperclass;
    644         }
    645         mSuperclass = superclass;
    646       } else {
    647         mSuperclass = mRealSuperclass;
    648       }
    649     }
    650     return mSuperclass;
    651   }
    652 
    653   public ClassInfo realSuperclass() {
    654     return mRealSuperclass;
    655   }
    656 
    657   /**
    658    * always the real superclass, not the collapsed one we get through superclass(), also has the
    659    * type parameter info if it's generic.
    660    */
    661   public TypeInfo superclassType() {
    662     return mRealSuperclassType;
    663   }
    664 
    665   public TypeInfo asTypeInfo() {
    666     return mTypeInfo;
    667   }
    668 
    669   ArrayList<TypeInfo> interfaceTypes() {
    670       ArrayList<TypeInfo> types = new ArrayList<TypeInfo>();
    671       for (ClassInfo iface : interfaces()) {
    672           types.add(iface.asTypeInfo());
    673       }
    674       return types;
    675   }
    676 
    677   public String htmlPage() {
    678     String s = containingPackage().name();
    679     s = s.replace('.', '/');
    680     s += '/';
    681     s += name();
    682     s += ".html";
    683     s = Doclava.javadocDir + s;
    684     return s;
    685   }
    686 
    687   /** Even indirectly */
    688   public boolean isDerivedFrom(ClassInfo cl) {
    689     return isDerivedFrom(cl.qualifiedName());
    690   }
    691 
    692   /** Even indirectly */
    693   public boolean isDerivedFrom(String qualifiedName) {
    694     ClassInfo dad = this.superclass();
    695     if (dad != null) {
    696       if (dad.mQualifiedName.equals(qualifiedName)) {
    697         return true;
    698       } else {
    699         if (dad.isDerivedFrom(qualifiedName)) {
    700           return true;
    701         }
    702       }
    703     }
    704     for (ClassInfo iface : interfaces()) {
    705       if (iface.mQualifiedName.equals(qualifiedName)) {
    706         return true;
    707       } else {
    708         if (iface.isDerivedFrom(qualifiedName)) {
    709           return true;
    710         }
    711       }
    712     }
    713     return false;
    714   }
    715 
    716   public void makeKeywordEntries(List<KeywordEntry> keywords) {
    717     if (!checkLevel()) {
    718       return;
    719     }
    720 
    721     String htmlPage = htmlPage();
    722     String qualifiedName = qualifiedName();
    723 
    724     keywords.add(new KeywordEntry(name(), htmlPage, "class in " + containingPackage().name()));
    725 
    726     ArrayList<FieldInfo> fields = selfFields();
    727     //ArrayList<FieldInfo> enumConstants = enumConstants();
    728     ArrayList<MethodInfo> ctors = constructors();
    729     ArrayList<MethodInfo> methods = selfMethods();
    730 
    731     // enum constants
    732     for (FieldInfo field : enumConstants()) {
    733       if (field.checkLevel()) {
    734         keywords.add(new KeywordEntry(field.name(), htmlPage + "#" + field.anchor(),
    735             "enum constant in " + qualifiedName));
    736       }
    737     }
    738 
    739     // constants
    740     for (FieldInfo field : fields) {
    741       if (field.isConstant() && field.checkLevel()) {
    742         keywords.add(new KeywordEntry(field.name(), htmlPage + "#" + field.anchor(), "constant in "
    743             + qualifiedName));
    744       }
    745     }
    746 
    747     // fields
    748     for (FieldInfo field : fields) {
    749       if (!field.isConstant() && field.checkLevel()) {
    750         keywords.add(new KeywordEntry(field.name(), htmlPage + "#" + field.anchor(), "field in "
    751             + qualifiedName));
    752       }
    753     }
    754 
    755     // public constructors
    756     for (MethodInfo m : ctors) {
    757       if (m.isPublic() && m.checkLevel()) {
    758         keywords.add(new KeywordEntry(m.prettySignature(), htmlPage + "#" + m.anchor(),
    759             "constructor in " + qualifiedName));
    760       }
    761     }
    762 
    763     // protected constructors
    764     if (Doclava.checkLevel(Doclava.SHOW_PROTECTED)) {
    765       for (MethodInfo m : ctors) {
    766         if (m.isProtected() && m.checkLevel()) {
    767           keywords.add(new KeywordEntry(m.prettySignature(),
    768               htmlPage + "#" + m.anchor(), "constructor in " + qualifiedName));
    769         }
    770       }
    771     }
    772 
    773     // package private constructors
    774     if (Doclava.checkLevel(Doclava.SHOW_PACKAGE)) {
    775       for (MethodInfo m : ctors) {
    776         if (m.isPackagePrivate() && m.checkLevel()) {
    777           keywords.add(new KeywordEntry(m.prettySignature(),
    778               htmlPage + "#" + m.anchor(), "constructor in " + qualifiedName));
    779         }
    780       }
    781     }
    782 
    783     // private constructors
    784     if (Doclava.checkLevel(Doclava.SHOW_PRIVATE)) {
    785       for (MethodInfo m : ctors) {
    786         if (m.isPrivate() && m.checkLevel()) {
    787           keywords.add(new KeywordEntry(m.name() + m.prettySignature(),
    788               htmlPage + "#" + m.anchor(), "constructor in " + qualifiedName));
    789         }
    790       }
    791     }
    792 
    793     // public methods
    794     for (MethodInfo m : methods) {
    795       if (m.isPublic() && m.checkLevel()) {
    796         keywords.add(new KeywordEntry(m.name() + m.prettySignature(), htmlPage + "#" + m.anchor(),
    797             "method in " + qualifiedName));
    798       }
    799     }
    800 
    801     // protected methods
    802     if (Doclava.checkLevel(Doclava.SHOW_PROTECTED)) {
    803       for (MethodInfo m : methods) {
    804         if (m.isProtected() && m.checkLevel()) {
    805           keywords.add(new KeywordEntry(m.name() + m.prettySignature(),
    806               htmlPage + "#" + m.anchor(), "method in " + qualifiedName));
    807         }
    808       }
    809     }
    810 
    811     // package private methods
    812     if (Doclava.checkLevel(Doclava.SHOW_PACKAGE)) {
    813       for (MethodInfo m : methods) {
    814         if (m.isPackagePrivate() && m.checkLevel()) {
    815           keywords.add(new KeywordEntry(m.name() + m.prettySignature(),
    816               htmlPage + "#" + m.anchor(), "method in " + qualifiedName));
    817         }
    818       }
    819     }
    820 
    821     // private methods
    822     if (Doclava.checkLevel(Doclava.SHOW_PRIVATE)) {
    823       for (MethodInfo m : methods) {
    824         if (m.isPrivate() && m.checkLevel()) {
    825           keywords.add(new KeywordEntry(m.name() + m.prettySignature(),
    826               htmlPage + "#" + m.anchor(), "method in " + qualifiedName));
    827         }
    828       }
    829     }
    830   }
    831 
    832   public void makeLink(Data data, String base) {
    833     data.setValue(base + ".label", this.name());
    834     if (!this.isPrimitive() && this.isIncluded() && this.checkLevel()) {
    835       data.setValue(base + ".link", this.htmlPage());
    836     }
    837   }
    838 
    839   public static void makeLinkListHDF(Data data, String base, ClassInfo[] classes) {
    840     final int N = classes.length;
    841     for (int i = 0; i < N; i++) {
    842       ClassInfo cl = classes[i];
    843       if (cl.checkLevel()) {
    844         cl.asTypeInfo().makeHDF(data, base + "." + i);
    845       }
    846     }
    847   }
    848 
    849   /**
    850    * Used in lists of this class (packages, nested classes, known subclasses)
    851    */
    852   public void makeShortDescrHDF(Data data, String base) {
    853     mTypeInfo.makeHDF(data, base + ".type");
    854     data.setValue(base + ".kind", this.kind());
    855     TagInfo.makeHDF(data, base + ".shortDescr", this.firstSentenceTags());
    856     TagInfo.makeHDF(data, base + ".deprecated", deprecatedTags());
    857     data.setValue(base + ".since", getSince());
    858     if (isDeprecated()) {
    859       data.setValue(base + ".deprecatedsince", getDeprecatedSince());
    860     }
    861     setFederatedReferences(data, base);
    862   }
    863 
    864   /**
    865    * Turns into the main class page
    866    */
    867   public void makeHDF(Data data) {
    868     int i, j, n;
    869     String name = name();
    870     String qualified = qualifiedName();
    871     ArrayList<AttributeInfo> selfAttributes = selfAttributes();
    872     ArrayList<MethodInfo> methods = selfMethods();
    873     ArrayList<FieldInfo> fields = selfFields();
    874     ArrayList<FieldInfo> enumConstants = enumConstants();
    875     ArrayList<MethodInfo> ctors = constructors();
    876     ArrayList<ClassInfo> inners = innerClasses();
    877 
    878     // class name
    879     mTypeInfo.makeHDF(data, "class.type");
    880     mTypeInfo.makeQualifiedHDF(data, "class.qualifiedType");
    881     data.setValue("class.name", name);
    882     data.setValue("class.qualified", qualified);
    883     if (isProtected()) {
    884       data.setValue("class.scope", "protected");
    885     } else if (isPublic()) {
    886       data.setValue("class.scope", "public");
    887     }
    888     if (isStatic()) {
    889       data.setValue("class.static", "static");
    890     }
    891     if (isFinal()) {
    892       data.setValue("class.final", "final");
    893     }
    894     if (isAbstract() && !isInterface()) {
    895       data.setValue("class.abstract", "abstract");
    896     }
    897 
    898     // class info
    899     String kind = kind();
    900     if (kind != null) {
    901       data.setValue("class.kind", kind);
    902     }
    903     data.setValue("class.since", getSince());
    904     if (isDeprecated()) {
    905       data.setValue("class.deprecatedsince", getDeprecatedSince());
    906     }
    907     setFederatedReferences(data, "class");
    908 
    909     // the containing package -- note that this can be passed to type_link,
    910     // but it also contains the list of all of the packages
    911     containingPackage().makeClassLinkListHDF(data, "class.package");
    912 
    913     // inheritance hierarchy
    914     Vector<ClassInfo> superClasses = new Vector<ClassInfo>();
    915     superClasses.add(this);
    916     ClassInfo supr = superclass();
    917     while (supr != null) {
    918       superClasses.add(supr);
    919       supr = supr.superclass();
    920     }
    921     n = superClasses.size();
    922     for (i = 0; i < n; i++) {
    923       supr = superClasses.elementAt(n - i - 1);
    924 
    925       supr.asTypeInfo().makeQualifiedHDF(data, "class.inheritance." + i + ".class");
    926       supr.asTypeInfo().makeHDF(data, "class.inheritance." + i + ".short_class");
    927       j = 0;
    928       for (TypeInfo t : supr.interfaceTypes()) {
    929         t.makeHDF(data, "class.inheritance." + i + ".interfaces." + j);
    930         j++;
    931       }
    932     }
    933 
    934     // class description
    935     TagInfo.makeHDF(data, "class.descr", inlineTags());
    936     TagInfo.makeHDF(data, "class.seeAlso", comment().seeTags());
    937     TagInfo.makeHDF(data, "class.deprecated", deprecatedTags());
    938 
    939     // known subclasses
    940     TreeMap<String, ClassInfo> direct = new TreeMap<String, ClassInfo>();
    941     TreeMap<String, ClassInfo> indirect = new TreeMap<String, ClassInfo>();
    942     ClassInfo[] all = Converter.rootClasses();
    943     for (ClassInfo cl : all) {
    944       if (cl.superclass() != null && cl.superclass().equals(this)) {
    945         direct.put(cl.name(), cl);
    946       } else if (cl.isDerivedFrom(this)) {
    947         indirect.put(cl.name(), cl);
    948       }
    949     }
    950     // direct
    951     i = 0;
    952     for (ClassInfo cl : direct.values()) {
    953       if (cl.checkLevel()) {
    954         cl.makeShortDescrHDF(data, "class.subclasses.direct." + i);
    955       }
    956       i++;
    957     }
    958     // indirect
    959     i = 0;
    960     for (ClassInfo cl : indirect.values()) {
    961       if (cl.checkLevel()) {
    962         cl.makeShortDescrHDF(data, "class.subclasses.indirect." + i);
    963       }
    964       i++;
    965     }
    966 
    967     // hide special cases
    968     if ("java.lang.Object".equals(qualified) || "java.io.Serializable".equals(qualified)) {
    969       data.setValue("class.subclasses.hidden", "1");
    970     } else {
    971       data.setValue("class.subclasses.hidden", "0");
    972     }
    973 
    974     // nested classes
    975     i = 0;
    976     for (ClassInfo inner : inners) {
    977       if (inner.checkLevel()) {
    978         inner.makeShortDescrHDF(data, "class.inners." + i);
    979       }
    980       i++;
    981     }
    982 
    983     // enum constants
    984     i = 0;
    985     for (FieldInfo field : enumConstants) {
    986       field.makeHDF(data, "class.enumConstants." + i);
    987       i++;
    988     }
    989 
    990     // constants
    991     i = 0;
    992     for (FieldInfo field : fields) {
    993       if (field.isConstant()) {
    994         field.makeHDF(data, "class.constants." + i);
    995         i++;
    996       }
    997     }
    998 
    999     // fields
   1000     i = 0;
   1001     for (FieldInfo field : fields) {
   1002       if (!field.isConstant()) {
   1003         field.makeHDF(data, "class.fields." + i);
   1004         i++;
   1005       }
   1006     }
   1007 
   1008     // public constructors
   1009     i = 0;
   1010     for (MethodInfo ctor : ctors) {
   1011       if (ctor.isPublic()) {
   1012         ctor.makeHDF(data, "class.ctors.public." + i);
   1013         i++;
   1014       }
   1015     }
   1016 
   1017     // protected constructors
   1018     if (Doclava.checkLevel(Doclava.SHOW_PROTECTED)) {
   1019       i = 0;
   1020       for (MethodInfo ctor : ctors) {
   1021         if (ctor.isProtected()) {
   1022           ctor.makeHDF(data, "class.ctors.protected." + i);
   1023           i++;
   1024         }
   1025       }
   1026     }
   1027 
   1028     // package private constructors
   1029     if (Doclava.checkLevel(Doclava.SHOW_PACKAGE)) {
   1030       i = 0;
   1031       for (MethodInfo ctor : ctors) {
   1032         if (ctor.isPackagePrivate()) {
   1033           ctor.makeHDF(data, "class.ctors.package." + i);
   1034           i++;
   1035         }
   1036       }
   1037     }
   1038 
   1039     // private constructors
   1040     if (Doclava.checkLevel(Doclava.SHOW_PRIVATE)) {
   1041       i = 0;
   1042       for (MethodInfo ctor : ctors) {
   1043         if (ctor.isPrivate()) {
   1044           ctor.makeHDF(data, "class.ctors.private." + i);
   1045           i++;
   1046         }
   1047       }
   1048     }
   1049 
   1050     // public methods
   1051     i = 0;
   1052     for (MethodInfo method : methods) {
   1053       if (method.isPublic()) {
   1054         method.makeHDF(data, "class.methods.public." + i);
   1055         i++;
   1056       }
   1057     }
   1058 
   1059     // protected methods
   1060     if (Doclava.checkLevel(Doclava.SHOW_PROTECTED)) {
   1061       i = 0;
   1062       for (MethodInfo method : methods) {
   1063         if (method.isProtected()) {
   1064           method.makeHDF(data, "class.methods.protected." + i);
   1065           i++;
   1066         }
   1067       }
   1068     }
   1069 
   1070     // package private methods
   1071     if (Doclava.checkLevel(Doclava.SHOW_PACKAGE)) {
   1072       i = 0;
   1073       for (MethodInfo method : methods) {
   1074         if (method.isPackagePrivate()) {
   1075           method.makeHDF(data, "class.methods.package." + i);
   1076           i++;
   1077         }
   1078       }
   1079     }
   1080 
   1081     // private methods
   1082     if (Doclava.checkLevel(Doclava.SHOW_PRIVATE)) {
   1083       i = 0;
   1084       for (MethodInfo method : methods) {
   1085         if (method.isPrivate()) {
   1086           method.makeHDF(data, "class.methods.private." + i);
   1087           i++;
   1088         }
   1089       }
   1090     }
   1091 
   1092     // xml attributes
   1093     i = 0;
   1094     for (AttributeInfo attr : selfAttributes) {
   1095       if (attr.checkLevel()) {
   1096         attr.makeHDF(data, "class.attrs." + i);
   1097         i++;
   1098       }
   1099     }
   1100 
   1101     // inherited methods
   1102     Set<ClassInfo> interfaces = new TreeSet<ClassInfo>();
   1103     addInterfaces(interfaces(), interfaces);
   1104     ClassInfo cl = superclass();
   1105     i = 0;
   1106     while (cl != null) {
   1107       addInterfaces(cl.interfaces(), interfaces);
   1108       makeInheritedHDF(data, i, cl);
   1109       cl = cl.superclass();
   1110       i++;
   1111     }
   1112     for (ClassInfo iface : interfaces) {
   1113       makeInheritedHDF(data, i, iface);
   1114       i++;
   1115     }
   1116   }
   1117 
   1118   private static void addInterfaces(ArrayList<ClassInfo> ifaces, Set<ClassInfo> out) {
   1119     for (ClassInfo cl : ifaces) {
   1120       out.add(cl);
   1121       addInterfaces(cl.interfaces(), out);
   1122     }
   1123   }
   1124 
   1125   private static void makeInheritedHDF(Data data, int index, ClassInfo cl) {
   1126     int i;
   1127 
   1128     String base = "class.inherited." + index;
   1129     data.setValue(base + ".qualified", cl.qualifiedName());
   1130     if (cl.checkLevel()) {
   1131       data.setValue(base + ".link", cl.htmlPage());
   1132     }
   1133     String kind = cl.kind();
   1134     if (kind != null) {
   1135       data.setValue(base + ".kind", kind);
   1136     }
   1137 
   1138     if (cl.mIsIncluded) {
   1139       data.setValue(base + ".included", "true");
   1140     } else {
   1141       Doclava.federationTagger.tagAll(new ClassInfo[] {cl});
   1142       if (!cl.getFederatedReferences().isEmpty()) {
   1143         FederatedSite site = cl.getFederatedReferences().iterator().next();
   1144         data.setValue(base + ".link", site.linkFor(cl.htmlPage()));
   1145         data.setValue(base + ".federated", site.name());
   1146       }
   1147     }
   1148 
   1149     // xml attributes
   1150     i = 0;
   1151     for (AttributeInfo attr : cl.selfAttributes()) {
   1152       attr.makeHDF(data, base + ".attrs." + i);
   1153       i++;
   1154     }
   1155 
   1156     // methods
   1157     i = 0;
   1158     for (MethodInfo method : cl.selfMethods()) {
   1159       method.makeHDF(data, base + ".methods." + i);
   1160       i++;
   1161     }
   1162 
   1163     // fields
   1164     i = 0;
   1165     for (FieldInfo field : cl.selfFields()) {
   1166       if (!field.isConstant()) {
   1167         field.makeHDF(data, base + ".fields." + i);
   1168         i++;
   1169       }
   1170     }
   1171 
   1172     // constants
   1173     i = 0;
   1174     for (FieldInfo field : cl.selfFields()) {
   1175       if (field.isConstant()) {
   1176         field.makeHDF(data, base + ".constants." + i);
   1177         i++;
   1178       }
   1179     }
   1180   }
   1181 
   1182   @Override
   1183   public boolean isHidden() {
   1184     int val = mHidden;
   1185     if (val >= 0) {
   1186       return val != 0;
   1187     } else {
   1188       boolean v = isHiddenImpl();
   1189       mHidden = v ? 1 : 0;
   1190       return v;
   1191     }
   1192   }
   1193 
   1194   public boolean isHiddenImpl() {
   1195     ClassInfo cl = this;
   1196     while (cl != null) {
   1197       PackageInfo pkg = cl.containingPackage();
   1198       if (pkg != null && pkg.isHidden()) {
   1199         return true;
   1200       }
   1201       if (cl.annotations() != null) {
   1202         for (AnnotationInstanceInfo info : cl.annotations()) {
   1203           if (Doclava.showAnnotations.contains(info.type().qualifiedName())) {
   1204             return false;
   1205           }
   1206         }
   1207       }
   1208       if (cl.comment().isHidden()) {
   1209         return true;
   1210       }
   1211       cl = cl.containingClass();
   1212     }
   1213     return false;
   1214   }
   1215 
   1216   private MethodInfo matchMethod(ArrayList<MethodInfo> methods, String name, String[] params,
   1217       String[] dimensions, boolean varargs) {
   1218     for (MethodInfo method : methods) {
   1219       if (method.name().equals(name)) {
   1220         if (params == null) {
   1221           return method;
   1222         } else {
   1223           if (method.matchesParams(params, dimensions, varargs)) {
   1224             return method;
   1225           }
   1226         }
   1227       }
   1228     }
   1229     return null;
   1230   }
   1231 
   1232   public MethodInfo findMethod(String name, String[] params, String[] dimensions, boolean varargs) {
   1233     // first look on our class, and our superclasses
   1234 
   1235     // for methods
   1236     MethodInfo rv;
   1237     rv = matchMethod(methods(), name, params, dimensions, varargs);
   1238 
   1239     if (rv != null) {
   1240       return rv;
   1241     }
   1242 
   1243     // for constructors
   1244     rv = matchMethod(constructors(), name, params, dimensions, varargs);
   1245     if (rv != null) {
   1246       return rv;
   1247     }
   1248 
   1249     // then recursively look at our containing class
   1250     ClassInfo containing = containingClass();
   1251     if (containing != null) {
   1252       return containing.findMethod(name, params, dimensions, varargs);
   1253     }
   1254 
   1255     return null;
   1256   }
   1257 
   1258   public boolean supportsMethod(MethodInfo method) {
   1259     for (MethodInfo m : methods()) {
   1260       if (m.getHashableName().equals(method.getHashableName())) {
   1261         return true;
   1262       }
   1263     }
   1264     return false;
   1265   }
   1266 
   1267   private ClassInfo searchInnerClasses(String[] nameParts, int index) {
   1268     String part = nameParts[index];
   1269 
   1270     ArrayList<ClassInfo> inners = mInnerClasses;
   1271     for (ClassInfo in : inners) {
   1272       String[] innerParts = in.nameParts();
   1273       if (part.equals(innerParts[innerParts.length - 1])) {
   1274         if (index == nameParts.length - 1) {
   1275           return in;
   1276         } else {
   1277           return in.searchInnerClasses(nameParts, index + 1);
   1278         }
   1279       }
   1280     }
   1281     return null;
   1282   }
   1283 
   1284   public ClassInfo extendedFindClass(String className) {
   1285     // ClassDoc.findClass has this bug that we're working around here:
   1286     // If you have a class PackageManager with an inner class PackageInfo
   1287     // and you call it with "PackageInfo" it doesn't find it.
   1288     return searchInnerClasses(className.split("\\."), 0);
   1289   }
   1290 
   1291   public ClassInfo findClass(String className) {
   1292     return Converter.obtainClass(mClass.findClass(className));
   1293   }
   1294 
   1295   public ClassInfo findInnerClass(String className) {
   1296     // ClassDoc.findClass won't find inner classes. To deal with that,
   1297     // we try what they gave us first, but if that didn't work, then
   1298     // we see if there are any periods in className, and start searching
   1299     // from there.
   1300     String[] nodes = className.split("\\.");
   1301     ClassDoc cl = mClass;
   1302     for (String n : nodes) {
   1303       cl = cl.findClass(n);
   1304       if (cl == null) {
   1305         return null;
   1306       }
   1307     }
   1308     return Converter.obtainClass(cl);
   1309   }
   1310 
   1311   public FieldInfo findField(String name) {
   1312     // first look on our class, and our superclasses
   1313     for (FieldInfo f : fields()) {
   1314       if (f.name().equals(name)) {
   1315         return f;
   1316       }
   1317     }
   1318 
   1319     // then look at our enum constants (these are really fields, maybe
   1320     // they should be mixed into fields(). not sure)
   1321     for (FieldInfo f : enumConstants()) {
   1322       if (f.name().equals(name)) {
   1323         return f;
   1324       }
   1325     }
   1326 
   1327     // then recursively look at our containing class
   1328     ClassInfo containing = containingClass();
   1329     if (containing != null) {
   1330       return containing.findField(name);
   1331     }
   1332 
   1333     return null;
   1334   }
   1335 
   1336   public static ClassInfo[] sortByName(ClassInfo[] classes) {
   1337     int i;
   1338     Sorter[] sorted = new Sorter[classes.length];
   1339     for (i = 0; i < sorted.length; i++) {
   1340       ClassInfo cl = classes[i];
   1341       sorted[i] = new Sorter(cl.name(), cl);
   1342     }
   1343 
   1344     Arrays.sort(sorted);
   1345 
   1346     ClassInfo[] rv = new ClassInfo[classes.length];
   1347     for (i = 0; i < rv.length; i++) {
   1348       rv[i] = (ClassInfo) sorted[i].data;
   1349     }
   1350 
   1351     return rv;
   1352   }
   1353 
   1354   public boolean equals(ClassInfo that) {
   1355     if (that != null) {
   1356       return this.qualifiedName().equals(that.qualifiedName());
   1357     } else {
   1358       return false;
   1359     }
   1360   }
   1361 
   1362   public void setNonWrittenConstructors(ArrayList<MethodInfo> nonWritten) {
   1363     mNonWrittenConstructors = nonWritten;
   1364   }
   1365 
   1366   public ArrayList<MethodInfo> getNonWrittenConstructors() {
   1367     return mNonWrittenConstructors;
   1368   }
   1369 
   1370   public String kind() {
   1371     if (isOrdinaryClass()) {
   1372       return "class";
   1373     } else if (isInterface()) {
   1374       return "interface";
   1375     } else if (isEnum()) {
   1376       return "enum";
   1377     } else if (isError()) {
   1378       return "class";
   1379     } else if (isException()) {
   1380       return "class";
   1381     } else if (isAnnotation()) {
   1382       return "@interface";
   1383     }
   1384     return null;
   1385   }
   1386 
   1387   public String scope() {
   1388     if (isPublic()) {
   1389       return "public";
   1390     } else if (isProtected()) {
   1391       return "protected";
   1392     } else if (isPackagePrivate()) {
   1393       return "";
   1394     } else if (isPrivate()) {
   1395       return "private";
   1396     } else {
   1397       throw new RuntimeException("invalid scope for object " + this);
   1398     }
   1399   }
   1400 
   1401   public void setHiddenMethods(ArrayList<MethodInfo> mInfo) {
   1402     mHiddenMethods = mInfo;
   1403   }
   1404 
   1405   public ArrayList<MethodInfo> getHiddenMethods() {
   1406     return mHiddenMethods;
   1407   }
   1408 
   1409   @Override
   1410   public String toString() {
   1411     return this.qualifiedName();
   1412   }
   1413 
   1414   public void setReasonIncluded(String reason) {
   1415     mReasonIncluded = reason;
   1416   }
   1417 
   1418   public String getReasonIncluded() {
   1419     return mReasonIncluded;
   1420   }
   1421 
   1422   private ClassDoc mClass;
   1423 
   1424   // ctor
   1425   private boolean mIsPublic;
   1426   private boolean mIsProtected;
   1427   private boolean mIsPackagePrivate;
   1428   private boolean mIsPrivate;
   1429   private boolean mIsStatic;
   1430   private boolean mIsInterface;
   1431   private boolean mIsAbstract;
   1432   private boolean mIsOrdinaryClass;
   1433   private boolean mIsException;
   1434   private boolean mIsError;
   1435   private boolean mIsEnum;
   1436   private boolean mIsAnnotation;
   1437   private boolean mIsFinal;
   1438   private boolean mIsIncluded;
   1439   private String mName;
   1440   private String mQualifiedName;
   1441   private String mQualifiedTypeName;
   1442   private boolean mIsPrimitive;
   1443   private TypeInfo mTypeInfo;
   1444   private String[] mNameParts;
   1445 
   1446   // init
   1447   private ArrayList<ClassInfo> mRealInterfaces = new ArrayList<ClassInfo>();
   1448   private ArrayList<ClassInfo> mInterfaces;
   1449   private ArrayList<TypeInfo> mRealInterfaceTypes;
   1450   private ArrayList<ClassInfo> mInnerClasses;
   1451   private ArrayList<MethodInfo> mAllConstructors = new ArrayList<MethodInfo>();
   1452   private ArrayList<MethodInfo> mAllSelfMethods = new ArrayList<MethodInfo>();
   1453   private ArrayList<MethodInfo> mAnnotationElements = new ArrayList<MethodInfo>(); // if this class is an annotation
   1454   private ArrayList<FieldInfo> mAllSelfFields = new ArrayList<FieldInfo>();
   1455   private ArrayList<FieldInfo> mEnumConstants = new ArrayList<FieldInfo>();
   1456   private PackageInfo mContainingPackage;
   1457   private ClassInfo mContainingClass;
   1458   private ClassInfo mRealSuperclass;
   1459   private TypeInfo mRealSuperclassType;
   1460   private ClassInfo mSuperclass;
   1461   private ArrayList<AnnotationInstanceInfo> mAnnotations;
   1462   private boolean mSuperclassInit;
   1463   private boolean mDeprecatedKnown;
   1464 
   1465   // lazy
   1466   private ArrayList<MethodInfo> mConstructors;
   1467   private ArrayList<ClassInfo> mRealInnerClasses;
   1468   private ArrayList<MethodInfo> mSelfMethods;
   1469   private ArrayList<FieldInfo> mSelfFields;
   1470   private ArrayList<AttributeInfo> mSelfAttributes;
   1471   private ArrayList<MethodInfo> mMethods;
   1472   private ArrayList<FieldInfo> mFields;
   1473   private ArrayList<TypeInfo> mTypeParameters;
   1474   private ArrayList<MethodInfo> mHiddenMethods;
   1475   private int mHidden = -1;
   1476   private int mCheckLevel = -1;
   1477   private String mReasonIncluded;
   1478   private ArrayList<MethodInfo> mNonWrittenConstructors;
   1479   private boolean mIsDeprecated;
   1480 
   1481   // TODO: Temporary members from apicheck migration.
   1482   private HashMap<String, MethodInfo> mApiCheckConstructors = new HashMap<String, MethodInfo>();
   1483   private HashMap<String, MethodInfo> mApiCheckMethods = new HashMap<String, MethodInfo>();
   1484   private HashMap<String, FieldInfo> mApiCheckFields = new HashMap<String, FieldInfo>();
   1485   private HashMap<String, FieldInfo> mApiCheckEnumConstants = new HashMap<String, FieldInfo>();
   1486 
   1487   // Resolutions
   1488   private ArrayList<Resolution> mResolutions;
   1489 
   1490   /**
   1491    * Returns true if {@code cl} implements the interface {@code iface} either by either being that
   1492    * interface, implementing that interface or extending a type that implements the interface.
   1493    */
   1494   private boolean implementsInterface(ClassInfo cl, String iface) {
   1495     if (cl.qualifiedName().equals(iface)) {
   1496       return true;
   1497     }
   1498     for (ClassInfo clImplements : cl.interfaces()) {
   1499       if (implementsInterface(clImplements, iface)) {
   1500         return true;
   1501       }
   1502     }
   1503     if (cl.mSuperclass != null && implementsInterface(cl.mSuperclass, iface)) {
   1504       return true;
   1505     }
   1506     return false;
   1507   }
   1508 
   1509   public void addInterface(ClassInfo iface) {
   1510     mRealInterfaces.add(iface);
   1511   }
   1512 
   1513   public void addConstructor(MethodInfo ctor) {
   1514     mApiCheckConstructors.put(ctor.getHashableName(), ctor);
   1515 
   1516     mAllConstructors.add(ctor);
   1517     mConstructors = null; // flush this, hopefully it hasn't been used yet.
   1518   }
   1519 
   1520   public void addField(FieldInfo field) {
   1521     mApiCheckFields.put(field.name(), field);
   1522 
   1523     mAllSelfFields.add(field);
   1524 
   1525     mSelfFields = null; // flush this, hopefully it hasn't been used yet.
   1526   }
   1527 
   1528   public void addEnumConstant(FieldInfo field) {
   1529     mApiCheckEnumConstants.put(field.name(), field);
   1530 
   1531     mEnumConstants.add(field);
   1532   }
   1533 
   1534   public void setSuperClass(ClassInfo superclass) {
   1535     mRealSuperclass = superclass;
   1536     mSuperclass = superclass;
   1537   }
   1538 
   1539   public Map<String, MethodInfo> allConstructorsMap() {
   1540     return mApiCheckConstructors;
   1541   }
   1542 
   1543   public Map<String, FieldInfo> allFields() {
   1544     return mApiCheckFields;
   1545   }
   1546 
   1547   /**
   1548    * Returns all methods defined directly in this class. For a list of all
   1549    * methods supported by this class, see {@link #methods()}.
   1550    */
   1551   public Map<String, MethodInfo> allMethods() {
   1552     return mApiCheckMethods;
   1553   }
   1554 
   1555   /**
   1556    * Returns the class hierarchy for this class, starting with this class.
   1557    */
   1558   public Iterable<ClassInfo> hierarchy() {
   1559     List<ClassInfo> result = new ArrayList<ClassInfo>(4);
   1560     for (ClassInfo c = this; c != null; c = c.mSuperclass) {
   1561       result.add(c);
   1562     }
   1563     return result;
   1564   }
   1565 
   1566   public String superclassName() {
   1567     if (mSuperclass == null) {
   1568       if (mQualifiedName.equals("java.lang.Object")) {
   1569         return null;
   1570       }
   1571       throw new UnsupportedOperationException("Superclass not set for " + qualifiedName());
   1572     }
   1573     return mSuperclass.mQualifiedName;
   1574   }
   1575 
   1576   public void setAnnotations(ArrayList<AnnotationInstanceInfo> annotations) {
   1577     mAnnotations = annotations;
   1578   }
   1579 
   1580   public boolean isConsistent(ClassInfo cl) {
   1581     boolean consistent = true;
   1582 
   1583     if (isInterface() != cl.isInterface()) {
   1584       Errors.error(Errors.CHANGED_CLASS, cl.position(), "Class " + cl.qualifiedName()
   1585           + " changed class/interface declaration");
   1586       consistent = false;
   1587     }
   1588     for (ClassInfo iface : mRealInterfaces) {
   1589       if (!implementsInterface(cl, iface.mQualifiedName)) {
   1590         Errors.error(Errors.REMOVED_INTERFACE, cl.position(), "Class " + qualifiedName()
   1591             + " no longer implements " + iface);
   1592       }
   1593     }
   1594     for (ClassInfo iface : cl.mRealInterfaces) {
   1595       if (!implementsInterface(this, iface.mQualifiedName)) {
   1596         Errors.error(Errors.ADDED_INTERFACE, cl.position(), "Added interface " + iface
   1597             + " to class " + qualifiedName());
   1598         consistent = false;
   1599       }
   1600     }
   1601 
   1602     for (MethodInfo mInfo : mApiCheckMethods.values()) {
   1603       if (cl.mApiCheckMethods.containsKey(mInfo.getHashableName())) {
   1604         if (!mInfo.isConsistent(cl.mApiCheckMethods.get(mInfo.getHashableName()))) {
   1605           consistent = false;
   1606         }
   1607       } else {
   1608         /*
   1609          * This class formerly provided this method directly, and now does not. Check our ancestry
   1610          * to see if there's an inherited version that still fulfills the API requirement.
   1611          */
   1612         MethodInfo mi = ClassInfo.overriddenMethod(mInfo, cl);
   1613         if (mi == null) {
   1614           mi = ClassInfo.interfaceMethod(mInfo, cl);
   1615         }
   1616         if (mi == null) {
   1617           Errors.error(Errors.REMOVED_METHOD, mInfo.position(), "Removed public method "
   1618               + mInfo.qualifiedName());
   1619           consistent = false;
   1620         }
   1621       }
   1622     }
   1623     for (MethodInfo mInfo : cl.mApiCheckMethods.values()) {
   1624       if (!mApiCheckMethods.containsKey(mInfo.getHashableName())) {
   1625         /*
   1626          * Similarly to the above, do not fail if this "new" method is really an override of an
   1627          * existing superclass method.
   1628          */
   1629         MethodInfo mi = ClassInfo.overriddenMethod(mInfo, this);
   1630         if (mi == null) {
   1631           Errors.error(Errors.ADDED_METHOD, mInfo.position(), "Added public method "
   1632               + mInfo.qualifiedName());
   1633           consistent = false;
   1634         }
   1635       }
   1636     }
   1637 
   1638     for (MethodInfo mInfo : mApiCheckConstructors.values()) {
   1639       if (cl.mApiCheckConstructors.containsKey(mInfo.getHashableName())) {
   1640         if (!mInfo.isConsistent(cl.mApiCheckConstructors.get(mInfo.getHashableName()))) {
   1641           consistent = false;
   1642         }
   1643       } else {
   1644         Errors.error(Errors.REMOVED_METHOD, mInfo.position(), "Removed public constructor "
   1645             + mInfo.prettySignature());
   1646         consistent = false;
   1647       }
   1648     }
   1649     for (MethodInfo mInfo : cl.mApiCheckConstructors.values()) {
   1650       if (!mApiCheckConstructors.containsKey(mInfo.getHashableName())) {
   1651         Errors.error(Errors.ADDED_METHOD, mInfo.position(), "Added public constructor "
   1652             + mInfo.prettySignature());
   1653         consistent = false;
   1654       }
   1655     }
   1656 
   1657     for (FieldInfo mInfo : mApiCheckFields.values()) {
   1658       if (cl.mApiCheckFields.containsKey(mInfo.name())) {
   1659         if (!mInfo.isConsistent(cl.mApiCheckFields.get(mInfo.name()))) {
   1660           consistent = false;
   1661         }
   1662       } else {
   1663         Errors.error(Errors.REMOVED_FIELD, mInfo.position(), "Removed field "
   1664             + mInfo.qualifiedName());
   1665         consistent = false;
   1666       }
   1667     }
   1668     for (FieldInfo mInfo : cl.mApiCheckFields.values()) {
   1669       if (!mApiCheckFields.containsKey(mInfo.name())) {
   1670         Errors.error(Errors.ADDED_FIELD, mInfo.position(), "Added public field "
   1671             + mInfo.qualifiedName());
   1672         consistent = false;
   1673       }
   1674     }
   1675 
   1676     for (FieldInfo info : mApiCheckEnumConstants.values()) {
   1677       if (cl.mApiCheckEnumConstants.containsKey(info.name())) {
   1678         if (!info.isConsistent(cl.mApiCheckEnumConstants.get(info.name()))) {
   1679           consistent = false;
   1680         }
   1681       } else {
   1682         Errors.error(Errors.REMOVED_FIELD, info.position(), "Removed enum constant "
   1683             + info.qualifiedName());
   1684         consistent = false;
   1685       }
   1686     }
   1687     for (FieldInfo info : cl.mApiCheckEnumConstants.values()) {
   1688       if (!mApiCheckEnumConstants.containsKey(info.name())) {
   1689         Errors.error(Errors.ADDED_FIELD, info.position(), "Added enum constant "
   1690             + info.qualifiedName());
   1691         consistent = false;
   1692       }
   1693     }
   1694 
   1695     if (mIsAbstract != cl.mIsAbstract) {
   1696       consistent = false;
   1697       Errors.error(Errors.CHANGED_ABSTRACT, cl.position(), "Class " + cl.qualifiedName()
   1698           + " changed abstract qualifier");
   1699     }
   1700 
   1701     if (!mIsFinal && cl.mIsFinal) {
   1702       /*
   1703        * It is safe to make a class final if it did not previously have any public
   1704        * constructors because it was impossible for an application to create a subclass.
   1705        */
   1706       if (mApiCheckConstructors.isEmpty()) {
   1707         consistent = false;
   1708         Errors.error(Errors.ADDED_FINAL_UNINSTANTIABLE, cl.position(),
   1709             "Class " + cl.qualifiedName() + " added final qualifier but "
   1710             + "was previously uninstantiable and therefore could not be subclassed");
   1711       } else {
   1712         consistent = false;
   1713         Errors.error(Errors.ADDED_FINAL, cl.position(), "Class " + cl.qualifiedName()
   1714             + " added final qualifier");
   1715       }
   1716     } else if (mIsFinal && !cl.mIsFinal) {
   1717       consistent = false;
   1718       Errors.error(Errors.REMOVED_FINAL, cl.position(), "Class " + cl.qualifiedName()
   1719           + " removed final qualifier");
   1720     }
   1721 
   1722     if (mIsStatic != cl.mIsStatic) {
   1723       consistent = false;
   1724       Errors.error(Errors.CHANGED_STATIC, cl.position(), "Class " + cl.qualifiedName()
   1725           + " changed static qualifier");
   1726     }
   1727 
   1728     if (!scope().equals(cl.scope())) {
   1729       consistent = false;
   1730       Errors.error(Errors.CHANGED_SCOPE, cl.position(), "Class " + cl.qualifiedName()
   1731           + " scope changed from " + scope() + " to " + cl.scope());
   1732     }
   1733 
   1734     if (!isDeprecated() == cl.isDeprecated()) {
   1735       consistent = false;
   1736       Errors.error(Errors.CHANGED_DEPRECATED, cl.position(), "Class " + cl.qualifiedName()
   1737           + " has changed deprecation state");
   1738     }
   1739 
   1740     if (superclassName() != null) {
   1741       if (cl.superclassName() == null || !superclassName().equals(cl.superclassName())) {
   1742         consistent = false;
   1743         Errors.error(Errors.CHANGED_SUPERCLASS, cl.position(), "Class " + qualifiedName()
   1744             + " superclass changed from " + superclassName() + " to " + cl.superclassName());
   1745       }
   1746     } else if (cl.superclassName() != null) {
   1747       consistent = false;
   1748       Errors.error(Errors.CHANGED_SUPERCLASS, cl.position(), "Class " + qualifiedName()
   1749           + " superclass changed from " + "null to " + cl.superclassName());
   1750     }
   1751 
   1752     return consistent;
   1753   }
   1754 
   1755   // Find a superclass implementation of the given method.
   1756   public static MethodInfo overriddenMethod(MethodInfo candidate, ClassInfo newClassObj) {
   1757     if (newClassObj == null) {
   1758       return null;
   1759     }
   1760     for (MethodInfo mi : newClassObj.mApiCheckMethods.values()) {
   1761       if (mi.matches(candidate)) {
   1762         // found it
   1763         return mi;
   1764       }
   1765     }
   1766 
   1767     // not found here. recursively search ancestors
   1768     return ClassInfo.overriddenMethod(candidate, newClassObj.mSuperclass);
   1769   }
   1770 
   1771   // Find a superinterface declaration of the given method.
   1772   public static MethodInfo interfaceMethod(MethodInfo candidate, ClassInfo newClassObj) {
   1773     if (newClassObj == null) {
   1774       return null;
   1775     }
   1776     for (ClassInfo interfaceInfo : newClassObj.interfaces()) {
   1777       for (MethodInfo mi : interfaceInfo.mApiCheckMethods.values()) {
   1778         if (mi.matches(candidate)) {
   1779           return mi;
   1780         }
   1781       }
   1782     }
   1783     return ClassInfo.interfaceMethod(candidate, newClassObj.mSuperclass);
   1784   }
   1785 
   1786   public boolean hasConstructor(MethodInfo constructor) {
   1787     String name = constructor.getHashableName();
   1788     for (MethodInfo ctor : mApiCheckConstructors.values()) {
   1789       if (name.equals(ctor.getHashableName())) {
   1790         return true;
   1791       }
   1792     }
   1793     return false;
   1794   }
   1795 
   1796   public void setTypeInfo(TypeInfo typeInfo) {
   1797     mTypeInfo = typeInfo;
   1798   }
   1799 
   1800   public TypeInfo type() {
   1801       return mTypeInfo;
   1802   }
   1803 
   1804   public void addInnerClass(ClassInfo innerClass) {
   1805       if (mInnerClasses == null) {
   1806           mInnerClasses = new ArrayList<ClassInfo>();
   1807       }
   1808 
   1809       mInnerClasses.add(innerClass);
   1810   }
   1811 
   1812   public void setContainingClass(ClassInfo containingClass) {
   1813       mContainingClass = containingClass;
   1814   }
   1815 
   1816   public void setSuperclassType(TypeInfo superclassType) {
   1817       mRealSuperclassType = superclassType;
   1818   }
   1819 
   1820   public void printResolutions() {
   1821       if (mResolutions == null || mResolutions.isEmpty()) {
   1822           return;
   1823       }
   1824 
   1825       System.out.println("Resolutions for Class " + mName + ":");
   1826 
   1827       for (Resolution r : mResolutions) {
   1828           System.out.println(r);
   1829       }
   1830   }
   1831 
   1832   public void addResolution(Resolution resolution) {
   1833       if (mResolutions == null) {
   1834           mResolutions = new ArrayList<Resolution>();
   1835       }
   1836 
   1837       mResolutions.add(resolution);
   1838   }
   1839 
   1840   public boolean resolveResolutions() {
   1841       ArrayList<Resolution> resolutions = mResolutions;
   1842       mResolutions = new ArrayList<Resolution>();
   1843 
   1844       boolean allResolved = true;
   1845       for (Resolution resolution : resolutions) {
   1846           StringBuilder qualifiedClassName = new StringBuilder();
   1847           InfoBuilder.resolveQualifiedName(resolution.getValue(), qualifiedClassName,
   1848                   resolution.getInfoBuilder());
   1849 
   1850           // if we still couldn't resolve it, save it for the next pass
   1851           if ("".equals(qualifiedClassName.toString())) {
   1852               mResolutions.add(resolution);
   1853               allResolved = false;
   1854           } else if ("superclassQualifiedName".equals(resolution.getVariable())) {
   1855               setSuperClass(InfoBuilder.Caches.obtainClass(qualifiedClassName.toString()));
   1856           } else if ("interfaceQualifiedName".equals(resolution.getVariable())) {
   1857               addInterface(InfoBuilder.Caches.obtainClass(qualifiedClassName.toString()));
   1858           }
   1859       }
   1860 
   1861       return allResolved;
   1862   }
   1863 }
   1864