1 /* 2 * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.tools.javadoc; 27 28 import java.io.File; 29 import java.io.IOException; 30 import java.lang.reflect.Modifier; 31 import java.net.URI; 32 import java.util.HashSet; 33 import java.util.Set; 34 35 import javax.tools.FileObject; 36 import javax.tools.JavaFileManager.Location; 37 import javax.tools.StandardJavaFileManager; 38 import javax.tools.StandardLocation; 39 40 import com.sun.javadoc.*; 41 import com.sun.source.util.TreePath; 42 import com.sun.tools.javac.code.Flags; 43 import com.sun.tools.javac.code.Kinds; 44 import com.sun.tools.javac.code.Scope; 45 import com.sun.tools.javac.code.Symbol; 46 import com.sun.tools.javac.code.Symbol.*; 47 import com.sun.tools.javac.code.Type; 48 import com.sun.tools.javac.code.Type.ClassType; 49 import com.sun.tools.javac.code.TypeTag; 50 import com.sun.tools.javac.comp.AttrContext; 51 import com.sun.tools.javac.comp.Env; 52 import com.sun.tools.javac.tree.JCTree; 53 import com.sun.tools.javac.tree.JCTree.JCFieldAccess; 54 import com.sun.tools.javac.tree.JCTree.JCImport; 55 import com.sun.tools.javac.tree.TreeInfo; 56 import com.sun.tools.javac.util.List; 57 import com.sun.tools.javac.util.ListBuffer; 58 import com.sun.tools.javac.util.Name; 59 import com.sun.tools.javac.util.Names; 60 import com.sun.tools.javac.util.Position; 61 import static com.sun.tools.javac.code.Kinds.*; 62 import static com.sun.tools.javac.code.TypeTag.CLASS; 63 import static com.sun.tools.javac.tree.JCTree.Tag.*; 64 65 /** 66 * Represents a java class and provides access to information 67 * about the class, the class' comment and tags, and the 68 * members of the class. A ClassDocImpl only exists if it was 69 * processed in this run of javadoc. References to classes 70 * which may or may not have been processed in this run are 71 * referred to using Type (which can be converted to ClassDocImpl, 72 * if possible). 73 * 74 * <p><b>This is NOT part of any supported API. 75 * If you write code that depends on this, you do so at your own risk. 76 * This code and its internal interfaces are subject to change or 77 * deletion without notice.</b> 78 * 79 * @see Type 80 * 81 * @since 1.2 82 * @author Robert Field 83 * @author Neal Gafter (rewrite) 84 * @author Scott Seligman (generics, enums, annotations) 85 */ 86 87 public class ClassDocImpl extends ProgramElementDocImpl implements ClassDoc { 88 89 public final ClassType type; // protected->public for debugging 90 protected final ClassSymbol tsym; 91 92 boolean isIncluded = false; // Set in RootDocImpl 93 94 private SerializedForm serializedForm; 95 96 /** 97 * Constructor 98 */ ClassDocImpl(DocEnv env, ClassSymbol sym)99 public ClassDocImpl(DocEnv env, ClassSymbol sym) { 100 this(env, sym, null); 101 } 102 103 /** 104 * Constructor 105 */ ClassDocImpl(DocEnv env, ClassSymbol sym, TreePath treePath)106 public ClassDocImpl(DocEnv env, ClassSymbol sym, TreePath treePath) { 107 super(env, sym, treePath); 108 this.type = (ClassType)sym.type; 109 this.tsym = sym; 110 } 111 getElementType()112 public com.sun.javadoc.Type getElementType() { 113 return null; 114 } 115 116 /** 117 * Returns the flags in terms of javac's flags 118 */ getFlags()119 protected long getFlags() { 120 return getFlags(tsym); 121 } 122 123 /** 124 * Returns the flags of a ClassSymbol in terms of javac's flags 125 */ getFlags(ClassSymbol clazz)126 static long getFlags(ClassSymbol clazz) { 127 try { 128 return clazz.flags(); 129 } catch (CompletionFailure ex) { 130 /* Quietly ignore completion failures and try again - the type 131 * for which the CompletionFailure was thrown shouldn't be completed 132 * again by the completer that threw the CompletionFailure. 133 */ 134 return getFlags(clazz); 135 } 136 } 137 138 /** 139 * Is a ClassSymbol an annotation type? 140 */ isAnnotationType(ClassSymbol clazz)141 static boolean isAnnotationType(ClassSymbol clazz) { 142 return (getFlags(clazz) & Flags.ANNOTATION) != 0; 143 } 144 145 /** 146 * Identify the containing class 147 */ getContainingClass()148 protected ClassSymbol getContainingClass() { 149 return tsym.owner.enclClass(); 150 } 151 152 /** 153 * Return true if this is a class, not an interface. 154 */ 155 @Override isClass()156 public boolean isClass() { 157 return !Modifier.isInterface(getModifiers()); 158 } 159 160 /** 161 * Return true if this is a ordinary class, 162 * not an enumeration, exception, an error, or an interface. 163 */ 164 @Override isOrdinaryClass()165 public boolean isOrdinaryClass() { 166 if (isEnum() || isInterface() || isAnnotationType()) { 167 return false; 168 } 169 for (Type t = type; t.hasTag(CLASS); t = env.types.supertype(t)) { 170 if (t.tsym == env.syms.errorType.tsym || 171 t.tsym == env.syms.exceptionType.tsym) { 172 return false; 173 } 174 } 175 return true; 176 } 177 178 /** 179 * Return true if this is an enumeration. 180 * (For legacy doclets, return false.) 181 */ 182 @Override isEnum()183 public boolean isEnum() { 184 return (getFlags() & Flags.ENUM) != 0 185 && 186 !env.legacyDoclet; 187 } 188 189 /** 190 * Return true if this is an interface, but not an annotation type. 191 * Overridden by AnnotationTypeDocImpl. 192 */ 193 @Override isInterface()194 public boolean isInterface() { 195 return Modifier.isInterface(getModifiers()); 196 } 197 198 /** 199 * Return true if this is an exception class 200 */ 201 @Override isException()202 public boolean isException() { 203 if (isEnum() || isInterface() || isAnnotationType()) { 204 return false; 205 } 206 for (Type t = type; t.hasTag(CLASS); t = env.types.supertype(t)) { 207 if (t.tsym == env.syms.exceptionType.tsym) { 208 return true; 209 } 210 } 211 return false; 212 } 213 214 /** 215 * Return true if this is an error class 216 */ 217 @Override isError()218 public boolean isError() { 219 if (isEnum() || isInterface() || isAnnotationType()) { 220 return false; 221 } 222 for (Type t = type; t.hasTag(CLASS); t = env.types.supertype(t)) { 223 if (t.tsym == env.syms.errorType.tsym) { 224 return true; 225 } 226 } 227 return false; 228 } 229 230 /** 231 * Return true if this is a throwable class 232 */ isThrowable()233 public boolean isThrowable() { 234 if (isEnum() || isInterface() || isAnnotationType()) { 235 return false; 236 } 237 for (Type t = type; t.hasTag(CLASS); t = env.types.supertype(t)) { 238 if (t.tsym == env.syms.throwableType.tsym) { 239 return true; 240 } 241 } 242 return false; 243 } 244 245 /** 246 * Return true if this class is abstract 247 */ isAbstract()248 public boolean isAbstract() { 249 return Modifier.isAbstract(getModifiers()); 250 } 251 252 /** 253 * Returns true if this class was synthesized by the compiler. 254 */ isSynthetic()255 public boolean isSynthetic() { 256 return (getFlags() & Flags.SYNTHETIC) != 0; 257 } 258 259 /** 260 * Return true if this class is included in the active set. 261 * A ClassDoc is included iff either it is specified on the 262 * commandline, or if it's containing package is specified 263 * on the command line, or if it is a member class of an 264 * included class. 265 */ 266 isIncluded()267 public boolean isIncluded() { 268 if (isIncluded) { 269 return true; 270 } 271 if (env.shouldDocument(tsym)) { 272 // Class is nameable from top-level and 273 // the class and all enclosing classes 274 // pass the modifier filter. 275 if (containingPackage().isIncluded()) { 276 return isIncluded=true; 277 } 278 ClassDoc outer = containingClass(); 279 if (outer != null && outer.isIncluded()) { 280 return isIncluded=true; 281 } 282 } 283 return false; 284 } 285 286 /** 287 * Return the package that this class is contained in. 288 */ 289 @Override containingPackage()290 public PackageDoc containingPackage() { 291 PackageDocImpl p = env.getPackageDoc(tsym.packge()); 292 if (p.setDocPath == false) { 293 FileObject docPath; 294 try { 295 Location location = env.fileManager.hasLocation(StandardLocation.SOURCE_PATH) 296 ? StandardLocation.SOURCE_PATH : StandardLocation.CLASS_PATH; 297 298 docPath = env.fileManager.getFileForInput( 299 location, p.qualifiedName(), "package.html"); 300 } catch (IOException e) { 301 docPath = null; 302 } 303 304 if (docPath == null) { 305 // fall back on older semantics of looking in same directory as 306 // source file for this class 307 SourcePosition po = position(); 308 if (env.fileManager instanceof StandardJavaFileManager && 309 po instanceof SourcePositionImpl) { 310 URI uri = ((SourcePositionImpl) po).filename.toUri(); 311 if ("file".equals(uri.getScheme())) { 312 File f = new File(uri); 313 File dir = f.getParentFile(); 314 if (dir != null) { 315 File pf = new File(dir, "package.html"); 316 if (pf.exists()) { 317 StandardJavaFileManager sfm = (StandardJavaFileManager) env.fileManager; 318 docPath = sfm.getJavaFileObjects(pf).iterator().next(); 319 } 320 } 321 322 } 323 } 324 } 325 326 p.setDocPath(docPath); 327 } 328 return p; 329 } 330 331 /** 332 * Return the class name without package qualifier - but with 333 * enclosing class qualifier - as a String. 334 * <pre> 335 * Examples: 336 * for java.util.Hashtable 337 * return Hashtable 338 * for java.util.Map.Entry 339 * return Map.Entry 340 * </pre> 341 */ name()342 public String name() { 343 if (name == null) { 344 name = getClassName(tsym, false); 345 } 346 return name; 347 } 348 349 private String name; 350 351 /** 352 * Return the qualified class name as a String. 353 * <pre> 354 * Example: 355 * for java.util.Hashtable 356 * return java.util.Hashtable 357 * if no qualifier, just return flat name 358 * </pre> 359 */ qualifiedName()360 public String qualifiedName() { 361 if (qualifiedName == null) { 362 qualifiedName = getClassName(tsym, true); 363 } 364 return qualifiedName; 365 } 366 367 private String qualifiedName; 368 369 /** 370 * Return unqualified name of type excluding any dimension information. 371 * <p> 372 * For example, a two dimensional array of String returns 'String'. 373 */ typeName()374 public String typeName() { 375 return name(); 376 } 377 378 /** 379 * Return qualified name of type excluding any dimension information. 380 *<p> 381 * For example, a two dimensional array of String 382 * returns 'java.lang.String'. 383 */ qualifiedTypeName()384 public String qualifiedTypeName() { 385 return qualifiedName(); 386 } 387 388 /** 389 * Return the simple name of this type. 390 */ simpleTypeName()391 public String simpleTypeName() { 392 if (simpleTypeName == null) { 393 simpleTypeName = tsym.name.toString(); 394 } 395 return simpleTypeName; 396 } 397 398 private String simpleTypeName; 399 400 /** 401 * Return the qualified name and any type parameters. 402 * Each parameter is a type variable with optional bounds. 403 */ 404 @Override toString()405 public String toString() { 406 return classToString(env, tsym, true); 407 } 408 409 /** 410 * Return the class name as a string. If "full" is true the name is 411 * qualified, otherwise it is qualified by its enclosing class(es) only. 412 */ getClassName(ClassSymbol c, boolean full)413 static String getClassName(ClassSymbol c, boolean full) { 414 if (full) { 415 return c.getQualifiedName().toString(); 416 } else { 417 String n = ""; 418 for ( ; c != null; c = c.owner.enclClass()) { 419 n = c.name + (n.equals("") ? "" : ".") + n; 420 } 421 return n; 422 } 423 } 424 425 /** 426 * Return the class name with any type parameters as a string. 427 * Each parameter is a type variable with optional bounds. 428 * If "full" is true all names are qualified, otherwise they are 429 * qualified by their enclosing class(es) only. 430 */ classToString(DocEnv env, ClassSymbol c, boolean full)431 static String classToString(DocEnv env, ClassSymbol c, boolean full) { 432 StringBuilder s = new StringBuilder(); 433 if (!c.isInner()) { // if c is not an inner class 434 s.append(getClassName(c, full)); 435 } else { 436 // c is an inner class, so include type params of outer. 437 ClassSymbol encl = c.owner.enclClass(); 438 s.append(classToString(env, encl, full)) 439 .append('.') 440 .append(c.name); 441 } 442 s.append(TypeMaker.typeParametersString(env, c, full)); 443 return s.toString(); 444 } 445 446 /** 447 * Is this class (or any enclosing class) generic? That is, does 448 * it have type parameters? 449 */ isGeneric(ClassSymbol c)450 static boolean isGeneric(ClassSymbol c) { 451 return c.type.allparams().nonEmpty(); 452 } 453 454 /** 455 * Return the formal type parameters of this class or interface. 456 * Return an empty array if there are none. 457 */ typeParameters()458 public TypeVariable[] typeParameters() { 459 if (env.legacyDoclet) { 460 return new TypeVariable[0]; 461 } 462 TypeVariable res[] = new TypeVariable[type.getTypeArguments().length()]; 463 TypeMaker.getTypes(env, type.getTypeArguments(), res); 464 return res; 465 } 466 467 /** 468 * Return the type parameter tags of this class or interface. 469 */ typeParamTags()470 public ParamTag[] typeParamTags() { 471 return (env.legacyDoclet) 472 ? new ParamTag[0] 473 : comment().typeParamTags(); 474 } 475 476 /** 477 * Return the modifier string for this class. If it's an interface 478 * exclude 'abstract' keyword from the modifier string 479 */ 480 @Override modifiers()481 public String modifiers() { 482 return Modifier.toString(modifierSpecifier()); 483 } 484 485 @Override modifierSpecifier()486 public int modifierSpecifier() { 487 int modifiers = getModifiers(); 488 return (isInterface() || isAnnotationType()) 489 ? modifiers & ~Modifier.ABSTRACT 490 : modifiers; 491 } 492 493 /** 494 * Return the superclass of this class 495 * 496 * @return the ClassDocImpl for the superclass of this class, null 497 * if there is no superclass. 498 */ superclass()499 public ClassDoc superclass() { 500 if (isInterface() || isAnnotationType()) return null; 501 if (tsym == env.syms.objectType.tsym) return null; 502 ClassSymbol c = (ClassSymbol)env.types.supertype(type).tsym; 503 if (c == null || c == tsym) c = (ClassSymbol)env.syms.objectType.tsym; 504 return env.getClassDoc(c); 505 } 506 507 /** 508 * Return the superclass of this class. Return null if this is an 509 * interface. A superclass is represented by either a 510 * <code>ClassDoc</code> or a <code>ParameterizedType</code>. 511 */ superclassType()512 public com.sun.javadoc.Type superclassType() { 513 if (isInterface() || isAnnotationType() || 514 (tsym == env.syms.objectType.tsym)) 515 return null; 516 Type sup = env.types.supertype(type); 517 return TypeMaker.getType(env, 518 (sup.hasTag(TypeTag.NONE)) ? env.syms.objectType : sup); 519 } 520 521 /** 522 * Test whether this class is a subclass of the specified class. 523 * 524 * @param cd the candidate superclass. 525 * @return true if cd is a superclass of this class. 526 */ subclassOf(ClassDoc cd)527 public boolean subclassOf(ClassDoc cd) { 528 return tsym.isSubClass(((ClassDocImpl)cd).tsym, env.types); 529 } 530 531 /** 532 * Return interfaces implemented by this class or interfaces 533 * extended by this interface. 534 * 535 * @return An array of ClassDocImpl representing the interfaces. 536 * Return an empty array if there are no interfaces. 537 */ interfaces()538 public ClassDoc[] interfaces() { 539 ListBuffer<ClassDocImpl> ta = new ListBuffer<ClassDocImpl>(); 540 for (Type t : env.types.interfaces(type)) { 541 ta.append(env.getClassDoc((ClassSymbol)t.tsym)); 542 } 543 //### Cache ta here? 544 return ta.toArray(new ClassDocImpl[ta.length()]); 545 } 546 547 /** 548 * Return interfaces implemented by this class or interfaces extended 549 * by this interface. Includes only directly-declared interfaces, not 550 * inherited interfaces. 551 * Return an empty array if there are no interfaces. 552 */ interfaceTypes()553 public com.sun.javadoc.Type[] interfaceTypes() { 554 //### Cache result here? 555 return TypeMaker.getTypes(env, env.types.interfaces(type)); 556 } 557 558 /** 559 * Return fields in class. 560 * @param filter include only the included fields if filter==true 561 */ fields(boolean filter)562 public FieldDoc[] fields(boolean filter) { 563 return fields(filter, false); 564 } 565 566 /** 567 * Return included fields in class. 568 */ fields()569 public FieldDoc[] fields() { 570 return fields(true, false); 571 } 572 573 /** 574 * Return the enum constants if this is an enum type. 575 */ enumConstants()576 public FieldDoc[] enumConstants() { 577 return fields(false, true); 578 } 579 580 /** 581 * Return fields in class. 582 * @param filter if true, return only the included fields 583 * @param enumConstants if true, return the enum constants instead 584 */ fields(boolean filter, boolean enumConstants)585 private FieldDoc[] fields(boolean filter, boolean enumConstants) { 586 List<FieldDocImpl> fields = List.nil(); 587 for (Scope.Entry e = tsym.members().elems; e != null; e = e.sibling) { 588 if (e.sym != null && e.sym.kind == VAR) { 589 VarSymbol s = (VarSymbol)e.sym; 590 boolean isEnum = ((s.flags() & Flags.ENUM) != 0) && 591 !env.legacyDoclet; 592 if (isEnum == enumConstants && 593 (!filter || env.shouldDocument(s))) { 594 fields = fields.prepend(env.getFieldDoc(s)); 595 } 596 } 597 } 598 return fields.toArray(new FieldDocImpl[fields.length()]); 599 } 600 601 /** 602 * Return methods in class. 603 * This method is overridden by AnnotationTypeDocImpl. 604 * 605 * @param filter include only the included methods if filter==true 606 * @return an array of MethodDocImpl for representing the visible 607 * methods in this class. Does not include constructors. 608 */ methods(boolean filter)609 public MethodDoc[] methods(boolean filter) { 610 Names names = tsym.name.table.names; 611 List<MethodDocImpl> methods = List.nil(); 612 for (Scope.Entry e = tsym.members().elems; e != null; e = e.sibling) { 613 if (e.sym != null 614 && e.sym.kind == Kinds.MTH 615 && e.sym.name != names.init 616 && e.sym.name != names.clinit) { 617 MethodSymbol s = (MethodSymbol)e.sym; 618 if (!filter || env.shouldDocument(s)) { 619 methods = methods.prepend(env.getMethodDoc(s)); 620 } 621 } 622 } 623 //### Cache methods here? 624 return methods.toArray(new MethodDocImpl[methods.length()]); 625 } 626 627 /** 628 * Return included methods in class. 629 * 630 * @return an array of MethodDocImpl for representing the visible 631 * methods in this class. Does not include constructors. 632 */ methods()633 public MethodDoc[] methods() { 634 return methods(true); 635 } 636 637 /** 638 * Return constructors in class. 639 * 640 * @param filter include only the included constructors if filter==true 641 * @return an array of ConstructorDocImpl for representing the visible 642 * constructors in this class. 643 */ constructors(boolean filter)644 public ConstructorDoc[] constructors(boolean filter) { 645 Names names = tsym.name.table.names; 646 List<ConstructorDocImpl> constructors = List.nil(); 647 for (Scope.Entry e = tsym.members().elems; e != null; e = e.sibling) { 648 if (e.sym != null && 649 e.sym.kind == Kinds.MTH && e.sym.name == names.init) { 650 MethodSymbol s = (MethodSymbol)e.sym; 651 if (!filter || env.shouldDocument(s)) { 652 constructors = constructors.prepend(env.getConstructorDoc(s)); 653 } 654 } 655 } 656 //### Cache constructors here? 657 return constructors.toArray(new ConstructorDocImpl[constructors.length()]); 658 } 659 660 /** 661 * Return included constructors in class. 662 * 663 * @return an array of ConstructorDocImpl for representing the visible 664 * constructors in this class. 665 */ constructors()666 public ConstructorDoc[] constructors() { 667 return constructors(true); 668 } 669 670 /** 671 * Adds all inner classes of this class, and their 672 * inner classes recursively, to the list l. 673 */ addAllClasses(ListBuffer<ClassDocImpl> l, boolean filtered)674 void addAllClasses(ListBuffer<ClassDocImpl> l, boolean filtered) { 675 try { 676 if (isSynthetic()) return; 677 // sometimes synthetic classes are not marked synthetic 678 if (!JavadocTool.isValidClassName(tsym.name.toString())) return; 679 if (filtered && !env.shouldDocument(tsym)) return; 680 if (l.contains(this)) return; 681 l.append(this); 682 List<ClassDocImpl> more = List.nil(); 683 for (Scope.Entry e = tsym.members().elems; e != null; 684 e = e.sibling) { 685 if (e.sym != null && e.sym.kind == Kinds.TYP) { 686 ClassSymbol s = (ClassSymbol)e.sym; 687 ClassDocImpl c = env.getClassDoc(s); 688 if (c.isSynthetic()) continue; 689 if (c != null) more = more.prepend(c); 690 } 691 } 692 // this extra step preserves the ordering from oldjavadoc 693 for (; more.nonEmpty(); more=more.tail) { 694 more.head.addAllClasses(l, filtered); 695 } 696 } catch (CompletionFailure e) { 697 // quietly ignore completion failures 698 } 699 } 700 701 /** 702 * Return inner classes within this class. 703 * 704 * @param filter include only the included inner classes if filter==true. 705 * @return an array of ClassDocImpl for representing the visible 706 * classes defined in this class. Anonymous and local classes 707 * are not included. 708 */ innerClasses(boolean filter)709 public ClassDoc[] innerClasses(boolean filter) { 710 ListBuffer<ClassDocImpl> innerClasses = new ListBuffer<ClassDocImpl>(); 711 for (Scope.Entry e = tsym.members().elems; e != null; e = e.sibling) { 712 if (e.sym != null && e.sym.kind == Kinds.TYP) { 713 ClassSymbol s = (ClassSymbol)e.sym; 714 if ((s.flags_field & Flags.SYNTHETIC) != 0) continue; 715 if (!filter || env.isVisible(s)) { 716 innerClasses.prepend(env.getClassDoc(s)); 717 } 718 } 719 } 720 //### Cache classes here? 721 return innerClasses.toArray(new ClassDocImpl[innerClasses.length()]); 722 } 723 724 /** 725 * Return included inner classes within this class. 726 * 727 * @return an array of ClassDocImpl for representing the visible 728 * classes defined in this class. Anonymous and local classes 729 * are not included. 730 */ innerClasses()731 public ClassDoc[] innerClasses() { 732 return innerClasses(true); 733 } 734 735 /** 736 * Find a class within the context of this class. 737 * Search order: qualified name, in this class (inner), 738 * in this package, in the class imports, in the package 739 * imports. 740 * Return the ClassDocImpl if found, null if not found. 741 */ 742 //### The specified search order is not the normal rule the 743 //### compiler would use. Leave as specified or change it? findClass(String className)744 public ClassDoc findClass(String className) { 745 ClassDoc searchResult = searchClass(className); 746 if (searchResult == null) { 747 ClassDocImpl enclosingClass = (ClassDocImpl)containingClass(); 748 //Expand search space to include enclosing class. 749 while (enclosingClass != null && enclosingClass.containingClass() != null) { 750 enclosingClass = (ClassDocImpl)enclosingClass.containingClass(); 751 } 752 searchResult = enclosingClass == null ? 753 null : enclosingClass.searchClass(className); 754 } 755 return searchResult; 756 } 757 searchClass(String className)758 private ClassDoc searchClass(String className) { 759 Names names = tsym.name.table.names; 760 761 // search by qualified name first 762 ClassDoc cd = env.lookupClass(className); 763 if (cd != null) { 764 return cd; 765 } 766 767 // search inner classes 768 //### Add private entry point to avoid creating array? 769 //### Replicate code in innerClasses here to avoid consing? 770 for (ClassDoc icd : innerClasses()) { 771 if (icd.name().equals(className) || 772 //### This is from original javadoc but it looks suspicious to me... 773 //### I believe it is attempting to compensate for the confused 774 //### convention of including the nested class qualifiers in the 775 //### 'name' of the inner class, rather than the true simple name. 776 icd.name().endsWith("." + className)) { 777 return icd; 778 } else { 779 ClassDoc innercd = ((ClassDocImpl) icd).searchClass(className); 780 if (innercd != null) { 781 return innercd; 782 } 783 } 784 } 785 786 // check in this package 787 cd = containingPackage().findClass(className); 788 if (cd != null) { 789 return cd; 790 } 791 792 // make sure that this symbol has been completed 793 if (tsym.completer != null) { 794 tsym.complete(); 795 } 796 797 // search imports 798 799 if (tsym.sourcefile != null) { 800 801 //### This information is available only for source classes. 802 803 Env<AttrContext> compenv = env.enter.getEnv(tsym); 804 if (compenv == null) return null; 805 806 Scope s = compenv.toplevel.namedImportScope; 807 for (Scope.Entry e = s.lookup(names.fromString(className)); e.scope != null; e = e.next()) { 808 if (e.sym.kind == Kinds.TYP) { 809 ClassDoc c = env.getClassDoc((ClassSymbol)e.sym); 810 return c; 811 } 812 } 813 814 s = compenv.toplevel.starImportScope; 815 for (Scope.Entry e = s.lookup(names.fromString(className)); e.scope != null; e = e.next()) { 816 if (e.sym.kind == Kinds.TYP) { 817 ClassDoc c = env.getClassDoc((ClassSymbol)e.sym); 818 return c; 819 } 820 } 821 } 822 823 return null; // not found 824 } 825 826 hasParameterTypes(MethodSymbol method, String[] argTypes)827 private boolean hasParameterTypes(MethodSymbol method, String[] argTypes) { 828 829 if (argTypes == null) { 830 // wildcard 831 return true; 832 } 833 834 int i = 0; 835 List<Type> types = method.type.getParameterTypes(); 836 837 if (argTypes.length != types.length()) { 838 return false; 839 } 840 841 for (Type t : types) { 842 String argType = argTypes[i++]; 843 // For vararg method, "T..." matches type T[]. 844 if (i == argTypes.length) { 845 argType = argType.replace("...", "[]"); 846 } 847 if (!hasTypeName(env.types.erasure(t), argType)) { //###(gj) 848 return false; 849 } 850 } 851 return true; 852 } 853 // where hasTypeName(Type t, String name)854 private boolean hasTypeName(Type t, String name) { 855 return 856 name.equals(TypeMaker.getTypeName(t, true)) 857 || 858 name.equals(TypeMaker.getTypeName(t, false)) 859 || 860 (qualifiedName() + "." + name).equals(TypeMaker.getTypeName(t, true)); 861 } 862 863 864 865 /** 866 * Find a method in this class scope. 867 * Search order: this class, interfaces, superclasses, outerclasses. 868 * Note that this is not necessarily what the compiler would do! 869 * 870 * @param methodName the unqualified name to search for. 871 * @param paramTypes the array of Strings for method parameter types. 872 * @return the first MethodDocImpl which matches, null if not found. 873 */ findMethod(String methodName, String[] paramTypes)874 public MethodDocImpl findMethod(String methodName, String[] paramTypes) { 875 // Use hash table 'searched' to avoid searching same class twice. 876 //### It is not clear how this could happen. 877 return searchMethod(methodName, paramTypes, new HashSet<ClassDocImpl>()); 878 } 879 searchMethod(String methodName, String[] paramTypes, Set<ClassDocImpl> searched)880 private MethodDocImpl searchMethod(String methodName, 881 String[] paramTypes, Set<ClassDocImpl> searched) { 882 //### Note that this search is not necessarily what the compiler would do! 883 884 Names names = tsym.name.table.names; 885 // do not match constructors 886 if (names.init.contentEquals(methodName)) { 887 return null; 888 } 889 890 ClassDocImpl cdi; 891 MethodDocImpl mdi; 892 893 if (searched.contains(this)) { 894 return null; 895 } 896 searched.add(this); 897 898 //DEBUG 899 /*---------------------------------* 900 System.out.print("searching " + this + " for " + methodName); 901 if (paramTypes == null) { 902 System.out.println("()"); 903 } else { 904 System.out.print("("); 905 for (int k=0; k < paramTypes.length; k++) { 906 System.out.print(paramTypes[k]); 907 if ((k + 1) < paramTypes.length) { 908 System.out.print(", "); 909 } 910 } 911 System.out.println(")"); 912 } 913 *---------------------------------*/ 914 915 // search current class 916 Scope.Entry e = tsym.members().lookup(names.fromString(methodName)); 917 918 //### Using modifier filter here isn't really correct, 919 //### but emulates the old behavior. Instead, we should 920 //### apply the normal rules of visibility and inheritance. 921 922 if (paramTypes == null) { 923 // If no parameters specified, we are allowed to return 924 // any method with a matching name. In practice, the old 925 // code returned the first method, which is now the last! 926 // In order to provide textually identical results, we 927 // attempt to emulate the old behavior. 928 MethodSymbol lastFound = null; 929 for (; e.scope != null; e = e.next()) { 930 if (e.sym.kind == Kinds.MTH) { 931 //### Should intern methodName as Name. 932 if (e.sym.name.toString().equals(methodName)) { 933 lastFound = (MethodSymbol)e.sym; 934 } 935 } 936 } 937 if (lastFound != null) { 938 return env.getMethodDoc(lastFound); 939 } 940 } else { 941 for (; e.scope != null; e = e.next()) { 942 if (e.sym != null && 943 e.sym.kind == Kinds.MTH) { 944 //### Should intern methodName as Name. 945 if (hasParameterTypes((MethodSymbol)e.sym, paramTypes)) { 946 return env.getMethodDoc((MethodSymbol)e.sym); 947 } 948 } 949 } 950 } 951 952 //### If we found a MethodDoc above, but which did not pass 953 //### the modifier filter, we should return failure here! 954 955 // search superclass 956 cdi = (ClassDocImpl)superclass(); 957 if (cdi != null) { 958 mdi = cdi.searchMethod(methodName, paramTypes, searched); 959 if (mdi != null) { 960 return mdi; 961 } 962 } 963 964 // search interfaces 965 ClassDoc intf[] = interfaces(); 966 for (int i = 0; i < intf.length; i++) { 967 cdi = (ClassDocImpl)intf[i]; 968 mdi = cdi.searchMethod(methodName, paramTypes, searched); 969 if (mdi != null) { 970 return mdi; 971 } 972 } 973 974 // search enclosing class 975 cdi = (ClassDocImpl)containingClass(); 976 if (cdi != null) { 977 mdi = cdi.searchMethod(methodName, paramTypes, searched); 978 if (mdi != null) { 979 return mdi; 980 } 981 } 982 983 //###(gj) As a temporary measure until type variables are better 984 //### handled, try again without the parameter types. 985 //### This should most often find the right method, and occassionally 986 //### find the wrong one. 987 //if (paramTypes != null) { 988 // return findMethod(methodName, null); 989 //} 990 991 return null; 992 } 993 994 /** 995 * Find constructor in this class. 996 * 997 * @param constrName the unqualified name to search for. 998 * @param paramTypes the array of Strings for constructor parameters. 999 * @return the first ConstructorDocImpl which matches, null if not found. 1000 */ findConstructor(String constrName, String[] paramTypes)1001 public ConstructorDoc findConstructor(String constrName, 1002 String[] paramTypes) { 1003 Names names = tsym.name.table.names; 1004 for (Scope.Entry e = tsym.members().lookup(names.fromString("<init>")); e.scope != null; e = e.next()) { 1005 if (e.sym.kind == Kinds.MTH) { 1006 if (hasParameterTypes((MethodSymbol)e.sym, paramTypes)) { 1007 return env.getConstructorDoc((MethodSymbol)e.sym); 1008 } 1009 } 1010 } 1011 1012 //###(gj) As a temporary measure until type variables are better 1013 //### handled, try again without the parameter types. 1014 //### This will often find the right constructor, and occassionally 1015 //### find the wrong one. 1016 //if (paramTypes != null) { 1017 // return findConstructor(constrName, null); 1018 //} 1019 1020 return null; 1021 } 1022 1023 /** 1024 * Find a field in this class scope. 1025 * Search order: this class, outerclasses, interfaces, 1026 * superclasses. IMP: If see tag is defined in an inner class, 1027 * which extends a super class and if outerclass and the super 1028 * class have a visible field in common then Java compiler cribs 1029 * about the ambiguity, but the following code will search in the 1030 * above given search order. 1031 * 1032 * @param fieldName the unqualified name to search for. 1033 * @return the first FieldDocImpl which matches, null if not found. 1034 */ findField(String fieldName)1035 public FieldDoc findField(String fieldName) { 1036 return searchField(fieldName, new HashSet<ClassDocImpl>()); 1037 } 1038 searchField(String fieldName, Set<ClassDocImpl> searched)1039 private FieldDocImpl searchField(String fieldName, Set<ClassDocImpl> searched) { 1040 Names names = tsym.name.table.names; 1041 if (searched.contains(this)) { 1042 return null; 1043 } 1044 searched.add(this); 1045 1046 for (Scope.Entry e = tsym.members().lookup(names.fromString(fieldName)); e.scope != null; e = e.next()) { 1047 if (e.sym.kind == Kinds.VAR) { 1048 //### Should intern fieldName as Name. 1049 return env.getFieldDoc((VarSymbol)e.sym); 1050 } 1051 } 1052 1053 //### If we found a FieldDoc above, but which did not pass 1054 //### the modifier filter, we should return failure here! 1055 1056 ClassDocImpl cdi = (ClassDocImpl)containingClass(); 1057 if (cdi != null) { 1058 FieldDocImpl fdi = cdi.searchField(fieldName, searched); 1059 if (fdi != null) { 1060 return fdi; 1061 } 1062 } 1063 1064 // search superclass 1065 cdi = (ClassDocImpl)superclass(); 1066 if (cdi != null) { 1067 FieldDocImpl fdi = cdi.searchField(fieldName, searched); 1068 if (fdi != null) { 1069 return fdi; 1070 } 1071 } 1072 1073 // search interfaces 1074 ClassDoc intf[] = interfaces(); 1075 for (int i = 0; i < intf.length; i++) { 1076 cdi = (ClassDocImpl)intf[i]; 1077 FieldDocImpl fdi = cdi.searchField(fieldName, searched); 1078 if (fdi != null) { 1079 return fdi; 1080 } 1081 } 1082 1083 return null; 1084 } 1085 1086 /** 1087 * Get the list of classes declared as imported. 1088 * These are called "single-type-import declarations" in the JLS. 1089 * This method is deprecated in the ClassDoc interface. 1090 * 1091 * @return an array of ClassDocImpl representing the imported classes. 1092 * 1093 * @deprecated Import declarations are implementation details that 1094 * should not be exposed here. In addition, not all imported 1095 * classes are imported through single-type-import declarations. 1096 */ 1097 @Deprecated importedClasses()1098 public ClassDoc[] importedClasses() { 1099 // information is not available for binary classfiles 1100 if (tsym.sourcefile == null) return new ClassDoc[0]; 1101 1102 ListBuffer<ClassDocImpl> importedClasses = new ListBuffer<ClassDocImpl>(); 1103 1104 Env<AttrContext> compenv = env.enter.getEnv(tsym); 1105 if (compenv == null) return new ClassDocImpl[0]; 1106 1107 Name asterisk = tsym.name.table.names.asterisk; 1108 for (JCTree t : compenv.toplevel.defs) { 1109 if (t.hasTag(IMPORT)) { 1110 JCTree imp = ((JCImport) t).qualid; 1111 if ((TreeInfo.name(imp) != asterisk) && 1112 (imp.type.tsym.kind & Kinds.TYP) != 0) { 1113 importedClasses.append( 1114 env.getClassDoc((ClassSymbol)imp.type.tsym)); 1115 } 1116 } 1117 } 1118 1119 return importedClasses.toArray(new ClassDocImpl[importedClasses.length()]); 1120 } 1121 1122 /** 1123 * Get the list of packages declared as imported. 1124 * These are called "type-import-on-demand declarations" in the JLS. 1125 * This method is deprecated in the ClassDoc interface. 1126 * 1127 * @return an array of PackageDocImpl representing the imported packages. 1128 * 1129 * ###NOTE: the syntax supports importing all inner classes from a class as well. 1130 * @deprecated Import declarations are implementation details that 1131 * should not be exposed here. In addition, this method's 1132 * return type does not allow for all type-import-on-demand 1133 * declarations to be returned. 1134 */ 1135 @Deprecated importedPackages()1136 public PackageDoc[] importedPackages() { 1137 // information is not available for binary classfiles 1138 if (tsym.sourcefile == null) return new PackageDoc[0]; 1139 1140 ListBuffer<PackageDocImpl> importedPackages = new ListBuffer<PackageDocImpl>(); 1141 1142 //### Add the implicit "import java.lang.*" to the result 1143 Names names = tsym.name.table.names; 1144 importedPackages.append(env.getPackageDoc(env.reader.enterPackage(names.java_lang))); 1145 1146 Env<AttrContext> compenv = env.enter.getEnv(tsym); 1147 if (compenv == null) return new PackageDocImpl[0]; 1148 1149 for (JCTree t : compenv.toplevel.defs) { 1150 if (t.hasTag(IMPORT)) { 1151 JCTree imp = ((JCImport) t).qualid; 1152 if (TreeInfo.name(imp) == names.asterisk) { 1153 JCFieldAccess sel = (JCFieldAccess)imp; 1154 Symbol s = sel.selected.type.tsym; 1155 PackageDocImpl pdoc = env.getPackageDoc(s.packge()); 1156 if (!importedPackages.contains(pdoc)) 1157 importedPackages.append(pdoc); 1158 } 1159 } 1160 } 1161 1162 return importedPackages.toArray(new PackageDocImpl[importedPackages.length()]); 1163 } 1164 1165 /** 1166 * Return the type's dimension information. 1167 * Always return "", as this is not an array type. 1168 */ dimension()1169 public String dimension() { 1170 return ""; 1171 } 1172 1173 /** 1174 * Return this type as a class, which it already is. 1175 */ asClassDoc()1176 public ClassDoc asClassDoc() { 1177 return this; 1178 } 1179 1180 /** 1181 * Return null (unless overridden), as this is not an annotation type. 1182 */ asAnnotationTypeDoc()1183 public AnnotationTypeDoc asAnnotationTypeDoc() { 1184 return null; 1185 } 1186 1187 /** 1188 * Return null, as this is not a class instantiation. 1189 */ asParameterizedType()1190 public ParameterizedType asParameterizedType() { 1191 return null; 1192 } 1193 1194 /** 1195 * Return null, as this is not a type variable. 1196 */ asTypeVariable()1197 public TypeVariable asTypeVariable() { 1198 return null; 1199 } 1200 1201 /** 1202 * Return null, as this is not a wildcard type. 1203 */ asWildcardType()1204 public WildcardType asWildcardType() { 1205 return null; 1206 } 1207 1208 /** 1209 * Returns null, as this is not an annotated type. 1210 */ asAnnotatedType()1211 public AnnotatedType asAnnotatedType() { 1212 return null; 1213 } 1214 1215 /** 1216 * Return false, as this is not a primitive type. 1217 */ isPrimitive()1218 public boolean isPrimitive() { 1219 return false; 1220 } 1221 1222 //--- Serialization --- 1223 1224 //### These methods ignore modifier filter. 1225 1226 /** 1227 * Return true if this class implements <code>java.io.Serializable</code>. 1228 * 1229 * Since <code>java.io.Externalizable</code> extends 1230 * <code>java.io.Serializable</code>, 1231 * Externalizable objects are also Serializable. 1232 */ isSerializable()1233 public boolean isSerializable() { 1234 try { 1235 return env.types.isSubtype(type, env.syms.serializableType); 1236 } catch (CompletionFailure ex) { 1237 // quietly ignore completion failures 1238 return false; 1239 } 1240 } 1241 1242 /** 1243 * Return true if this class implements 1244 * <code>java.io.Externalizable</code>. 1245 */ isExternalizable()1246 public boolean isExternalizable() { 1247 try { 1248 return env.types.isSubtype(type, env.externalizableSym.type); 1249 } catch (CompletionFailure ex) { 1250 // quietly ignore completion failures 1251 return false; 1252 } 1253 } 1254 1255 /** 1256 * Return the serialization methods for this class. 1257 * 1258 * @return an array of <code>MethodDocImpl</code> that represents 1259 * the serialization methods for this class. 1260 */ serializationMethods()1261 public MethodDoc[] serializationMethods() { 1262 if (serializedForm == null) { 1263 serializedForm = new SerializedForm(env, tsym, this); 1264 } 1265 //### Clone this? 1266 return serializedForm.methods(); 1267 } 1268 1269 /** 1270 * Return the Serializable fields of class.<p> 1271 * 1272 * Return either a list of default fields documented by 1273 * <code>serial</code> tag<br> 1274 * or return a single <code>FieldDoc</code> for 1275 * <code>serialPersistentField</code> member. 1276 * There should be a <code>serialField</code> tag for 1277 * each Serializable field defined by an <code>ObjectStreamField</code> 1278 * array component of <code>serialPersistentField</code>. 1279 * 1280 * @returns an array of <code>FieldDoc</code> for the Serializable fields 1281 * of this class. 1282 * 1283 * @see #definesSerializableFields() 1284 * @see SerialFieldTagImpl 1285 */ serializableFields()1286 public FieldDoc[] serializableFields() { 1287 if (serializedForm == null) { 1288 serializedForm = new SerializedForm(env, tsym, this); 1289 } 1290 //### Clone this? 1291 return serializedForm.fields(); 1292 } 1293 1294 /** 1295 * Return true if Serializable fields are explicitly defined with 1296 * the special class member <code>serialPersistentFields</code>. 1297 * 1298 * @see #serializableFields() 1299 * @see SerialFieldTagImpl 1300 */ definesSerializableFields()1301 public boolean definesSerializableFields() { 1302 if (!isSerializable() || isExternalizable()) { 1303 return false; 1304 } else { 1305 if (serializedForm == null) { 1306 serializedForm = new SerializedForm(env, tsym, this); 1307 } 1308 //### Clone this? 1309 return serializedForm.definesSerializableFields(); 1310 } 1311 } 1312 1313 /** 1314 * Determine if a class is a RuntimeException. 1315 * <p> 1316 * Used only by ThrowsTagImpl. 1317 */ isRuntimeException()1318 boolean isRuntimeException() { 1319 return tsym.isSubClass(env.syms.runtimeExceptionType.tsym, env.types); 1320 } 1321 1322 /** 1323 * Return the source position of the entity, or null if 1324 * no position is available. 1325 */ 1326 @Override position()1327 public SourcePosition position() { 1328 if (tsym.sourcefile == null) return null; 1329 return SourcePositionImpl.make(tsym.sourcefile, 1330 (tree==null) ? Position.NOPOS : tree.pos, 1331 lineMap); 1332 } 1333 } 1334