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