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 for (FieldInfo enumConst : mEnumConstants) { 471 if (!enumConst.isHidden()) { 472 all.put(enumConst.name(), enumConst); 473 } 474 } 475 476 mFields = new ArrayList<FieldInfo>(all.values()); 477 } 478 return mFields; 479 } 480 481 public void gatherFields(ClassInfo owner, ClassInfo cl, HashMap<String, FieldInfo> fields) { 482 for (FieldInfo f : cl.selfFields()) { 483 if (f.checkLevel()) { 484 fields.put(f.name(), f.cloneForClass(owner)); 485 } 486 } 487 } 488 489 public ArrayList<FieldInfo> selfFields() { 490 if (mSelfFields == null) { 491 HashMap<String, FieldInfo> fields = new HashMap<String, FieldInfo>(); 492 // our hidden parents 493 if (mRealSuperclass != null && !mRealSuperclass.checkLevel()) { 494 gatherFields(this, mRealSuperclass, fields); 495 } 496 for (ClassInfo iface : mRealInterfaces) { 497 if (!iface.checkLevel()) { 498 gatherFields(this, iface, fields); 499 } 500 } 501 502 for (FieldInfo f : mAllSelfFields) { 503 if (!f.isHidden()) { 504 fields.put(f.name(), f); 505 } 506 } 507 508 mSelfFields = new ArrayList<FieldInfo>(fields.values()); 509 Collections.sort(mSelfFields, FieldInfo.comparator); 510 } 511 return mSelfFields; 512 } 513 514 public ArrayList<FieldInfo> allSelfFields() { 515 return mAllSelfFields; 516 } 517 518 private void gatherMethods(ClassInfo owner, ClassInfo cl, HashMap<String, MethodInfo> methods) { 519 for (MethodInfo m : cl.selfMethods()) { 520 if (m.checkLevel()) { 521 methods.put(m.name() + m.signature(), m.cloneForClass(owner)); 522 } 523 } 524 } 525 526 public ArrayList<MethodInfo> selfMethods() { 527 if (mSelfMethods == null) { 528 HashMap<String, MethodInfo> methods = new HashMap<String, MethodInfo>(); 529 // our hidden parents 530 if (mRealSuperclass != null && !mRealSuperclass.checkLevel()) { 531 gatherMethods(this, mRealSuperclass, methods); 532 } 533 for (ClassInfo iface : mRealInterfaces) { 534 if (!iface.checkLevel()) { 535 gatherMethods(this, iface, methods); 536 } 537 } 538 // mine 539 if (mAllSelfMethods != null) { 540 for (MethodInfo m : mAllSelfMethods) { 541 if (m.checkLevel()) { 542 methods.put(m.name() + m.signature(), m); 543 } 544 } 545 } 546 547 // sort it 548 mSelfMethods = new ArrayList<MethodInfo>(methods.values()); 549 Collections.sort(mSelfMethods, MethodInfo.comparator); 550 } 551 return mSelfMethods; 552 } 553 554 public ArrayList<MethodInfo> allSelfMethods() { 555 return mAllSelfMethods; 556 } 557 558 public void addMethod(MethodInfo method) { 559 mApiCheckMethods.put(method.getHashableName(), method); 560 561 mAllSelfMethods.add(method); 562 mSelfMethods = null; // flush this, hopefully it hasn't been used yet. 563 } 564 565 public void addAnnotationElement(MethodInfo method) { 566 mAnnotationElements.add(method); 567 } 568 569 // Called by PackageInfo when a ClassInfo is added to a package. 570 // This is needed because ApiCheck uses PackageInfo.addClass 571 // rather than using setContainingPackage to dispatch to the 572 // appropriate method. TODO: move ApiCheck away from addClass. 573 void setPackage(PackageInfo pkg) { 574 mContainingPackage = pkg; 575 } 576 577 public void setContainingPackage(PackageInfo pkg) { 578 mContainingPackage = pkg; 579 580 if (mContainingPackage != null) { 581 if (mIsEnum) { 582 mContainingPackage.addEnum(this); 583 } else if (mIsInterface) { 584 mContainingPackage.addInterface(this); 585 } else { 586 mContainingPackage.addOrdinaryClass(this); 587 } 588 } 589 } 590 591 public ArrayList<AttributeInfo> selfAttributes() { 592 if (mSelfAttributes == null) { 593 TreeMap<FieldInfo, AttributeInfo> attrs = new TreeMap<FieldInfo, AttributeInfo>(); 594 595 // the ones in the class comment won't have any methods 596 for (AttrTagInfo tag : 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 } 606 } 607 608 // in the methods 609 for (MethodInfo m : selfMethods()) { 610 for (AttrTagInfo tag : m.comment().attrTags()) { 611 FieldInfo field = tag.reference(); 612 if (field != null) { 613 AttributeInfo attr = attrs.get(field); 614 if (attr == null) { 615 attr = new AttributeInfo(this, field); 616 attrs.put(field, attr); 617 } 618 tag.setAttribute(attr); 619 attr.methods.add(m); 620 } 621 } 622 } 623 624 // constructors too 625 for (MethodInfo m : constructors()) { 626 for (AttrTagInfo tag : m.comment().attrTags()) { 627 FieldInfo field = tag.reference(); 628 if (field != null) { 629 AttributeInfo attr = attrs.get(field); 630 if (attr == null) { 631 attr = new AttributeInfo(this, field); 632 attrs.put(field, attr); 633 } 634 tag.setAttribute(attr); 635 attr.methods.add(m); 636 } 637 } 638 } 639 640 mSelfAttributes = new ArrayList<AttributeInfo>(attrs.values()); 641 Collections.sort(mSelfAttributes, AttributeInfo.comparator); 642 } 643 return mSelfAttributes; 644 } 645 646 public ArrayList<FieldInfo> enumConstants() { 647 return mEnumConstants; 648 } 649 650 public ClassInfo superclass() { 651 if (!mSuperclassInit) { 652 if (this.checkLevel()) { 653 // rearrange our little inheritance hierarchy, because we need to hide classes that 654 // don't pass checkLevel 655 ClassInfo superclass = mRealSuperclass; 656 while (superclass != null && !superclass.checkLevel()) { 657 superclass = superclass.mRealSuperclass; 658 } 659 mSuperclass = superclass; 660 } else { 661 mSuperclass = mRealSuperclass; 662 } 663 } 664 return mSuperclass; 665 } 666 667 public ClassInfo realSuperclass() { 668 return mRealSuperclass; 669 } 670 671 /** 672 * always the real superclass, not the collapsed one we get through superclass(), also has the 673 * type parameter info if it's generic. 674 */ 675 public TypeInfo superclassType() { 676 return mRealSuperclassType; 677 } 678 679 public TypeInfo asTypeInfo() { 680 return mTypeInfo; 681 } 682 683 ArrayList<TypeInfo> interfaceTypes() { 684 ArrayList<TypeInfo> types = new ArrayList<TypeInfo>(); 685 for (ClassInfo iface : interfaces()) { 686 types.add(iface.asTypeInfo()); 687 } 688 return types; 689 } 690 691 public String htmlPage() { 692 String s = containingPackage().name(); 693 s = s.replace('.', '/'); 694 s += '/'; 695 s += name(); 696 s += ".html"; 697 s = Doclava.javadocDir + s; 698 return s; 699 } 700 701 /** Even indirectly */ 702 public boolean isDerivedFrom(ClassInfo cl) { 703 return isDerivedFrom(cl.qualifiedName()); 704 } 705 706 /** Even indirectly */ 707 public boolean isDerivedFrom(String qualifiedName) { 708 ClassInfo dad = this.superclass(); 709 if (dad != null) { 710 if (dad.mQualifiedName.equals(qualifiedName)) { 711 return true; 712 } else { 713 if (dad.isDerivedFrom(qualifiedName)) { 714 return true; 715 } 716 } 717 } 718 for (ClassInfo iface : interfaces()) { 719 if (iface.mQualifiedName.equals(qualifiedName)) { 720 return true; 721 } else { 722 if (iface.isDerivedFrom(qualifiedName)) { 723 return true; 724 } 725 } 726 } 727 return false; 728 } 729 730 public void makeKeywordEntries(List<KeywordEntry> keywords) { 731 if (!checkLevel()) { 732 return; 733 } 734 735 String htmlPage = htmlPage(); 736 String qualifiedName = qualifiedName(); 737 738 keywords.add(new KeywordEntry(name(), htmlPage, "class in " + containingPackage().name())); 739 740 ArrayList<FieldInfo> fields = selfFields(); 741 //ArrayList<FieldInfo> enumConstants = enumConstants(); 742 ArrayList<MethodInfo> ctors = constructors(); 743 ArrayList<MethodInfo> methods = selfMethods(); 744 745 // enum constants 746 for (FieldInfo field : enumConstants()) { 747 if (field.checkLevel()) { 748 keywords.add(new KeywordEntry(field.name(), htmlPage + "#" + field.anchor(), 749 "enum constant in " + qualifiedName)); 750 } 751 } 752 753 // constants 754 for (FieldInfo field : fields) { 755 if (field.isConstant() && field.checkLevel()) { 756 keywords.add(new KeywordEntry(field.name(), htmlPage + "#" + field.anchor(), "constant in " 757 + qualifiedName)); 758 } 759 } 760 761 // fields 762 for (FieldInfo field : fields) { 763 if (!field.isConstant() && field.checkLevel()) { 764 keywords.add(new KeywordEntry(field.name(), htmlPage + "#" + field.anchor(), "field in " 765 + qualifiedName)); 766 } 767 } 768 769 // public constructors 770 for (MethodInfo m : ctors) { 771 if (m.isPublic() && m.checkLevel()) { 772 keywords.add(new KeywordEntry(m.prettySignature(), htmlPage + "#" + m.anchor(), 773 "constructor in " + qualifiedName)); 774 } 775 } 776 777 // protected constructors 778 if (Doclava.checkLevel(Doclava.SHOW_PROTECTED)) { 779 for (MethodInfo m : ctors) { 780 if (m.isProtected() && m.checkLevel()) { 781 keywords.add(new KeywordEntry(m.prettySignature(), 782 htmlPage + "#" + m.anchor(), "constructor in " + qualifiedName)); 783 } 784 } 785 } 786 787 // package private constructors 788 if (Doclava.checkLevel(Doclava.SHOW_PACKAGE)) { 789 for (MethodInfo m : ctors) { 790 if (m.isPackagePrivate() && m.checkLevel()) { 791 keywords.add(new KeywordEntry(m.prettySignature(), 792 htmlPage + "#" + m.anchor(), "constructor in " + qualifiedName)); 793 } 794 } 795 } 796 797 // private constructors 798 if (Doclava.checkLevel(Doclava.SHOW_PRIVATE)) { 799 for (MethodInfo m : ctors) { 800 if (m.isPrivate() && m.checkLevel()) { 801 keywords.add(new KeywordEntry(m.name() + m.prettySignature(), 802 htmlPage + "#" + m.anchor(), "constructor in " + qualifiedName)); 803 } 804 } 805 } 806 807 // public methods 808 for (MethodInfo m : methods) { 809 if (m.isPublic() && m.checkLevel()) { 810 keywords.add(new KeywordEntry(m.name() + m.prettySignature(), htmlPage + "#" + m.anchor(), 811 "method in " + qualifiedName)); 812 } 813 } 814 815 // protected methods 816 if (Doclava.checkLevel(Doclava.SHOW_PROTECTED)) { 817 for (MethodInfo m : methods) { 818 if (m.isProtected() && m.checkLevel()) { 819 keywords.add(new KeywordEntry(m.name() + m.prettySignature(), 820 htmlPage + "#" + m.anchor(), "method in " + qualifiedName)); 821 } 822 } 823 } 824 825 // package private methods 826 if (Doclava.checkLevel(Doclava.SHOW_PACKAGE)) { 827 for (MethodInfo m : methods) { 828 if (m.isPackagePrivate() && m.checkLevel()) { 829 keywords.add(new KeywordEntry(m.name() + m.prettySignature(), 830 htmlPage + "#" + m.anchor(), "method in " + qualifiedName)); 831 } 832 } 833 } 834 835 // private methods 836 if (Doclava.checkLevel(Doclava.SHOW_PRIVATE)) { 837 for (MethodInfo m : methods) { 838 if (m.isPrivate() && m.checkLevel()) { 839 keywords.add(new KeywordEntry(m.name() + m.prettySignature(), 840 htmlPage + "#" + m.anchor(), "method in " + qualifiedName)); 841 } 842 } 843 } 844 } 845 846 public void makeLink(Data data, String base) { 847 data.setValue(base + ".label", this.name()); 848 if (!this.isPrimitive() && this.isIncluded() && this.checkLevel()) { 849 data.setValue(base + ".link", this.htmlPage()); 850 } 851 } 852 853 public static void makeLinkListHDF(Data data, String base, ClassInfo[] classes) { 854 final int N = classes.length; 855 for (int i = 0; i < N; i++) { 856 ClassInfo cl = classes[i]; 857 if (cl.checkLevel()) { 858 cl.asTypeInfo().makeHDF(data, base + "." + i); 859 } 860 } 861 } 862 863 /** 864 * Used in lists of this class (packages, nested classes, known subclasses) 865 */ 866 public void makeShortDescrHDF(Data data, String base) { 867 mTypeInfo.makeHDF(data, base + ".type"); 868 data.setValue(base + ".kind", this.kind()); 869 TagInfo.makeHDF(data, base + ".shortDescr", this.firstSentenceTags()); 870 TagInfo.makeHDF(data, base + ".deprecated", deprecatedTags()); 871 data.setValue(base + ".since", getSince()); 872 if (isDeprecated()) { 873 data.setValue(base + ".deprecatedsince", getDeprecatedSince()); 874 } 875 setFederatedReferences(data, base); 876 } 877 878 /** 879 * Turns into the main class page 880 */ 881 public void makeHDF(Data data) { 882 int i, j, n; 883 String name = name(); 884 String qualified = qualifiedName(); 885 ArrayList<AttributeInfo> selfAttributes = selfAttributes(); 886 ArrayList<MethodInfo> methods = selfMethods(); 887 ArrayList<FieldInfo> fields = selfFields(); 888 ArrayList<FieldInfo> enumConstants = enumConstants(); 889 ArrayList<MethodInfo> ctors = constructors(); 890 ArrayList<ClassInfo> inners = innerClasses(); 891 892 // class name 893 mTypeInfo.makeHDF(data, "class.type"); 894 mTypeInfo.makeQualifiedHDF(data, "class.qualifiedType"); 895 data.setValue("class.name", name); 896 data.setValue("class.qualified", qualified); 897 if (isProtected()) { 898 data.setValue("class.scope", "protected"); 899 } else if (isPublic()) { 900 data.setValue("class.scope", "public"); 901 } 902 if (isStatic()) { 903 data.setValue("class.static", "static"); 904 } 905 if (isFinal()) { 906 data.setValue("class.final", "final"); 907 } 908 if (isAbstract() && !isInterface()) { 909 data.setValue("class.abstract", "abstract"); 910 } 911 912 // class info 913 String kind = kind(); 914 if (kind != null) { 915 data.setValue("class.kind", kind); 916 } 917 data.setValue("class.since", getSince()); 918 if (isDeprecated()) { 919 data.setValue("class.deprecatedsince", getDeprecatedSince()); 920 } 921 setFederatedReferences(data, "class"); 922 923 // the containing package -- note that this can be passed to type_link, 924 // but it also contains the list of all of the packages 925 containingPackage().makeClassLinkListHDF(data, "class.package"); 926 927 // inheritance hierarchy 928 Vector<ClassInfo> superClasses = new Vector<ClassInfo>(); 929 superClasses.add(this); 930 ClassInfo supr = superclass(); 931 while (supr != null) { 932 superClasses.add(supr); 933 supr = supr.superclass(); 934 } 935 n = superClasses.size(); 936 for (i = 0; i < n; i++) { 937 supr = superClasses.elementAt(n - i - 1); 938 939 supr.asTypeInfo().makeQualifiedHDF(data, "class.inheritance." + i + ".class"); 940 supr.asTypeInfo().makeHDF(data, "class.inheritance." + i + ".short_class"); 941 j = 0; 942 for (TypeInfo t : supr.interfaceTypes()) { 943 t.makeHDF(data, "class.inheritance." + i + ".interfaces." + j); 944 j++; 945 } 946 } 947 948 // class description 949 TagInfo.makeHDF(data, "class.descr", inlineTags()); 950 TagInfo.makeHDF(data, "class.seeAlso", comment().seeTags()); 951 TagInfo.makeHDF(data, "class.deprecated", deprecatedTags()); 952 953 // known subclasses 954 TreeMap<String, ClassInfo> direct = new TreeMap<String, ClassInfo>(); 955 TreeMap<String, ClassInfo> indirect = new TreeMap<String, ClassInfo>(); 956 ClassInfo[] all = Converter.rootClasses(); 957 for (ClassInfo cl : all) { 958 if (cl.superclass() != null && cl.superclass().equals(this)) { 959 direct.put(cl.name(), cl); 960 } else if (cl.isDerivedFrom(this)) { 961 indirect.put(cl.name(), cl); 962 } 963 } 964 // direct 965 i = 0; 966 for (ClassInfo cl : direct.values()) { 967 if (cl.checkLevel()) { 968 cl.makeShortDescrHDF(data, "class.subclasses.direct." + i); 969 } 970 i++; 971 } 972 // indirect 973 i = 0; 974 for (ClassInfo cl : indirect.values()) { 975 if (cl.checkLevel()) { 976 cl.makeShortDescrHDF(data, "class.subclasses.indirect." + i); 977 } 978 i++; 979 } 980 981 // hide special cases 982 if ("java.lang.Object".equals(qualified) || "java.io.Serializable".equals(qualified)) { 983 data.setValue("class.subclasses.hidden", "1"); 984 } else { 985 data.setValue("class.subclasses.hidden", "0"); 986 } 987 988 // nested classes 989 i = 0; 990 for (ClassInfo inner : inners) { 991 if (inner.checkLevel()) { 992 inner.makeShortDescrHDF(data, "class.inners." + i); 993 } 994 i++; 995 } 996 997 // enum constants 998 i = 0; 999 for (FieldInfo field : enumConstants) { 1000 field.makeHDF(data, "class.enumConstants." + i); 1001 i++; 1002 } 1003 1004 // constants 1005 i = 0; 1006 for (FieldInfo field : fields) { 1007 if (field.isConstant()) { 1008 field.makeHDF(data, "class.constants." + i); 1009 i++; 1010 } 1011 } 1012 1013 // fields 1014 i = 0; 1015 for (FieldInfo field : fields) { 1016 if (!field.isConstant()) { 1017 field.makeHDF(data, "class.fields." + i); 1018 i++; 1019 } 1020 } 1021 1022 // public constructors 1023 i = 0; 1024 for (MethodInfo ctor : ctors) { 1025 if (ctor.isPublic()) { 1026 ctor.makeHDF(data, "class.ctors.public." + i); 1027 i++; 1028 } 1029 } 1030 1031 // protected constructors 1032 if (Doclava.checkLevel(Doclava.SHOW_PROTECTED)) { 1033 i = 0; 1034 for (MethodInfo ctor : ctors) { 1035 if (ctor.isProtected()) { 1036 ctor.makeHDF(data, "class.ctors.protected." + i); 1037 i++; 1038 } 1039 } 1040 } 1041 1042 // package private constructors 1043 if (Doclava.checkLevel(Doclava.SHOW_PACKAGE)) { 1044 i = 0; 1045 for (MethodInfo ctor : ctors) { 1046 if (ctor.isPackagePrivate()) { 1047 ctor.makeHDF(data, "class.ctors.package." + i); 1048 i++; 1049 } 1050 } 1051 } 1052 1053 // private constructors 1054 if (Doclava.checkLevel(Doclava.SHOW_PRIVATE)) { 1055 i = 0; 1056 for (MethodInfo ctor : ctors) { 1057 if (ctor.isPrivate()) { 1058 ctor.makeHDF(data, "class.ctors.private." + i); 1059 i++; 1060 } 1061 } 1062 } 1063 1064 // public methods 1065 i = 0; 1066 for (MethodInfo method : methods) { 1067 if (method.isPublic()) { 1068 method.makeHDF(data, "class.methods.public." + i); 1069 i++; 1070 } 1071 } 1072 1073 // protected methods 1074 if (Doclava.checkLevel(Doclava.SHOW_PROTECTED)) { 1075 i = 0; 1076 for (MethodInfo method : methods) { 1077 if (method.isProtected()) { 1078 method.makeHDF(data, "class.methods.protected." + i); 1079 i++; 1080 } 1081 } 1082 } 1083 1084 // package private methods 1085 if (Doclava.checkLevel(Doclava.SHOW_PACKAGE)) { 1086 i = 0; 1087 for (MethodInfo method : methods) { 1088 if (method.isPackagePrivate()) { 1089 method.makeHDF(data, "class.methods.package." + i); 1090 i++; 1091 } 1092 } 1093 } 1094 1095 // private methods 1096 if (Doclava.checkLevel(Doclava.SHOW_PRIVATE)) { 1097 i = 0; 1098 for (MethodInfo method : methods) { 1099 if (method.isPrivate()) { 1100 method.makeHDF(data, "class.methods.private." + i); 1101 i++; 1102 } 1103 } 1104 } 1105 1106 // xml attributes 1107 i = 0; 1108 for (AttributeInfo attr : selfAttributes) { 1109 if (attr.checkLevel()) { 1110 attr.makeHDF(data, "class.attrs." + i); 1111 i++; 1112 } 1113 } 1114 1115 // inherited methods 1116 Set<ClassInfo> interfaces = new TreeSet<ClassInfo>(); 1117 addInterfaces(interfaces(), interfaces); 1118 ClassInfo cl = superclass(); 1119 i = 0; 1120 while (cl != null) { 1121 addInterfaces(cl.interfaces(), interfaces); 1122 makeInheritedHDF(data, i, cl); 1123 cl = cl.superclass(); 1124 i++; 1125 } 1126 for (ClassInfo iface : interfaces) { 1127 makeInheritedHDF(data, i, iface); 1128 i++; 1129 } 1130 } 1131 1132 private static void addInterfaces(ArrayList<ClassInfo> ifaces, Set<ClassInfo> out) { 1133 for (ClassInfo cl : ifaces) { 1134 out.add(cl); 1135 addInterfaces(cl.interfaces(), out); 1136 } 1137 } 1138 1139 private static void makeInheritedHDF(Data data, int index, ClassInfo cl) { 1140 int i; 1141 1142 String base = "class.inherited." + index; 1143 data.setValue(base + ".qualified", cl.qualifiedName()); 1144 if (cl.checkLevel()) { 1145 data.setValue(base + ".link", cl.htmlPage()); 1146 } 1147 String kind = cl.kind(); 1148 if (kind != null) { 1149 data.setValue(base + ".kind", kind); 1150 } 1151 1152 if (cl.mIsIncluded) { 1153 data.setValue(base + ".included", "true"); 1154 } else { 1155 Doclava.federationTagger.tagAll(new ClassInfo[] {cl}); 1156 if (!cl.getFederatedReferences().isEmpty()) { 1157 FederatedSite site = cl.getFederatedReferences().iterator().next(); 1158 data.setValue(base + ".link", site.linkFor(cl.htmlPage())); 1159 data.setValue(base + ".federated", site.name()); 1160 } 1161 } 1162 1163 // xml attributes 1164 i = 0; 1165 for (AttributeInfo attr : cl.selfAttributes()) { 1166 attr.makeHDF(data, base + ".attrs." + i); 1167 i++; 1168 } 1169 1170 // methods 1171 i = 0; 1172 for (MethodInfo method : cl.selfMethods()) { 1173 method.makeHDF(data, base + ".methods." + i); 1174 i++; 1175 } 1176 1177 // fields 1178 i = 0; 1179 for (FieldInfo field : cl.selfFields()) { 1180 if (!field.isConstant()) { 1181 field.makeHDF(data, base + ".fields." + i); 1182 i++; 1183 } 1184 } 1185 1186 // constants 1187 i = 0; 1188 for (FieldInfo field : cl.selfFields()) { 1189 if (field.isConstant()) { 1190 field.makeHDF(data, base + ".constants." + i); 1191 i++; 1192 } 1193 } 1194 } 1195 1196 @Override 1197 public boolean isHidden() { 1198 int val = mHidden; 1199 if (val >= 0) { 1200 return val != 0; 1201 } else { 1202 boolean v = isHiddenImpl(); 1203 mHidden = v ? 1 : 0; 1204 return v; 1205 } 1206 } 1207 1208 public boolean isHiddenImpl() { 1209 ClassInfo cl = this; 1210 while (cl != null) { 1211 PackageInfo pkg = cl.containingPackage(); 1212 if (pkg != null && pkg.isHidden()) { 1213 return true; 1214 } 1215 if (cl.annotations() != null) { 1216 for (AnnotationInstanceInfo info : cl.annotations()) { 1217 if (Doclava.showAnnotations.contains(info.type().qualifiedName())) { 1218 return false; 1219 } 1220 } 1221 } 1222 if (cl.comment().isHidden()) { 1223 return true; 1224 } 1225 cl = cl.containingClass(); 1226 } 1227 return false; 1228 } 1229 1230 private MethodInfo matchMethod(ArrayList<MethodInfo> methods, String name, String[] params, 1231 String[] dimensions, boolean varargs) { 1232 for (MethodInfo method : methods) { 1233 if (method.name().equals(name)) { 1234 if (params == null) { 1235 return method; 1236 } else { 1237 if (method.matchesParams(params, dimensions, varargs)) { 1238 return method; 1239 } 1240 } 1241 } 1242 } 1243 return null; 1244 } 1245 1246 public MethodInfo findMethod(String name, String[] params, String[] dimensions, boolean varargs) { 1247 // first look on our class, and our superclasses 1248 1249 // for methods 1250 MethodInfo rv; 1251 rv = matchMethod(methods(), name, params, dimensions, varargs); 1252 1253 if (rv != null) { 1254 return rv; 1255 } 1256 1257 // for constructors 1258 rv = matchMethod(constructors(), name, params, dimensions, varargs); 1259 if (rv != null) { 1260 return rv; 1261 } 1262 1263 // then recursively look at our containing class 1264 ClassInfo containing = containingClass(); 1265 if (containing != null) { 1266 return containing.findMethod(name, params, dimensions, varargs); 1267 } 1268 1269 return null; 1270 } 1271 1272 public boolean supportsMethod(MethodInfo method) { 1273 for (MethodInfo m : methods()) { 1274 if (m.getHashableName().equals(method.getHashableName())) { 1275 return true; 1276 } 1277 } 1278 return false; 1279 } 1280 1281 private ClassInfo searchInnerClasses(String[] nameParts, int index) { 1282 String part = nameParts[index]; 1283 1284 ArrayList<ClassInfo> inners = mInnerClasses; 1285 for (ClassInfo in : inners) { 1286 String[] innerParts = in.nameParts(); 1287 if (part.equals(innerParts[innerParts.length - 1])) { 1288 if (index == nameParts.length - 1) { 1289 return in; 1290 } else { 1291 return in.searchInnerClasses(nameParts, index + 1); 1292 } 1293 } 1294 } 1295 return null; 1296 } 1297 1298 public ClassInfo extendedFindClass(String className) { 1299 // ClassDoc.findClass has this bug that we're working around here: 1300 // If you have a class PackageManager with an inner class PackageInfo 1301 // and you call it with "PackageInfo" it doesn't find it. 1302 return searchInnerClasses(className.split("\\."), 0); 1303 } 1304 1305 public ClassInfo findClass(String className) { 1306 return Converter.obtainClass(mClass.findClass(className)); 1307 } 1308 1309 public ClassInfo findInnerClass(String className) { 1310 // ClassDoc.findClass won't find inner classes. To deal with that, 1311 // we try what they gave us first, but if that didn't work, then 1312 // we see if there are any periods in className, and start searching 1313 // from there. 1314 String[] nodes = className.split("\\."); 1315 ClassDoc cl = mClass; 1316 for (String n : nodes) { 1317 cl = cl.findClass(n); 1318 if (cl == null) { 1319 return null; 1320 } 1321 } 1322 return Converter.obtainClass(cl); 1323 } 1324 1325 public FieldInfo findField(String name) { 1326 // first look on our class, and our superclasses 1327 for (FieldInfo f : fields()) { 1328 if (f.name().equals(name)) { 1329 return f; 1330 } 1331 } 1332 1333 // then look at our enum constants (these are really fields, maybe 1334 // they should be mixed into fields(). not sure) 1335 for (FieldInfo f : enumConstants()) { 1336 if (f.name().equals(name)) { 1337 return f; 1338 } 1339 } 1340 1341 // then recursively look at our containing class 1342 ClassInfo containing = containingClass(); 1343 if (containing != null) { 1344 return containing.findField(name); 1345 } 1346 1347 return null; 1348 } 1349 1350 public static ClassInfo[] sortByName(ClassInfo[] classes) { 1351 int i; 1352 Sorter[] sorted = new Sorter[classes.length]; 1353 for (i = 0; i < sorted.length; i++) { 1354 ClassInfo cl = classes[i]; 1355 sorted[i] = new Sorter(cl.name(), cl); 1356 } 1357 1358 Arrays.sort(sorted); 1359 1360 ClassInfo[] rv = new ClassInfo[classes.length]; 1361 for (i = 0; i < rv.length; i++) { 1362 rv[i] = (ClassInfo) sorted[i].data; 1363 } 1364 1365 return rv; 1366 } 1367 1368 public boolean equals(ClassInfo that) { 1369 if (that != null) { 1370 return this.qualifiedName().equals(that.qualifiedName()); 1371 } else { 1372 return false; 1373 } 1374 } 1375 1376 public void setNonWrittenConstructors(ArrayList<MethodInfo> nonWritten) { 1377 mNonWrittenConstructors = nonWritten; 1378 } 1379 1380 public ArrayList<MethodInfo> getNonWrittenConstructors() { 1381 return mNonWrittenConstructors; 1382 } 1383 1384 public String kind() { 1385 if (isOrdinaryClass()) { 1386 return "class"; 1387 } else if (isInterface()) { 1388 return "interface"; 1389 } else if (isEnum()) { 1390 return "enum"; 1391 } else if (isError()) { 1392 return "class"; 1393 } else if (isException()) { 1394 return "class"; 1395 } else if (isAnnotation()) { 1396 return "@interface"; 1397 } 1398 return null; 1399 } 1400 1401 public String scope() { 1402 if (isPublic()) { 1403 return "public"; 1404 } else if (isProtected()) { 1405 return "protected"; 1406 } else if (isPackagePrivate()) { 1407 return ""; 1408 } else if (isPrivate()) { 1409 return "private"; 1410 } else { 1411 throw new RuntimeException("invalid scope for object " + this); 1412 } 1413 } 1414 1415 public void setHiddenMethods(ArrayList<MethodInfo> mInfo) { 1416 mHiddenMethods = mInfo; 1417 } 1418 1419 public ArrayList<MethodInfo> getHiddenMethods() { 1420 return mHiddenMethods; 1421 } 1422 1423 @Override 1424 public String toString() { 1425 return this.qualifiedName(); 1426 } 1427 1428 public void setReasonIncluded(String reason) { 1429 mReasonIncluded = reason; 1430 } 1431 1432 public String getReasonIncluded() { 1433 return mReasonIncluded; 1434 } 1435 1436 private ClassDoc mClass; 1437 1438 // ctor 1439 private boolean mIsPublic; 1440 private boolean mIsProtected; 1441 private boolean mIsPackagePrivate; 1442 private boolean mIsPrivate; 1443 private boolean mIsStatic; 1444 private boolean mIsInterface; 1445 private boolean mIsAbstract; 1446 private boolean mIsOrdinaryClass; 1447 private boolean mIsException; 1448 private boolean mIsError; 1449 private boolean mIsEnum; 1450 private boolean mIsAnnotation; 1451 private boolean mIsFinal; 1452 private boolean mIsIncluded; 1453 private String mName; 1454 private String mQualifiedName; 1455 private String mQualifiedTypeName; 1456 private boolean mIsPrimitive; 1457 private TypeInfo mTypeInfo; 1458 private String[] mNameParts; 1459 1460 // init 1461 private ArrayList<ClassInfo> mRealInterfaces = new ArrayList<ClassInfo>(); 1462 private ArrayList<ClassInfo> mInterfaces; 1463 private ArrayList<TypeInfo> mRealInterfaceTypes; 1464 private ArrayList<ClassInfo> mInnerClasses; 1465 private ArrayList<MethodInfo> mAllConstructors = new ArrayList<MethodInfo>(); 1466 private ArrayList<MethodInfo> mAllSelfMethods = new ArrayList<MethodInfo>(); 1467 private ArrayList<MethodInfo> mAnnotationElements = new ArrayList<MethodInfo>(); // if this class is an annotation 1468 private ArrayList<FieldInfo> mAllSelfFields = new ArrayList<FieldInfo>(); 1469 private ArrayList<FieldInfo> mEnumConstants = new ArrayList<FieldInfo>(); 1470 private PackageInfo mContainingPackage; 1471 private ClassInfo mContainingClass; 1472 private ClassInfo mRealSuperclass; 1473 private TypeInfo mRealSuperclassType; 1474 private ClassInfo mSuperclass; 1475 private ArrayList<AnnotationInstanceInfo> mAnnotations; 1476 private boolean mSuperclassInit; 1477 private boolean mDeprecatedKnown; 1478 1479 // lazy 1480 private ArrayList<MethodInfo> mConstructors; 1481 private ArrayList<ClassInfo> mRealInnerClasses; 1482 private ArrayList<MethodInfo> mSelfMethods; 1483 private ArrayList<FieldInfo> mSelfFields; 1484 private ArrayList<AttributeInfo> mSelfAttributes; 1485 private ArrayList<MethodInfo> mMethods; 1486 private ArrayList<FieldInfo> mFields; 1487 private ArrayList<TypeInfo> mTypeParameters; 1488 private ArrayList<MethodInfo> mHiddenMethods; 1489 private int mHidden = -1; 1490 private int mCheckLevel = -1; 1491 private String mReasonIncluded; 1492 private ArrayList<MethodInfo> mNonWrittenConstructors; 1493 private boolean mIsDeprecated; 1494 1495 // TODO: Temporary members from apicheck migration. 1496 private HashMap<String, MethodInfo> mApiCheckConstructors = new HashMap<String, MethodInfo>(); 1497 private HashMap<String, MethodInfo> mApiCheckMethods = new HashMap<String, MethodInfo>(); 1498 private HashMap<String, FieldInfo> mApiCheckFields = new HashMap<String, FieldInfo>(); 1499 private HashMap<String, FieldInfo> mApiCheckEnumConstants = new HashMap<String, FieldInfo>(); 1500 1501 // Resolutions 1502 private ArrayList<Resolution> mResolutions; 1503 1504 /** 1505 * Returns true if {@code cl} implements the interface {@code iface} either by either being that 1506 * interface, implementing that interface or extending a type that implements the interface. 1507 */ 1508 public boolean implementsInterface(String iface) { 1509 if (qualifiedName().equals(iface)) { 1510 return true; 1511 } 1512 for (ClassInfo clImplements : interfaces()) { 1513 if (clImplements.implementsInterface(iface)) { 1514 return true; 1515 } 1516 } 1517 if (mSuperclass != null && mSuperclass.implementsInterface(iface)) { 1518 return true; 1519 } 1520 return false; 1521 } 1522 1523 /** 1524 * Returns true if {@code this} extends the class {@code ext}. 1525 */ 1526 public boolean extendsClass(String cl) { 1527 if (qualifiedName().equals(cl)) { 1528 return true; 1529 } 1530 if (mSuperclass != null && mSuperclass.extendsClass(cl)) { 1531 return true; 1532 } 1533 return false; 1534 } 1535 1536 /** 1537 * Returns true if {@code this} is assignable to cl 1538 */ 1539 public boolean isAssignableTo(String cl) { 1540 return implementsInterface(cl) || extendsClass(cl); 1541 } 1542 1543 public void addInterface(ClassInfo iface) { 1544 mRealInterfaces.add(iface); 1545 } 1546 1547 public void addConstructor(MethodInfo ctor) { 1548 mApiCheckConstructors.put(ctor.getHashableName(), ctor); 1549 1550 mAllConstructors.add(ctor); 1551 mConstructors = null; // flush this, hopefully it hasn't been used yet. 1552 } 1553 1554 public void addField(FieldInfo field) { 1555 mApiCheckFields.put(field.name(), field); 1556 1557 mAllSelfFields.add(field); 1558 1559 mSelfFields = null; // flush this, hopefully it hasn't been used yet. 1560 } 1561 1562 public void addEnumConstant(FieldInfo field) { 1563 mApiCheckEnumConstants.put(field.name(), field); 1564 1565 mEnumConstants.add(field); 1566 } 1567 1568 public void setSuperClass(ClassInfo superclass) { 1569 mRealSuperclass = superclass; 1570 mSuperclass = superclass; 1571 } 1572 1573 public Map<String, MethodInfo> allConstructorsMap() { 1574 return mApiCheckConstructors; 1575 } 1576 1577 public Map<String, FieldInfo> allFields() { 1578 return mApiCheckFields; 1579 } 1580 1581 public Map<String, FieldInfo> allEnums() { 1582 return mApiCheckEnumConstants; 1583 } 1584 1585 /** 1586 * Returns all methods defined directly in this class. For a list of all 1587 * methods supported by this class, see {@link #methods()}. 1588 */ 1589 public Map<String, MethodInfo> allMethods() { 1590 return mApiCheckMethods; 1591 } 1592 1593 /** 1594 * Returns the class hierarchy for this class, starting with this class. 1595 */ 1596 public Iterable<ClassInfo> hierarchy() { 1597 List<ClassInfo> result = new ArrayList<ClassInfo>(4); 1598 for (ClassInfo c = this; c != null; c = c.mSuperclass) { 1599 result.add(c); 1600 } 1601 return result; 1602 } 1603 1604 public String superclassName() { 1605 if (mSuperclass == null) { 1606 if (mQualifiedName.equals("java.lang.Object")) { 1607 return null; 1608 } 1609 throw new UnsupportedOperationException("Superclass not set for " + qualifiedName()); 1610 } 1611 return mSuperclass.mQualifiedName; 1612 } 1613 1614 public void setAnnotations(ArrayList<AnnotationInstanceInfo> annotations) { 1615 mAnnotations = annotations; 1616 } 1617 1618 public boolean isConsistent(ClassInfo cl) { 1619 boolean consistent = true; 1620 1621 if (isInterface() != cl.isInterface()) { 1622 Errors.error(Errors.CHANGED_CLASS, cl.position(), "Class " + cl.qualifiedName() 1623 + " changed class/interface declaration"); 1624 consistent = false; 1625 } 1626 for (ClassInfo iface : mRealInterfaces) { 1627 if (!cl.implementsInterface(iface.mQualifiedName)) { 1628 Errors.error(Errors.REMOVED_INTERFACE, cl.position(), "Class " + qualifiedName() 1629 + " no longer implements " + iface); 1630 } 1631 } 1632 for (ClassInfo iface : cl.mRealInterfaces) { 1633 if (!implementsInterface(iface.mQualifiedName)) { 1634 Errors.error(Errors.ADDED_INTERFACE, cl.position(), "Added interface " + iface 1635 + " to class " + qualifiedName()); 1636 consistent = false; 1637 } 1638 } 1639 1640 for (MethodInfo mInfo : mApiCheckMethods.values()) { 1641 if (cl.mApiCheckMethods.containsKey(mInfo.getHashableName())) { 1642 if (!mInfo.isConsistent(cl.mApiCheckMethods.get(mInfo.getHashableName()))) { 1643 consistent = false; 1644 } 1645 } else { 1646 /* 1647 * This class formerly provided this method directly, and now does not. Check our ancestry 1648 * to see if there's an inherited version that still fulfills the API requirement. 1649 */ 1650 MethodInfo mi = ClassInfo.overriddenMethod(mInfo, cl); 1651 if (mi == null) { 1652 mi = ClassInfo.interfaceMethod(mInfo, cl); 1653 } 1654 if (mi == null) { 1655 Errors.error(Errors.REMOVED_METHOD, mInfo.position(), "Removed public method " 1656 + mInfo.qualifiedName()); 1657 consistent = false; 1658 } 1659 } 1660 } 1661 for (MethodInfo mInfo : cl.mApiCheckMethods.values()) { 1662 if (!mApiCheckMethods.containsKey(mInfo.getHashableName())) { 1663 /* 1664 * Similarly to the above, do not fail if this "new" method is really an override of an 1665 * existing superclass method. 1666 */ 1667 MethodInfo mi = ClassInfo.overriddenMethod(mInfo, this); 1668 if (mi == null) { 1669 Errors.error(Errors.ADDED_METHOD, mInfo.position(), "Added public method " 1670 + mInfo.qualifiedName()); 1671 consistent = false; 1672 } 1673 } 1674 } 1675 1676 for (MethodInfo mInfo : mApiCheckConstructors.values()) { 1677 if (cl.mApiCheckConstructors.containsKey(mInfo.getHashableName())) { 1678 if (!mInfo.isConsistent(cl.mApiCheckConstructors.get(mInfo.getHashableName()))) { 1679 consistent = false; 1680 } 1681 } else { 1682 Errors.error(Errors.REMOVED_METHOD, mInfo.position(), "Removed public constructor " 1683 + mInfo.prettySignature()); 1684 consistent = false; 1685 } 1686 } 1687 for (MethodInfo mInfo : cl.mApiCheckConstructors.values()) { 1688 if (!mApiCheckConstructors.containsKey(mInfo.getHashableName())) { 1689 Errors.error(Errors.ADDED_METHOD, mInfo.position(), "Added public constructor " 1690 + mInfo.prettySignature()); 1691 consistent = false; 1692 } 1693 } 1694 1695 for (FieldInfo mInfo : mApiCheckFields.values()) { 1696 if (cl.mApiCheckFields.containsKey(mInfo.name())) { 1697 if (!mInfo.isConsistent(cl.mApiCheckFields.get(mInfo.name()))) { 1698 consistent = false; 1699 } 1700 } else { 1701 Errors.error(Errors.REMOVED_FIELD, mInfo.position(), "Removed field " 1702 + mInfo.qualifiedName()); 1703 consistent = false; 1704 } 1705 } 1706 for (FieldInfo mInfo : cl.mApiCheckFields.values()) { 1707 if (!mApiCheckFields.containsKey(mInfo.name())) { 1708 Errors.error(Errors.ADDED_FIELD, mInfo.position(), "Added public field " 1709 + mInfo.qualifiedName()); 1710 consistent = false; 1711 } 1712 } 1713 1714 for (FieldInfo info : mApiCheckEnumConstants.values()) { 1715 if (cl.mApiCheckEnumConstants.containsKey(info.name())) { 1716 if (!info.isConsistent(cl.mApiCheckEnumConstants.get(info.name()))) { 1717 consistent = false; 1718 } 1719 } else { 1720 Errors.error(Errors.REMOVED_FIELD, info.position(), "Removed enum constant " 1721 + info.qualifiedName()); 1722 consistent = false; 1723 } 1724 } 1725 for (FieldInfo info : cl.mApiCheckEnumConstants.values()) { 1726 if (!mApiCheckEnumConstants.containsKey(info.name())) { 1727 Errors.error(Errors.ADDED_FIELD, info.position(), "Added enum constant " 1728 + info.qualifiedName()); 1729 consistent = false; 1730 } 1731 } 1732 1733 if (mIsAbstract != cl.mIsAbstract) { 1734 consistent = false; 1735 Errors.error(Errors.CHANGED_ABSTRACT, cl.position(), "Class " + cl.qualifiedName() 1736 + " changed abstract qualifier"); 1737 } 1738 1739 if (!mIsFinal && cl.mIsFinal) { 1740 /* 1741 * It is safe to make a class final if it did not previously have any public 1742 * constructors because it was impossible for an application to create a subclass. 1743 */ 1744 if (mApiCheckConstructors.isEmpty()) { 1745 consistent = false; 1746 Errors.error(Errors.ADDED_FINAL_UNINSTANTIABLE, cl.position(), 1747 "Class " + cl.qualifiedName() + " added final qualifier but " 1748 + "was previously uninstantiable and therefore could not be subclassed"); 1749 } else { 1750 consistent = false; 1751 Errors.error(Errors.ADDED_FINAL, cl.position(), "Class " + cl.qualifiedName() 1752 + " added final qualifier"); 1753 } 1754 } else if (mIsFinal && !cl.mIsFinal) { 1755 consistent = false; 1756 Errors.error(Errors.REMOVED_FINAL, cl.position(), "Class " + cl.qualifiedName() 1757 + " removed final qualifier"); 1758 } 1759 1760 if (mIsStatic != cl.mIsStatic) { 1761 consistent = false; 1762 Errors.error(Errors.CHANGED_STATIC, cl.position(), "Class " + cl.qualifiedName() 1763 + " changed static qualifier"); 1764 } 1765 1766 if (!scope().equals(cl.scope())) { 1767 consistent = false; 1768 Errors.error(Errors.CHANGED_SCOPE, cl.position(), "Class " + cl.qualifiedName() 1769 + " scope changed from " + scope() + " to " + cl.scope()); 1770 } 1771 1772 if (!isDeprecated() == cl.isDeprecated()) { 1773 consistent = false; 1774 Errors.error(Errors.CHANGED_DEPRECATED, cl.position(), "Class " + cl.qualifiedName() 1775 + " has changed deprecation state"); 1776 } 1777 1778 if (superclassName() != null) { // java.lang.Object can't have a superclass. 1779 if (!cl.extendsClass(superclassName())) { 1780 consistent = false; 1781 Errors.error(Errors.CHANGED_SUPERCLASS, cl.position(), "Class " + qualifiedName() 1782 + " superclass changed from " + superclassName() + " to " + cl.superclassName()); 1783 } 1784 } 1785 1786 return consistent; 1787 } 1788 1789 // Find a superclass implementation of the given method. 1790 public static MethodInfo overriddenMethod(MethodInfo candidate, ClassInfo newClassObj) { 1791 if (newClassObj == null) { 1792 return null; 1793 } 1794 for (MethodInfo mi : newClassObj.mApiCheckMethods.values()) { 1795 if (mi.matches(candidate)) { 1796 // found it 1797 return mi; 1798 } 1799 } 1800 1801 // not found here. recursively search ancestors 1802 return ClassInfo.overriddenMethod(candidate, newClassObj.mSuperclass); 1803 } 1804 1805 // Find a superinterface declaration of the given method. 1806 public static MethodInfo interfaceMethod(MethodInfo candidate, ClassInfo newClassObj) { 1807 if (newClassObj == null) { 1808 return null; 1809 } 1810 for (ClassInfo interfaceInfo : newClassObj.interfaces()) { 1811 for (MethodInfo mi : interfaceInfo.mApiCheckMethods.values()) { 1812 if (mi.matches(candidate)) { 1813 return mi; 1814 } 1815 } 1816 } 1817 return ClassInfo.interfaceMethod(candidate, newClassObj.mSuperclass); 1818 } 1819 1820 public boolean hasConstructor(MethodInfo constructor) { 1821 String name = constructor.getHashableName(); 1822 for (MethodInfo ctor : mApiCheckConstructors.values()) { 1823 if (name.equals(ctor.getHashableName())) { 1824 return true; 1825 } 1826 } 1827 return false; 1828 } 1829 1830 public void setTypeInfo(TypeInfo typeInfo) { 1831 mTypeInfo = typeInfo; 1832 } 1833 1834 public TypeInfo type() { 1835 return mTypeInfo; 1836 } 1837 1838 public void addInnerClass(ClassInfo innerClass) { 1839 if (mInnerClasses == null) { 1840 mInnerClasses = new ArrayList<ClassInfo>(); 1841 } 1842 1843 mInnerClasses.add(innerClass); 1844 } 1845 1846 public void setContainingClass(ClassInfo containingClass) { 1847 mContainingClass = containingClass; 1848 } 1849 1850 public void setSuperclassType(TypeInfo superclassType) { 1851 mRealSuperclassType = superclassType; 1852 } 1853 1854 public void printResolutions() { 1855 if (mResolutions == null || mResolutions.isEmpty()) { 1856 return; 1857 } 1858 1859 System.out.println("Resolutions for Class " + mName + ":"); 1860 1861 for (Resolution r : mResolutions) { 1862 System.out.println(r); 1863 } 1864 } 1865 1866 public void addResolution(Resolution resolution) { 1867 if (mResolutions == null) { 1868 mResolutions = new ArrayList<Resolution>(); 1869 } 1870 1871 mResolutions.add(resolution); 1872 } 1873 1874 public boolean resolveResolutions() { 1875 ArrayList<Resolution> resolutions = mResolutions; 1876 mResolutions = new ArrayList<Resolution>(); 1877 1878 boolean allResolved = true; 1879 for (Resolution resolution : resolutions) { 1880 StringBuilder qualifiedClassName = new StringBuilder(); 1881 InfoBuilder.resolveQualifiedName(resolution.getValue(), qualifiedClassName, 1882 resolution.getInfoBuilder()); 1883 1884 // if we still couldn't resolve it, save it for the next pass 1885 if ("".equals(qualifiedClassName.toString())) { 1886 mResolutions.add(resolution); 1887 allResolved = false; 1888 } else if ("superclassQualifiedName".equals(resolution.getVariable())) { 1889 setSuperClass(InfoBuilder.Caches.obtainClass(qualifiedClassName.toString())); 1890 } else if ("interfaceQualifiedName".equals(resolution.getVariable())) { 1891 addInterface(InfoBuilder.Caches.obtainClass(qualifiedClassName.toString())); 1892 } 1893 } 1894 1895 return allResolved; 1896 } 1897 } 1898