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