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