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.ArrayDeque;
23import java.util.ArrayList;
24import java.util.Arrays;
25import java.util.Collection;
26import java.util.Collections;
27import java.util.Comparator;
28import java.util.HashMap;
29import java.util.HashSet;
30import java.util.Iterator;
31import java.util.LinkedHashSet;
32import java.util.LinkedList;
33import java.util.List;
34import java.util.Map;
35import java.util.Objects;
36import java.util.Queue;
37import java.util.Set;
38import java.util.TreeMap;
39import java.util.function.Predicate;
40
41public class ClassInfo extends DocInfo implements ContainerInfo, Comparable, Scoped, Resolvable {
42
43  /**
44   * Contains a ClassInfo and a TypeInfo.
45   * <p>
46   * This is used to match a ClassInfo, which doesn't keep track of its type parameters
47   * and a type which does.
48   */
49  private class ClassTypePair {
50    private final ClassInfo mClassInfo;
51    private final TypeInfo mTypeInfo;
52
53    public ClassTypePair(ClassInfo cl, TypeInfo t) {
54      mClassInfo = cl;
55      mTypeInfo = t;
56    }
57
58    public ClassInfo classInfo() {
59      return mClassInfo;
60    }
61
62    public TypeInfo typeInfo() {
63      return mTypeInfo;
64    }
65
66    public Map<String, TypeInfo> getTypeArgumentMapping() {
67      return TypeInfo.getTypeArgumentMapping(classInfo(), typeInfo());
68    }
69  }
70
71  public static final Comparator<ClassInfo> comparator = new Comparator<ClassInfo>() {
72    public int compare(ClassInfo a, ClassInfo b) {
73      return a.name().compareTo(b.name());
74    }
75  };
76
77  public static final Comparator<ClassInfo> qualifiedComparator = new Comparator<ClassInfo>() {
78    public int compare(ClassInfo a, ClassInfo b) {
79      return a.qualifiedName().compareTo(b.qualifiedName());
80    }
81  };
82
83  /**
84   * Constructs a stub representation of a class.
85   */
86  public ClassInfo(String qualifiedName) {
87    super("", SourcePositionInfo.UNKNOWN);
88    mQualifiedName = qualifiedName;
89    if (qualifiedName.lastIndexOf('.') != -1) {
90      mName = qualifiedName.substring(qualifiedName.lastIndexOf('.') + 1);
91    } else {
92      mName = qualifiedName;
93    }
94  }
95
96  public ClassInfo(ClassDoc cl, String rawCommentText, SourcePositionInfo position,
97          boolean isPublic, boolean isProtected, boolean isPackagePrivate, boolean isPrivate,
98          boolean isStatic, boolean isInterface, boolean isAbstract, boolean isOrdinaryClass,
99          boolean isException, boolean isError, boolean isEnum, boolean isAnnotation, boolean isFinal,
100          boolean isIncluded, String name, String qualifiedName, String qualifiedTypeName,
101          boolean isPrimitive) {
102      super(rawCommentText, position);
103
104      initialize(rawCommentText, position,
105              isPublic, isProtected, isPackagePrivate, isPrivate,
106              isStatic, isInterface, isAbstract, isOrdinaryClass,
107              isException, isError, isEnum, isAnnotation, isFinal,
108              isIncluded, qualifiedTypeName, isPrimitive, null);
109
110      mName = name;
111      mQualifiedName = qualifiedName;
112      mNameParts = name.split("\\.");
113      mClass = cl;
114  }
115
116  public void initialize(String rawCommentText, SourcePositionInfo position,
117          boolean isPublic, boolean isProtected, boolean isPackagePrivate, boolean isPrivate,
118          boolean isStatic, boolean isInterface, boolean isAbstract, boolean isOrdinaryClass,
119          boolean isException, boolean isError, boolean isEnum, boolean isAnnotation, boolean isFinal,
120          boolean isIncluded, String qualifiedTypeName, boolean isPrimitive, ArrayList<AnnotationInstanceInfo> annotations) {
121
122    // calls
123    setPosition(position);
124    setRawCommentText(rawCommentText);
125    mIsPublic = isPublic;
126    mIsProtected = isProtected;
127    mIsPackagePrivate = isPackagePrivate;
128    mIsPrivate = isPrivate;
129    mIsStatic = isStatic;
130    mIsInterface = isInterface;
131    mIsAbstract = isAbstract;
132    mIsOrdinaryClass = isOrdinaryClass;
133    mIsException = isException;
134    mIsError = isError;
135    mIsEnum = isEnum;
136    mIsAnnotation = isAnnotation;
137    mIsFinal = isFinal;
138    mIsIncluded = isIncluded;
139    mQualifiedTypeName = qualifiedTypeName;
140    mIsPrimitive = isPrimitive;
141    mAnnotations = annotations;
142    mShowAnnotations = AnnotationInstanceInfo.getShowAnnotationsIntersection(annotations);
143    mHideAnnotations = AnnotationInstanceInfo.getHideAnnotationsIntersection(annotations);
144  }
145
146  public void init(TypeInfo typeInfo, ArrayList<ClassInfo> interfaces,
147          ArrayList<TypeInfo> interfaceTypes, ArrayList<ClassInfo> innerClasses,
148          ArrayList<MethodInfo> constructors, ArrayList<MethodInfo> methods,
149          ArrayList<MethodInfo> annotationElements, ArrayList<FieldInfo> fields,
150          ArrayList<FieldInfo> enumConstants, PackageInfo containingPackage,
151          ClassInfo containingClass, ClassInfo superclass,
152      TypeInfo superclassType, ArrayList<AnnotationInstanceInfo> annotations) {
153    mTypeInfo = typeInfo;
154    mRealInterfaces = new ArrayList<ClassInfo>(interfaces);
155    mRealInterfaceTypes = interfaceTypes;
156    mInnerClasses = innerClasses;
157    // mAllConstructors will not contain *all* constructors. Only the constructors that pass
158    // checkLevel. @see {@link Converter#convertMethods(ConstructorDoc[])}
159    mAllConstructors = constructors;
160    // mAllSelfMethods will not contain *all* self methods. Only the methods that pass
161    // checkLevel. @see {@link Converter#convertMethods(MethodDoc[])}
162    mAllSelfMethods = methods;
163    mAnnotationElements = annotationElements;
164    // mAllSelfFields will not contain *all* self fields. Only the fields that pass
165    // checkLevel. @see {@link Converter#convetFields(FieldDoc[])}
166    mAllSelfFields = fields;
167    // mEnumConstants will not contain *all* enum constants. Only the enums that pass
168    // checkLevel. @see {@link Converter#convetFields(FieldDoc[])}
169    mEnumConstants = enumConstants;
170    mContainingPackage = containingPackage;
171    mContainingClass = containingClass;
172    mRealSuperclass = superclass;
173    mRealSuperclassType = superclassType;
174    mAnnotations = annotations;
175    mShowAnnotations = AnnotationInstanceInfo.getShowAnnotationsIntersection(annotations);
176    mHideAnnotations = AnnotationInstanceInfo.getHideAnnotationsIntersection(annotations);
177
178    // after providing new methods and new superclass info,clear any cached
179    // lists of self + superclass methods, ctors, etc.
180    mSuperclassInit = false;
181    mConstructors = null;
182    mMethods = null;
183    mSelfMethods = null;
184    mFields = null;
185    mSelfFields = null;
186    mSelfAttributes = null;
187    mDeprecatedKnown = false;
188    mSuperclassesWithTypes = null;
189    mInterfacesWithTypes = null;
190    mAllInterfacesWithTypes = null;
191
192    Collections.sort(mEnumConstants, FieldInfo.comparator);
193    Collections.sort(mInnerClasses, ClassInfo.comparator);
194  }
195
196  public void init2() {
197    // calling this here forces the AttrTagInfo objects to be linked to the AttribtueInfo
198    // objects
199    selfAttributes();
200  }
201
202  public void init3(ArrayList<TypeInfo> types, ArrayList<ClassInfo> realInnerClasses) {
203    mTypeParameters = types;
204    mRealInnerClasses = realInnerClasses;
205  }
206
207  public class ClassMemberInfo extends MemberInfo {
208    public ClassMemberInfo() {
209      super(ClassInfo.this.getRawCommentText(), ClassInfo.this.name(), ClassInfo.this.name(),
210          ClassInfo.this, ClassInfo.this, ClassInfo.this.isPublic(), ClassInfo.this.isProtected(),
211          ClassInfo.this.isPackagePrivate(), ClassInfo.this.isPrivate(), ClassInfo.this.isFinal(),
212          ClassInfo.this.isStatic(), false, ClassInfo.this.kind(), ClassInfo.this.position(),
213          ClassInfo.this.annotations());
214    }
215
216    @Override
217    public boolean isExecutable() {
218      return false;
219    }
220  }
221
222  /**
223   * Return representation of this class as {@link MemberInfo}. This normally
224   * doesn't make any sense, but it's useful for {@link Predicate} testing.
225   */
226  public MemberInfo asMemberInfo() {
227    return new ClassMemberInfo();
228  }
229
230  public ArrayList<ClassInfo> getRealInnerClasses() {
231    return mRealInnerClasses;
232  }
233
234  public ArrayList<TypeInfo> getTypeParameters() {
235    return mTypeParameters;
236  }
237
238  /**
239   * @return true if this class needs to be shown in api txt, based on the
240   * hidden/removed status of the class and the show level setting in doclava.
241   */
242  public boolean checkLevel() {
243    if (mCheckLevel == null) {
244      mCheckLevel = Doclava.checkLevel(mIsPublic, mIsProtected, mIsPackagePrivate, mIsPrivate,
245          isHiddenOrRemoved());
246    }
247
248    return mCheckLevel;
249  }
250
251  public int compareTo(Object that) {
252    if (that instanceof ClassInfo) {
253      return mQualifiedName.compareTo(((ClassInfo) that).mQualifiedName);
254    } else {
255      return this.hashCode() - that.hashCode();
256    }
257  }
258
259  @Override
260  public ContainerInfo parent() {
261    return this;
262  }
263
264  public boolean isPublic() {
265    return mIsPublic;
266  }
267
268  public boolean isProtected() {
269    return mIsProtected;
270  }
271
272  public boolean isPackagePrivate() {
273    return mIsPackagePrivate;
274  }
275
276  public boolean isPrivate() {
277    return mIsPrivate;
278  }
279
280  public boolean isStatic() {
281    return mIsStatic;
282  }
283
284  public boolean isInterface() {
285    return mIsInterface;
286  }
287
288  public boolean isAbstract() {
289    return mIsAbstract;
290  }
291
292  public PackageInfo containingPackage() {
293    return mContainingPackage;
294  }
295
296  public ClassInfo containingClass() {
297    return mContainingClass;
298  }
299
300  public boolean isOrdinaryClass() {
301    return mIsOrdinaryClass;
302  }
303
304  public boolean isException() {
305    return mIsException;
306  }
307
308  public boolean isError() {
309    return mIsError;
310  }
311
312  public boolean isEnum() {
313    return mIsEnum;
314  }
315
316  public boolean isAnnotation() {
317    return mIsAnnotation;
318  }
319
320  public boolean isFinal() {
321    return mIsFinal;
322  }
323
324  public boolean isEffectivelyFinal() {
325    return mIsFinal || mApiCheckConstructors.isEmpty();
326  }
327
328  public boolean isIncluded() {
329    return mIsIncluded;
330  }
331
332  public HashSet<String> typeVariables() {
333    HashSet<String> result = TypeInfo.typeVariables(mTypeInfo.typeArguments());
334    ClassInfo cl = containingClass();
335    while (cl != null) {
336      ArrayList<TypeInfo> types = cl.asTypeInfo().typeArguments();
337      if (types != null) {
338        TypeInfo.typeVariables(types, result);
339      }
340      cl = cl.containingClass();
341    }
342    return result;
343  }
344
345  public TypeInfo getTypeParameter(String qualifiedTypeName) {
346      List<TypeInfo> parameters = mTypeInfo.typeArguments();
347      if (parameters == null) {
348          return null;
349      }
350      for (TypeInfo parameter : parameters) {
351          if (parameter.qualifiedTypeName().equals(qualifiedTypeName)) {
352              return parameter;
353          }
354      }
355      return null;
356  }
357
358  /**
359   * List of only direct interface's classes, without worrying about type param mapping.
360   * This can't be lazy loaded, because its overloads depend on changing type parameters
361   * passed in from the callers.
362   */
363  private List<ClassTypePair> justMyInterfacesWithTypes() {
364    return justMyInterfacesWithTypes(Collections.<String, TypeInfo>emptyMap());
365  }
366
367  /**
368   * List of only direct interface's classes and their parameterized types.
369   * This can't be lazy loaded, because of the passed in typeArgumentsMap.
370   */
371  private List<ClassTypePair> justMyInterfacesWithTypes(Map<String, TypeInfo> typeArgumentsMap) {
372    if (mRealInterfaces == null || mRealInterfaceTypes == null) {
373      return Collections.<ClassTypePair>emptyList();
374    }
375
376    List<ClassTypePair> list = new ArrayList<ClassTypePair>();
377    for (int i = 0; i < mRealInterfaces.size(); i++) {
378      ClassInfo iface = mRealInterfaces.get(i);
379      TypeInfo type = mRealInterfaceTypes.get(i);
380      if (iface != null && type != null) {
381        type = type.getTypeWithArguments(typeArgumentsMap);
382        if (iface.checkLevel()) {
383          list.add(new ClassTypePair(iface, type));
384        } else {
385          // add the interface's interfaces
386          Map<String, TypeInfo> map = TypeInfo.getTypeArgumentMapping(iface.asTypeInfo(), type);
387          list.addAll(iface.justMyInterfacesWithTypes(map));
388        }
389      }
390    }
391    return list;
392  }
393
394  /**
395   * List of only direct interface's classes, and any hidden superclass's direct interfaces
396   * between this class and the first visible superclass and those interface class's parameterized types.
397   */
398  private ArrayList<ClassTypePair> interfacesWithTypes() {
399    if (mInterfacesWithTypes == null) {
400      mInterfacesWithTypes = new ArrayList<ClassTypePair>();
401
402      Iterator<ClassTypePair> itr = superClassesWithTypes().iterator();
403      // skip the first one, which is this class
404      itr.next();
405      while (itr.hasNext()) {
406        ClassTypePair ctp = itr.next();
407        if (ctp.classInfo().checkLevel()) {
408          break;
409        } else {
410          // fill mInterfacesWithTypes from the hidden superclass
411          mInterfacesWithTypes.addAll(
412              ctp.classInfo().justMyInterfacesWithTypes(ctp.getTypeArgumentMapping()));
413        }
414      }
415      mInterfacesWithTypes.addAll(
416          justMyInterfacesWithTypes());
417    }
418    return mInterfacesWithTypes;
419  }
420
421  /**
422   * List of all interface's classes reachable in this class's inheritance hierarchy
423   * and those interface class's parameterized types.
424   */
425  private ArrayList<ClassTypePair> allInterfacesWithTypes() {
426    if (mAllInterfacesWithTypes == null) {
427        mAllInterfacesWithTypes = new ArrayList<ClassTypePair>();
428        Queue<ClassTypePair> toParse = new ArrayDeque<ClassTypePair>();
429        Set<String> visited = new HashSet<String>();
430
431        Iterator<ClassTypePair> itr = superClassesWithTypes().iterator();
432        // skip the first one, which is this class
433        itr.next();
434        while (itr.hasNext()) {
435          ClassTypePair ctp = itr.next();
436          toParse.addAll(
437              ctp.classInfo().justMyInterfacesWithTypes(ctp.getTypeArgumentMapping()));
438        }
439        toParse.addAll(justMyInterfacesWithTypes());
440        while (!toParse.isEmpty()) {
441          ClassTypePair ctp = toParse.remove();
442          if (!visited.contains(ctp.typeInfo().fullName())) {
443            mAllInterfacesWithTypes.add(ctp);
444            visited.add(ctp.typeInfo().fullName());
445            toParse.addAll(ctp.classInfo().justMyInterfacesWithTypes(ctp.getTypeArgumentMapping()));
446          }
447        }
448    }
449    return mAllInterfacesWithTypes;
450  }
451
452  /**
453   * A list of ClassTypePairs that contain all superclasses
454   * and their corresponding types. The types will have type parameters
455   * cascaded upwards so they match, if any classes along the way set them.
456   * The list includes the current class, and is an ascending order up the
457   * heirarchy tree.
458   * */
459  private ArrayList<ClassTypePair> superClassesWithTypes() {
460    if (mSuperclassesWithTypes == null) {
461      mSuperclassesWithTypes = new ArrayList<ClassTypePair>();
462
463      ClassTypePair lastCtp = new ClassTypePair(this, this.asTypeInfo());
464      mSuperclassesWithTypes.add(lastCtp);
465
466      Map<String, TypeInfo> typeArgumentsMap;
467      ClassInfo superclass = mRealSuperclass;
468      TypeInfo supertype = mRealSuperclassType;
469      TypeInfo nextType;
470      while (superclass != null && supertype != null) {
471        typeArgumentsMap = lastCtp.getTypeArgumentMapping();
472        lastCtp = new ClassTypePair(superclass, supertype.getTypeWithArguments(typeArgumentsMap));
473        mSuperclassesWithTypes.add(lastCtp);
474
475        supertype = superclass.mRealSuperclassType;
476        superclass = superclass.mRealSuperclass;
477      }
478    }
479    return mSuperclassesWithTypes;
480  }
481
482  private static void gatherHiddenInterfaces(ClassInfo cl, HashSet<ClassInfo> interfaces) {
483    for (ClassInfo iface : cl.mRealInterfaces) {
484      if (iface.checkLevel()) {
485        interfaces.add(iface);
486      } else {
487        gatherHiddenInterfaces(iface, interfaces);
488      }
489    }
490  }
491
492  public ArrayList<ClassInfo> interfaces() {
493    if (mInterfaces == null) {
494      if (checkLevel()) {
495        HashSet<ClassInfo> interfaces = new HashSet<ClassInfo>();
496        ClassInfo superclass = mRealSuperclass;
497        while (superclass != null && !superclass.checkLevel()) {
498          gatherHiddenInterfaces(superclass, interfaces);
499          superclass = superclass.mRealSuperclass;
500        }
501        gatherHiddenInterfaces(this, interfaces);
502        mInterfaces = new ArrayList<ClassInfo>(interfaces);
503      } else {
504        // put something here in case someone uses it
505        mInterfaces = new ArrayList<ClassInfo>(mRealInterfaces);
506      }
507      Collections.sort(mInterfaces, ClassInfo.qualifiedComparator);
508    }
509    return mInterfaces;
510  }
511
512  public ArrayList<ClassInfo> realInterfaces() {
513    return mRealInterfaces;
514  }
515
516  ArrayList<TypeInfo> realInterfaceTypes() {
517    return mRealInterfaceTypes;
518  }
519
520  public void addInterfaceType(TypeInfo type) {
521      if (mRealInterfaceTypes == null) {
522          mRealInterfaceTypes = new ArrayList<TypeInfo>();
523      }
524
525      mRealInterfaceTypes.add(type);
526  }
527
528  public String name() {
529    return mName;
530  }
531
532  public String[] nameParts() {
533    return mNameParts;
534  }
535
536  public String leafName() {
537    return mNameParts[mNameParts.length - 1];
538  }
539
540  public String qualifiedName() {
541    return mQualifiedName;
542  }
543
544  public String qualifiedTypeName() {
545    return mQualifiedTypeName;
546  }
547
548  public boolean isPrimitive() {
549    return mIsPrimitive;
550  }
551
552  public ArrayList<MethodInfo> allConstructors() {
553    return mAllConstructors;
554  }
555
556  public ArrayList<MethodInfo> constructors() {
557    if (mConstructors == null) {
558      if (mAllConstructors == null) {
559        return new ArrayList<MethodInfo>();
560      }
561
562      mConstructors = new ArrayList<MethodInfo>();
563      for (MethodInfo m : mAllConstructors) {
564        if (!m.isHiddenOrRemoved()) {
565            mConstructors.add(m);
566        }
567      }
568
569      Collections.sort(mConstructors, MethodInfo.comparator);
570    }
571    return mConstructors;
572  }
573
574  public ArrayList<ClassInfo> innerClasses() {
575    return mInnerClasses;
576  }
577
578  public TagInfo[] inlineTags() {
579    return comment().tags();
580  }
581
582  public TagInfo[] firstSentenceTags() {
583    return comment().briefTags();
584  }
585
586  public void setDeprecated(boolean deprecated) {
587    mDeprecatedKnown = true;
588    mIsDeprecated = deprecated;
589  }
590
591  public boolean isDeprecated() {
592    if (!mDeprecatedKnown) {
593      boolean commentDeprecated = comment().isDeprecated();
594      boolean annotationDeprecated = false;
595      for (AnnotationInstanceInfo annotation : annotations()) {
596        if (annotation.type().qualifiedName().equals("java.lang.Deprecated")) {
597          annotationDeprecated = true;
598          break;
599        }
600      }
601
602      // Check to see that the JavaDoc contains @deprecated AND the method is marked as @Deprecated.
603      // Otherwise, warn.
604      // Note: We only do this for "included" classes (i.e. those we have source code for); we do
605      // not have comments for classes from .class files but we do know whether a class is marked
606      // as @Deprecated.
607      if (isIncluded() && !isHiddenOrRemoved() && commentDeprecated != annotationDeprecated) {
608        Errors.error(Errors.DEPRECATION_MISMATCH, position(), "Class " + qualifiedName()
609            + ": @Deprecated annotation (" + (annotationDeprecated ? "" : "not ")
610            + "present) and @deprecated doc tag (" + (commentDeprecated ? "" : "not ")
611            + "present) do not match");
612      }
613
614      mIsDeprecated = commentDeprecated | annotationDeprecated;
615      mDeprecatedKnown = true;
616    }
617    return mIsDeprecated;
618  }
619
620  public TagInfo[] deprecatedTags() {
621    // Should we also do the interfaces?
622    return comment().deprecatedTags();
623  }
624
625  public ArrayList<MethodInfo> methods() {
626      if (mMethods == null) {
627          TreeMap<String, MethodInfo> all = new TreeMap<String, MethodInfo>();
628
629          ArrayList<ClassInfo> interfaces = interfaces();
630          for (ClassInfo iface : interfaces) {
631            if (iface != null) {
632              for (MethodInfo method : iface.methods()) {
633                all.put(method.getHashableName(), method);
634              }
635            }
636          }
637
638          ClassInfo superclass = superclass();
639          if (superclass != null) {
640            for (MethodInfo method : superclass.methods()) {
641                all.put(method.getHashableName(), method);
642            }
643          }
644
645          for (MethodInfo method : selfMethods()) {
646              all.put(method.getHashableName(), method);
647          }
648
649          mMethods = new ArrayList<MethodInfo>(all.values());
650          Collections.sort(mMethods, MethodInfo.comparator);
651      }
652    return mMethods;
653  }
654
655  public ArrayList<MethodInfo> annotationElements() {
656    return mAnnotationElements;
657  }
658
659  public ArrayList<AnnotationInstanceInfo> annotations() {
660    return mAnnotations;
661  }
662
663  private static void addFields(ClassInfo cl, TreeMap<String, FieldInfo> all) {
664    for (FieldInfo field : cl.fields()) {
665        all.put(field.name(), field);
666    }
667  }
668
669  public ArrayList<FieldInfo> fields() {
670    if (mFields == null) {
671      TreeMap<String, FieldInfo> all = new TreeMap<String, FieldInfo>();
672
673      for (ClassInfo iface : interfaces()) {
674        addFields(iface, all);
675      }
676
677      ClassInfo superclass = superclass();
678      if (superclass != null) {
679        addFields(superclass, all);
680      }
681
682      for (FieldInfo field : selfFields()) {
683        if (!field.isHiddenOrRemoved()) {
684            all.put(field.name(), field);
685        }
686      }
687
688      for (FieldInfo enumConst : mEnumConstants) {
689        if (!enumConst.isHiddenOrRemoved()) {
690            all.put(enumConst.name(), enumConst);
691        }
692      }
693
694      mFields = new ArrayList<FieldInfo>(all.values());
695    }
696    return mFields;
697  }
698
699  public void gatherFields(ClassInfo owner, ClassInfo cl, HashMap<String, FieldInfo> fields) {
700    for (FieldInfo f : cl.selfFields()) {
701      if (f.checkLevel()) {
702        fields.put(f.name(), f.cloneForClass(owner));
703      }
704    }
705  }
706
707  public ArrayList<FieldInfo> selfFields() {
708    if (mSelfFields == null) {
709        HashMap<String, FieldInfo> fields = new HashMap<String, FieldInfo>();
710      // our hidden parents
711      if (mRealSuperclass != null && !mRealSuperclass.checkLevel()) {
712        gatherFields(this, mRealSuperclass, fields);
713      }
714      for (ClassInfo iface : mRealInterfaces) {
715        if (!iface.checkLevel()) {
716          gatherFields(this, iface, fields);
717        }
718      }
719
720      for (FieldInfo f : mAllSelfFields) {
721          if (!f.isHiddenOrRemoved()) {
722              fields.put(f.name(), f);
723          }
724      }
725
726      mSelfFields = new ArrayList<FieldInfo>(fields.values());
727      Collections.sort(mSelfFields, FieldInfo.comparator);
728    }
729    return mSelfFields;
730  }
731
732  public ArrayList<FieldInfo> allSelfFields() {
733    return mAllSelfFields;
734  }
735
736  private void gatherMethods(ClassInfo owner, ClassTypePair ctp, HashMap<String, MethodInfo> methods) {
737    for (MethodInfo m : ctp.classInfo().selfMethods()) {
738      if (m.checkLevel()) {
739        methods.put(m.name() + m.signature(), m.cloneForClass(owner, ctp.getTypeArgumentMapping()));
740      }
741    }
742  }
743
744  public ArrayList<MethodInfo> selfMethods() {
745    if (mSelfMethods == null) {
746        HashMap<String, MethodInfo> methods = new HashMap<String, MethodInfo>();
747      // our hidden parents
748      for (ClassTypePair ctp : superClassesWithTypes()) {
749        // this class is included in this list, so skip it!
750        if (ctp.classInfo() != this) {
751          if (ctp.classInfo().checkLevel()) {
752            break;
753          }
754          gatherMethods(this, ctp, methods);
755        }
756      }
757      for (ClassTypePair ctp : justMyInterfacesWithTypes(Collections.<String, TypeInfo>emptyMap())) {
758        if (!ctp.classInfo().checkLevel()) {
759          gatherMethods(this, ctp, methods);
760        }
761      }
762      // mine
763      if (mAllSelfMethods != null) {
764        for (MethodInfo m : mAllSelfMethods) {
765          if (m.checkLevel()) {
766            methods.put(m.name() + m.signature(), m);
767          }
768        }
769      }
770
771      for (MethodInfo mi : annotationElements()) {
772        if (!mi.isHiddenOrRemoved()) {
773          // add annotation element as a field
774          methods.put(mi.name() + mi.signature(), mi);
775        }
776      }
777
778      // sort it
779      mSelfMethods = new ArrayList<MethodInfo>(methods.values());
780      Collections.sort(mSelfMethods, MethodInfo.comparator);
781    }
782    return mSelfMethods;
783  }
784
785  public ArrayList<MethodInfo> allSelfMethods() {
786    return mAllSelfMethods;
787  }
788
789  /**
790   * @param removedMethods the removed methods regardless of access levels.
791   */
792  public void setRemovedMethods(List<MethodInfo> removedMethods) {
793    Collections.sort(removedMethods, MethodInfo.comparator);
794    mRemovedMethods = Collections.unmodifiableList(removedMethods);
795  }
796
797  public void setExhaustiveConstructors(List<MethodInfo> constructors) {
798    mExhaustiveConstructors = constructors;
799  }
800
801  public void setExhaustiveMethods(List<MethodInfo> methods) {
802    mExhaustiveMethods = methods;
803  }
804
805  public void setExhaustiveEnumConstants(List<FieldInfo> enumConstants) {
806    mExhaustiveEnumConstants = enumConstants;
807  }
808
809  public void setExhaustiveFields(List<FieldInfo> fields) {
810    mExhaustiveFields = fields;
811  }
812
813  /**
814   * @return all methods that are marked as removed, regardless of access levels.
815   * The returned list is sorted and unmodifiable.
816   */
817  public List<MethodInfo> getRemovedMethods() {
818    return mRemovedMethods;
819  }
820
821  public List<MethodInfo> getExhaustiveConstructors() {
822    return mExhaustiveConstructors;
823  }
824
825  public List<MethodInfo> getExhaustiveMethods() {
826    return mExhaustiveMethods;
827  }
828
829  public List<FieldInfo> getExhaustiveEnumConstants() {
830    return mExhaustiveEnumConstants;
831  }
832
833  public List<FieldInfo> getExhaustiveFields() {
834    return mExhaustiveFields;
835  }
836
837  /**
838   * Return list of ancestor classes that contribute to this class through
839   * inheritance. Ordered from most general to most specific with all interfaces
840   * listed before concrete classes.
841   */
842  public List<ClassInfo> gatherAncestorClasses() {
843    LinkedList<ClassInfo> classes = gatherAncestorClasses(new LinkedList<>());
844    classes.removeLast();
845    return classes;
846  }
847
848  private LinkedList<ClassInfo> gatherAncestorClasses(LinkedList<ClassInfo> classes) {
849    classes.add(0, this);
850    if (mRealSuperclass != null) {
851      mRealSuperclass.gatherAncestorClasses(classes);
852    }
853    if (mRealInterfaces != null) {
854      for (ClassInfo clazz : mRealInterfaces) {
855        clazz.gatherAncestorClasses(classes);
856      }
857    }
858    return classes;
859  }
860
861  /**
862   * Return superclass matching the given predicate. When a superclass doesn't
863   * match, we'll keep crawling up the tree until we find someone who matches.
864   */
865  public ClassInfo filteredSuperclass(Predicate<MemberInfo> predicate) {
866    if (mRealSuperclass == null) {
867      return null;
868    } else if (predicate.test(mRealSuperclass.asMemberInfo())) {
869      return mRealSuperclass;
870    } else {
871      return mRealSuperclass.filteredSuperclass(predicate);
872    }
873  }
874
875  /**
876   * Return interfaces matching the given predicate. When a superclass or
877   * interface doesn't match, we'll keep crawling up the tree until we find
878   * someone who matches.
879   */
880  public Collection<ClassInfo> filteredInterfaces(Predicate<MemberInfo> predicate) {
881    return filteredInterfaces(predicate, new LinkedHashSet<>());
882  }
883
884  private LinkedHashSet<ClassInfo> filteredInterfaces(Predicate<MemberInfo> predicate,
885      LinkedHashSet<ClassInfo> classes) {
886    if (mRealSuperclass != null && !predicate.test(mRealSuperclass.asMemberInfo())) {
887      mRealSuperclass.filteredInterfaces(predicate, classes);
888    }
889    if (mRealInterfaces != null) {
890      for (ClassInfo clazz : mRealInterfaces) {
891        if (predicate.test(clazz.asMemberInfo())) {
892          classes.add(clazz);
893        } else {
894          clazz.filteredInterfaces(predicate, classes);
895        }
896      }
897    }
898    return classes;
899  }
900
901  /**
902   * Return methods matching the given predicate. Forcibly includes local
903   * methods that override a matching method in an ancestor class.
904   */
905  public Collection<MethodInfo> filteredMethods(Predicate<MemberInfo> predicate) {
906    Set<MethodInfo> methods = new LinkedHashSet<>();
907    for (MethodInfo method : getExhaustiveMethods()) {
908      if (predicate.test(method) || (method.findPredicateOverriddenMethod(predicate) != null)) {
909        methods.remove(method);
910        methods.add(method);
911      }
912    }
913    return methods;
914  }
915
916  /**
917   * Return fields matching the given predicate. Also clones fields from
918   * ancestors that would match had they been defined in this class.
919   */
920  public Collection<FieldInfo> filteredFields(Predicate<MemberInfo> predicate) {
921    Set<FieldInfo> fields = new LinkedHashSet<>();
922    if (Doclava.showUnannotated) {
923      for (ClassInfo clazz : gatherAncestorClasses()) {
924        if (!clazz.isInterface()) continue;
925        for (FieldInfo field : clazz.getExhaustiveFields()) {
926          if (!predicate.test(field)) {
927            field = field.cloneForClass(this);
928            if (predicate.test(field)) {
929              fields.remove(field);
930              fields.add(field);
931            }
932          }
933        }
934      }
935    }
936    for (FieldInfo field : getExhaustiveFields()) {
937      if (predicate.test(field)) {
938        fields.remove(field);
939        fields.add(field);
940      }
941    }
942    return fields;
943  }
944
945  public void addMethod(MethodInfo method) {
946    mApiCheckMethods.put(method.getHashableName(), method);
947
948    mAllSelfMethods.add(method);
949    mSelfMethods = null; // flush this, hopefully it hasn't been used yet.
950  }
951
952  public void addAnnotationElement(MethodInfo method) {
953    mAnnotationElements.add(method);
954  }
955
956  // Called by PackageInfo when a ClassInfo is added to a package.
957  // This is needed because ApiCheck uses PackageInfo.addClass
958  // rather than using setContainingPackage to dispatch to the
959  // appropriate method. TODO: move ApiCheck away from addClass.
960  void setPackage(PackageInfo pkg) {
961    mContainingPackage = pkg;
962  }
963
964  public void setContainingPackage(PackageInfo pkg) {
965    mContainingPackage = pkg;
966
967    if (mContainingPackage != null) {
968        if (mIsEnum) {
969            mContainingPackage.addEnum(this);
970        } else if (mIsInterface) {
971            mContainingPackage.addInterface(this);
972        } else {
973            mContainingPackage.addOrdinaryClass(this);
974        }
975    }
976  }
977
978  public ArrayList<AttributeInfo> selfAttributes() {
979    if (mSelfAttributes == null) {
980      TreeMap<FieldInfo, AttributeInfo> attrs = new TreeMap<FieldInfo, AttributeInfo>();
981
982      // the ones in the class comment won't have any methods
983      for (AttrTagInfo tag : comment().attrTags()) {
984        FieldInfo field = tag.reference();
985        if (field != null) {
986          AttributeInfo attr = attrs.get(field);
987          if (attr == null) {
988            attr = new AttributeInfo(this, field);
989            attrs.put(field, attr);
990          }
991          tag.setAttribute(attr);
992        }
993      }
994
995      // in the methods
996      for (MethodInfo m : selfMethods()) {
997        for (AttrTagInfo tag : m.comment().attrTags()) {
998          FieldInfo field = tag.reference();
999          if (field != null) {
1000            AttributeInfo attr = attrs.get(field);
1001            if (attr == null) {
1002              attr = new AttributeInfo(this, field);
1003              attrs.put(field, attr);
1004            }
1005            tag.setAttribute(attr);
1006            attr.methods.add(m);
1007          }
1008        }
1009      }
1010
1011      // constructors too
1012      for (MethodInfo m : constructors()) {
1013        for (AttrTagInfo tag : m.comment().attrTags()) {
1014          FieldInfo field = tag.reference();
1015          if (field != null) {
1016            AttributeInfo attr = attrs.get(field);
1017            if (attr == null) {
1018              attr = new AttributeInfo(this, field);
1019              attrs.put(field, attr);
1020            }
1021            tag.setAttribute(attr);
1022            attr.methods.add(m);
1023          }
1024        }
1025      }
1026
1027      mSelfAttributes = new ArrayList<AttributeInfo>(attrs.values());
1028      Collections.sort(mSelfAttributes, AttributeInfo.comparator);
1029    }
1030    return mSelfAttributes;
1031  }
1032
1033  public ArrayList<FieldInfo> enumConstants() {
1034    return mEnumConstants;
1035  }
1036
1037  public ClassInfo superclass() {
1038    if (!mSuperclassInit) {
1039      if (this.checkLevel()) {
1040        // rearrange our little inheritance hierarchy, because we need to hide classes that
1041        // don't pass checkLevel
1042        ClassInfo superclass = mRealSuperclass;
1043        while (superclass != null && !superclass.checkLevel()) {
1044          superclass = superclass.mRealSuperclass;
1045        }
1046        mSuperclass = superclass;
1047      } else {
1048        mSuperclass = mRealSuperclass;
1049      }
1050    }
1051    return mSuperclass;
1052  }
1053
1054  public ClassInfo realSuperclass() {
1055    return mRealSuperclass;
1056  }
1057
1058  /**
1059   * always the real superclass, not the collapsed one we get through superclass(), also has the
1060   * type parameter info if it's generic.
1061   */
1062  public TypeInfo superclassType() {
1063    return mRealSuperclassType;
1064  }
1065
1066  public TypeInfo asTypeInfo() {
1067    return mTypeInfo;
1068  }
1069
1070  ArrayList<TypeInfo> interfaceTypes() {
1071      ArrayList<TypeInfo> types = new ArrayList<TypeInfo>();
1072      for (ClassInfo iface : interfaces()) {
1073          types.add(iface.asTypeInfo());
1074      }
1075      return types;
1076  }
1077
1078  public String htmlPage() {
1079    String s = containingPackage().name();
1080    s = s.replace('.', '/');
1081    s += '/';
1082    s += name();
1083    s += ".html";
1084    s = Doclava.javadocDir + s;
1085    return s;
1086  }
1087
1088  /** Even indirectly */
1089  public boolean isDerivedFrom(ClassInfo cl) {
1090    return isDerivedFrom(cl.qualifiedName());
1091  }
1092
1093  /** Even indirectly */
1094  public boolean isDerivedFrom(String qualifiedName) {
1095    ClassInfo dad = this.superclass();
1096    if (dad != null) {
1097      if (dad.mQualifiedName.equals(qualifiedName)) {
1098        return true;
1099      } else {
1100        if (dad.isDerivedFrom(qualifiedName)) {
1101          return true;
1102        }
1103      }
1104    }
1105    for (ClassInfo iface : interfaces()) {
1106      if (iface.mQualifiedName.equals(qualifiedName)) {
1107        return true;
1108      } else {
1109        if (iface.isDerivedFrom(qualifiedName)) {
1110          return true;
1111        }
1112      }
1113    }
1114    return false;
1115  }
1116
1117  public void makeKeywordEntries(List<KeywordEntry> keywords) {
1118    if (!checkLevel()) {
1119      return;
1120    }
1121
1122    String htmlPage = htmlPage();
1123    String qualifiedName = qualifiedName();
1124
1125    keywords.add(new KeywordEntry(name(), htmlPage, "class in " + containingPackage().name()));
1126
1127    ArrayList<FieldInfo> fields = selfFields();
1128    //ArrayList<FieldInfo> enumConstants = enumConstants();
1129    ArrayList<MethodInfo> ctors = constructors();
1130    ArrayList<MethodInfo> methods = selfMethods();
1131
1132    // enum constants
1133    for (FieldInfo field : enumConstants()) {
1134      if (field.checkLevel()) {
1135        keywords.add(new KeywordEntry(field.name(), htmlPage + "#" + field.anchor(),
1136            "enum constant in " + qualifiedName));
1137      }
1138    }
1139
1140    // constants
1141    for (FieldInfo field : fields) {
1142      if (field.isConstant() && field.checkLevel()) {
1143        keywords.add(new KeywordEntry(field.name(), htmlPage + "#" + field.anchor(), "constant in "
1144            + qualifiedName));
1145      }
1146    }
1147
1148    // fields
1149    for (FieldInfo field : fields) {
1150      if (!field.isConstant() && field.checkLevel()) {
1151        keywords.add(new KeywordEntry(field.name(), htmlPage + "#" + field.anchor(), "field in "
1152            + qualifiedName));
1153      }
1154    }
1155
1156    // public constructors
1157    for (MethodInfo m : ctors) {
1158      if (m.isPublic() && m.checkLevel()) {
1159        keywords.add(new KeywordEntry(m.prettySignature(), htmlPage + "#" + m.anchor(),
1160            "constructor in " + qualifiedName));
1161      }
1162    }
1163
1164    // protected constructors
1165    if (Doclava.checkLevel(Doclava.SHOW_PROTECTED)) {
1166      for (MethodInfo m : ctors) {
1167        if (m.isProtected() && m.checkLevel()) {
1168          keywords.add(new KeywordEntry(m.prettySignature(),
1169              htmlPage + "#" + m.anchor(), "constructor in " + qualifiedName));
1170        }
1171      }
1172    }
1173
1174    // package private constructors
1175    if (Doclava.checkLevel(Doclava.SHOW_PACKAGE)) {
1176      for (MethodInfo m : ctors) {
1177        if (m.isPackagePrivate() && m.checkLevel()) {
1178          keywords.add(new KeywordEntry(m.prettySignature(),
1179              htmlPage + "#" + m.anchor(), "constructor in " + qualifiedName));
1180        }
1181      }
1182    }
1183
1184    // private constructors
1185    if (Doclava.checkLevel(Doclava.SHOW_PRIVATE)) {
1186      for (MethodInfo m : ctors) {
1187        if (m.isPrivate() && m.checkLevel()) {
1188          keywords.add(new KeywordEntry(m.name() + m.prettySignature(),
1189              htmlPage + "#" + m.anchor(), "constructor in " + qualifiedName));
1190        }
1191      }
1192    }
1193
1194    // public methods
1195    for (MethodInfo m : methods) {
1196      if (m.isPublic() && m.checkLevel()) {
1197        keywords.add(new KeywordEntry(m.name() + m.prettySignature(), htmlPage + "#" + m.anchor(),
1198            "method in " + qualifiedName));
1199      }
1200    }
1201
1202    // protected methods
1203    if (Doclava.checkLevel(Doclava.SHOW_PROTECTED)) {
1204      for (MethodInfo m : methods) {
1205        if (m.isProtected() && m.checkLevel()) {
1206          keywords.add(new KeywordEntry(m.name() + m.prettySignature(),
1207              htmlPage + "#" + m.anchor(), "method in " + qualifiedName));
1208        }
1209      }
1210    }
1211
1212    // package private methods
1213    if (Doclava.checkLevel(Doclava.SHOW_PACKAGE)) {
1214      for (MethodInfo m : methods) {
1215        if (m.isPackagePrivate() && m.checkLevel()) {
1216          keywords.add(new KeywordEntry(m.name() + m.prettySignature(),
1217              htmlPage + "#" + m.anchor(), "method in " + qualifiedName));
1218        }
1219      }
1220    }
1221
1222    // private methods
1223    if (Doclava.checkLevel(Doclava.SHOW_PRIVATE)) {
1224      for (MethodInfo m : methods) {
1225        if (m.isPrivate() && m.checkLevel()) {
1226          keywords.add(new KeywordEntry(m.name() + m.prettySignature(),
1227              htmlPage + "#" + m.anchor(), "method in " + qualifiedName));
1228        }
1229      }
1230    }
1231  }
1232
1233  public void makeLink(Data data, String base) {
1234    data.setValue(base + ".label", this.name());
1235    if (!this.isPrimitive() && this.isIncluded() && this.checkLevel()) {
1236      data.setValue(base + ".link", this.htmlPage());
1237    }
1238  }
1239
1240  public static void makeLinkListHDF(Data data, String base, ClassInfo[] classes) {
1241    final int N = classes.length;
1242    for (int i = 0; i < N; i++) {
1243      ClassInfo cl = classes[i];
1244      if (cl.checkLevel()) {
1245        cl.asTypeInfo().makeHDF(data, base + "." + i);
1246      }
1247    }
1248  }
1249
1250  /**
1251   * Used in lists of this class (packages, nested classes, known subclasses)
1252   */
1253  public void makeShortDescrHDF(Data data, String base) {
1254    mTypeInfo.makeHDF(data, base + ".type");
1255    data.setValue(base + ".kind", this.kind());
1256    TagInfo.makeHDF(data, base + ".shortDescr", this.firstSentenceTags());
1257    TagInfo.makeHDF(data, base + ".deprecated", deprecatedTags());
1258    data.setValue(base + ".since", getSince());
1259    if (isDeprecated()) {
1260      data.setValue(base + ".deprecatedsince", getDeprecatedSince());
1261    }
1262    data.setValue(base + ".artifact", getArtifact());
1263
1264    ArrayList<AnnotationInstanceInfo> showAnnos = getShowAnnotationsIncludeOuters();
1265    AnnotationInstanceInfo.makeLinkListHDF(
1266      data,
1267      base + ".showAnnotations",
1268      showAnnos.toArray(new AnnotationInstanceInfo[showAnnos.size()]));
1269
1270    setFederatedReferences(data, base);
1271  }
1272
1273  /**
1274   * Turns into the main class page
1275   */
1276  public void makeHDF(Data data) {
1277    int i, j, n;
1278    String name = name();
1279    String qualified = qualifiedName();
1280    ArrayList<AttributeInfo> selfAttributes = selfAttributes();
1281    ArrayList<MethodInfo> methods = selfMethods();
1282    ArrayList<FieldInfo> fields = selfFields();
1283    ArrayList<FieldInfo> enumConstants = enumConstants();
1284    ArrayList<MethodInfo> ctors = constructors();
1285    ArrayList<ClassInfo> inners = innerClasses();
1286
1287    // class name
1288    mTypeInfo.makeHDF(data, "class.type");
1289    mTypeInfo.makeQualifiedHDF(data, "class.qualifiedType");
1290    data.setValue("class.name", name);
1291    data.setValue("class.qualified", qualified);
1292    if (isProtected()) {
1293      data.setValue("class.scope", "protected");
1294    } else if (isPublic()) {
1295      data.setValue("class.scope", "public");
1296    }
1297    if (isStatic()) {
1298      data.setValue("class.static", "static");
1299    }
1300    if (isFinal()) {
1301      data.setValue("class.final", "final");
1302    }
1303    if (isAbstract() && !isInterface()) {
1304      data.setValue("class.abstract", "abstract");
1305    }
1306
1307    int numAnnotationDocumentation = 0;
1308    for (AnnotationInstanceInfo aii : annotations()) {
1309      String annotationDocumentation = Doclava.getDocumentationStringForAnnotation(
1310          aii.type().qualifiedName());
1311      if (annotationDocumentation != null) {
1312        data.setValue("class.annotationdocumentation." + numAnnotationDocumentation + ".text",
1313            annotationDocumentation);
1314        numAnnotationDocumentation++;
1315      }
1316    }
1317
1318    ArrayList<AnnotationInstanceInfo> showAnnos = getShowAnnotationsIncludeOuters();
1319    AnnotationInstanceInfo.makeLinkListHDF(
1320      data,
1321      "class.showAnnotations",
1322      showAnnos.toArray(new AnnotationInstanceInfo[showAnnos.size()]));
1323
1324    // class info
1325    String kind = kind();
1326    if (kind != null) {
1327      data.setValue("class.kind", kind);
1328    }
1329    data.setValue("class.since", getSince());
1330    if (isDeprecated()) {
1331      data.setValue("class.deprecatedsince", getDeprecatedSince());
1332    }
1333    data.setValue("class.artifact", getArtifact());
1334    setFederatedReferences(data, "class");
1335
1336    // the containing package -- note that this can be passed to type_link,
1337    // but it also contains the list of all of the packages
1338    containingPackage().makeClassLinkListHDF(data, "class.package");
1339
1340    // inheritance hierarchy
1341    List<ClassTypePair> ctplist = superClassesWithTypes();
1342    n = ctplist.size();
1343    for (i = 0; i < ctplist.size(); i++) {
1344      // go in reverse order
1345      ClassTypePair ctp = ctplist.get(n - i - 1);
1346      if (ctp.classInfo().checkLevel()) {
1347        ctp.typeInfo().makeQualifiedHDF(data, "class.inheritance." + i + ".class");
1348        ctp.typeInfo().makeHDF(data, "class.inheritance." + i + ".short_class");
1349        j = 0;
1350        for (ClassTypePair t : ctp.classInfo().interfacesWithTypes()) {
1351          t.typeInfo().makeHDF(data, "class.inheritance." + i + ".interfaces." + j);
1352          j++;
1353        }
1354      }
1355    }
1356
1357    // class description
1358    TagInfo.makeHDF(data, "class.descr", inlineTags());
1359    TagInfo.makeHDF(data, "class.descrAux", Doclava.auxSource.classAuxTags(this));
1360    TagInfo.makeHDF(data, "class.seeAlso", comment().seeTags());
1361    TagInfo.makeHDF(data, "class.deprecated", deprecatedTags());
1362
1363    // known subclasses
1364    TreeMap<String, ClassInfo> direct = new TreeMap<String, ClassInfo>();
1365    TreeMap<String, ClassInfo> indirect = new TreeMap<String, ClassInfo>();
1366    Collection<ClassInfo> all = Converter.rootClasses();
1367    for (ClassInfo cl : all) {
1368      if (cl.superclass() != null && cl.superclass().equals(this)) {
1369        direct.put(cl.name(), cl);
1370      } else if (cl.isDerivedFrom(this)) {
1371        indirect.put(cl.name(), cl);
1372      }
1373    }
1374    // direct
1375    i = 0;
1376    for (ClassInfo cl : direct.values()) {
1377      if (cl.checkLevel()) {
1378        cl.makeShortDescrHDF(data, "class.subclasses.direct." + i);
1379      }
1380      i++;
1381    }
1382    // indirect
1383    i = 0;
1384    for (ClassInfo cl : indirect.values()) {
1385      if (cl.checkLevel()) {
1386        cl.makeShortDescrHDF(data, "class.subclasses.indirect." + i);
1387      }
1388      i++;
1389    }
1390
1391    // hide special cases
1392    if ("java.lang.Object".equals(qualified) || "java.io.Serializable".equals(qualified)) {
1393      data.setValue("class.subclasses.hidden", "1");
1394    } else {
1395      data.setValue("class.subclasses.hidden", "0");
1396    }
1397
1398    // nested classes
1399    i = 0;
1400    for (ClassInfo inner : inners) {
1401      if (inner.checkLevel()) {
1402        inner.makeShortDescrHDF(data, "class.inners." + i);
1403      }
1404      i++;
1405    }
1406
1407    // enum constants
1408    i = 0;
1409    for (FieldInfo field : enumConstants) {
1410      field.makeHDF(data, "class.enumConstants." + i);
1411      i++;
1412    }
1413
1414    // constants
1415    i = 0;
1416    for (FieldInfo field : fields) {
1417      if (field.isConstant()) {
1418        field.makeHDF(data, "class.constants." + i);
1419        i++;
1420      }
1421    }
1422
1423    // fields
1424    i = 0;
1425    for (FieldInfo field : fields) {
1426      if (!field.isConstant()) {
1427        field.makeHDF(data, "class.fields." + i);
1428        i++;
1429      }
1430    }
1431
1432    // public constructors
1433    i = 0;
1434    for (MethodInfo ctor : ctors) {
1435      if (ctor.isPublic()) {
1436        ctor.makeHDF(data, "class.ctors.public." + i);
1437        i++;
1438      }
1439    }
1440
1441    // protected constructors
1442    if (Doclava.checkLevel(Doclava.SHOW_PROTECTED)) {
1443      i = 0;
1444      for (MethodInfo ctor : ctors) {
1445        if (ctor.isProtected()) {
1446          ctor.makeHDF(data, "class.ctors.protected." + i);
1447          i++;
1448        }
1449      }
1450    }
1451
1452    // package private constructors
1453    if (Doclava.checkLevel(Doclava.SHOW_PACKAGE)) {
1454      i = 0;
1455      for (MethodInfo ctor : ctors) {
1456        if (ctor.isPackagePrivate()) {
1457          ctor.makeHDF(data, "class.ctors.package." + i);
1458          i++;
1459        }
1460      }
1461    }
1462
1463    // private constructors
1464    if (Doclava.checkLevel(Doclava.SHOW_PRIVATE)) {
1465      i = 0;
1466      for (MethodInfo ctor : ctors) {
1467        if (ctor.isPrivate()) {
1468          ctor.makeHDF(data, "class.ctors.private." + i);
1469          i++;
1470        }
1471      }
1472    }
1473
1474    // public methods
1475    i = 0;
1476    for (MethodInfo method : methods) {
1477      if (method.isPublic()) {
1478        method.makeHDF(data, "class.methods.public." + i);
1479        i++;
1480      }
1481    }
1482
1483    // protected methods
1484    if (Doclava.checkLevel(Doclava.SHOW_PROTECTED)) {
1485      i = 0;
1486      for (MethodInfo method : methods) {
1487        if (method.isProtected()) {
1488          method.makeHDF(data, "class.methods.protected." + i);
1489          i++;
1490        }
1491      }
1492    }
1493
1494    // package private methods
1495    if (Doclava.checkLevel(Doclava.SHOW_PACKAGE)) {
1496      i = 0;
1497      for (MethodInfo method : methods) {
1498        if (method.isPackagePrivate()) {
1499          method.makeHDF(data, "class.methods.package." + i);
1500          i++;
1501        }
1502      }
1503    }
1504
1505    // private methods
1506    if (Doclava.checkLevel(Doclava.SHOW_PRIVATE)) {
1507      i = 0;
1508      for (MethodInfo method : methods) {
1509        if (method.isPrivate()) {
1510          method.makeHDF(data, "class.methods.private." + i);
1511          i++;
1512        }
1513      }
1514    }
1515
1516    // xml attributes
1517    i = 0;
1518    for (AttributeInfo attr : selfAttributes) {
1519      if (attr.checkLevel()) {
1520        attr.makeHDF(data, "class.attrs." + i);
1521        i++;
1522      }
1523    }
1524
1525    // inherited methods
1526    Iterator<ClassTypePair> superclassesItr = superClassesWithTypes().iterator();
1527    superclassesItr.next(); // skip the first one, which is the current class
1528    ClassTypePair superCtp;
1529    i = 0;
1530    while (superclassesItr.hasNext()) {
1531      superCtp = superclassesItr.next();
1532      if (superCtp.classInfo().checkLevel()) {
1533        makeInheritedHDF(data, i, superCtp);
1534        i++;
1535      }
1536    }
1537    Iterator<ClassTypePair> interfacesItr = allInterfacesWithTypes().iterator();
1538    while (interfacesItr.hasNext()) {
1539      superCtp = interfacesItr.next();
1540      if (superCtp.classInfo().checkLevel()) {
1541        makeInheritedHDF(data, i, superCtp);
1542        i++;
1543      }
1544    }
1545  }
1546
1547  private static void makeInheritedHDF(Data data, int index, ClassTypePair ctp) {
1548    int i;
1549
1550    String base = "class.inherited." + index;
1551    data.setValue(base + ".qualified", ctp.classInfo().qualifiedName());
1552    if (ctp.classInfo().checkLevel()) {
1553      data.setValue(base + ".link", ctp.classInfo().htmlPage());
1554    }
1555    String kind = ctp.classInfo().kind();
1556    if (kind != null) {
1557      data.setValue(base + ".kind", kind);
1558    }
1559
1560    if (ctp.classInfo().mIsIncluded) {
1561      data.setValue(base + ".included", "true");
1562    } else {
1563      Doclava.federationTagger.tagAll(Arrays.asList(ctp.classInfo()));
1564      if (!ctp.classInfo().getFederatedReferences().isEmpty()) {
1565        FederatedSite site = ctp.classInfo().getFederatedReferences().iterator().next();
1566        data.setValue(base + ".link", site.linkFor(ctp.classInfo().htmlPage()));
1567        data.setValue(base + ".federated", site.name());
1568      }
1569    }
1570
1571    // xml attributes
1572    i = 0;
1573    for (AttributeInfo attr : ctp.classInfo().selfAttributes()) {
1574      attr.makeHDF(data, base + ".attrs." + i);
1575      i++;
1576    }
1577
1578    // methods
1579    i = 0;
1580    for (MethodInfo method : ctp.classInfo().selfMethods()) {
1581      method.makeHDF(data, base + ".methods." + i, ctp.getTypeArgumentMapping());
1582      i++;
1583    }
1584
1585    // fields
1586    i = 0;
1587    for (FieldInfo field : ctp.classInfo().selfFields()) {
1588      if (!field.isConstant()) {
1589        field.makeHDF(data, base + ".fields." + i);
1590        i++;
1591      }
1592    }
1593
1594    // constants
1595    i = 0;
1596    for (FieldInfo field : ctp.classInfo().selfFields()) {
1597      if (field.isConstant()) {
1598        field.makeHDF(data, base + ".constants." + i);
1599        i++;
1600      }
1601    }
1602  }
1603
1604  @Override
1605  public boolean isHidden() {
1606    if (mHidden == null) {
1607      mHidden = isHiddenImpl();
1608    }
1609
1610    return mHidden;
1611  }
1612
1613  /**
1614   * @return true if the containing package has @hide comment, a hide annotaion,
1615   * or a containing class of this class is hidden.
1616   */
1617  public boolean isHiddenImpl() {
1618    ClassInfo cl = this;
1619    while (cl != null) {
1620      if (cl.hasShowAnnotation()) {
1621        return false;
1622      }
1623      PackageInfo pkg = cl.containingPackage();
1624      if (pkg != null && pkg.hasHideComment()) {
1625        return true;
1626      }
1627      if (cl.comment().isHidden() || cl.hasHideAnnotation()) {
1628        return true;
1629      }
1630      cl = cl.containingClass();
1631    }
1632    return false;
1633  }
1634
1635  @Override
1636  public boolean isRemoved() {
1637    if (mRemoved == null) {
1638      mRemoved = isRemovedImpl();
1639    }
1640
1641    return mRemoved;
1642  }
1643
1644  /**
1645   * @return true if the containing package has @removed comment, or an ancestor
1646   * class of this class is removed, or this class has @removed comment.
1647   */
1648  public boolean isRemovedImpl() {
1649    ClassInfo cl = this;
1650    while (cl != null) {
1651      PackageInfo pkg = cl.containingPackage();
1652      if (pkg != null && pkg.hasRemovedComment()) {
1653        return true;
1654      }
1655      if (cl.comment().isRemoved()) {
1656        return true;
1657      }
1658      cl = cl.containingClass();
1659    }
1660    return false;
1661  }
1662
1663  @Override
1664  public boolean isHiddenOrRemoved() {
1665    return isHidden() || isRemoved();
1666  }
1667
1668  public boolean hasShowAnnotation() {
1669    return mShowAnnotations != null && mShowAnnotations.size() > 0;
1670  }
1671
1672  public ArrayList<AnnotationInstanceInfo> showAnnotations() {
1673    return mShowAnnotations;
1674  }
1675
1676  public boolean hasHideAnnotation() {
1677    return mHideAnnotations != null && mHideAnnotations.size() > 0;
1678  }
1679
1680  public ArrayList<AnnotationInstanceInfo> hideAnnotations() {
1681    return mHideAnnotations;
1682  }
1683
1684  public ArrayList<AnnotationInstanceInfo> getShowAnnotationsIncludeOuters() {
1685    ArrayList<AnnotationInstanceInfo> allAnnotations = new ArrayList<AnnotationInstanceInfo>();
1686    ClassInfo cl = this;
1687    while (cl != null) {
1688      if (cl.showAnnotations() != null) {
1689        // Don't allow duplicates into the merged list
1690        for (AnnotationInstanceInfo newAii : cl.showAnnotations()) {
1691          boolean addIt = true;
1692          for (AnnotationInstanceInfo existingAii : allAnnotations) {
1693            if (existingAii.type().name() == newAii.type().name()) {
1694              addIt = false;
1695              break;
1696            }
1697          }
1698          if (addIt) {
1699            allAnnotations.add(newAii);
1700          }
1701        }
1702      }
1703      cl = cl.containingClass();
1704    }
1705    return allAnnotations;
1706  }
1707
1708  private MethodInfo matchMethod(ArrayList<MethodInfo> methods, String name, String[] params,
1709      String[] dimensions, boolean varargs) {
1710    for (MethodInfo method : methods) {
1711      if (method.name().equals(name)) {
1712        if (params == null) {
1713          return method;
1714        } else {
1715          if (method.matchesParams(params, dimensions, varargs)) {
1716            return method;
1717          }
1718        }
1719      }
1720    }
1721    return null;
1722  }
1723
1724  public MethodInfo findMethod(String name, String[] params, String[] dimensions, boolean varargs) {
1725    // first look on our class, and our superclasses
1726
1727    // for methods
1728    MethodInfo rv;
1729    rv = matchMethod(methods(), name, params, dimensions, varargs);
1730
1731    if (rv != null) {
1732      return rv;
1733    }
1734
1735    // for constructors
1736    rv = matchMethod(constructors(), name, params, dimensions, varargs);
1737    if (rv != null) {
1738      return rv;
1739    }
1740
1741    // then recursively look at our containing class
1742    ClassInfo containing = containingClass();
1743    if (containing != null) {
1744      return containing.findMethod(name, params, dimensions, varargs);
1745    }
1746
1747    return null;
1748  }
1749
1750  public boolean supportsMethod(MethodInfo method) {
1751    for (MethodInfo m : methods()) {
1752      if (m.getHashableName().equals(method.getHashableName())) {
1753        return true;
1754      }
1755    }
1756    return false;
1757  }
1758
1759  private ClassInfo searchInnerClasses(String[] nameParts, int index) {
1760    String part = nameParts[index];
1761
1762    ArrayList<ClassInfo> inners = mInnerClasses;
1763    for (ClassInfo in : inners) {
1764      String[] innerParts = in.nameParts();
1765      if (part.equals(innerParts[innerParts.length - 1])) {
1766        if (index == nameParts.length - 1) {
1767          return in;
1768        } else {
1769          return in.searchInnerClasses(nameParts, index + 1);
1770        }
1771      }
1772    }
1773    return null;
1774  }
1775
1776  public ClassInfo extendedFindClass(String className) {
1777    // ClassDoc.findClass has this bug that we're working around here:
1778    // If you have a class PackageManager with an inner class PackageInfo
1779    // and you call it with "PackageInfo" it doesn't find it.
1780    return searchInnerClasses(className.split("\\."), 0);
1781  }
1782
1783  public ClassInfo findClass(String className) {
1784    return Converter.obtainClass(mClass.findClass(className));
1785  }
1786
1787  public ClassInfo findInnerClass(String className) {
1788    // ClassDoc.findClass won't find inner classes. To deal with that,
1789    // we try what they gave us first, but if that didn't work, then
1790    // we see if there are any periods in className, and start searching
1791    // from there.
1792    String[] nodes = className.split("\\.");
1793    ClassDoc cl = mClass;
1794
1795    int N = nodes.length;
1796    for (int i = 0; i < N; ++i) {
1797      final String n = nodes[i];
1798      if (n.isEmpty() && i == 0) {
1799        // We skip over an empty classname component if it's at location 0. This is
1800        // to deal with names like ".Inner". java7 will return a bogus ClassInfo when
1801        // we call "findClass("") and the next iteration of the loop will throw a
1802        // runtime exception.
1803        continue;
1804      }
1805
1806      cl = cl.findClass(n);
1807      if (cl == null) {
1808        return null;
1809      }
1810    }
1811
1812    return Converter.obtainClass(cl);
1813  }
1814
1815  public FieldInfo findField(String name) {
1816    // first look on our class, and our superclasses
1817    for (FieldInfo f : fields()) {
1818      if (f.name().equals(name)) {
1819        return f;
1820      }
1821    }
1822
1823    // then look at our enum constants (these are really fields, maybe
1824    // they should be mixed into fields(). not sure)
1825    for (FieldInfo f : enumConstants()) {
1826      if (f.name().equals(name)) {
1827        return f;
1828      }
1829    }
1830
1831    // then recursively look at our containing class
1832    ClassInfo containing = containingClass();
1833    if (containing != null) {
1834      return containing.findField(name);
1835    }
1836
1837    return null;
1838  }
1839
1840  public static ClassInfo[] sortByName(ClassInfo[] classes) {
1841    int i;
1842    Sorter[] sorted = new Sorter[classes.length];
1843    for (i = 0; i < sorted.length; i++) {
1844      ClassInfo cl = classes[i];
1845      sorted[i] = new Sorter(cl.name(), cl);
1846    }
1847
1848    Arrays.sort(sorted);
1849
1850    ClassInfo[] rv = new ClassInfo[classes.length];
1851    for (i = 0; i < rv.length; i++) {
1852      rv[i] = (ClassInfo) sorted[i].data;
1853    }
1854
1855    return rv;
1856  }
1857
1858  public void setNonWrittenConstructors(ArrayList<MethodInfo> nonWritten) {
1859    mNonWrittenConstructors = nonWritten;
1860  }
1861
1862  public ArrayList<MethodInfo> getNonWrittenConstructors() {
1863    return mNonWrittenConstructors;
1864  }
1865
1866  public String kind() {
1867    if (isOrdinaryClass()) {
1868      return "class";
1869    } else if (isInterface()) {
1870      return "interface";
1871    } else if (isEnum()) {
1872      return "enum";
1873    } else if (isError()) {
1874      return "class";
1875    } else if (isException()) {
1876      return "class";
1877    } else if (isAnnotation()) {
1878      return "@interface";
1879    }
1880    return null;
1881  }
1882
1883  public String scope() {
1884    if (isPublic()) {
1885      return "public";
1886    } else if (isProtected()) {
1887      return "protected";
1888    } else if (isPackagePrivate()) {
1889      return "";
1890    } else if (isPrivate()) {
1891      return "private";
1892    } else {
1893      throw new RuntimeException("invalid scope for object " + this);
1894    }
1895  }
1896
1897  public void setHiddenMethods(ArrayList<MethodInfo> mInfo) {
1898    mHiddenMethods = mInfo;
1899  }
1900
1901  public ArrayList<MethodInfo> getHiddenMethods() {
1902    return mHiddenMethods;
1903  }
1904
1905  @Override
1906  public String toString() {
1907    return this.qualifiedName();
1908  }
1909
1910  @Override
1911  public boolean equals(Object o) {
1912    if (this == o) {
1913      return true;
1914    } else if (o instanceof ClassInfo) {
1915      final ClassInfo c = (ClassInfo) o;
1916      return mQualifiedName.equals(c.mQualifiedName);
1917    } else {
1918      return false;
1919    }
1920  }
1921
1922  @Override
1923  public int hashCode() {
1924    return mQualifiedName.hashCode();
1925  }
1926
1927  public void setReasonIncluded(String reason) {
1928    mReasonIncluded = reason;
1929  }
1930
1931  public String getReasonIncluded() {
1932    return mReasonIncluded;
1933  }
1934
1935  private ClassDoc mClass;
1936
1937  // ctor
1938  private boolean mIsPublic;
1939  private boolean mIsProtected;
1940  private boolean mIsPackagePrivate;
1941  private boolean mIsPrivate;
1942  private boolean mIsStatic;
1943  private boolean mIsInterface;
1944  private boolean mIsAbstract;
1945  private boolean mIsOrdinaryClass;
1946  private boolean mIsException;
1947  private boolean mIsError;
1948  private boolean mIsEnum;
1949  private boolean mIsAnnotation;
1950  private boolean mIsFinal;
1951  private boolean mIsIncluded;
1952  private String mName;
1953  private String mQualifiedName;
1954  private String mQualifiedTypeName;
1955  private boolean mIsPrimitive;
1956  private TypeInfo mTypeInfo;
1957  private String[] mNameParts;
1958
1959  // init
1960  private ArrayList<ClassInfo> mRealInterfaces = new ArrayList<ClassInfo>();
1961  private ArrayList<ClassInfo> mInterfaces;
1962  private ArrayList<TypeInfo> mRealInterfaceTypes;
1963  private ArrayList<ClassInfo> mInnerClasses;
1964  // mAllConstructors will not contain *all* constructors. Only the constructors that pass
1965  // checkLevel. @see {@link Converter#convertMethods(ConstructorDoc[])}
1966  private ArrayList<MethodInfo> mAllConstructors = new ArrayList<MethodInfo>();
1967  // mAllSelfMethods will not contain *all* self methods. Only the methods that pass
1968  // checkLevel. @see {@link Converter#convertMethods(MethodDoc[])}
1969  private ArrayList<MethodInfo> mAllSelfMethods = new ArrayList<MethodInfo>();
1970  private ArrayList<MethodInfo> mAnnotationElements = new ArrayList<MethodInfo>(); // if this class is an annotation
1971  private ArrayList<FieldInfo> mAllSelfFields = new ArrayList<FieldInfo>();
1972  private ArrayList<FieldInfo> mEnumConstants = new ArrayList<FieldInfo>();
1973  private PackageInfo mContainingPackage;
1974  private ClassInfo mContainingClass;
1975  private ClassInfo mRealSuperclass;
1976  private TypeInfo mRealSuperclassType;
1977  private ClassInfo mSuperclass;
1978  private ArrayList<AnnotationInstanceInfo> mAnnotations;
1979  private ArrayList<AnnotationInstanceInfo> mShowAnnotations;
1980  private ArrayList<AnnotationInstanceInfo> mHideAnnotations;
1981  private boolean mSuperclassInit;
1982  private boolean mDeprecatedKnown;
1983
1984  // lazy
1985  private ArrayList<ClassTypePair> mSuperclassesWithTypes;
1986  private ArrayList<ClassTypePair> mInterfacesWithTypes;
1987  private ArrayList<ClassTypePair> mAllInterfacesWithTypes;
1988  private ArrayList<MethodInfo> mConstructors;
1989  private ArrayList<ClassInfo> mRealInnerClasses;
1990  private ArrayList<MethodInfo> mSelfMethods;
1991  private ArrayList<FieldInfo> mSelfFields;
1992  private ArrayList<AttributeInfo> mSelfAttributes;
1993  private ArrayList<MethodInfo> mMethods;
1994  private ArrayList<FieldInfo> mFields;
1995  private ArrayList<TypeInfo> mTypeParameters;
1996  private ArrayList<MethodInfo> mHiddenMethods;
1997  private Boolean mHidden = null;
1998  private Boolean mRemoved = null;
1999  private Boolean mCheckLevel = null;
2000  private String mReasonIncluded;
2001  private ArrayList<MethodInfo> mNonWrittenConstructors;
2002  private boolean mIsDeprecated;
2003
2004  // TODO: Temporary members from apicheck migration.
2005  private HashMap<String, MethodInfo> mApiCheckConstructors = new HashMap<String, MethodInfo>();
2006  private HashMap<String, MethodInfo> mApiCheckMethods = new HashMap<String, MethodInfo>();
2007  private HashMap<String, FieldInfo> mApiCheckFields = new HashMap<String, FieldInfo>();
2008  private HashMap<String, FieldInfo> mApiCheckEnumConstants = new HashMap<String, FieldInfo>();
2009
2010  // Resolutions
2011  private ArrayList<Resolution> mResolutions;
2012
2013  private List<MethodInfo> mRemovedMethods; // immutable after you set its value.
2014
2015  private List<MethodInfo> mExhaustiveConstructors; // immutable after you set its value.
2016  private List<MethodInfo> mExhaustiveMethods; // immutable after you set its value.
2017  private List<FieldInfo> mExhaustiveEnumConstants; // immutable after you set its value.
2018  private List<FieldInfo> mExhaustiveFields; // immutable after you set its value.
2019
2020  /**
2021   * Returns true if {@code cl} implements the interface {@code iface} either by either being that
2022   * interface, implementing that interface or extending a type that implements the interface.
2023   */
2024  public boolean implementsInterface(String iface) {
2025    if (qualifiedName().equals(iface)) {
2026      return true;
2027    }
2028    for (ClassInfo clImplements : realInterfaces()) {
2029      if (clImplements.implementsInterface(iface)) {
2030        return true;
2031      }
2032    }
2033    if (mSuperclass != null && mSuperclass.implementsInterface(iface)) {
2034      return true;
2035    }
2036    return false;
2037  }
2038
2039  /**
2040   * Returns true if {@code this} extends the class {@code ext}.
2041   */
2042  public boolean extendsClass(String cl) {
2043    if (qualifiedName().equals(cl)) {
2044      return true;
2045    }
2046    if (mSuperclass != null && mSuperclass.extendsClass(cl)) {
2047      return true;
2048    }
2049    return false;
2050  }
2051
2052  /**
2053   * Returns true if {@code this} is assignable to cl
2054   */
2055  public boolean isAssignableTo(String cl) {
2056    return implementsInterface(cl) || extendsClass(cl);
2057  }
2058
2059  public void addInterface(ClassInfo iface) {
2060    mRealInterfaces.add(iface);
2061  }
2062
2063  public void addConstructor(MethodInfo ctor) {
2064    mApiCheckConstructors.put(ctor.getHashableName(), ctor);
2065
2066    mAllConstructors.add(ctor);
2067    mConstructors = null; // flush this, hopefully it hasn't been used yet.
2068  }
2069
2070  public void addField(FieldInfo field) {
2071    mApiCheckFields.put(field.name(), field);
2072
2073    mAllSelfFields.add(field);
2074
2075    mSelfFields = null; // flush this, hopefully it hasn't been used yet.
2076  }
2077
2078  public void addEnumConstant(FieldInfo field) {
2079    mApiCheckEnumConstants.put(field.name(), field);
2080
2081    mEnumConstants.add(field);
2082  }
2083
2084  public void setSuperClass(ClassInfo superclass) {
2085    mRealSuperclass = superclass;
2086    mSuperclass = superclass;
2087  }
2088
2089  public Map<String, MethodInfo> allConstructorsMap() {
2090    return mApiCheckConstructors;
2091  }
2092
2093  public Map<String, FieldInfo> allFields() {
2094    return mApiCheckFields;
2095  }
2096
2097  public Map<String, FieldInfo> allEnums() {
2098    return mApiCheckEnumConstants;
2099  }
2100
2101  /**
2102   * Returns all methods defined directly in this class. For a list of all
2103   * methods supported by this class, see {@link #methods()}.
2104   */
2105  public Map<String, MethodInfo> allMethods() {
2106    return mApiCheckMethods;
2107  }
2108
2109  /**
2110   * Returns the class hierarchy for this class, starting with this class.
2111   */
2112  public Iterable<ClassInfo> hierarchy() {
2113    List<ClassInfo> result = new ArrayList<ClassInfo>(4);
2114    for (ClassInfo c = this; c != null; c = c.mSuperclass) {
2115      result.add(c);
2116    }
2117    return result;
2118  }
2119
2120  public String superclassName() {
2121    if (mSuperclass == null) {
2122      if (mQualifiedName.equals("java.lang.Object")) {
2123        return null;
2124      }
2125      throw new UnsupportedOperationException("Superclass not set for " + qualifiedName());
2126    }
2127    return mSuperclass.mQualifiedName;
2128  }
2129
2130  public void setAnnotations(ArrayList<AnnotationInstanceInfo> annotations) {
2131    mAnnotations = annotations;
2132  }
2133
2134  public boolean isConsistent(ClassInfo cl) {
2135    return isConsistent(cl, null, null);
2136  }
2137
2138  public boolean isConsistent(ClassInfo cl, List<MethodInfo> newCtors, List<MethodInfo> newMethods) {
2139    boolean consistent = true;
2140    boolean diffMode = (newCtors != null) && (newMethods != null);
2141
2142    if (isInterface() != cl.isInterface()) {
2143      Errors.error(Errors.CHANGED_CLASS, cl.position(), "Class " + cl.qualifiedName()
2144          + " changed class/interface declaration");
2145      consistent = false;
2146    }
2147    for (ClassInfo iface : mRealInterfaces) {
2148      if (!cl.implementsInterface(iface.mQualifiedName)) {
2149        Errors.error(Errors.REMOVED_INTERFACE, cl.position(), "Class " + qualifiedName()
2150            + " no longer implements " + iface);
2151      }
2152    }
2153    for (ClassInfo iface : cl.mRealInterfaces) {
2154      if (!implementsInterface(iface.mQualifiedName)) {
2155        Errors.error(Errors.ADDED_INTERFACE, cl.position(), "Added interface " + iface
2156            + " to class " + qualifiedName());
2157        consistent = false;
2158      }
2159    }
2160
2161    for (MethodInfo mInfo : mApiCheckMethods.values()) {
2162      if (cl.mApiCheckMethods.containsKey(mInfo.getHashableName())) {
2163        if (!mInfo.isConsistent(cl.mApiCheckMethods.get(mInfo.getHashableName()))) {
2164          consistent = false;
2165        }
2166      } else {
2167        /*
2168         * This class formerly provided this method directly, and now does not. Check our ancestry
2169         * to see if there's an inherited version that still fulfills the API requirement.
2170         */
2171        MethodInfo mi = ClassInfo.overriddenMethod(mInfo, cl);
2172        if (mi == null) {
2173          mi = ClassInfo.interfaceMethod(mInfo, cl);
2174        }
2175        if (mi == null) {
2176          if (mInfo.isDeprecated()) {
2177            Errors.error(Errors.REMOVED_DEPRECATED_METHOD, mInfo.position(),
2178                "Removed deprecated public method " + mInfo.prettyQualifiedSignature());
2179          } else {
2180            Errors.error(Errors.REMOVED_METHOD, mInfo.position(),
2181                "Removed public method " + mInfo.prettyQualifiedSignature());
2182          }
2183          consistent = false;
2184        }
2185      }
2186    }
2187    for (MethodInfo mInfo : cl.mApiCheckMethods.values()) {
2188      if (!mApiCheckMethods.containsKey(mInfo.getHashableName())) {
2189        /*
2190         * Similarly to the above, do not fail if this "new" method is really an override of an
2191         * existing superclass method.
2192         * But we should fail if this is overriding an abstract method, because method's
2193         * abstractness affects how users use it. See also Stubs.methodIsOverride().
2194         */
2195        MethodInfo mi = ClassInfo.overriddenMethod(mInfo, this);
2196        if (mi == null && mInfo.isAbstract()) {
2197          Errors.error(Errors.ADDED_ABSTRACT_METHOD, mInfo.position(),
2198              "Added abstract public method "
2199              + mInfo.prettyQualifiedSignature() + " to existing class");
2200          consistent = false;
2201        } else if (mi == null || mi.isAbstract() != mInfo.isAbstract()) {
2202            Errors.error(Errors.ADDED_METHOD, mInfo.position(), "Added public method "
2203                + mInfo.prettyQualifiedSignature());
2204            if (diffMode) {
2205              newMethods.add(mInfo);
2206            }
2207            consistent = false;
2208          }
2209        }
2210    }
2211    if (diffMode) {
2212      Collections.sort(newMethods, MethodInfo.comparator);
2213    }
2214
2215    for (MethodInfo mInfo : mApiCheckConstructors.values()) {
2216      if (cl.mApiCheckConstructors.containsKey(mInfo.getHashableName())) {
2217        if (!mInfo.isConsistent(cl.mApiCheckConstructors.get(mInfo.getHashableName()))) {
2218          consistent = false;
2219        }
2220      } else {
2221        if (mInfo.isDeprecated()) {
2222          Errors.error(Errors.REMOVED_DEPRECATED_METHOD, mInfo.position(),
2223              "Removed deprecated public constructor " + mInfo.prettyQualifiedSignature());
2224        } else {
2225          Errors.error(Errors.REMOVED_METHOD, mInfo.position(),
2226              "Removed public constructor " + mInfo.prettyQualifiedSignature());
2227        }
2228        consistent = false;
2229      }
2230    }
2231    for (MethodInfo mInfo : cl.mApiCheckConstructors.values()) {
2232      if (!mApiCheckConstructors.containsKey(mInfo.getHashableName())) {
2233        Errors.error(Errors.ADDED_METHOD, mInfo.position(), "Added public constructor "
2234            + mInfo.prettyQualifiedSignature());
2235        if (diffMode) {
2236          newCtors.add(mInfo);
2237        }
2238        consistent = false;
2239      }
2240    }
2241    if (diffMode) {
2242      Collections.sort(newCtors, MethodInfo.comparator);
2243    }
2244
2245    for (FieldInfo mInfo : mApiCheckFields.values()) {
2246      if (cl.mApiCheckFields.containsKey(mInfo.name())) {
2247        if (!mInfo.isConsistent(cl.mApiCheckFields.get(mInfo.name()))) {
2248          consistent = false;
2249        }
2250      } else {
2251        if (mInfo.isDeprecated()) {
2252          Errors.error(Errors.REMOVED_DEPRECATED_FIELD, mInfo.position(),
2253              "Removed deprecated field " + mInfo.qualifiedName());
2254        } else {
2255          Errors.error(Errors.REMOVED_FIELD, mInfo.position(),
2256              "Removed field " + mInfo.qualifiedName());
2257        }
2258        consistent = false;
2259      }
2260    }
2261    for (FieldInfo mInfo : cl.mApiCheckFields.values()) {
2262      if (!mApiCheckFields.containsKey(mInfo.name())) {
2263        Errors.error(Errors.ADDED_FIELD, mInfo.position(), "Added public field "
2264            + mInfo.qualifiedName());
2265        consistent = false;
2266      }
2267    }
2268
2269    for (FieldInfo info : mApiCheckEnumConstants.values()) {
2270      if (cl.mApiCheckEnumConstants.containsKey(info.name())) {
2271        if (!info.isConsistent(cl.mApiCheckEnumConstants.get(info.name()))) {
2272          consistent = false;
2273        }
2274      } else {
2275        if (info.isDeprecated()) {
2276          Errors.error(Errors.REMOVED_DEPRECATED_FIELD, info.position(),
2277              "Removed deprecated enum constant " + info.qualifiedName());
2278        } else {
2279          Errors.error(Errors.REMOVED_FIELD, info.position(),
2280              "Removed enum constant " + info.qualifiedName());
2281        }
2282        consistent = false;
2283      }
2284    }
2285    for (FieldInfo info : cl.mApiCheckEnumConstants.values()) {
2286      if (!mApiCheckEnumConstants.containsKey(info.name())) {
2287        Errors.error(Errors.ADDED_FIELD, info.position(), "Added enum constant "
2288            + info.qualifiedName());
2289        consistent = false;
2290      }
2291    }
2292
2293    if (mIsAbstract != cl.mIsAbstract) {
2294      consistent = false;
2295      Errors.error(Errors.CHANGED_ABSTRACT, cl.position(), "Class " + cl.qualifiedName()
2296          + " changed abstract qualifier");
2297    }
2298
2299    if (!mIsFinal && cl.mIsFinal) {
2300      /*
2301       * It is safe to make a class final if it did not previously have any public
2302       * constructors because it was impossible for an application to create a subclass.
2303       */
2304      if (mApiCheckConstructors.isEmpty()) {
2305        consistent = false;
2306        Errors.error(Errors.ADDED_FINAL_UNINSTANTIABLE, cl.position(),
2307            "Class " + cl.qualifiedName() + " added final qualifier but "
2308            + "was previously uninstantiable and therefore could not be subclassed");
2309      } else {
2310        consistent = false;
2311        Errors.error(Errors.ADDED_FINAL, cl.position(), "Class " + cl.qualifiedName()
2312            + " added final qualifier");
2313      }
2314    } else if (mIsFinal && !cl.mIsFinal) {
2315      consistent = false;
2316      Errors.error(Errors.REMOVED_FINAL, cl.position(), "Class " + cl.qualifiedName()
2317          + " removed final qualifier");
2318    }
2319
2320    if (mIsStatic != cl.mIsStatic) {
2321      consistent = false;
2322      Errors.error(Errors.CHANGED_STATIC, cl.position(), "Class " + cl.qualifiedName()
2323          + " changed static qualifier");
2324    }
2325
2326    if (!scope().equals(cl.scope())) {
2327      consistent = false;
2328      Errors.error(Errors.CHANGED_SCOPE, cl.position(), "Class " + cl.qualifiedName()
2329          + " scope changed from " + scope() + " to " + cl.scope());
2330    }
2331
2332    if (!isDeprecated() == cl.isDeprecated()) {
2333      consistent = false;
2334      Errors.error(Errors.CHANGED_DEPRECATED, cl.position(), "Class " + cl.qualifiedName()
2335          + " has changed deprecation state " + isDeprecated() + " --> " + cl.isDeprecated());
2336    }
2337
2338    if (superclassName() != null) { // java.lang.Object can't have a superclass.
2339      if (!cl.extendsClass(superclassName())) {
2340        consistent = false;
2341        Errors.error(Errors.CHANGED_SUPERCLASS, cl.position(), "Class " + qualifiedName()
2342            + " superclass changed from " + superclassName() + " to " + cl.superclassName());
2343      }
2344    }
2345
2346    if (hasTypeParameters() && cl.hasTypeParameters()) {
2347      ArrayList<TypeInfo> oldParams = typeParameters();
2348      ArrayList<TypeInfo> newParams = cl.typeParameters();
2349      if (oldParams.size() != newParams.size()) {
2350        consistent = false;
2351        Errors.error(Errors.CHANGED_TYPE, cl.position(), "Class " + qualifiedName()
2352            + " changed number of type parameters from " + oldParams.size()
2353            + " to " + newParams.size());
2354      }
2355    }
2356
2357    return consistent;
2358  }
2359
2360  // Find a superclass implementation of the given method based on the methods in mApiCheckMethods.
2361  public static MethodInfo overriddenMethod(MethodInfo candidate, ClassInfo newClassObj) {
2362    if (newClassObj == null) {
2363      return null;
2364    }
2365    for (MethodInfo mi : newClassObj.mApiCheckMethods.values()) {
2366      if (mi.matches(candidate)) {
2367        // found it
2368        return mi;
2369      }
2370    }
2371
2372    // not found here. recursively search ancestors
2373    return ClassInfo.overriddenMethod(candidate, newClassObj.mSuperclass);
2374  }
2375
2376  // Find a superinterface declaration of the given method.
2377  public static MethodInfo interfaceMethod(MethodInfo candidate, ClassInfo newClassObj) {
2378    if (newClassObj == null) {
2379      return null;
2380    }
2381    for (ClassInfo interfaceInfo : newClassObj.interfaces()) {
2382      for (MethodInfo mi : interfaceInfo.mApiCheckMethods.values()) {
2383        if (mi.matches(candidate)) {
2384          return mi;
2385        }
2386      }
2387    }
2388    return ClassInfo.interfaceMethod(candidate, newClassObj.mSuperclass);
2389  }
2390
2391  public boolean hasConstructor(MethodInfo constructor) {
2392    String name = constructor.getHashableName();
2393    for (MethodInfo ctor : mApiCheckConstructors.values()) {
2394      if (name.equals(ctor.getHashableName())) {
2395        return true;
2396      }
2397    }
2398    return false;
2399  }
2400
2401  public void setTypeInfo(TypeInfo typeInfo) {
2402    mTypeInfo = typeInfo;
2403  }
2404
2405  public TypeInfo type() {
2406      return mTypeInfo;
2407  }
2408
2409  public boolean hasTypeParameters() {
2410      if (mTypeInfo != null && mTypeInfo.typeArguments() != null) {
2411          return !mTypeInfo.typeArguments().isEmpty();
2412      }
2413      return false;
2414  }
2415
2416  public ArrayList<TypeInfo> typeParameters() {
2417      if (hasTypeParameters()) {
2418          return mTypeInfo.typeArguments();
2419      }
2420      return null;
2421  }
2422
2423  public void addInnerClass(ClassInfo innerClass) {
2424      if (mInnerClasses == null) {
2425          mInnerClasses = new ArrayList<ClassInfo>();
2426      }
2427
2428      mInnerClasses.add(innerClass);
2429  }
2430
2431  public void setContainingClass(ClassInfo containingClass) {
2432      mContainingClass = containingClass;
2433  }
2434
2435  public void setSuperclassType(TypeInfo superclassType) {
2436      mRealSuperclassType = superclassType;
2437  }
2438
2439  public void printResolutions() {
2440      if (mResolutions == null || mResolutions.isEmpty()) {
2441          return;
2442      }
2443
2444      System.out.println("Resolutions for Class " + mName + ":");
2445
2446      for (Resolution r : mResolutions) {
2447          System.out.println(r);
2448      }
2449  }
2450
2451  public void addResolution(Resolution resolution) {
2452      if (mResolutions == null) {
2453          mResolutions = new ArrayList<Resolution>();
2454      }
2455
2456      mResolutions.add(resolution);
2457  }
2458
2459  public boolean resolveResolutions() {
2460      ArrayList<Resolution> resolutions = mResolutions;
2461      mResolutions = new ArrayList<Resolution>();
2462
2463      boolean allResolved = true;
2464      for (Resolution resolution : resolutions) {
2465          StringBuilder qualifiedClassName = new StringBuilder();
2466          InfoBuilder.resolveQualifiedName(resolution.getValue(), qualifiedClassName,
2467                  resolution.getInfoBuilder());
2468
2469          // if we still couldn't resolve it, save it for the next pass
2470          if ("".equals(qualifiedClassName.toString())) {
2471              mResolutions.add(resolution);
2472              allResolved = false;
2473          } else if ("superclassQualifiedName".equals(resolution.getVariable())) {
2474              setSuperClass(InfoBuilder.Caches.obtainClass(qualifiedClassName.toString()));
2475          } else if ("interfaceQualifiedName".equals(resolution.getVariable())) {
2476              addInterface(InfoBuilder.Caches.obtainClass(qualifiedClassName.toString()));
2477          }
2478      }
2479
2480      return allResolved;
2481  }
2482}
2483