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