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