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 52 public ClassTypePair(ClassInfo cl, TypeInfo t) { 53 mClassInfo = cl; 54 mTypeInfo = t; 55 } 56 57 public ClassInfo classInfo() { 58 return mClassInfo; 59 } 60 61 public TypeInfo typeInfo() { 62 return mTypeInfo; 63 } 64 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 */ 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 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 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 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 195 public void init2() { 196 // calling this here forces the AttrTagInfo objects to be linked to the AttribtueInfo 197 // objects 198 selfAttributes(); 199 } 200 201 public void init3(ArrayList<TypeInfo> types, ArrayList<ClassInfo> realInnerClasses) { 202 mTypeParameters = types; 203 mRealInnerClasses = realInnerClasses; 204 } 205 206 public class ClassMemberInfo extends MemberInfo { 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 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 */ 225 public MemberInfo asMemberInfo() { 226 return new ClassMemberInfo(); 227 } 228 229 public ArrayList<ClassInfo> getRealInnerClasses() { 230 return mRealInnerClasses; 231 } 232 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 */ 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 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 259 public ContainerInfo parent() { 260 return this; 261 } 262 263 public boolean isPublic() { 264 return mIsPublic; 265 } 266 267 public boolean isProtected() { 268 return mIsProtected; 269 } 270 271 public boolean isPackagePrivate() { 272 return mIsPackagePrivate; 273 } 274 275 public boolean isPrivate() { 276 return mIsPrivate; 277 } 278 279 public boolean isStatic() { 280 return mIsStatic; 281 } 282 283 public boolean isInterface() { 284 return mIsInterface; 285 } 286 287 public boolean isAbstract() { 288 return mIsAbstract; 289 } 290 291 public PackageInfo containingPackage() { 292 return mContainingPackage; 293 } 294 295 public ClassInfo containingClass() { 296 return mContainingClass; 297 } 298 299 public boolean isOrdinaryClass() { 300 return mIsOrdinaryClass; 301 } 302 303 public boolean isException() { 304 return mIsException; 305 } 306 307 public boolean isError() { 308 return mIsError; 309 } 310 311 public boolean isEnum() { 312 return mIsEnum; 313 } 314 315 public boolean isAnnotation() { 316 return mIsAnnotation; 317 } 318 319 public boolean isFinal() { 320 return mIsFinal; 321 } 322 323 public boolean isEffectivelyFinal() { 324 return mIsFinal || mApiCheckConstructors.isEmpty(); 325 } 326 327 public boolean isIncluded() { 328 return mIsIncluded; 329 } 330 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 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 */ 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 */ 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 */ 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 */ 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 * */ 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 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 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 511 public ArrayList<ClassInfo> realInterfaces() { 512 return mRealInterfaces; 513 } 514 515 ArrayList<TypeInfo> realInterfaceTypes() { 516 return mRealInterfaceTypes; 517 } 518 519 public void addInterfaceType(TypeInfo type) { 520 if (mRealInterfaceTypes == null) { 521 mRealInterfaceTypes = new ArrayList<TypeInfo>(); 522 } 523 524 mRealInterfaceTypes.add(type); 525 } 526 527 public String name() { 528 return mName; 529 } 530 531 public String[] nameParts() { 532 return mNameParts; 533 } 534 535 public String leafName() { 536 return mNameParts[mNameParts.length - 1]; 537 } 538 539 public String qualifiedName() { 540 return mQualifiedName; 541 } 542 543 public String qualifiedTypeName() { 544 return mQualifiedTypeName; 545 } 546 547 public boolean isPrimitive() { 548 return mIsPrimitive; 549 } 550 551 public ArrayList<MethodInfo> allConstructors() { 552 return mAllConstructors; 553 } 554 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 573 public ArrayList<ClassInfo> innerClasses() { 574 return mInnerClasses; 575 } 576 577 public TagInfo[] inlineTags() { 578 return comment().tags(); 579 } 580 581 public TagInfo[] firstSentenceTags() { 582 return comment().briefTags(); 583 } 584 585 public void setDeprecated(boolean deprecated) { 586 mDeprecatedKnown = true; 587 mIsDeprecated = deprecated; 588 } 589 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 619 public TagInfo[] deprecatedTags() { 620 // Should we also do the interfaces? 621 return comment().deprecatedTags(); 622 } 623 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 654 public ArrayList<MethodInfo> annotationElements() { 655 return mAnnotationElements; 656 } 657 658 public ArrayList<AnnotationInstanceInfo> annotations() { 659 return mAnnotations; 660 } 661 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 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 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 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 731 public ArrayList<FieldInfo> allSelfFields() { 732 return mAllSelfFields; 733 } 734 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 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 784 public ArrayList<MethodInfo> allSelfMethods() { 785 return mAllSelfMethods; 786 } 787 788 /** 789 * @param removedMethods the removed methods regardless of access levels. 790 */ 791 public void setRemovedMethods(List<MethodInfo> removedMethods) { 792 Collections.sort(removedMethods, MethodInfo.comparator); 793 mRemovedMethods = Collections.unmodifiableList(removedMethods); 794 } 795 796 public void setExhaustiveConstructors(List<MethodInfo> constructors) { 797 mExhaustiveConstructors = constructors; 798 } 799 800 public void setExhaustiveMethods(List<MethodInfo> methods) { 801 mExhaustiveMethods = methods; 802 } 803 804 public void setExhaustiveEnumConstants(List<FieldInfo> enumConstants) { 805 mExhaustiveEnumConstants = enumConstants; 806 } 807 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 */ 816 public List<MethodInfo> getRemovedMethods() { 817 return mRemovedMethods; 818 } 819 820 public List<MethodInfo> getExhaustiveConstructors() { 821 return mExhaustiveConstructors; 822 } 823 824 public List<MethodInfo> getExhaustiveMethods() { 825 return mExhaustiveMethods; 826 } 827 828 public List<FieldInfo> getExhaustiveEnumConstants() { 829 return mExhaustiveEnumConstants; 830 } 831 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 */ 841 public List<ClassInfo> gatherAncestorClasses() { 842 LinkedList<ClassInfo> classes = gatherAncestorClasses(new LinkedList<>()); 843 classes.removeLast(); 844 return classes; 845 } 846 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 */ 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 */ 879 public Collection<ClassInfo> filteredInterfaces(Predicate<MemberInfo> predicate) { 880 return filteredInterfaces(predicate, new LinkedHashSet<>()); 881 } 882 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 */ 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 */ 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 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 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. 959 void setPackage(PackageInfo pkg) { 960 mContainingPackage = pkg; 961 } 962 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 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 1032 public ArrayList<FieldInfo> enumConstants() { 1033 return mEnumConstants; 1034 } 1035 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 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 */ 1061 public TypeInfo superclassType() { 1062 return mRealSuperclassType; 1063 } 1064 1065 public TypeInfo asTypeInfo() { 1066 return mTypeInfo; 1067 } 1068 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 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 */ 1088 public boolean isDerivedFrom(ClassInfo cl) { 1089 return isDerivedFrom(cl.qualifiedName()); 1090 } 1091 1092 /** Even indirectly */ 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 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 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 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 */ 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 if (isDeprecated()) { 1259 data.setValue(base + ".deprecatedsince", getDeprecatedSince()); 1260 } 1261 data.setValue(base + ".artifact", getArtifact()); 1262 1263 ArrayList<AnnotationInstanceInfo> showAnnos = getShowAnnotationsIncludeOuters(); 1264 AnnotationInstanceInfo.makeLinkListHDF( 1265 data, 1266 base + ".showAnnotations", 1267 showAnnos.toArray(new AnnotationInstanceInfo[showAnnos.size()])); 1268 1269 setFederatedReferences(data, base); 1270 } 1271 1272 /** 1273 * Turns into the main class page 1274 */ 1275 public void makeHDF(Data data) { 1276 int i, j, n; 1277 String name = name(); 1278 String qualified = qualifiedName(); 1279 ArrayList<AttributeInfo> selfAttributes = selfAttributes(); 1280 ArrayList<MethodInfo> methods = selfMethods(); 1281 ArrayList<FieldInfo> fields = selfFields(); 1282 ArrayList<FieldInfo> enumConstants = enumConstants(); 1283 ArrayList<MethodInfo> ctors = constructors(); 1284 ArrayList<ClassInfo> inners = innerClasses(); 1285 1286 // class name 1287 mTypeInfo.makeHDF(data, "class.type"); 1288 mTypeInfo.makeQualifiedHDF(data, "class.qualifiedType"); 1289 data.setValue("class.name", name); 1290 data.setValue("class.qualified", qualified); 1291 if (isProtected()) { 1292 data.setValue("class.scope", "protected"); 1293 } else if (isPublic()) { 1294 data.setValue("class.scope", "public"); 1295 } 1296 if (isStatic()) { 1297 data.setValue("class.static", "static"); 1298 } 1299 if (isFinal()) { 1300 data.setValue("class.final", "final"); 1301 } 1302 if (isAbstract() && !isInterface()) { 1303 data.setValue("class.abstract", "abstract"); 1304 } 1305 1306 int numAnnotationDocumentation = 0; 1307 for (AnnotationInstanceInfo aii : annotations()) { 1308 String annotationDocumentation = Doclava.getDocumentationStringForAnnotation( 1309 aii.type().qualifiedName()); 1310 if (annotationDocumentation != null) { 1311 data.setValue("class.annotationdocumentation." + numAnnotationDocumentation + ".text", 1312 annotationDocumentation); 1313 numAnnotationDocumentation++; 1314 } 1315 } 1316 1317 ArrayList<AnnotationInstanceInfo> showAnnos = getShowAnnotationsIncludeOuters(); 1318 AnnotationInstanceInfo.makeLinkListHDF( 1319 data, 1320 "class.showAnnotations", 1321 showAnnos.toArray(new AnnotationInstanceInfo[showAnnos.size()])); 1322 1323 // class info 1324 String kind = kind(); 1325 if (kind != null) { 1326 data.setValue("class.kind", kind); 1327 } 1328 data.setValue("class.since", getSince()); 1329 if (isDeprecated()) { 1330 data.setValue("class.deprecatedsince", getDeprecatedSince()); 1331 } 1332 data.setValue("class.artifact", getArtifact()); 1333 setFederatedReferences(data, "class"); 1334 1335 // the containing package -- note that this can be passed to type_link, 1336 // but it also contains the list of all of the packages 1337 containingPackage().makeClassLinkListHDF(data, "class.package"); 1338 1339 // inheritance hierarchy 1340 List<ClassTypePair> ctplist = superClassesWithTypes(); 1341 n = ctplist.size(); 1342 for (i = 0; i < ctplist.size(); i++) { 1343 // go in reverse order 1344 ClassTypePair ctp = ctplist.get(n - i - 1); 1345 if (ctp.classInfo().checkLevel()) { 1346 ctp.typeInfo().makeQualifiedHDF(data, "class.inheritance." + i + ".class"); 1347 ctp.typeInfo().makeHDF(data, "class.inheritance." + i + ".short_class"); 1348 j = 0; 1349 for (ClassTypePair t : ctp.classInfo().interfacesWithTypes()) { 1350 t.typeInfo().makeHDF(data, "class.inheritance." + i + ".interfaces." + j); 1351 j++; 1352 } 1353 } 1354 } 1355 1356 // class description 1357 TagInfo.makeHDF(data, "class.descr", inlineTags()); 1358 TagInfo.makeHDF(data, "class.descrAux", Doclava.auxSource.classAuxTags(this)); 1359 TagInfo.makeHDF(data, "class.seeAlso", comment().seeTags()); 1360 TagInfo.makeHDF(data, "class.deprecated", deprecatedTags()); 1361 1362 // known subclasses 1363 TreeMap<String, ClassInfo> direct = new TreeMap<String, ClassInfo>(); 1364 TreeMap<String, ClassInfo> indirect = new TreeMap<String, ClassInfo>(); 1365 Collection<ClassInfo> all = Converter.rootClasses(); 1366 for (ClassInfo cl : all) { 1367 if (cl.superclass() != null && cl.superclass().equals(this)) { 1368 direct.put(cl.name(), cl); 1369 } else if (cl.isDerivedFrom(this)) { 1370 indirect.put(cl.name(), cl); 1371 } 1372 } 1373 // direct 1374 i = 0; 1375 for (ClassInfo cl : direct.values()) { 1376 if (cl.checkLevel()) { 1377 cl.makeShortDescrHDF(data, "class.subclasses.direct." + i); 1378 } 1379 i++; 1380 } 1381 // indirect 1382 i = 0; 1383 for (ClassInfo cl : indirect.values()) { 1384 if (cl.checkLevel()) { 1385 cl.makeShortDescrHDF(data, "class.subclasses.indirect." + i); 1386 } 1387 i++; 1388 } 1389 1390 // hide special cases 1391 if ("java.lang.Object".equals(qualified) || "java.io.Serializable".equals(qualified)) { 1392 data.setValue("class.subclasses.hidden", "1"); 1393 } else { 1394 data.setValue("class.subclasses.hidden", "0"); 1395 } 1396 1397 // nested classes 1398 i = 0; 1399 for (ClassInfo inner : inners) { 1400 if (inner.checkLevel()) { 1401 inner.makeShortDescrHDF(data, "class.inners." + i); 1402 } 1403 i++; 1404 } 1405 1406 // enum constants 1407 i = 0; 1408 for (FieldInfo field : enumConstants) { 1409 field.makeHDF(data, "class.enumConstants." + i); 1410 i++; 1411 } 1412 1413 // constants 1414 i = 0; 1415 for (FieldInfo field : fields) { 1416 if (field.isConstant()) { 1417 field.makeHDF(data, "class.constants." + i); 1418 i++; 1419 } 1420 } 1421 1422 // fields 1423 i = 0; 1424 for (FieldInfo field : fields) { 1425 if (!field.isConstant()) { 1426 field.makeHDF(data, "class.fields." + i); 1427 i++; 1428 } 1429 } 1430 1431 // public constructors 1432 i = 0; 1433 for (MethodInfo ctor : ctors) { 1434 if (ctor.isPublic()) { 1435 ctor.makeHDF(data, "class.ctors.public." + i); 1436 i++; 1437 } 1438 } 1439 1440 // protected constructors 1441 if (Doclava.checkLevel(Doclava.SHOW_PROTECTED)) { 1442 i = 0; 1443 for (MethodInfo ctor : ctors) { 1444 if (ctor.isProtected()) { 1445 ctor.makeHDF(data, "class.ctors.protected." + i); 1446 i++; 1447 } 1448 } 1449 } 1450 1451 // package private constructors 1452 if (Doclava.checkLevel(Doclava.SHOW_PACKAGE)) { 1453 i = 0; 1454 for (MethodInfo ctor : ctors) { 1455 if (ctor.isPackagePrivate()) { 1456 ctor.makeHDF(data, "class.ctors.package." + i); 1457 i++; 1458 } 1459 } 1460 } 1461 1462 // private constructors 1463 if (Doclava.checkLevel(Doclava.SHOW_PRIVATE)) { 1464 i = 0; 1465 for (MethodInfo ctor : ctors) { 1466 if (ctor.isPrivate()) { 1467 ctor.makeHDF(data, "class.ctors.private." + i); 1468 i++; 1469 } 1470 } 1471 } 1472 1473 // public methods 1474 i = 0; 1475 for (MethodInfo method : methods) { 1476 if (method.isPublic()) { 1477 method.makeHDF(data, "class.methods.public." + i); 1478 i++; 1479 } 1480 } 1481 1482 // protected methods 1483 if (Doclava.checkLevel(Doclava.SHOW_PROTECTED)) { 1484 i = 0; 1485 for (MethodInfo method : methods) { 1486 if (method.isProtected()) { 1487 method.makeHDF(data, "class.methods.protected." + i); 1488 i++; 1489 } 1490 } 1491 } 1492 1493 // package private methods 1494 if (Doclava.checkLevel(Doclava.SHOW_PACKAGE)) { 1495 i = 0; 1496 for (MethodInfo method : methods) { 1497 if (method.isPackagePrivate()) { 1498 method.makeHDF(data, "class.methods.package." + i); 1499 i++; 1500 } 1501 } 1502 } 1503 1504 // private methods 1505 if (Doclava.checkLevel(Doclava.SHOW_PRIVATE)) { 1506 i = 0; 1507 for (MethodInfo method : methods) { 1508 if (method.isPrivate()) { 1509 method.makeHDF(data, "class.methods.private." + i); 1510 i++; 1511 } 1512 } 1513 } 1514 1515 // xml attributes 1516 i = 0; 1517 for (AttributeInfo attr : selfAttributes) { 1518 if (attr.checkLevel()) { 1519 attr.makeHDF(data, "class.attrs." + i); 1520 i++; 1521 } 1522 } 1523 1524 // inherited methods 1525 Iterator<ClassTypePair> superclassesItr = superClassesWithTypes().iterator(); 1526 superclassesItr.next(); // skip the first one, which is the current class 1527 ClassTypePair superCtp; 1528 i = 0; 1529 while (superclassesItr.hasNext()) { 1530 superCtp = superclassesItr.next(); 1531 if (superCtp.classInfo().checkLevel()) { 1532 makeInheritedHDF(data, i, superCtp); 1533 i++; 1534 } 1535 } 1536 Iterator<ClassTypePair> interfacesItr = allInterfacesWithTypes().iterator(); 1537 while (interfacesItr.hasNext()) { 1538 superCtp = interfacesItr.next(); 1539 if (superCtp.classInfo().checkLevel()) { 1540 makeInheritedHDF(data, i, superCtp); 1541 i++; 1542 } 1543 } 1544 } 1545 1546 private static void makeInheritedHDF(Data data, int index, ClassTypePair ctp) { 1547 int i; 1548 1549 String base = "class.inherited." + index; 1550 data.setValue(base + ".qualified", ctp.classInfo().qualifiedName()); 1551 if (ctp.classInfo().checkLevel()) { 1552 data.setValue(base + ".link", ctp.classInfo().htmlPage()); 1553 } 1554 String kind = ctp.classInfo().kind(); 1555 if (kind != null) { 1556 data.setValue(base + ".kind", kind); 1557 } 1558 1559 if (ctp.classInfo().mIsIncluded) { 1560 data.setValue(base + ".included", "true"); 1561 } else { 1562 Doclava.federationTagger.tagAll(Arrays.asList(ctp.classInfo())); 1563 if (!ctp.classInfo().getFederatedReferences().isEmpty()) { 1564 FederatedSite site = ctp.classInfo().getFederatedReferences().iterator().next(); 1565 data.setValue(base + ".link", site.linkFor(ctp.classInfo().htmlPage())); 1566 data.setValue(base + ".federated", site.name()); 1567 } 1568 } 1569 1570 // xml attributes 1571 i = 0; 1572 for (AttributeInfo attr : ctp.classInfo().selfAttributes()) { 1573 attr.makeHDF(data, base + ".attrs." + i); 1574 i++; 1575 } 1576 1577 // methods 1578 i = 0; 1579 for (MethodInfo method : ctp.classInfo().selfMethods()) { 1580 method.makeHDF(data, base + ".methods." + i, ctp.getTypeArgumentMapping()); 1581 i++; 1582 } 1583 1584 // fields 1585 i = 0; 1586 for (FieldInfo field : ctp.classInfo().selfFields()) { 1587 if (!field.isConstant()) { 1588 field.makeHDF(data, base + ".fields." + i); 1589 i++; 1590 } 1591 } 1592 1593 // constants 1594 i = 0; 1595 for (FieldInfo field : ctp.classInfo().selfFields()) { 1596 if (field.isConstant()) { 1597 field.makeHDF(data, base + ".constants." + i); 1598 i++; 1599 } 1600 } 1601 } 1602 1603 @Override 1604 public boolean isHidden() { 1605 if (mHidden == null) { 1606 mHidden = isHiddenImpl(); 1607 } 1608 1609 return mHidden; 1610 } 1611 1612 /** 1613 * @return true if the containing package has @hide comment, a hide annotaion, 1614 * or a containing class of this class is hidden. 1615 */ 1616 public boolean isHiddenImpl() { 1617 ClassInfo cl = this; 1618 while (cl != null) { 1619 if (cl.hasShowAnnotation()) { 1620 return false; 1621 } 1622 PackageInfo pkg = cl.containingPackage(); 1623 if (pkg != null && pkg.hasHideComment()) { 1624 return true; 1625 } 1626 if (cl.comment().isHidden() || cl.hasHideAnnotation()) { 1627 return true; 1628 } 1629 cl = cl.containingClass(); 1630 } 1631 return false; 1632 } 1633 1634 @Override 1635 public boolean isRemoved() { 1636 if (mRemoved == null) { 1637 mRemoved = isRemovedImpl(); 1638 } 1639 1640 return mRemoved; 1641 } 1642 1643 /** 1644 * @return true if the containing package has @removed comment, or an ancestor 1645 * class of this class is removed, or this class has @removed comment. 1646 */ 1647 public boolean isRemovedImpl() { 1648 ClassInfo cl = this; 1649 while (cl != null) { 1650 PackageInfo pkg = cl.containingPackage(); 1651 if (pkg != null && pkg.hasRemovedComment()) { 1652 return true; 1653 } 1654 if (cl.comment().isRemoved()) { 1655 return true; 1656 } 1657 cl = cl.containingClass(); 1658 } 1659 return false; 1660 } 1661 1662 @Override 1663 public boolean isHiddenOrRemoved() { 1664 return isHidden() || isRemoved(); 1665 } 1666 1667 public boolean hasShowAnnotation() { 1668 return mShowAnnotations != null && mShowAnnotations.size() > 0; 1669 } 1670 1671 public ArrayList<AnnotationInstanceInfo> showAnnotations() { 1672 return mShowAnnotations; 1673 } 1674 1675 public boolean hasHideAnnotation() { 1676 return mHideAnnotations != null && mHideAnnotations.size() > 0; 1677 } 1678 1679 public ArrayList<AnnotationInstanceInfo> hideAnnotations() { 1680 return mHideAnnotations; 1681 } 1682 1683 public ArrayList<AnnotationInstanceInfo> getShowAnnotationsIncludeOuters() { 1684 ArrayList<AnnotationInstanceInfo> allAnnotations = new ArrayList<AnnotationInstanceInfo>(); 1685 ClassInfo cl = this; 1686 while (cl != null) { 1687 if (cl.showAnnotations() != null) { 1688 // Don't allow duplicates into the merged list 1689 for (AnnotationInstanceInfo newAii : cl.showAnnotations()) { 1690 boolean addIt = true; 1691 for (AnnotationInstanceInfo existingAii : allAnnotations) { 1692 if (existingAii.type().name() == newAii.type().name()) { 1693 addIt = false; 1694 break; 1695 } 1696 } 1697 if (addIt) { 1698 allAnnotations.add(newAii); 1699 } 1700 } 1701 } 1702 cl = cl.containingClass(); 1703 } 1704 return allAnnotations; 1705 } 1706 1707 private MethodInfo matchMethod(ArrayList<MethodInfo> methods, String name, String[] params, 1708 String[] dimensions, boolean varargs) { 1709 for (MethodInfo method : methods) { 1710 if (method.name().equals(name)) { 1711 if (params == null) { 1712 return method; 1713 } else { 1714 if (method.matchesParams(params, dimensions, varargs)) { 1715 return method; 1716 } 1717 } 1718 } 1719 } 1720 return null; 1721 } 1722 1723 public MethodInfo findMethod(String name, String[] params, String[] dimensions, boolean varargs) { 1724 // first look on our class, and our superclasses 1725 1726 // for methods 1727 MethodInfo rv; 1728 rv = matchMethod(methods(), name, params, dimensions, varargs); 1729 1730 if (rv != null) { 1731 return rv; 1732 } 1733 1734 // for constructors 1735 rv = matchMethod(constructors(), name, params, dimensions, varargs); 1736 if (rv != null) { 1737 return rv; 1738 } 1739 1740 // then recursively look at our containing class 1741 ClassInfo containing = containingClass(); 1742 if (containing != null) { 1743 return containing.findMethod(name, params, dimensions, varargs); 1744 } 1745 1746 return null; 1747 } 1748 1749 public boolean supportsMethod(MethodInfo method) { 1750 for (MethodInfo m : methods()) { 1751 if (m.getHashableName().equals(method.getHashableName())) { 1752 return true; 1753 } 1754 } 1755 return false; 1756 } 1757 1758 private ClassInfo searchInnerClasses(String[] nameParts, int index) { 1759 String part = nameParts[index]; 1760 1761 ArrayList<ClassInfo> inners = mInnerClasses; 1762 for (ClassInfo in : inners) { 1763 String[] innerParts = in.nameParts(); 1764 if (part.equals(innerParts[innerParts.length - 1])) { 1765 if (index == nameParts.length - 1) { 1766 return in; 1767 } else { 1768 return in.searchInnerClasses(nameParts, index + 1); 1769 } 1770 } 1771 } 1772 return null; 1773 } 1774 1775 public ClassInfo extendedFindClass(String className) { 1776 // ClassDoc.findClass has this bug that we're working around here: 1777 // If you have a class PackageManager with an inner class PackageInfo 1778 // and you call it with "PackageInfo" it doesn't find it. 1779 return searchInnerClasses(className.split("\\."), 0); 1780 } 1781 1782 public ClassInfo findClass(String className) { 1783 return Converter.obtainClass(mClass.findClass(className)); 1784 } 1785 1786 public ClassInfo findInnerClass(String className) { 1787 // ClassDoc.findClass won't find inner classes. To deal with that, 1788 // we try what they gave us first, but if that didn't work, then 1789 // we see if there are any periods in className, and start searching 1790 // from there. 1791 String[] nodes = className.split("\\."); 1792 ClassDoc cl = mClass; 1793 1794 int N = nodes.length; 1795 for (int i = 0; i < N; ++i) { 1796 final String n = nodes[i]; 1797 if (n.isEmpty() && i == 0) { 1798 // We skip over an empty classname component if it's at location 0. This is 1799 // to deal with names like ".Inner". java7 will return a bogus ClassInfo when 1800 // we call "findClass("") and the next iteration of the loop will throw a 1801 // runtime exception. 1802 continue; 1803 } 1804 1805 cl = cl.findClass(n); 1806 if (cl == null) { 1807 return null; 1808 } 1809 } 1810 1811 return Converter.obtainClass(cl); 1812 } 1813 1814 public FieldInfo findField(String name) { 1815 // first look on our class, and our superclasses 1816 for (FieldInfo f : fields()) { 1817 if (f.name().equals(name)) { 1818 return f; 1819 } 1820 } 1821 1822 // then look at our enum constants (these are really fields, maybe 1823 // they should be mixed into fields(). not sure) 1824 for (FieldInfo f : enumConstants()) { 1825 if (f.name().equals(name)) { 1826 return f; 1827 } 1828 } 1829 1830 // then recursively look at our containing class 1831 ClassInfo containing = containingClass(); 1832 if (containing != null) { 1833 return containing.findField(name); 1834 } 1835 1836 return null; 1837 } 1838 1839 public static ClassInfo[] sortByName(ClassInfo[] classes) { 1840 int i; 1841 Sorter[] sorted = new Sorter[classes.length]; 1842 for (i = 0; i < sorted.length; i++) { 1843 ClassInfo cl = classes[i]; 1844 sorted[i] = new Sorter(cl.name(), cl); 1845 } 1846 1847 Arrays.sort(sorted); 1848 1849 ClassInfo[] rv = new ClassInfo[classes.length]; 1850 for (i = 0; i < rv.length; i++) { 1851 rv[i] = (ClassInfo) sorted[i].data; 1852 } 1853 1854 return rv; 1855 } 1856 1857 public void setNonWrittenConstructors(ArrayList<MethodInfo> nonWritten) { 1858 mNonWrittenConstructors = nonWritten; 1859 } 1860 1861 public ArrayList<MethodInfo> getNonWrittenConstructors() { 1862 return mNonWrittenConstructors; 1863 } 1864 1865 public String kind() { 1866 if (isOrdinaryClass()) { 1867 return "class"; 1868 } else if (isInterface()) { 1869 return "interface"; 1870 } else if (isEnum()) { 1871 return "enum"; 1872 } else if (isError()) { 1873 return "class"; 1874 } else if (isException()) { 1875 return "class"; 1876 } else if (isAnnotation()) { 1877 return "@interface"; 1878 } 1879 return null; 1880 } 1881 1882 public String scope() { 1883 if (isPublic()) { 1884 return "public"; 1885 } else if (isProtected()) { 1886 return "protected"; 1887 } else if (isPackagePrivate()) { 1888 return ""; 1889 } else if (isPrivate()) { 1890 return "private"; 1891 } else { 1892 throw new RuntimeException("invalid scope for object " + this); 1893 } 1894 } 1895 1896 public void setHiddenMethods(ArrayList<MethodInfo> mInfo) { 1897 mHiddenMethods = mInfo; 1898 } 1899 1900 public ArrayList<MethodInfo> getHiddenMethods() { 1901 return mHiddenMethods; 1902 } 1903 1904 @Override 1905 public String toString() { 1906 return this.qualifiedName(); 1907 } 1908 1909 @Override 1910 public boolean equals(Object o) { 1911 if (this == o) { 1912 return true; 1913 } else if (o instanceof ClassInfo) { 1914 final ClassInfo c = (ClassInfo) o; 1915 return mQualifiedName.equals(c.mQualifiedName); 1916 } else { 1917 return false; 1918 } 1919 } 1920 1921 @Override 1922 public int hashCode() { 1923 return mQualifiedName.hashCode(); 1924 } 1925 1926 public void setReasonIncluded(String reason) { 1927 mReasonIncluded = reason; 1928 } 1929 1930 public String getReasonIncluded() { 1931 return mReasonIncluded; 1932 } 1933 1934 private ClassDoc mClass; 1935 1936 // ctor 1937 private boolean mIsPublic; 1938 private boolean mIsProtected; 1939 private boolean mIsPackagePrivate; 1940 private boolean mIsPrivate; 1941 private boolean mIsStatic; 1942 private boolean mIsInterface; 1943 private boolean mIsAbstract; 1944 private boolean mIsOrdinaryClass; 1945 private boolean mIsException; 1946 private boolean mIsError; 1947 private boolean mIsEnum; 1948 private boolean mIsAnnotation; 1949 private boolean mIsFinal; 1950 private boolean mIsIncluded; 1951 private String mName; 1952 private String mQualifiedName; 1953 private String mQualifiedTypeName; 1954 private boolean mIsPrimitive; 1955 private TypeInfo mTypeInfo; 1956 private String[] mNameParts; 1957 1958 // init 1959 private ArrayList<ClassInfo> mRealInterfaces = new ArrayList<ClassInfo>(); 1960 private ArrayList<ClassInfo> mInterfaces; 1961 private ArrayList<TypeInfo> mRealInterfaceTypes; 1962 private ArrayList<ClassInfo> mInnerClasses; 1963 // mAllConstructors will not contain *all* constructors. Only the constructors that pass 1964 // checkLevel. @see {@link Converter#convertMethods(ConstructorDoc[])} 1965 private ArrayList<MethodInfo> mAllConstructors = new ArrayList<MethodInfo>(); 1966 // mAllSelfMethods will not contain *all* self methods. Only the methods that pass 1967 // checkLevel. @see {@link Converter#convertMethods(MethodDoc[])} 1968 private ArrayList<MethodInfo> mAllSelfMethods = new ArrayList<MethodInfo>(); 1969 private ArrayList<MethodInfo> mAnnotationElements = new ArrayList<MethodInfo>(); // if this class is an annotation 1970 private ArrayList<FieldInfo> mAllSelfFields = new ArrayList<FieldInfo>(); 1971 private ArrayList<FieldInfo> mEnumConstants = new ArrayList<FieldInfo>(); 1972 private PackageInfo mContainingPackage; 1973 private ClassInfo mContainingClass; 1974 private ClassInfo mRealSuperclass; 1975 private TypeInfo mRealSuperclassType; 1976 private ClassInfo mSuperclass; 1977 private ArrayList<AnnotationInstanceInfo> mAnnotations; 1978 private ArrayList<AnnotationInstanceInfo> mShowAnnotations; 1979 private ArrayList<AnnotationInstanceInfo> mHideAnnotations; 1980 private boolean mSuperclassInit; 1981 private boolean mDeprecatedKnown; 1982 1983 // lazy 1984 private ArrayList<ClassTypePair> mSuperclassesWithTypes; 1985 private ArrayList<ClassTypePair> mInterfacesWithTypes; 1986 private ArrayList<ClassTypePair> mAllInterfacesWithTypes; 1987 private ArrayList<MethodInfo> mConstructors; 1988 private ArrayList<ClassInfo> mRealInnerClasses; 1989 private ArrayList<MethodInfo> mSelfMethods; 1990 private ArrayList<FieldInfo> mSelfFields; 1991 private ArrayList<AttributeInfo> mSelfAttributes; 1992 private ArrayList<MethodInfo> mMethods; 1993 private ArrayList<FieldInfo> mFields; 1994 private ArrayList<TypeInfo> mTypeParameters; 1995 private ArrayList<MethodInfo> mHiddenMethods; 1996 private Boolean mHidden = null; 1997 private Boolean mRemoved = null; 1998 private Boolean mCheckLevel = null; 1999 private String mReasonIncluded; 2000 private ArrayList<MethodInfo> mNonWrittenConstructors; 2001 private boolean mIsDeprecated; 2002 2003 // TODO: Temporary members from apicheck migration. 2004 private HashMap<String, MethodInfo> mApiCheckConstructors = new HashMap<String, MethodInfo>(); 2005 private HashMap<String, MethodInfo> mApiCheckMethods = new HashMap<String, MethodInfo>(); 2006 private HashMap<String, FieldInfo> mApiCheckFields = new HashMap<String, FieldInfo>(); 2007 private HashMap<String, FieldInfo> mApiCheckEnumConstants = new HashMap<String, FieldInfo>(); 2008 2009 // Resolutions 2010 private ArrayList<Resolution> mResolutions; 2011 2012 private List<MethodInfo> mRemovedMethods; // immutable after you set its value. 2013 2014 private List<MethodInfo> mExhaustiveConstructors; // immutable after you set its value. 2015 private List<MethodInfo> mExhaustiveMethods; // immutable after you set its value. 2016 private List<FieldInfo> mExhaustiveEnumConstants; // immutable after you set its value. 2017 private List<FieldInfo> mExhaustiveFields; // immutable after you set its value. 2018 2019 /** 2020 * Returns true if {@code cl} implements the interface {@code iface} either by either being that 2021 * interface, implementing that interface or extending a type that implements the interface. 2022 */ 2023 public boolean implementsInterface(String iface) { 2024 if (qualifiedName().equals(iface)) { 2025 return true; 2026 } 2027 for (ClassInfo clImplements : realInterfaces()) { 2028 if (clImplements.implementsInterface(iface)) { 2029 return true; 2030 } 2031 } 2032 if (mSuperclass != null && mSuperclass.implementsInterface(iface)) { 2033 return true; 2034 } 2035 return false; 2036 } 2037 2038 /** 2039 * Returns true if {@code this} extends the class {@code ext}. 2040 */ 2041 public boolean extendsClass(String cl) { 2042 if (qualifiedName().equals(cl)) { 2043 return true; 2044 } 2045 if (mSuperclass != null && mSuperclass.extendsClass(cl)) { 2046 return true; 2047 } 2048 return false; 2049 } 2050 2051 /** 2052 * Returns true if {@code this} is assignable to cl 2053 */ 2054 public boolean isAssignableTo(String cl) { 2055 return implementsInterface(cl) || extendsClass(cl); 2056 } 2057 2058 public void addInterface(ClassInfo iface) { 2059 mRealInterfaces.add(iface); 2060 } 2061 2062 public void addConstructor(MethodInfo ctor) { 2063 mApiCheckConstructors.put(ctor.getHashableName(), ctor); 2064 2065 mAllConstructors.add(ctor); 2066 mConstructors = null; // flush this, hopefully it hasn't been used yet. 2067 } 2068 2069 public void addField(FieldInfo field) { 2070 mApiCheckFields.put(field.name(), field); 2071 2072 mAllSelfFields.add(field); 2073 2074 mSelfFields = null; // flush this, hopefully it hasn't been used yet. 2075 } 2076 2077 public void addEnumConstant(FieldInfo field) { 2078 mApiCheckEnumConstants.put(field.name(), field); 2079 2080 mEnumConstants.add(field); 2081 } 2082 2083 public void setSuperClass(ClassInfo superclass) { 2084 mRealSuperclass = superclass; 2085 mSuperclass = superclass; 2086 } 2087 2088 public Map<String, MethodInfo> allConstructorsMap() { 2089 return mApiCheckConstructors; 2090 } 2091 2092 public Map<String, FieldInfo> allFields() { 2093 return mApiCheckFields; 2094 } 2095 2096 public Map<String, FieldInfo> allEnums() { 2097 return mApiCheckEnumConstants; 2098 } 2099 2100 /** 2101 * Returns all methods defined directly in this class. For a list of all 2102 * methods supported by this class, see {@link #methods()}. 2103 */ 2104 public Map<String, MethodInfo> allMethods() { 2105 return mApiCheckMethods; 2106 } 2107 2108 /** 2109 * Returns the class hierarchy for this class, starting with this class. 2110 */ 2111 public Iterable<ClassInfo> hierarchy() { 2112 List<ClassInfo> result = new ArrayList<ClassInfo>(4); 2113 for (ClassInfo c = this; c != null; c = c.mSuperclass) { 2114 result.add(c); 2115 } 2116 return result; 2117 } 2118 2119 public String superclassName() { 2120 if (mSuperclass == null) { 2121 if (mQualifiedName.equals("java.lang.Object")) { 2122 return null; 2123 } 2124 throw new UnsupportedOperationException("Superclass not set for " + qualifiedName()); 2125 } 2126 return mSuperclass.mQualifiedName; 2127 } 2128 2129 public void setAnnotations(ArrayList<AnnotationInstanceInfo> annotations) { 2130 mAnnotations = annotations; 2131 } 2132 2133 public boolean isConsistent(ClassInfo cl) { 2134 return isConsistent(cl, null, null); 2135 } 2136 2137 public boolean isConsistent(ClassInfo cl, List<MethodInfo> newCtors, List<MethodInfo> newMethods) { 2138 boolean consistent = true; 2139 boolean diffMode = (newCtors != null) && (newMethods != null); 2140 2141 if (isInterface() != cl.isInterface()) { 2142 Errors.error(Errors.CHANGED_CLASS, cl.position(), "Class " + cl.qualifiedName() 2143 + " changed class/interface declaration"); 2144 consistent = false; 2145 } 2146 for (ClassInfo iface : mRealInterfaces) { 2147 if (!cl.implementsInterface(iface.mQualifiedName)) { 2148 Errors.error(Errors.REMOVED_INTERFACE, cl.position(), "Class " + qualifiedName() 2149 + " no longer implements " + iface); 2150 } 2151 } 2152 for (ClassInfo iface : cl.mRealInterfaces) { 2153 if (!implementsInterface(iface.mQualifiedName)) { 2154 Errors.error(Errors.ADDED_INTERFACE, cl.position(), "Added interface " + iface 2155 + " to class " + qualifiedName()); 2156 consistent = false; 2157 } 2158 } 2159 2160 for (MethodInfo mInfo : mApiCheckMethods.values()) { 2161 if (cl.mApiCheckMethods.containsKey(mInfo.getHashableName())) { 2162 if (!mInfo.isConsistent(cl.mApiCheckMethods.get(mInfo.getHashableName()))) { 2163 consistent = false; 2164 } 2165 } else { 2166 /* 2167 * This class formerly provided this method directly, and now does not. Check our ancestry 2168 * to see if there's an inherited version that still fulfills the API requirement. 2169 */ 2170 MethodInfo mi = ClassInfo.overriddenMethod(mInfo, cl); 2171 if (mi == null) { 2172 mi = ClassInfo.interfaceMethod(mInfo, cl); 2173 } 2174 if (mi == null) { 2175 if (mInfo.isDeprecated()) { 2176 Errors.error(Errors.REMOVED_DEPRECATED_METHOD, mInfo.position(), 2177 "Removed deprecated public method " + mInfo.prettyQualifiedSignature()); 2178 } else { 2179 Errors.error(Errors.REMOVED_METHOD, mInfo.position(), 2180 "Removed public method " + mInfo.prettyQualifiedSignature()); 2181 } 2182 consistent = false; 2183 } 2184 } 2185 } 2186 for (MethodInfo mInfo : cl.mApiCheckMethods.values()) { 2187 if (!mApiCheckMethods.containsKey(mInfo.getHashableName())) { 2188 /* 2189 * Similarly to the above, do not fail if this "new" method is really an override of an 2190 * existing superclass method. 2191 * But we should fail if this is overriding an abstract method, because method's 2192 * abstractness affects how users use it. See also Stubs.methodIsOverride(). 2193 */ 2194 MethodInfo mi = ClassInfo.overriddenMethod(mInfo, this); 2195 if (mi == null && mInfo.isAbstract()) { 2196 Errors.error(Errors.ADDED_ABSTRACT_METHOD, mInfo.position(), 2197 "Added abstract public method " 2198 + mInfo.prettyQualifiedSignature() + " to existing class"); 2199 consistent = false; 2200 } else if (mi == null || mi.isAbstract() != mInfo.isAbstract()) { 2201 Errors.error(Errors.ADDED_METHOD, mInfo.position(), "Added public method " 2202 + mInfo.prettyQualifiedSignature()); 2203 if (diffMode) { 2204 newMethods.add(mInfo); 2205 } 2206 consistent = false; 2207 } 2208 } 2209 } 2210 if (diffMode) { 2211 Collections.sort(newMethods, MethodInfo.comparator); 2212 } 2213 2214 for (MethodInfo mInfo : mApiCheckConstructors.values()) { 2215 if (cl.mApiCheckConstructors.containsKey(mInfo.getHashableName())) { 2216 if (!mInfo.isConsistent(cl.mApiCheckConstructors.get(mInfo.getHashableName()))) { 2217 consistent = false; 2218 } 2219 } else { 2220 if (mInfo.isDeprecated()) { 2221 Errors.error(Errors.REMOVED_DEPRECATED_METHOD, mInfo.position(), 2222 "Removed deprecated public constructor " + mInfo.prettyQualifiedSignature()); 2223 } else { 2224 Errors.error(Errors.REMOVED_METHOD, mInfo.position(), 2225 "Removed public constructor " + mInfo.prettyQualifiedSignature()); 2226 } 2227 consistent = false; 2228 } 2229 } 2230 for (MethodInfo mInfo : cl.mApiCheckConstructors.values()) { 2231 if (!mApiCheckConstructors.containsKey(mInfo.getHashableName())) { 2232 Errors.error(Errors.ADDED_METHOD, mInfo.position(), "Added public constructor " 2233 + mInfo.prettyQualifiedSignature()); 2234 if (diffMode) { 2235 newCtors.add(mInfo); 2236 } 2237 consistent = false; 2238 } 2239 } 2240 if (diffMode) { 2241 Collections.sort(newCtors, MethodInfo.comparator); 2242 } 2243 2244 for (FieldInfo mInfo : mApiCheckFields.values()) { 2245 if (cl.mApiCheckFields.containsKey(mInfo.name())) { 2246 if (!mInfo.isConsistent(cl.mApiCheckFields.get(mInfo.name()))) { 2247 consistent = false; 2248 } 2249 } else { 2250 if (mInfo.isDeprecated()) { 2251 Errors.error(Errors.REMOVED_DEPRECATED_FIELD, mInfo.position(), 2252 "Removed deprecated field " + mInfo.qualifiedName()); 2253 } else { 2254 Errors.error(Errors.REMOVED_FIELD, mInfo.position(), 2255 "Removed field " + mInfo.qualifiedName()); 2256 } 2257 consistent = false; 2258 } 2259 } 2260 for (FieldInfo mInfo : cl.mApiCheckFields.values()) { 2261 if (!mApiCheckFields.containsKey(mInfo.name())) { 2262 Errors.error(Errors.ADDED_FIELD, mInfo.position(), "Added public field " 2263 + mInfo.qualifiedName()); 2264 consistent = false; 2265 } 2266 } 2267 2268 for (FieldInfo info : mApiCheckEnumConstants.values()) { 2269 if (cl.mApiCheckEnumConstants.containsKey(info.name())) { 2270 if (!info.isConsistent(cl.mApiCheckEnumConstants.get(info.name()))) { 2271 consistent = false; 2272 } 2273 } else { 2274 if (info.isDeprecated()) { 2275 Errors.error(Errors.REMOVED_DEPRECATED_FIELD, info.position(), 2276 "Removed deprecated enum constant " + info.qualifiedName()); 2277 } else { 2278 Errors.error(Errors.REMOVED_FIELD, info.position(), 2279 "Removed enum constant " + info.qualifiedName()); 2280 } 2281 consistent = false; 2282 } 2283 } 2284 for (FieldInfo info : cl.mApiCheckEnumConstants.values()) { 2285 if (!mApiCheckEnumConstants.containsKey(info.name())) { 2286 Errors.error(Errors.ADDED_FIELD, info.position(), "Added enum constant " 2287 + info.qualifiedName()); 2288 consistent = false; 2289 } 2290 } 2291 2292 if (mIsAbstract != cl.mIsAbstract) { 2293 consistent = false; 2294 Errors.error(Errors.CHANGED_ABSTRACT, cl.position(), "Class " + cl.qualifiedName() 2295 + " changed abstract qualifier"); 2296 } 2297 2298 if (!mIsFinal && cl.mIsFinal) { 2299 /* 2300 * It is safe to make a class final if it did not previously have any public 2301 * constructors because it was impossible for an application to create a subclass. 2302 */ 2303 if (mApiCheckConstructors.isEmpty()) { 2304 consistent = false; 2305 Errors.error(Errors.ADDED_FINAL_UNINSTANTIABLE, cl.position(), 2306 "Class " + cl.qualifiedName() + " added final qualifier but " 2307 + "was previously uninstantiable and therefore could not be subclassed"); 2308 } else { 2309 consistent = false; 2310 Errors.error(Errors.ADDED_FINAL, cl.position(), "Class " + cl.qualifiedName() 2311 + " added final qualifier"); 2312 } 2313 } else if (mIsFinal && !cl.mIsFinal) { 2314 consistent = false; 2315 Errors.error(Errors.REMOVED_FINAL, cl.position(), "Class " + cl.qualifiedName() 2316 + " removed final qualifier"); 2317 } 2318 2319 if (mIsStatic != cl.mIsStatic) { 2320 consistent = false; 2321 Errors.error(Errors.CHANGED_STATIC, cl.position(), "Class " + cl.qualifiedName() 2322 + " changed static qualifier"); 2323 } 2324 2325 if (!scope().equals(cl.scope())) { 2326 consistent = false; 2327 Errors.error(Errors.CHANGED_SCOPE, cl.position(), "Class " + cl.qualifiedName() 2328 + " scope changed from " + scope() + " to " + cl.scope()); 2329 } 2330 2331 if (!isDeprecated() == cl.isDeprecated()) { 2332 consistent = false; 2333 Errors.error(Errors.CHANGED_DEPRECATED, cl.position(), "Class " + cl.qualifiedName() 2334 + " has changed deprecation state " + isDeprecated() + " --> " + cl.isDeprecated()); 2335 } 2336 2337 if (superclassName() != null) { // java.lang.Object can't have a superclass. 2338 if (!cl.extendsClass(superclassName())) { 2339 consistent = false; 2340 Errors.error(Errors.CHANGED_SUPERCLASS, cl.position(), "Class " + qualifiedName() 2341 + " superclass changed from " + superclassName() + " to " + cl.superclassName()); 2342 } 2343 } 2344 2345 if (hasTypeParameters() && cl.hasTypeParameters()) { 2346 ArrayList<TypeInfo> oldParams = typeParameters(); 2347 ArrayList<TypeInfo> newParams = cl.typeParameters(); 2348 if (oldParams.size() != newParams.size()) { 2349 consistent = false; 2350 Errors.error(Errors.CHANGED_TYPE, cl.position(), "Class " + qualifiedName() 2351 + " changed number of type parameters from " + oldParams.size() 2352 + " to " + newParams.size()); 2353 } 2354 } 2355 2356 return consistent; 2357 } 2358 2359 // Find a superclass implementation of the given method based on the methods in mApiCheckMethods. 2360 public static MethodInfo overriddenMethod(MethodInfo candidate, ClassInfo newClassObj) { 2361 if (newClassObj == null) { 2362 return null; 2363 } 2364 for (MethodInfo mi : newClassObj.mApiCheckMethods.values()) { 2365 if (mi.matches(candidate)) { 2366 // found it 2367 return mi; 2368 } 2369 } 2370 2371 // not found here. recursively search ancestors 2372 return ClassInfo.overriddenMethod(candidate, newClassObj.mSuperclass); 2373 } 2374 2375 // Find a superinterface declaration of the given method. 2376 public static MethodInfo interfaceMethod(MethodInfo candidate, ClassInfo newClassObj) { 2377 if (newClassObj == null) { 2378 return null; 2379 } 2380 for (ClassInfo interfaceInfo : newClassObj.interfaces()) { 2381 for (MethodInfo mi : interfaceInfo.mApiCheckMethods.values()) { 2382 if (mi.matches(candidate)) { 2383 return mi; 2384 } 2385 } 2386 } 2387 return ClassInfo.interfaceMethod(candidate, newClassObj.mSuperclass); 2388 } 2389 2390 public boolean hasConstructor(MethodInfo constructor) { 2391 String name = constructor.getHashableName(); 2392 for (MethodInfo ctor : mApiCheckConstructors.values()) { 2393 if (name.equals(ctor.getHashableName())) { 2394 return true; 2395 } 2396 } 2397 return false; 2398 } 2399 2400 public void setTypeInfo(TypeInfo typeInfo) { 2401 mTypeInfo = typeInfo; 2402 } 2403 2404 public TypeInfo type() { 2405 return mTypeInfo; 2406 } 2407 2408 public boolean hasTypeParameters() { 2409 if (mTypeInfo != null && mTypeInfo.typeArguments() != null) { 2410 return !mTypeInfo.typeArguments().isEmpty(); 2411 } 2412 return false; 2413 } 2414 2415 public ArrayList<TypeInfo> typeParameters() { 2416 if (hasTypeParameters()) { 2417 return mTypeInfo.typeArguments(); 2418 } 2419 return null; 2420 } 2421 2422 public void addInnerClass(ClassInfo innerClass) { 2423 if (mInnerClasses == null) { 2424 mInnerClasses = new ArrayList<ClassInfo>(); 2425 } 2426 2427 mInnerClasses.add(innerClass); 2428 } 2429 2430 public void setContainingClass(ClassInfo containingClass) { 2431 mContainingClass = containingClass; 2432 } 2433 2434 public void setSuperclassType(TypeInfo superclassType) { 2435 mRealSuperclassType = superclassType; 2436 } 2437 2438 public void printResolutions() { 2439 if (mResolutions == null || mResolutions.isEmpty()) { 2440 return; 2441 } 2442 2443 System.out.println("Resolutions for Class " + mName + ":"); 2444 2445 for (Resolution r : mResolutions) { 2446 System.out.println(r); 2447 } 2448 } 2449 2450 public void addResolution(Resolution resolution) { 2451 if (mResolutions == null) { 2452 mResolutions = new ArrayList<Resolution>(); 2453 } 2454 2455 mResolutions.add(resolution); 2456 } 2457 2458 public boolean resolveResolutions() { 2459 ArrayList<Resolution> resolutions = mResolutions; 2460 mResolutions = new ArrayList<Resolution>(); 2461 2462 boolean allResolved = true; 2463 for (Resolution resolution : resolutions) { 2464 StringBuilder qualifiedClassName = new StringBuilder(); 2465 InfoBuilder.resolveQualifiedName(resolution.getValue(), qualifiedClassName, 2466 resolution.getInfoBuilder()); 2467 2468 // if we still couldn't resolve it, save it for the next pass 2469 if ("".equals(qualifiedClassName.toString())) { 2470 mResolutions.add(resolution); 2471 allResolved = false; 2472 } else if ("superclassQualifiedName".equals(resolution.getVariable())) { 2473 setSuperClass(InfoBuilder.Caches.obtainClass(qualifiedClassName.toString())); 2474 } else if ("interfaceQualifiedName".equals(resolution.getVariable())) { 2475 addInterface(InfoBuilder.Caches.obtainClass(qualifiedClassName.toString())); 2476 } 2477 } 2478 2479 return allResolved; 2480 } 2481 } 2482