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