/* * 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 java.io.File; import java.io.IOException; import java.lang.reflect.Modifier; import java.net.URI; import java.util.HashSet; import java.util.Set; import javax.tools.FileObject; import javax.tools.JavaFileManager.Location; import javax.tools.StandardJavaFileManager; import javax.tools.StandardLocation; import com.sun.javadoc.*; import static com.sun.javadoc.LanguageVersion.*; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.code.Kinds; import com.sun.tools.javac.code.Scope; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Symbol.*; import com.sun.tools.javac.code.Type; import com.sun.tools.javac.code.Type.ClassType; import com.sun.tools.javac.code.TypeTags; 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.JCClassDecl; import com.sun.tools.javac.tree.JCTree.JCFieldAccess; import com.sun.tools.javac.tree.JCTree.JCImport; import com.sun.tools.javac.tree.TreeInfo; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; import com.sun.tools.javac.util.Name; import com.sun.tools.javac.util.Names; import com.sun.tools.javac.util.Position; import static com.sun.tools.javac.code.Flags.*; import static com.sun.tools.javac.code.Kinds.*; /** * 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()); if (p.setDocPath == false) { FileObject docPath; try { Location location = env.fileManager.hasLocation(StandardLocation.SOURCE_PATH) ? StandardLocation.SOURCE_PATH : StandardLocation.CLASS_PATH; docPath = env.fileManager.getFileForInput( location, p.qualifiedName(), "package.html"); } catch (IOException e) { docPath = null; } if (docPath == null) { // fall back on older semantics of looking in same directory as // source file for this class SourcePosition po = position(); if (env.fileManager instanceof StandardJavaFileManager && po instanceof SourcePositionImpl) { URI uri = ((SourcePositionImpl) po).filename.toUri(); if ("file".equals(uri.getScheme())) { File f = new File(uri.getPath()); File dir = f.getParentFile(); if (dir != null) { File pf = new File(dir, "package.html"); if (pf.exists()) { StandardJavaFileManager sfm = (StandardJavaFileManager) env.fileManager; docPath = sfm.getJavaFileObjects(pf).iterator().next(); } } } } } p.setDocPath(docPath); } return p; } /** * Return the class name without package qualifier - but with * enclosing class qualifier - as a String. *
* Examples: * for java.util.Hashtable * return Hashtable * for java.util.Map.Entry * return Map.Entry **/ public String name() { return getClassName(tsym, false); } /** * Return the qualified class name as a String. *
* Example: * for java.util.Hashtable * return java.util.Hashtable * if no qualifier, just return flat name **/ public String qualifiedName() { return getClassName(tsym, true); } /** * Return unqualified name of type excluding any dimension information. *
* For example, a two dimensional array of String returns 'String'. */ public String typeName() { return name(); } /** * Return qualified name of type excluding any dimension information. *
* For example, a two dimensional array of String
* returns 'java.lang.String'.
*/
public String qualifiedTypeName() {
return qualifiedName();
}
/**
* Return the simple name of this type.
*/
public String simpleTypeName() {
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
*
*
* Return either a list of default fields documented by
*
* 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,
(tree==null) ? Position.NOPOS : tree.pos,
lineMap);
}
}
ClassDoc
or a ParameterizedType
.
*/
public com.sun.javadoc.Type superclassType() {
if (isInterface() || isAnnotationType() ||
(tsym == env.syms.objectType.tsym))
return null;
Type sup = env.types.supertype(type);
return TypeMaker.getType(env,
(sup != 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() {
ListBufferjava.io.Serializable
.
*
* Since java.io.Externalizable
extends
* java.io.Serializable
,
* Externalizable objects are also Serializable.
*/
public boolean isSerializable() {
try {
return env.types.isSubtype(type, env.syms.serializableType);
} catch (CompletionFailure ex) {
// quietly ignore completion failures
return false;
}
}
/**
* Return true if this class implements
* java.io.Externalizable
.
*/
public boolean isExternalizable() {
try {
return env.types.isSubtype(type, env.externalizableSym.type);
} catch (CompletionFailure ex) {
// quietly ignore completion failures
return false;
}
}
/**
* Return the serialization methods for this class.
*
* @return an array of MethodDocImpl
that represents
* the serialization methods for this class.
*/
public MethodDoc[] serializationMethods() {
if (serializedForm == null) {
serializedForm = new SerializedForm(env, tsym, this);
}
//### Clone this?
return serializedForm.methods();
}
/**
* Return the Serializable fields of class.serial
tag
* or return a single FieldDoc
for
* serialPersistentField
member.
* There should be a serialField
tag for
* each Serializable field defined by an ObjectStreamField
* array component of serialPersistentField
.
*
* @returns an array of FieldDoc
for the Serializable fields
* of this class.
*
* @see #definesSerializableFields()
* @see SerialFieldTagImpl
*/
public FieldDoc[] serializableFields() {
if (serializedForm == null) {
serializedForm = new SerializedForm(env, tsym, this);
}
//### Clone this?
return serializedForm.fields();
}
/**
* Return true if Serializable fields are explicitly defined with
* the special class member serialPersistentFields
.
*
* @see #serializableFields()
* @see SerialFieldTagImpl
*/
public boolean definesSerializableFields() {
if (!isSerializable() || isExternalizable()) {
return false;
} else {
if (serializedForm == null) {
serializedForm = new SerializedForm(env, tsym, this);
}
//### Clone this?
return serializedForm.definesSerializableFields();
}
}
/**
* Determine if a class is a RuntimeException.
*