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 }