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

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

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

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

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

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

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