src/share/classes/com/sun/tools/javadoc/ClassDocImpl.java

Thu, 18 Apr 2013 20:00:14 -0700

author
jjg
date
Thu, 18 Apr 2013 20:00:14 -0700
changeset 1706
95d29b99e5b3
parent 1691
f10cffab99b4
child 1902
fae8f309ff80
permissions
-rw-r--r--

8012656: cache frequently used name strings for DocImpl classes
Reviewed-by: darcy

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

mercurial