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