diff -r 000000000000 -r 959103a6100f src/share/classes/com/sun/tools/javadoc/ClassDocImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/com/sun/tools/javadoc/ClassDocImpl.java Wed Apr 27 01:34:52 2016 +0800 @@ -0,0 +1,1338 @@ +/* + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.tools.javadoc; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Modifier; +import java.net.URI; +import java.util.HashSet; +import java.util.Set; + +import javax.tools.FileObject; +import javax.tools.JavaFileManager.Location; +import javax.tools.StandardJavaFileManager; +import javax.tools.StandardLocation; + +import com.sun.javadoc.*; +import com.sun.source.util.TreePath; +import com.sun.tools.javac.code.Flags; +import com.sun.tools.javac.code.Kinds; +import com.sun.tools.javac.code.Scope; +import com.sun.tools.javac.code.Symbol; +import com.sun.tools.javac.code.Symbol.*; +import com.sun.tools.javac.code.Type; +import com.sun.tools.javac.code.Type.ClassType; +import com.sun.tools.javac.code.TypeTag; +import com.sun.tools.javac.comp.AttrContext; +import com.sun.tools.javac.comp.Env; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.JCFieldAccess; +import com.sun.tools.javac.tree.JCTree.JCImport; +import com.sun.tools.javac.tree.TreeInfo; +import com.sun.tools.javac.util.List; +import com.sun.tools.javac.util.ListBuffer; +import com.sun.tools.javac.util.Name; +import com.sun.tools.javac.util.Names; +import com.sun.tools.javac.util.Position; +import static com.sun.tools.javac.code.Kinds.*; +import static com.sun.tools.javac.code.TypeTag.CLASS; +import static com.sun.tools.javac.tree.JCTree.Tag.*; + +/** + * Represents a java class and provides access to information + * about the class, the class' comment and tags, and the + * members of the class. A ClassDocImpl only exists if it was + * processed in this run of javadoc. References to classes + * which may or may not have been processed in this run are + * referred to using Type (which can be converted to ClassDocImpl, + * if possible). + * + *

This is NOT part of any supported API. + * If you write code that depends on this, you do so at your own risk. + * This code and its internal interfaces are subject to change or + * deletion without notice. + * + * @see Type + * + * @since 1.2 + * @author Robert Field + * @author Neal Gafter (rewrite) + * @author Scott Seligman (generics, enums, annotations) + */ + +public class ClassDocImpl extends ProgramElementDocImpl implements ClassDoc { + + public final ClassType type; // protected->public for debugging + protected final ClassSymbol tsym; + + boolean isIncluded = false; // Set in RootDocImpl + + private SerializedForm serializedForm; + + /** + * Constructor + */ + public ClassDocImpl(DocEnv env, ClassSymbol sym) { + this(env, sym, null); + } + + /** + * Constructor + */ + public ClassDocImpl(DocEnv env, ClassSymbol sym, TreePath treePath) { + super(env, sym, treePath); + this.type = (ClassType)sym.type; + this.tsym = sym; + } + + public com.sun.javadoc.Type getElementType() { + return null; + } + + /** + * Returns the flags in terms of javac's flags + */ + protected long getFlags() { + return getFlags(tsym); + } + + /** + * Returns the flags of a ClassSymbol in terms of javac's flags + */ + static long getFlags(ClassSymbol clazz) { + while (true) { + try { + return clazz.flags(); + } catch (CompletionFailure ex) { + /* Quietly ignore completion failures. + * Note that a CompletionFailure can only + * occur as a result of calling complete(), + * which will always remove the current + * completer, leaving it to be null or + * follow-up completer. Thus the loop + * is guaranteed to eventually terminate. + */ + } + } + } + + /** + * Is a ClassSymbol an annotation type? + */ + static boolean isAnnotationType(ClassSymbol clazz) { + return (getFlags(clazz) & Flags.ANNOTATION) != 0; + } + + /** + * Identify the containing class + */ + protected ClassSymbol getContainingClass() { + return tsym.owner.enclClass(); + } + + /** + * Return true if this is a class, not an interface. + */ + @Override + public boolean isClass() { + return !Modifier.isInterface(getModifiers()); + } + + /** + * Return true if this is a ordinary class, + * not an enumeration, exception, an error, or an interface. + */ + @Override + public boolean isOrdinaryClass() { + if (isEnum() || isInterface() || isAnnotationType()) { + return false; + } + for (Type t = type; t.hasTag(CLASS); t = env.types.supertype(t)) { + if (t.tsym == env.syms.errorType.tsym || + t.tsym == env.syms.exceptionType.tsym) { + return false; + } + } + return true; + } + + /** + * Return true if this is an enumeration. + * (For legacy doclets, return false.) + */ + @Override + public boolean isEnum() { + return (getFlags() & Flags.ENUM) != 0 + && + !env.legacyDoclet; + } + + /** + * Return true if this is an interface, but not an annotation type. + * Overridden by AnnotationTypeDocImpl. + */ + @Override + public boolean isInterface() { + return Modifier.isInterface(getModifiers()); + } + + /** + * Return true if this is an exception class + */ + @Override + public boolean isException() { + if (isEnum() || isInterface() || isAnnotationType()) { + return false; + } + for (Type t = type; t.hasTag(CLASS); t = env.types.supertype(t)) { + if (t.tsym == env.syms.exceptionType.tsym) { + return true; + } + } + return false; + } + + /** + * Return true if this is an error class + */ + @Override + public boolean isError() { + if (isEnum() || isInterface() || isAnnotationType()) { + return false; + } + for (Type t = type; t.hasTag(CLASS); t = env.types.supertype(t)) { + if (t.tsym == env.syms.errorType.tsym) { + return true; + } + } + return false; + } + + /** + * Return true if this is a throwable class + */ + public boolean isThrowable() { + if (isEnum() || isInterface() || isAnnotationType()) { + return false; + } + for (Type t = type; t.hasTag(CLASS); t = env.types.supertype(t)) { + if (t.tsym == env.syms.throwableType.tsym) { + return true; + } + } + return false; + } + + /** + * Return true if this class is abstract + */ + public boolean isAbstract() { + return Modifier.isAbstract(getModifiers()); + } + + /** + * Returns true if this class was synthesized by the compiler. + */ + public boolean isSynthetic() { + return (getFlags() & Flags.SYNTHETIC) != 0; + } + + /** + * Return true if this class is included in the active set. + * A ClassDoc is included iff either it is specified on the + * commandline, or if it's containing package is specified + * on the command line, or if it is a member class of an + * included class. + */ + + public boolean isIncluded() { + if (isIncluded) { + return true; + } + if (env.shouldDocument(tsym)) { + // Class is nameable from top-level and + // the class and all enclosing classes + // pass the modifier filter. + if (containingPackage().isIncluded()) { + return isIncluded=true; + } + ClassDoc outer = containingClass(); + if (outer != null && outer.isIncluded()) { + return isIncluded=true; + } + } + return false; + } + + /** + * Return the package that this class is contained in. + */ + @Override + public PackageDoc containingPackage() { + PackageDocImpl p = env.getPackageDoc(tsym.packge()); + if (p.setDocPath == false) { + FileObject docPath; + try { + Location location = env.fileManager.hasLocation(StandardLocation.SOURCE_PATH) + ? StandardLocation.SOURCE_PATH : StandardLocation.CLASS_PATH; + + docPath = env.fileManager.getFileForInput( + location, p.qualifiedName(), "package.html"); + } catch (IOException e) { + docPath = null; + } + + if (docPath == null) { + // fall back on older semantics of looking in same directory as + // source file for this class + SourcePosition po = position(); + if (env.fileManager instanceof StandardJavaFileManager && + po instanceof SourcePositionImpl) { + URI uri = ((SourcePositionImpl) po).filename.toUri(); + if ("file".equals(uri.getScheme())) { + File f = new File(uri); + File dir = f.getParentFile(); + if (dir != null) { + File pf = new File(dir, "package.html"); + if (pf.exists()) { + StandardJavaFileManager sfm = (StandardJavaFileManager) env.fileManager; + docPath = sfm.getJavaFileObjects(pf).iterator().next(); + } + } + + } + } + } + + p.setDocPath(docPath); + } + return p; + } + + /** + * Return the class name without package qualifier - but with + * enclosing class qualifier - as a String. + *

+     * Examples:
+     *  for java.util.Hashtable
+     *  return Hashtable
+     *  for java.util.Map.Entry
+     *  return Map.Entry
+     * 
+ */ + public String name() { + if (name == null) { + name = getClassName(tsym, false); + } + return name; + } + + private String name; + + /** + * Return the qualified class name as a String. + *
+     * Example:
+     *  for java.util.Hashtable
+     *  return java.util.Hashtable
+     *  if no qualifier, just return flat name
+     * 
+ */ + public String qualifiedName() { + if (qualifiedName == null) { + qualifiedName = getClassName(tsym, true); + } + return qualifiedName; + } + + private String qualifiedName; + + /** + * Return unqualified name of type excluding any dimension information. + *

+ * For example, a two dimensional array of String returns 'String'. + */ + public String typeName() { + return name(); + } + + /** + * Return qualified name of type excluding any dimension information. + *

+ * For example, a two dimensional array of String + * returns 'java.lang.String'. + */ + public String qualifiedTypeName() { + return qualifiedName(); + } + + /** + * Return the simple name of this type. + */ + public String simpleTypeName() { + if (simpleTypeName == null) { + simpleTypeName = tsym.name.toString(); + } + return simpleTypeName; + } + + private String simpleTypeName; + + /** + * Return the qualified name and any type parameters. + * Each parameter is a type variable with optional bounds. + */ + @Override + public String toString() { + return classToString(env, tsym, true); + } + + /** + * Return the class name as a string. If "full" is true the name is + * qualified, otherwise it is qualified by its enclosing class(es) only. + */ + static String getClassName(ClassSymbol c, boolean full) { + if (full) { + return c.getQualifiedName().toString(); + } else { + String n = ""; + for ( ; c != null; c = c.owner.enclClass()) { + n = c.name + (n.equals("") ? "" : ".") + n; + } + return n; + } + } + + /** + * Return the class name with any type parameters as a string. + * Each parameter is a type variable with optional bounds. + * If "full" is true all names are qualified, otherwise they are + * qualified by their enclosing class(es) only. + */ + static String classToString(DocEnv env, ClassSymbol c, boolean full) { + StringBuilder s = new StringBuilder(); + if (!c.isInner()) { // if c is not an inner class + s.append(getClassName(c, full)); + } else { + // c is an inner class, so include type params of outer. + ClassSymbol encl = c.owner.enclClass(); + s.append(classToString(env, encl, full)) + .append('.') + .append(c.name); + } + s.append(TypeMaker.typeParametersString(env, c, full)); + return s.toString(); + } + + /** + * Is this class (or any enclosing class) generic? That is, does + * it have type parameters? + */ + static boolean isGeneric(ClassSymbol c) { + return c.type.allparams().nonEmpty(); + } + + /** + * Return the formal type parameters of this class or interface. + * Return an empty array if there are none. + */ + public TypeVariable[] typeParameters() { + if (env.legacyDoclet) { + return new TypeVariable[0]; + } + TypeVariable res[] = new TypeVariable[type.getTypeArguments().length()]; + TypeMaker.getTypes(env, type.getTypeArguments(), res); + return res; + } + + /** + * Return the type parameter tags of this class or interface. + */ + public ParamTag[] typeParamTags() { + return (env.legacyDoclet) + ? new ParamTag[0] + : comment().typeParamTags(); + } + + /** + * Return the modifier string for this class. If it's an interface + * exclude 'abstract' keyword from the modifier string + */ + @Override + public String modifiers() { + return Modifier.toString(modifierSpecifier()); + } + + @Override + public int modifierSpecifier() { + int modifiers = getModifiers(); + return (isInterface() || isAnnotationType()) + ? modifiers & ~Modifier.ABSTRACT + : modifiers; + } + + /** + * Return the superclass of this class + * + * @return the ClassDocImpl for the superclass of this class, null + * if there is no superclass. + */ + public ClassDoc superclass() { + if (isInterface() || isAnnotationType()) return null; + if (tsym == env.syms.objectType.tsym) return null; + ClassSymbol c = (ClassSymbol)env.types.supertype(type).tsym; + if (c == null || c == tsym) c = (ClassSymbol)env.syms.objectType.tsym; + return env.getClassDoc(c); + } + + /** + * Return the superclass of this class. Return null if this is an + * interface. A superclass is represented by either a + * ClassDoc or a ParameterizedType. + */ + public com.sun.javadoc.Type superclassType() { + if (isInterface() || isAnnotationType() || + (tsym == env.syms.objectType.tsym)) + return null; + Type sup = env.types.supertype(type); + return TypeMaker.getType(env, + (sup.hasTag(TypeTag.NONE)) ? env.syms.objectType : sup); + } + + /** + * Test whether this class is a subclass of the specified class. + * + * @param cd the candidate superclass. + * @return true if cd is a superclass of this class. + */ + public boolean subclassOf(ClassDoc cd) { + return tsym.isSubClass(((ClassDocImpl)cd).tsym, env.types); + } + + /** + * Return interfaces implemented by this class or interfaces + * extended by this interface. + * + * @return An array of ClassDocImpl representing the interfaces. + * Return an empty array if there are no interfaces. + */ + public ClassDoc[] interfaces() { + ListBuffer ta = new ListBuffer(); + for (Type t : env.types.interfaces(type)) { + ta.append(env.getClassDoc((ClassSymbol)t.tsym)); + } + //### Cache ta here? + return ta.toArray(new ClassDocImpl[ta.length()]); + } + + /** + * Return interfaces implemented by this class or interfaces extended + * by this interface. Includes only directly-declared interfaces, not + * inherited interfaces. + * Return an empty array if there are no interfaces. + */ + public com.sun.javadoc.Type[] interfaceTypes() { + //### Cache result here? + return TypeMaker.getTypes(env, env.types.interfaces(type)); + } + + /** + * Return fields in class. + * @param filter include only the included fields if filter==true + */ + public FieldDoc[] fields(boolean filter) { + return fields(filter, false); + } + + /** + * Return included fields in class. + */ + public FieldDoc[] fields() { + return fields(true, false); + } + + /** + * Return the enum constants if this is an enum type. + */ + public FieldDoc[] enumConstants() { + return fields(false, true); + } + + /** + * Return fields in class. + * @param filter if true, return only the included fields + * @param enumConstants if true, return the enum constants instead + */ + private FieldDoc[] fields(boolean filter, boolean enumConstants) { + List fields = List.nil(); + for (Scope.Entry e = tsym.members().elems; e != null; e = e.sibling) { + if (e.sym != null && e.sym.kind == VAR) { + VarSymbol s = (VarSymbol)e.sym; + boolean isEnum = ((s.flags() & Flags.ENUM) != 0) && + !env.legacyDoclet; + if (isEnum == enumConstants && + (!filter || env.shouldDocument(s))) { + fields = fields.prepend(env.getFieldDoc(s)); + } + } + } + return fields.toArray(new FieldDocImpl[fields.length()]); + } + + /** + * Return methods in class. + * This method is overridden by AnnotationTypeDocImpl. + * + * @param filter include only the included methods if filter==true + * @return an array of MethodDocImpl for representing the visible + * methods in this class. Does not include constructors. + */ + public MethodDoc[] methods(boolean filter) { + Names names = tsym.name.table.names; + List methods = List.nil(); + for (Scope.Entry e = tsym.members().elems; e != null; e = e.sibling) { + if (e.sym != null + && e.sym.kind == Kinds.MTH + && e.sym.name != names.init + && e.sym.name != names.clinit) { + MethodSymbol s = (MethodSymbol)e.sym; + if (!filter || env.shouldDocument(s)) { + methods = methods.prepend(env.getMethodDoc(s)); + } + } + } + //### Cache methods here? + return methods.toArray(new MethodDocImpl[methods.length()]); + } + + /** + * Return included methods in class. + * + * @return an array of MethodDocImpl for representing the visible + * methods in this class. Does not include constructors. + */ + public MethodDoc[] methods() { + return methods(true); + } + + /** + * Return constructors in class. + * + * @param filter include only the included constructors if filter==true + * @return an array of ConstructorDocImpl for representing the visible + * constructors in this class. + */ + public ConstructorDoc[] constructors(boolean filter) { + Names names = tsym.name.table.names; + List constructors = List.nil(); + for (Scope.Entry e = tsym.members().elems; e != null; e = e.sibling) { + if (e.sym != null && + e.sym.kind == Kinds.MTH && e.sym.name == names.init) { + MethodSymbol s = (MethodSymbol)e.sym; + if (!filter || env.shouldDocument(s)) { + constructors = constructors.prepend(env.getConstructorDoc(s)); + } + } + } + //### Cache constructors here? + return constructors.toArray(new ConstructorDocImpl[constructors.length()]); + } + + /** + * Return included constructors in class. + * + * @return an array of ConstructorDocImpl for representing the visible + * constructors in this class. + */ + public ConstructorDoc[] constructors() { + return constructors(true); + } + + /** + * Adds all inner classes of this class, and their + * inner classes recursively, to the list l. + */ + void addAllClasses(ListBuffer l, boolean filtered) { + try { + if (isSynthetic()) return; + // sometimes synthetic classes are not marked synthetic + if (!JavadocTool.isValidClassName(tsym.name.toString())) return; + if (filtered && !env.shouldDocument(tsym)) return; + if (l.contains(this)) return; + l.append(this); + List more = List.nil(); + for (Scope.Entry e = tsym.members().elems; e != null; + e = e.sibling) { + if (e.sym != null && e.sym.kind == Kinds.TYP) { + ClassSymbol s = (ClassSymbol)e.sym; + ClassDocImpl c = env.getClassDoc(s); + if (c.isSynthetic()) continue; + if (c != null) more = more.prepend(c); + } + } + // this extra step preserves the ordering from oldjavadoc + for (; more.nonEmpty(); more=more.tail) { + more.head.addAllClasses(l, filtered); + } + } catch (CompletionFailure e) { + // quietly ignore completion failures + } + } + + /** + * Return inner classes within this class. + * + * @param filter include only the included inner classes if filter==true. + * @return an array of ClassDocImpl for representing the visible + * classes defined in this class. Anonymous and local classes + * are not included. + */ + public ClassDoc[] innerClasses(boolean filter) { + ListBuffer innerClasses = new ListBuffer(); + for (Scope.Entry e = tsym.members().elems; e != null; e = e.sibling) { + if (e.sym != null && e.sym.kind == Kinds.TYP) { + ClassSymbol s = (ClassSymbol)e.sym; + if ((s.flags_field & Flags.SYNTHETIC) != 0) continue; + if (!filter || env.isVisible(s)) { + innerClasses.prepend(env.getClassDoc(s)); + } + } + } + //### Cache classes here? + return innerClasses.toArray(new ClassDocImpl[innerClasses.length()]); + } + + /** + * Return included inner classes within this class. + * + * @return an array of ClassDocImpl for representing the visible + * classes defined in this class. Anonymous and local classes + * are not included. + */ + public ClassDoc[] innerClasses() { + return innerClasses(true); + } + + /** + * Find a class within the context of this class. + * Search order: qualified name, in this class (inner), + * in this package, in the class imports, in the package + * imports. + * Return the ClassDocImpl if found, null if not found. + */ + //### The specified search order is not the normal rule the + //### compiler would use. Leave as specified or change it? + public ClassDoc findClass(String className) { + ClassDoc searchResult = searchClass(className); + if (searchResult == null) { + ClassDocImpl enclosingClass = (ClassDocImpl)containingClass(); + //Expand search space to include enclosing class. + while (enclosingClass != null && enclosingClass.containingClass() != null) { + enclosingClass = (ClassDocImpl)enclosingClass.containingClass(); + } + searchResult = enclosingClass == null ? + null : enclosingClass.searchClass(className); + } + return searchResult; + } + + private ClassDoc searchClass(String className) { + Names names = tsym.name.table.names; + + // search by qualified name first + ClassDoc cd = env.lookupClass(className); + if (cd != null) { + return cd; + } + + // search inner classes + //### Add private entry point to avoid creating array? + //### Replicate code in innerClasses here to avoid consing? + for (ClassDoc icd : innerClasses()) { + if (icd.name().equals(className) || + //### This is from original javadoc but it looks suspicious to me... + //### I believe it is attempting to compensate for the confused + //### convention of including the nested class qualifiers in the + //### 'name' of the inner class, rather than the true simple name. + icd.name().endsWith("." + className)) { + return icd; + } else { + ClassDoc innercd = ((ClassDocImpl) icd).searchClass(className); + if (innercd != null) { + return innercd; + } + } + } + + // check in this package + cd = containingPackage().findClass(className); + if (cd != null) { + return cd; + } + + // make sure that this symbol has been completed + if (tsym.completer != null) { + tsym.complete(); + } + + // search imports + + if (tsym.sourcefile != null) { + + //### This information is available only for source classes. + + Env compenv = env.enter.getEnv(tsym); + if (compenv == null) return null; + + Scope s = compenv.toplevel.namedImportScope; + for (Scope.Entry e = s.lookup(names.fromString(className)); e.scope != null; e = e.next()) { + if (e.sym.kind == Kinds.TYP) { + ClassDoc c = env.getClassDoc((ClassSymbol)e.sym); + return c; + } + } + + s = compenv.toplevel.starImportScope; + for (Scope.Entry e = s.lookup(names.fromString(className)); e.scope != null; e = e.next()) { + if (e.sym.kind == Kinds.TYP) { + ClassDoc c = env.getClassDoc((ClassSymbol)e.sym); + return c; + } + } + } + + return null; // not found + } + + + private boolean hasParameterTypes(MethodSymbol method, String[] argTypes) { + + if (argTypes == null) { + // wildcard + return true; + } + + int i = 0; + List types = method.type.getParameterTypes(); + + if (argTypes.length != types.length()) { + return false; + } + + for (Type t : types) { + String argType = argTypes[i++]; + // For vararg method, "T..." matches type T[]. + if (i == argTypes.length) { + argType = argType.replace("...", "[]"); + } + if (!hasTypeName(env.types.erasure(t), argType)) { //###(gj) + return false; + } + } + return true; + } + // where + private boolean hasTypeName(Type t, String name) { + return + name.equals(TypeMaker.getTypeName(t, true)) + || + name.equals(TypeMaker.getTypeName(t, false)) + || + (qualifiedName() + "." + name).equals(TypeMaker.getTypeName(t, true)); + } + + + + /** + * Find a method in this class scope. + * Search order: this class, interfaces, superclasses, outerclasses. + * Note that this is not necessarily what the compiler would do! + * + * @param methodName the unqualified name to search for. + * @param paramTypes the array of Strings for method parameter types. + * @return the first MethodDocImpl which matches, null if not found. + */ + public MethodDocImpl findMethod(String methodName, String[] paramTypes) { + // Use hash table 'searched' to avoid searching same class twice. + //### It is not clear how this could happen. + return searchMethod(methodName, paramTypes, new HashSet()); + } + + private MethodDocImpl searchMethod(String methodName, + String[] paramTypes, Set searched) { + //### Note that this search is not necessarily what the compiler would do! + + Names names = tsym.name.table.names; + // do not match constructors + if (names.init.contentEquals(methodName)) { + return null; + } + + ClassDocImpl cdi; + MethodDocImpl mdi; + + if (searched.contains(this)) { + return null; + } + searched.add(this); + + //DEBUG + /*---------------------------------* + System.out.print("searching " + this + " for " + methodName); + if (paramTypes == null) { + System.out.println("()"); + } else { + System.out.print("("); + for (int k=0; k < paramTypes.length; k++) { + System.out.print(paramTypes[k]); + if ((k + 1) < paramTypes.length) { + System.out.print(", "); + } + } + System.out.println(")"); + } + *---------------------------------*/ + + // search current class + Scope.Entry e = tsym.members().lookup(names.fromString(methodName)); + + //### Using modifier filter here isn't really correct, + //### but emulates the old behavior. Instead, we should + //### apply the normal rules of visibility and inheritance. + + if (paramTypes == null) { + // If no parameters specified, we are allowed to return + // any method with a matching name. In practice, the old + // code returned the first method, which is now the last! + // In order to provide textually identical results, we + // attempt to emulate the old behavior. + MethodSymbol lastFound = null; + for (; e.scope != null; e = e.next()) { + if (e.sym.kind == Kinds.MTH) { + //### Should intern methodName as Name. + if (e.sym.name.toString().equals(methodName)) { + lastFound = (MethodSymbol)e.sym; + } + } + } + if (lastFound != null) { + return env.getMethodDoc(lastFound); + } + } else { + for (; e.scope != null; e = e.next()) { + if (e.sym != null && + e.sym.kind == Kinds.MTH) { + //### Should intern methodName as Name. + if (hasParameterTypes((MethodSymbol)e.sym, paramTypes)) { + return env.getMethodDoc((MethodSymbol)e.sym); + } + } + } + } + + //### If we found a MethodDoc above, but which did not pass + //### the modifier filter, we should return failure here! + + // search superclass + cdi = (ClassDocImpl)superclass(); + if (cdi != null) { + mdi = cdi.searchMethod(methodName, paramTypes, searched); + if (mdi != null) { + return mdi; + } + } + + // search interfaces + ClassDoc intf[] = interfaces(); + for (int i = 0; i < intf.length; i++) { + cdi = (ClassDocImpl)intf[i]; + mdi = cdi.searchMethod(methodName, paramTypes, searched); + if (mdi != null) { + return mdi; + } + } + + // search enclosing class + cdi = (ClassDocImpl)containingClass(); + if (cdi != null) { + mdi = cdi.searchMethod(methodName, paramTypes, searched); + if (mdi != null) { + return mdi; + } + } + + //###(gj) As a temporary measure until type variables are better + //### handled, try again without the parameter types. + //### This should most often find the right method, and occassionally + //### find the wrong one. + //if (paramTypes != null) { + // return findMethod(methodName, null); + //} + + return null; + } + + /** + * Find constructor in this class. + * + * @param constrName the unqualified name to search for. + * @param paramTypes the array of Strings for constructor parameters. + * @return the first ConstructorDocImpl which matches, null if not found. + */ + public ConstructorDoc findConstructor(String constrName, + String[] paramTypes) { + Names names = tsym.name.table.names; + for (Scope.Entry e = tsym.members().lookup(names.fromString("")); e.scope != null; e = e.next()) { + if (e.sym.kind == Kinds.MTH) { + if (hasParameterTypes((MethodSymbol)e.sym, paramTypes)) { + return env.getConstructorDoc((MethodSymbol)e.sym); + } + } + } + + //###(gj) As a temporary measure until type variables are better + //### handled, try again without the parameter types. + //### This will often find the right constructor, and occassionally + //### find the wrong one. + //if (paramTypes != null) { + // return findConstructor(constrName, null); + //} + + return null; + } + + /** + * Find a field in this class scope. + * Search order: this class, outerclasses, interfaces, + * superclasses. IMP: If see tag is defined in an inner class, + * which extends a super class and if outerclass and the super + * class have a visible field in common then Java compiler cribs + * about the ambiguity, but the following code will search in the + * above given search order. + * + * @param fieldName the unqualified name to search for. + * @return the first FieldDocImpl which matches, null if not found. + */ + public FieldDoc findField(String fieldName) { + return searchField(fieldName, new HashSet()); + } + + private FieldDocImpl searchField(String fieldName, Set searched) { + Names names = tsym.name.table.names; + if (searched.contains(this)) { + return null; + } + searched.add(this); + + for (Scope.Entry e = tsym.members().lookup(names.fromString(fieldName)); e.scope != null; e = e.next()) { + if (e.sym.kind == Kinds.VAR) { + //### Should intern fieldName as Name. + return env.getFieldDoc((VarSymbol)e.sym); + } + } + + //### If we found a FieldDoc above, but which did not pass + //### the modifier filter, we should return failure here! + + ClassDocImpl cdi = (ClassDocImpl)containingClass(); + if (cdi != null) { + FieldDocImpl fdi = cdi.searchField(fieldName, searched); + if (fdi != null) { + return fdi; + } + } + + // search superclass + cdi = (ClassDocImpl)superclass(); + if (cdi != null) { + FieldDocImpl fdi = cdi.searchField(fieldName, searched); + if (fdi != null) { + return fdi; + } + } + + // search interfaces + ClassDoc intf[] = interfaces(); + for (int i = 0; i < intf.length; i++) { + cdi = (ClassDocImpl)intf[i]; + FieldDocImpl fdi = cdi.searchField(fieldName, searched); + if (fdi != null) { + return fdi; + } + } + + return null; + } + + /** + * Get the list of classes declared as imported. + * These are called "single-type-import declarations" in the JLS. + * This method is deprecated in the ClassDoc interface. + * + * @return an array of ClassDocImpl representing the imported classes. + * + * @deprecated Import declarations are implementation details that + * should not be exposed here. In addition, not all imported + * classes are imported through single-type-import declarations. + */ + @Deprecated + public ClassDoc[] importedClasses() { + // information is not available for binary classfiles + if (tsym.sourcefile == null) return new ClassDoc[0]; + + ListBuffer importedClasses = new ListBuffer(); + + Env compenv = env.enter.getEnv(tsym); + if (compenv == null) return new ClassDocImpl[0]; + + Name asterisk = tsym.name.table.names.asterisk; + for (JCTree t : compenv.toplevel.defs) { + if (t.hasTag(IMPORT)) { + JCTree imp = ((JCImport) t).qualid; + if ((TreeInfo.name(imp) != asterisk) && + (imp.type.tsym.kind & Kinds.TYP) != 0) { + importedClasses.append( + env.getClassDoc((ClassSymbol)imp.type.tsym)); + } + } + } + + return importedClasses.toArray(new ClassDocImpl[importedClasses.length()]); + } + + /** + * Get the list of packages declared as imported. + * These are called "type-import-on-demand declarations" in the JLS. + * This method is deprecated in the ClassDoc interface. + * + * @return an array of PackageDocImpl representing the imported packages. + * + * ###NOTE: the syntax supports importing all inner classes from a class as well. + * @deprecated Import declarations are implementation details that + * should not be exposed here. In addition, this method's + * return type does not allow for all type-import-on-demand + * declarations to be returned. + */ + @Deprecated + public PackageDoc[] importedPackages() { + // information is not available for binary classfiles + if (tsym.sourcefile == null) return new PackageDoc[0]; + + ListBuffer importedPackages = new ListBuffer(); + + //### Add the implicit "import java.lang.*" to the result + Names names = tsym.name.table.names; + importedPackages.append(env.getPackageDoc(env.reader.enterPackage(names.java_lang))); + + Env compenv = env.enter.getEnv(tsym); + if (compenv == null) return new PackageDocImpl[0]; + + for (JCTree t : compenv.toplevel.defs) { + if (t.hasTag(IMPORT)) { + JCTree imp = ((JCImport) t).qualid; + if (TreeInfo.name(imp) == names.asterisk) { + JCFieldAccess sel = (JCFieldAccess)imp; + Symbol s = sel.selected.type.tsym; + PackageDocImpl pdoc = env.getPackageDoc(s.packge()); + if (!importedPackages.contains(pdoc)) + importedPackages.append(pdoc); + } + } + } + + return importedPackages.toArray(new PackageDocImpl[importedPackages.length()]); + } + + /** + * Return the type's dimension information. + * Always return "", as this is not an array type. + */ + public String dimension() { + return ""; + } + + /** + * Return this type as a class, which it already is. + */ + public ClassDoc asClassDoc() { + return this; + } + + /** + * Return null (unless overridden), as this is not an annotation type. + */ + public AnnotationTypeDoc asAnnotationTypeDoc() { + return null; + } + + /** + * Return null, as this is not a class instantiation. + */ + public ParameterizedType asParameterizedType() { + return null; + } + + /** + * Return null, as this is not a type variable. + */ + public TypeVariable asTypeVariable() { + return null; + } + + /** + * Return null, as this is not a wildcard type. + */ + public WildcardType asWildcardType() { + return null; + } + + /** + * Returns null, as this is not an annotated type. + */ + public AnnotatedType asAnnotatedType() { + return null; + } + + /** + * Return false, as this is not a primitive type. + */ + public boolean isPrimitive() { + return false; + } + + //--- Serialization --- + + //### These methods ignore modifier filter. + + /** + * Return true if this class implements java.io.Serializable. + * + * Since java.io.Externalizable extends + * java.io.Serializable, + * Externalizable objects are also Serializable. + */ + public boolean isSerializable() { + try { + return env.types.isSubtype(type, env.syms.serializableType); + } catch (CompletionFailure ex) { + // quietly ignore completion failures + return false; + } + } + + /** + * Return true if this class implements + * java.io.Externalizable. + */ + public boolean isExternalizable() { + try { + return env.types.isSubtype(type, env.externalizableSym.type); + } catch (CompletionFailure ex) { + // quietly ignore completion failures + return false; + } + } + + /** + * Return the serialization methods for this class. + * + * @return an array of MethodDocImpl that represents + * the serialization methods for this class. + */ + public MethodDoc[] serializationMethods() { + if (serializedForm == null) { + serializedForm = new SerializedForm(env, tsym, this); + } + //### Clone this? + return serializedForm.methods(); + } + + /** + * Return the Serializable fields of class.

+ * + * Return either a list of default fields documented by + * serial tag
+ * or return a single FieldDoc for + * serialPersistentField member. + * There should be a serialField tag for + * each Serializable field defined by an ObjectStreamField + * array component of serialPersistentField. + * + * @returns an array of FieldDoc for the Serializable fields + * of this class. + * + * @see #definesSerializableFields() + * @see SerialFieldTagImpl + */ + public FieldDoc[] serializableFields() { + if (serializedForm == null) { + serializedForm = new SerializedForm(env, tsym, this); + } + //### Clone this? + return serializedForm.fields(); + } + + /** + * Return true if Serializable fields are explicitly defined with + * the special class member serialPersistentFields. + * + * @see #serializableFields() + * @see SerialFieldTagImpl + */ + public boolean definesSerializableFields() { + if (!isSerializable() || isExternalizable()) { + return false; + } else { + if (serializedForm == null) { + serializedForm = new SerializedForm(env, tsym, this); + } + //### Clone this? + return serializedForm.definesSerializableFields(); + } + } + + /** + * Determine if a class is a RuntimeException. + *

+ * Used only by ThrowsTagImpl. + */ + boolean isRuntimeException() { + return tsym.isSubClass(env.syms.runtimeExceptionType.tsym, env.types); + } + + /** + * Return the source position of the entity, or null if + * no position is available. + */ + @Override + public SourcePosition position() { + if (tsym.sourcefile == null) return null; + return SourcePositionImpl.make(tsym.sourcefile, + (tree==null) ? Position.NOPOS : tree.pos, + lineMap); + } +}