duke@1: /*
xdono@117: * Copyright 1997-2008 Sun Microsystems, Inc. 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
duke@1: * published by the Free Software Foundation. Sun designates this
duke@1: * particular file as subject to the "Classpath" exception as provided
duke@1: * by Sun 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: *
duke@1: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
duke@1: * CA 95054 USA or visit www.sun.com if you need additional information or
duke@1: * have any 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@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.*;
duke@1:
duke@1: import static com.sun.javadoc.LanguageVersion.*;
duke@1:
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;
jjg@197: import com.sun.tools.javac.code.TypeTags;
duke@1:
duke@1: import com.sun.tools.javac.comp.AttrContext;
duke@1: import com.sun.tools.javac.comp.Env;
duke@1:
duke@1: import com.sun.tools.javac.tree.JCTree;
jjg@197: import com.sun.tools.javac.tree.JCTree.JCClassDecl;
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;
duke@1:
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;
jjg@197:
duke@1: import static com.sun.tools.javac.code.Flags.*;
duke@1: import static com.sun.tools.javac.code.Kinds.*;
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: *
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) {
duke@1: this(env, sym, null, null, null);
duke@1: }
duke@1:
duke@1: /**
duke@1: * Constructor
duke@1: */
duke@1: public ClassDocImpl(DocEnv env, ClassSymbol sym, String documentation,
duke@1: JCClassDecl tree, Position.LineMap lineMap) {
duke@1: super(env, sym, documentation, tree, lineMap);
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: */
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: */
duke@1: public boolean isOrdinaryClass() {
duke@1: if (isEnum() || isInterface() || isAnnotationType()) {
duke@1: return false;
duke@1: }
duke@1: for (Type t = type; t.tag == TypeTags.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: */
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: */
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: */
duke@1: public boolean isException() {
duke@1: if (isEnum() || isInterface() || isAnnotationType()) {
duke@1: return false;
duke@1: }
duke@1: for (Type t = type; t.tag == TypeTags.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: */
duke@1: public boolean isError() {
duke@1: if (isEnum() || isInterface() || isAnnotationType()) {
duke@1: return false;
duke@1: }
duke@1: for (Type t = type; t.tag == TypeTags.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: }
duke@1: for (Type t = type; t.tag == TypeTags.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:
duke@1: /**
duke@1: * Return the package that this class is contained in.
duke@1: */
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@197: File f = new File(uri.getPath());
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: */
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) {
duke@1: StringBuffer s = new StringBuffer();
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: */
duke@1: public String modifiers() {
duke@1: return Modifier.toString(modifierSpecifier());
duke@1: }
duke@1:
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?
duke@1: ClassDoc innerClasses[] = innerClasses();
duke@1: for (int i = 0; i < innerClasses.length; i++) {
duke@1: if (innerClasses[i].name().equals(className) ||
duke@1: //### This is from original javadoc but it looks suspicious to me...
duke@1: //### I believe it is attempting to compensate for the confused
duke@1: //### convention of including the nested class qualifiers in the
duke@1: //### 'name' of the inner class, rather than the true simple name.
duke@1: innerClasses[i].name().endsWith(className)) {
duke@1: return innerClasses[i];
duke@1: } else {
duke@1: ClassDoc innercd = ((ClassDocImpl) innerClasses[i]).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.
duke@1: * @param paramTypeArray 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:
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
jjg@113: Names names = tsym.name.table.names;
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.
duke@1: * @param paramTypeArray 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) {
duke@1: if (t.getTag() == JCTree.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) {
duke@1: if (t.getTag() == JCTree.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: /**
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: */
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: }