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