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