duke@1: /* jjg@1357: * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. duke@1: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. duke@1: * duke@1: * This code is free software; you can redistribute it and/or modify it duke@1: * under the terms of the GNU General Public License version 2 only, as ohair@554: * published by the Free Software Foundation. Oracle designates this duke@1: * particular file as subject to the "Classpath" exception as provided ohair@554: * by Oracle in the LICENSE file that accompanied this code. duke@1: * duke@1: * This code is distributed in the hope that it will be useful, but WITHOUT duke@1: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or duke@1: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License duke@1: * version 2 for more details (a copy is included in the LICENSE file that duke@1: * accompanied this code). duke@1: * duke@1: * You should have received a copy of the GNU General Public License version duke@1: * 2 along with this work; if not, write to the Free Software Foundation, duke@1: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. duke@1: * ohair@554: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ohair@554: * or visit www.oracle.com if you need additional information or have any ohair@554: * questions. duke@1: */ duke@1: duke@1: package com.sun.tools.javadoc; duke@1: jjg@197: import java.io.File; jjg@197: import java.io.IOException; jjg@197: import java.lang.reflect.Modifier; jjg@197: import java.net.URI; jjg@197: import java.util.HashSet; jjg@197: import java.util.Set; jjg@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 com.sun.tools.javac.code.Flags; duke@1: import com.sun.tools.javac.code.Kinds; duke@1: import com.sun.tools.javac.code.Scope; duke@1: import com.sun.tools.javac.code.Symbol; duke@1: import com.sun.tools.javac.code.Symbol.*; jjg@197: import com.sun.tools.javac.code.Type; jjg@197: import com.sun.tools.javac.code.Type.ClassType; duke@1: 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.Kinds.*; jjg@1374: import static com.sun.tools.javac.code.TypeTag.CLASS; jjg@1127: import static com.sun.tools.javac.tree.JCTree.Tag.*; duke@1: duke@1: /** duke@1: * Represents a java class and provides access to information duke@1: * about the class, the class' comment and tags, and the duke@1: * members of the class. A ClassDocImpl only exists if it was duke@1: * processed in this run of javadoc. References to classes duke@1: * which may or may not have been processed in this run are duke@1: * referred to using Type (which can be converted to ClassDocImpl, duke@1: * if possible). duke@1: * jjg@1359: *
This is NOT part of any supported API. jjg@1359: * If you write code that depends on this, you do so at your own risk. jjg@1359: * This code and its internal interfaces are subject to change or jjg@1359: * deletion without notice. jjg@1359: * duke@1: * @see Type duke@1: * duke@1: * @since 1.2 duke@1: * @author Robert Field duke@1: * @author Neal Gafter (rewrite) duke@1: * @author Scott Seligman (generics, enums, annotations) duke@1: */ duke@1: duke@1: public class ClassDocImpl extends ProgramElementDocImpl implements ClassDoc { duke@1: duke@1: public final ClassType type; // protected->public for debugging duke@1: protected final ClassSymbol tsym; duke@1: duke@1: boolean isIncluded = false; // Set in RootDocImpl duke@1: duke@1: private SerializedForm serializedForm; duke@1: duke@1: /** duke@1: * Constructor duke@1: */ duke@1: public ClassDocImpl(DocEnv env, ClassSymbol sym) { 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: */ jjg@910: @Override duke@1: public boolean isClass() { duke@1: return !Modifier.isInterface(getModifiers()); duke@1: } duke@1: duke@1: /** duke@1: * Return true if this is a ordinary class, duke@1: * not an enumeration, exception, an error, or an interface. duke@1: */ jjg@910: @Override duke@1: public boolean isOrdinaryClass() { duke@1: if (isEnum() || isInterface() || isAnnotationType()) { duke@1: return false; duke@1: } jjg@1374: for (Type t = type; t.hasTag(CLASS); t = env.types.supertype(t)) { duke@1: if (t.tsym == env.syms.errorType.tsym || duke@1: t.tsym == env.syms.exceptionType.tsym) { duke@1: return false; duke@1: } duke@1: } duke@1: return true; duke@1: } duke@1: duke@1: /** duke@1: * Return true if this is an enumeration. duke@1: * (For legacy doclets, return false.) duke@1: */ jjg@910: @Override duke@1: public boolean isEnum() { duke@1: return (getFlags() & Flags.ENUM) != 0 duke@1: && duke@1: !env.legacyDoclet; duke@1: } duke@1: duke@1: /** duke@1: * Return true if this is an interface, but not an annotation type. duke@1: * Overridden by AnnotationTypeDocImpl. duke@1: */ jjg@910: @Override duke@1: public boolean isInterface() { duke@1: return Modifier.isInterface(getModifiers()); duke@1: } duke@1: duke@1: /** duke@1: * Return true if this is an exception class duke@1: */ jjg@910: @Override duke@1: public boolean isException() { duke@1: if (isEnum() || isInterface() || isAnnotationType()) { duke@1: return false; duke@1: } jjg@1374: for (Type t = type; t.hasTag(CLASS); t = env.types.supertype(t)) { duke@1: if (t.tsym == env.syms.exceptionType.tsym) { duke@1: return true; duke@1: } duke@1: } duke@1: return false; duke@1: } duke@1: duke@1: /** duke@1: * Return true if this is an error class duke@1: */ jjg@910: @Override duke@1: public boolean isError() { duke@1: if (isEnum() || isInterface() || isAnnotationType()) { duke@1: return false; duke@1: } jjg@1374: for (Type t = type; t.hasTag(CLASS); t = env.types.supertype(t)) { duke@1: if (t.tsym == env.syms.errorType.tsym) { duke@1: return true; duke@1: } duke@1: } duke@1: return false; duke@1: } duke@1: duke@1: /** duke@1: * Return true if this is a throwable class duke@1: */ duke@1: public boolean isThrowable() { duke@1: if (isEnum() || isInterface() || isAnnotationType()) { duke@1: return false; duke@1: } jjg@1374: for (Type t = type; t.hasTag(CLASS); t = env.types.supertype(t)) { duke@1: if (t.tsym == env.syms.throwableType.tsym) { duke@1: return true; duke@1: } duke@1: } duke@1: return false; duke@1: } duke@1: duke@1: /** duke@1: * Return true if this class is abstract duke@1: */ duke@1: public boolean isAbstract() { duke@1: return Modifier.isAbstract(getModifiers()); duke@1: } duke@1: duke@1: /** duke@1: * Returns true if this class was synthesized by the compiler. duke@1: */ duke@1: public boolean isSynthetic() { duke@1: return (getFlags() & Flags.SYNTHETIC) != 0; duke@1: } duke@1: duke@1: /** duke@1: * Return true if this class is included in the active set. duke@1: * A ClassDoc is included iff either it is specified on the duke@1: * commandline, or if it's containing package is specified duke@1: * on the command line, or if it is a member class of an duke@1: * included class. duke@1: */ duke@1: duke@1: public boolean isIncluded() { duke@1: if (isIncluded) { duke@1: return true; duke@1: } duke@1: if (env.shouldDocument(tsym)) { duke@1: // Class is nameable from top-level and duke@1: // the class and all enclosing classes duke@1: // pass the modifier filter. duke@1: if (containingPackage().isIncluded()) { duke@1: return isIncluded=true; duke@1: } duke@1: ClassDoc outer = containingClass(); duke@1: if (outer != null && outer.isIncluded()) { duke@1: return isIncluded=true; duke@1: } duke@1: } duke@1: return false; duke@1: } duke@1: duke@1: /** duke@1: * Return the package that this class is contained in. duke@1: */ jjg@910: @Override duke@1: public PackageDoc containingPackage() { duke@1: PackageDocImpl p = env.getPackageDoc(tsym.packge()); jjg@197: if (p.setDocPath == false) { jjg@197: FileObject docPath; jjg@197: try { jjg@197: Location location = env.fileManager.hasLocation(StandardLocation.SOURCE_PATH) jjg@197: ? StandardLocation.SOURCE_PATH : StandardLocation.CLASS_PATH; jjg@197: jjg@197: docPath = env.fileManager.getFileForInput( jjg@197: location, p.qualifiedName(), "package.html"); jjg@197: } catch (IOException e) { jjg@197: docPath = null; duke@1: } jjg@197: jjg@197: if (docPath == null) { jjg@197: // fall back on older semantics of looking in same directory as jjg@197: // source file for this class jjg@197: SourcePosition po = position(); jjg@197: if (env.fileManager instanceof StandardJavaFileManager && jjg@197: po instanceof SourcePositionImpl) { jjg@197: URI uri = ((SourcePositionImpl) po).filename.toUri(); jjg@197: if ("file".equals(uri.getScheme())) { jjg@400: File f = new File(uri); jjg@197: File dir = f.getParentFile(); jjg@197: if (dir != null) { jjg@197: File pf = new File(dir, "package.html"); jjg@197: if (pf.exists()) { jjg@197: StandardJavaFileManager sfm = (StandardJavaFileManager) env.fileManager; jjg@197: docPath = sfm.getJavaFileObjects(pf).iterator().next(); jjg@197: } jjg@197: } jjg@197: jjg@197: } jjg@197: } jjg@197: } jjg@197: jjg@197: p.setDocPath(docPath); duke@1: } duke@1: return p; duke@1: } duke@1: duke@1: /** duke@1: * Return the class name without package qualifier - but with duke@1: * enclosing class qualifier - as a String. duke@1: *
duke@1: * Examples: duke@1: * for java.util.Hashtable duke@1: * return Hashtable duke@1: * for java.util.Map.Entry duke@1: * return Map.Entry duke@1: *duke@1: */ duke@1: public String name() { duke@1: return getClassName(tsym, false); duke@1: } duke@1: duke@1: /** duke@1: * Return the qualified class name as a String. duke@1: *
duke@1: * Example: duke@1: * for java.util.Hashtable duke@1: * return java.util.Hashtable duke@1: * if no qualifier, just return flat name duke@1: *duke@1: */ duke@1: public String qualifiedName() { duke@1: return getClassName(tsym, true); duke@1: } duke@1: duke@1: /** duke@1: * Return unqualified name of type excluding any dimension information. duke@1: *
duke@1: * For example, a two dimensional array of String returns 'String'. duke@1: */ duke@1: public String typeName() { duke@1: return name(); duke@1: } duke@1: duke@1: /** duke@1: * Return qualified name of type excluding any dimension information. duke@1: *
duke@1: * For example, a two dimensional array of String
duke@1: * returns 'java.lang.String'.
duke@1: */
duke@1: public String qualifiedTypeName() {
duke@1: return qualifiedName();
duke@1: }
duke@1:
duke@1: /**
duke@1: * Return the simple name of this type.
duke@1: */
duke@1: public String simpleTypeName() {
duke@1: return tsym.name.toString();
duke@1: }
duke@1:
duke@1: /**
duke@1: * Return the qualified name and any type parameters.
duke@1: * Each parameter is a type variable with optional bounds.
duke@1: */
jjg@910: @Override
duke@1: public String toString() {
duke@1: return classToString(env, tsym, true);
duke@1: }
duke@1:
duke@1: /**
duke@1: * Return the class name as a string. If "full" is true the name is
duke@1: * qualified, otherwise it is qualified by its enclosing class(es) only.
duke@1: */
duke@1: static String getClassName(ClassSymbol c, boolean full) {
duke@1: if (full) {
duke@1: return c.getQualifiedName().toString();
duke@1: } else {
duke@1: String n = "";
duke@1: for ( ; c != null; c = c.owner.enclClass()) {
duke@1: n = c.name + (n.equals("") ? "" : ".") + n;
duke@1: }
duke@1: return n;
duke@1: }
duke@1: }
duke@1:
duke@1: /**
duke@1: * Return the class name with any type parameters as a string.
duke@1: * Each parameter is a type variable with optional bounds.
duke@1: * If "full" is true all names are qualified, otherwise they are
duke@1: * qualified by their enclosing class(es) only.
duke@1: */
duke@1: static String classToString(DocEnv env, ClassSymbol c, boolean full) {
jjg@910: StringBuilder s = new StringBuilder();
duke@1: if (!c.isInner()) { // if c is not an inner class
duke@1: s.append(getClassName(c, full));
duke@1: } else {
duke@1: // c is an inner class, so include type params of outer.
duke@1: ClassSymbol encl = c.owner.enclClass();
duke@1: s.append(classToString(env, encl, full))
duke@1: .append('.')
duke@1: .append(c.name);
duke@1: }
duke@1: s.append(TypeMaker.typeParametersString(env, c, full));
duke@1: return s.toString();
duke@1: }
duke@1:
duke@1: /**
duke@1: * Is this class (or any enclosing class) generic? That is, does
duke@1: * it have type parameters?
duke@1: */
duke@1: static boolean isGeneric(ClassSymbol c) {
duke@1: return c.type.allparams().nonEmpty();
duke@1: }
duke@1:
duke@1: /**
duke@1: * Return the formal type parameters of this class or interface.
duke@1: * Return an empty array if there are none.
duke@1: */
duke@1: public TypeVariable[] typeParameters() {
duke@1: if (env.legacyDoclet) {
duke@1: return new TypeVariable[0];
duke@1: }
duke@1: TypeVariable res[] = new TypeVariable[type.getTypeArguments().length()];
duke@1: TypeMaker.getTypes(env, type.getTypeArguments(), res);
duke@1: return res;
duke@1: }
duke@1:
duke@1: /**
duke@1: * Return the type parameter tags of this class or interface.
duke@1: */
duke@1: public ParamTag[] typeParamTags() {
duke@1: return (env.legacyDoclet)
duke@1: ? new ParamTag[0]
duke@1: : comment().typeParamTags();
duke@1: }
duke@1:
duke@1: /**
duke@1: * Return the modifier string for this class. If it's an interface
duke@1: * exclude 'abstract' keyword from the modifier string
duke@1: */
jjg@910: @Override
duke@1: public String modifiers() {
duke@1: return Modifier.toString(modifierSpecifier());
duke@1: }
duke@1:
jjg@910: @Override
duke@1: public int modifierSpecifier() {
duke@1: int modifiers = getModifiers();
duke@1: return (isInterface() || isAnnotationType())
duke@1: ? modifiers & ~Modifier.ABSTRACT
duke@1: : modifiers;
duke@1: }
duke@1:
duke@1: /**
duke@1: * Return the superclass of this class
duke@1: *
duke@1: * @return the ClassDocImpl for the superclass of this class, null
duke@1: * if there is no superclass.
duke@1: */
duke@1: public ClassDoc superclass() {
duke@1: if (isInterface() || isAnnotationType()) return null;
duke@1: if (tsym == env.syms.objectType.tsym) return null;
duke@1: ClassSymbol c = (ClassSymbol)env.types.supertype(type).tsym;
duke@1: if (c == null || c == tsym) c = (ClassSymbol)env.syms.objectType.tsym;
duke@1: return env.getClassDoc(c);
duke@1: }
duke@1:
duke@1: /**
duke@1: * Return the superclass of this class. Return null if this is an
duke@1: * interface. A superclass is represented by either a
duke@1: *
duke@1: *
duke@1: * Return either a list of default fields documented by
duke@1: *
duke@1: * Used only by ThrowsTagImpl.
duke@1: */
duke@1: boolean isRuntimeException() {
duke@1: return tsym.isSubClass(env.syms.runtimeExceptionType.tsym, env.types);
duke@1: }
duke@1:
duke@1: /**
duke@1: * Return the source position of the entity, or null if
duke@1: * no position is available.
duke@1: */
jjg@910: @Override
duke@1: public SourcePosition position() {
duke@1: if (tsym.sourcefile == null) return null;
jjg@197: return SourcePositionImpl.make(tsym.sourcefile,
duke@1: (tree==null) ? Position.NOPOS : tree.pos,
duke@1: lineMap);
duke@1: }
duke@1: }
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: ListBufferjava.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.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: *