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