mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-18 01:54:47 +02:00
1258 lines
41 KiB
Java
1258 lines
41 KiB
Java
/*
|
|
* Copyright 1997-2008 Sun Microsystems, Inc. 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. Sun designates this
|
|
* particular file as subject to the "Classpath" exception as provided
|
|
* by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
|
* CA 95054 USA or visit www.sun.com if you need additional information or
|
|
* have any questions.
|
|
*/
|
|
|
|
package com.sun.tools.javadoc;
|
|
|
|
import com.sun.javadoc.*;
|
|
|
|
import static com.sun.javadoc.LanguageVersion.*;
|
|
|
|
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.Position;
|
|
|
|
import com.sun.tools.javac.code.Flags;
|
|
import com.sun.tools.javac.code.Kinds;
|
|
import com.sun.tools.javac.code.TypeTags;
|
|
import com.sun.tools.javac.code.Type;
|
|
import com.sun.tools.javac.code.Type.ClassType;
|
|
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.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.JCTree.JCClassDecl;
|
|
import com.sun.tools.javac.tree.TreeInfo;
|
|
|
|
import com.sun.tools.javac.util.Names;
|
|
import static com.sun.tools.javac.code.Flags.*;
|
|
import static com.sun.tools.javac.code.Kinds.*;
|
|
|
|
import java.io.File;
|
|
import java.util.Set;
|
|
import java.util.HashSet;
|
|
import java.lang.reflect.Modifier;
|
|
|
|
/**
|
|
* 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).
|
|
*
|
|
* @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, null, null);
|
|
}
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
public ClassDocImpl(DocEnv env, ClassSymbol sym, String documentation,
|
|
JCClassDecl tree, Position.LineMap lineMap) {
|
|
super(env, sym, documentation, tree, lineMap);
|
|
this.type = (ClassType)sym.type;
|
|
this.tsym = sym;
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
public boolean isClass() {
|
|
return !Modifier.isInterface(getModifiers());
|
|
}
|
|
|
|
/**
|
|
* Return true if this is a ordinary class,
|
|
* not an enumeration, exception, an error, or an interface.
|
|
*/
|
|
public boolean isOrdinaryClass() {
|
|
if (isEnum() || isInterface() || isAnnotationType()) {
|
|
return false;
|
|
}
|
|
for (Type t = type; t.tag == TypeTags.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.)
|
|
*/
|
|
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.
|
|
*/
|
|
public boolean isInterface() {
|
|
return Modifier.isInterface(getModifiers());
|
|
}
|
|
|
|
/**
|
|
* Return true if this is an exception class
|
|
*/
|
|
public boolean isException() {
|
|
if (isEnum() || isInterface() || isAnnotationType()) {
|
|
return false;
|
|
}
|
|
for (Type t = type; t.tag == TypeTags.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
|
|
*/
|
|
public boolean isError() {
|
|
if (isEnum() || isInterface() || isAnnotationType()) {
|
|
return false;
|
|
}
|
|
for (Type t = type; t.tag == TypeTags.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.tag == TypeTags.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.
|
|
*/
|
|
public PackageDoc containingPackage() {
|
|
PackageDocImpl p = env.getPackageDoc(tsym.packge());
|
|
SourcePosition po = position();
|
|
if (po != null && p.setDocPath == false && p.zipDocPath == null) {
|
|
//Set the package path if possible
|
|
File packageDir = po.file().getParentFile();
|
|
if (packageDir != null
|
|
&& (new File(packageDir, "package.html")).exists()) {
|
|
p.setDocPath(packageDir.getPath());
|
|
} else {
|
|
p.setDocPath(null);
|
|
}
|
|
}
|
|
return p;
|
|
}
|
|
|
|
/**
|
|
* Return the class name without package qualifier - but with
|
|
* enclosing class qualifier - as a String.
|
|
* <pre>
|
|
* Examples:
|
|
* for java.util.Hashtable
|
|
* return Hashtable
|
|
* for java.util.Map.Entry
|
|
* return Map.Entry
|
|
* </pre>
|
|
*/
|
|
public String name() {
|
|
return getClassName(tsym, false);
|
|
}
|
|
|
|
/**
|
|
* Return the qualified class name as a String.
|
|
* <pre>
|
|
* Example:
|
|
* for java.util.Hashtable
|
|
* return java.util.Hashtable
|
|
* if no qualifier, just return flat name
|
|
* </pre>
|
|
*/
|
|
public String qualifiedName() {
|
|
return getClassName(tsym, true);
|
|
}
|
|
|
|
/**
|
|
* Return unqualified name of type excluding any dimension information.
|
|
* <p>
|
|
* For example, a two dimensional array of String returns 'String'.
|
|
*/
|
|
public String typeName() {
|
|
return name();
|
|
}
|
|
|
|
/**
|
|
* Return qualified name of type excluding any dimension information.
|
|
*<p>
|
|
* 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() {
|
|
return tsym.name.toString();
|
|
}
|
|
|
|
/**
|
|
* Return the qualified name and any type parameters.
|
|
* Each parameter is a type variable with optional bounds.
|
|
*/
|
|
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) {
|
|
StringBuffer s = new StringBuffer();
|
|
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
|
|
*/
|
|
public String modifiers() {
|
|
return Modifier.toString(modifierSpecifier());
|
|
}
|
|
|
|
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
|
|
* <code>ClassDoc</code> or a <code>ParameterizedType</code>.
|
|
*/
|
|
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 != type) ? sup : env.syms.objectType);
|
|
}
|
|
|
|
/**
|
|
* 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<ClassDocImpl> ta = new ListBuffer<ClassDocImpl>();
|
|
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<FieldDocImpl> 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<MethodDocImpl> 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) {
|
|
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<ConstructorDocImpl> 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<ClassDocImpl> 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<ClassDocImpl> 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<ClassDocImpl> innerClasses = new ListBuffer<ClassDocImpl>();
|
|
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?
|
|
ClassDoc innerClasses[] = innerClasses();
|
|
for (int i = 0; i < innerClasses.length; i++) {
|
|
if (innerClasses[i].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.
|
|
innerClasses[i].name().endsWith(className)) {
|
|
return innerClasses[i];
|
|
} else {
|
|
ClassDoc innercd = ((ClassDocImpl) innerClasses[i]).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<AttrContext> 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<Type> 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 paramTypeArray 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<ClassDocImpl>());
|
|
}
|
|
|
|
private MethodDocImpl searchMethod(String methodName,
|
|
String[] paramTypes, Set<ClassDocImpl> searched) {
|
|
//### Note that this search is not necessarily what the compiler would do!
|
|
|
|
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
|
|
Names names = tsym.name.table.names;
|
|
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 paramTypeArray 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("<init>")); 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<ClassDocImpl>());
|
|
}
|
|
|
|
private FieldDocImpl searchField(String fieldName, Set<ClassDocImpl> 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<ClassDocImpl> importedClasses = new ListBuffer<ClassDocImpl>();
|
|
|
|
Env<AttrContext> 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.getTag() == JCTree.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<PackageDocImpl> importedPackages = new ListBuffer<PackageDocImpl>();
|
|
|
|
//### 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<AttrContext> compenv = env.enter.getEnv(tsym);
|
|
if (compenv == null) return new PackageDocImpl[0];
|
|
|
|
for (JCTree t : compenv.toplevel.defs) {
|
|
if (t.getTag() == JCTree.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;
|
|
}
|
|
|
|
/**
|
|
* 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 <code>java.io.Serializable</code>.
|
|
*
|
|
* Since <code>java.io.Externalizable</code> extends
|
|
* <code>java.io.Serializable</code>,
|
|
* 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
|
|
* <code>java.io.Externalizable</code>.
|
|
*/
|
|
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 <code>MethodDocImpl</code> 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.<p>
|
|
*
|
|
* Return either a list of default fields documented by
|
|
* <code>serial</code> tag<br>
|
|
* or return a single <code>FieldDoc</code> for
|
|
* <code>serialPersistentField</code> member.
|
|
* There should be a <code>serialField</code> tag for
|
|
* each Serializable field defined by an <code>ObjectStreamField</code>
|
|
* array component of <code>serialPersistentField</code>.
|
|
*
|
|
* @returns an array of <code>FieldDoc</code> 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 <code>serialPersistentFields</code>.
|
|
*
|
|
* @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.
|
|
* <p>
|
|
* 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.
|
|
*/
|
|
public SourcePosition position() {
|
|
if (tsym.sourcefile == null) return null;
|
|
return SourcePositionImpl.make(tsym.sourcefile.toString(),
|
|
(tree==null) ? Position.NOPOS : tree.pos,
|
|
lineMap);
|
|
}
|
|
}
|