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