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