0001: /*
0002: * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
0003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004: *
0005: * This code is free software; you can redistribute it and/or modify it
0006: * under the terms of the GNU General Public License version 2 only, as
0007: * published by the Free Software Foundation. Sun designates this
0008: * particular file as subject to the "Classpath" exception as provided
0009: * by Sun in the LICENSE file that accompanied this code.
0010: *
0011: * This code is distributed in the hope that it will be useful, but WITHOUT
0012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014: * version 2 for more details (a copy is included in the LICENSE file that
0015: * accompanied this code).
0016: *
0017: * You should have received a copy of the GNU General Public License version
0018: * 2 along with this work; if not, write to the Free Software Foundation,
0019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020: *
0021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022: * CA 95054 USA or visit www.sun.com if you need additional information or
0023: * have any questions.
0024: */
0025:
0026: package com.sun.tools.javadoc;
0027:
0028: import java.util.*;
0029:
0030: import com.sun.javadoc.*;
0031:
0032: import static com.sun.javadoc.LanguageVersion.*;
0033:
0034: import com.sun.tools.javac.util.List;
0035: import com.sun.tools.javac.util.ListBuffer;
0036: import com.sun.tools.javac.util.Name;
0037: import com.sun.tools.javac.util.Position;
0038:
0039: import com.sun.tools.javac.code.Flags;
0040: import com.sun.tools.javac.code.Kinds;
0041: import com.sun.tools.javac.code.TypeTags;
0042: import com.sun.tools.javac.code.Type;
0043: import com.sun.tools.javac.code.Types;
0044: import com.sun.tools.javac.code.Type.ClassType;
0045: import com.sun.tools.javac.code.Scope;
0046: import com.sun.tools.javac.code.Symbol;
0047: import com.sun.tools.javac.code.Symbol.*;
0048:
0049: import com.sun.tools.javac.comp.AttrContext;
0050: import com.sun.tools.javac.comp.Env;
0051:
0052: import com.sun.tools.javac.tree.JCTree;
0053: import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
0054: import com.sun.tools.javac.tree.JCTree.JCImport;
0055: import com.sun.tools.javac.tree.JCTree.JCClassDecl;
0056: import com.sun.tools.javac.tree.TreeInfo;
0057:
0058: import static com.sun.tools.javac.code.Flags.*;
0059: import static com.sun.tools.javac.code.Kinds.*;
0060: import static com.sun.tools.javac.code.TypeTags.*;
0061:
0062: import java.io.File;
0063: import java.util.Set;
0064: import java.util.HashSet;
0065: import java.lang.reflect.Modifier;
0066:
0067: /**
0068: * Represents a java class and provides access to information
0069: * about the class, the class' comment and tags, and the
0070: * members of the class. A ClassDocImpl only exists if it was
0071: * processed in this run of javadoc. References to classes
0072: * which may or may not have been processed in this run are
0073: * referred to using Type (which can be converted to ClassDocImpl,
0074: * if possible).
0075: *
0076: * @see Type
0077: *
0078: * @since 1.2
0079: * @author Robert Field
0080: * @author Neal Gafter (rewrite)
0081: * @author Scott Seligman (generics, enums, annotations)
0082: */
0083:
0084: public class ClassDocImpl extends ProgramElementDocImpl implements
0085: ClassDoc {
0086:
0087: public final ClassType type; // protected->public for debugging
0088: protected final ClassSymbol tsym;
0089:
0090: boolean isIncluded = false; // Set in RootDocImpl
0091:
0092: private SerializedForm serializedForm;
0093:
0094: /**
0095: * Constructor
0096: */
0097: public ClassDocImpl(DocEnv env, ClassSymbol sym) {
0098: this (env, sym, null, null, null);
0099: }
0100:
0101: /**
0102: * Constructor
0103: */
0104: public ClassDocImpl(DocEnv env, ClassSymbol sym,
0105: String documentation, JCClassDecl tree,
0106: Position.LineMap lineMap) {
0107: super (env, sym, documentation, tree, lineMap);
0108: this .type = (ClassType) sym.type;
0109: this .tsym = sym;
0110: }
0111:
0112: /**
0113: * Returns the flags in terms of javac's flags
0114: */
0115: protected long getFlags() {
0116: return getFlags(tsym);
0117: }
0118:
0119: /**
0120: * Returns the flags of a ClassSymbol in terms of javac's flags
0121: */
0122: static long getFlags(ClassSymbol clazz) {
0123: while (true) {
0124: try {
0125: return clazz.flags();
0126: } catch (CompletionFailure ex) {
0127: // quietly ignore completion failures
0128: }
0129: }
0130: }
0131:
0132: /**
0133: * Is a ClassSymbol an annotation type?
0134: */
0135: static boolean isAnnotationType(ClassSymbol clazz) {
0136: return (getFlags(clazz) & Flags.ANNOTATION) != 0;
0137: }
0138:
0139: /**
0140: * Identify the containing class
0141: */
0142: protected ClassSymbol getContainingClass() {
0143: return tsym.owner.enclClass();
0144: }
0145:
0146: /**
0147: * Return true if this is a class, not an interface.
0148: */
0149: public boolean isClass() {
0150: return !Modifier.isInterface(getModifiers());
0151: }
0152:
0153: /**
0154: * Return true if this is a ordinary class,
0155: * not an enumeration, exception, an error, or an interface.
0156: */
0157: public boolean isOrdinaryClass() {
0158: if (isEnum() || isInterface() || isAnnotationType()) {
0159: return false;
0160: }
0161: for (Type t = type; t.tag == TypeTags.CLASS; t = env.types
0162: .super type(t)) {
0163: if (t.tsym == env.syms.errorType.tsym
0164: || t.tsym == env.syms.exceptionType.tsym) {
0165: return false;
0166: }
0167: }
0168: return true;
0169: }
0170:
0171: /**
0172: * Return true if this is an enumeration.
0173: * (For legacy doclets, return false.)
0174: */
0175: public boolean isEnum() {
0176: return (getFlags() & Flags.ENUM) != 0 && !env.legacyDoclet;
0177: }
0178:
0179: /**
0180: * Return true if this is an interface, but not an annotation type.
0181: * Overridden by AnnotationTypeDocImpl.
0182: */
0183: public boolean isInterface() {
0184: return Modifier.isInterface(getModifiers());
0185: }
0186:
0187: /**
0188: * Return true if this is an exception class
0189: */
0190: public boolean isException() {
0191: if (isEnum() || isInterface() || isAnnotationType()) {
0192: return false;
0193: }
0194: for (Type t = type; t.tag == TypeTags.CLASS; t = env.types
0195: .super type(t)) {
0196: if (t.tsym == env.syms.exceptionType.tsym) {
0197: return true;
0198: }
0199: }
0200: return false;
0201: }
0202:
0203: /**
0204: * Return true if this is an error class
0205: */
0206: public boolean isError() {
0207: if (isEnum() || isInterface() || isAnnotationType()) {
0208: return false;
0209: }
0210: for (Type t = type; t.tag == TypeTags.CLASS; t = env.types
0211: .super type(t)) {
0212: if (t.tsym == env.syms.errorType.tsym) {
0213: return true;
0214: }
0215: }
0216: return false;
0217: }
0218:
0219: /**
0220: * Return true if this is a throwable class
0221: */
0222: public boolean isThrowable() {
0223: if (isEnum() || isInterface() || isAnnotationType()) {
0224: return false;
0225: }
0226: for (Type t = type; t.tag == TypeTags.CLASS; t = env.types
0227: .super type(t)) {
0228: if (t.tsym == env.syms.throwableType.tsym) {
0229: return true;
0230: }
0231: }
0232: return false;
0233: }
0234:
0235: /**
0236: * Return true if this class is abstract
0237: */
0238: public boolean isAbstract() {
0239: return Modifier.isAbstract(getModifiers());
0240: }
0241:
0242: /**
0243: * Returns true if this class was synthesized by the compiler.
0244: */
0245: public boolean isSynthetic() {
0246: return (getFlags() & Flags.SYNTHETIC) != 0;
0247: }
0248:
0249: /**
0250: * Return true if this class is included in the active set.
0251: * A ClassDoc is included iff either it is specified on the
0252: * commandline, or if it's containing package is specified
0253: * on the command line, or if it is a member class of an
0254: * included class.
0255: */
0256:
0257: public boolean isIncluded() {
0258: if (isIncluded) {
0259: return true;
0260: }
0261: if (env.shouldDocument(tsym)) {
0262: // Class is nameable from top-level and
0263: // the class and all enclosing classes
0264: // pass the modifier filter.
0265: if (containingPackage().isIncluded()) {
0266: return isIncluded = true;
0267: }
0268: ClassDoc outer = containingClass();
0269: if (outer != null && outer.isIncluded()) {
0270: return isIncluded = true;
0271: }
0272: }
0273: return false;
0274: }
0275:
0276: /**
0277: * Return the package that this class is contained in.
0278: */
0279: public PackageDoc containingPackage() {
0280: PackageDocImpl p = env.getPackageDoc(tsym.packge());
0281: SourcePosition po = position();
0282: if (po != null && p.setDocPath == false && p.zipDocPath == null) {
0283: //Set the package path if possible
0284: File packageDir = po.file().getParentFile();
0285: if (packageDir != null
0286: && (new File(packageDir, "package.html")).exists()) {
0287: p.setDocPath(packageDir.getPath());
0288: } else {
0289: p.setDocPath(null);
0290: }
0291: }
0292: return p;
0293: }
0294:
0295: /**
0296: * Return the class name without package qualifier - but with
0297: * enclosing class qualifier - as a String.
0298: * <pre>
0299: * Examples:
0300: * for java.util.Hashtable
0301: * return Hashtable
0302: * for java.util.Map.Entry
0303: * return Map.Entry
0304: * </pre>
0305: */
0306: public String name() {
0307: return getClassName(tsym, false);
0308: }
0309:
0310: /**
0311: * Return the qualified class name as a String.
0312: * <pre>
0313: * Example:
0314: * for java.util.Hashtable
0315: * return java.util.Hashtable
0316: * if no qualifier, just return flat name
0317: * </pre>
0318: */
0319: public String qualifiedName() {
0320: return getClassName(tsym, true);
0321: }
0322:
0323: /**
0324: * Return unqualified name of type excluding any dimension information.
0325: * <p>
0326: * For example, a two dimensional array of String returns 'String'.
0327: */
0328: public String typeName() {
0329: return name();
0330: }
0331:
0332: /**
0333: * Return qualified name of type excluding any dimension information.
0334: *<p>
0335: * For example, a two dimensional array of String
0336: * returns 'java.lang.String'.
0337: */
0338: public String qualifiedTypeName() {
0339: return qualifiedName();
0340: }
0341:
0342: /**
0343: * Return the simple name of this type.
0344: */
0345: public String simpleTypeName() {
0346: return tsym.name.toString();
0347: }
0348:
0349: /**
0350: * Return the qualified name and any type parameters.
0351: * Each parameter is a type variable with optional bounds.
0352: */
0353: public String toString() {
0354: return classToString(env, tsym, true);
0355: }
0356:
0357: /**
0358: * Return the class name as a string. If "full" is true the name is
0359: * qualified, otherwise it is qualified by its enclosing class(es) only.
0360: */
0361: static String getClassName(ClassSymbol c, boolean full) {
0362: if (full) {
0363: return c.getQualifiedName().toString();
0364: } else {
0365: String n = "";
0366: for (; c != null; c = c.owner.enclClass()) {
0367: n = c.name + (n.equals("") ? "" : ".") + n;
0368: }
0369: return n;
0370: }
0371: }
0372:
0373: /**
0374: * Return the class name with any type parameters as a string.
0375: * Each parameter is a type variable with optional bounds.
0376: * If "full" is true all names are qualified, otherwise they are
0377: * qualified by their enclosing class(es) only.
0378: */
0379: static String classToString(DocEnv env, ClassSymbol c, boolean full) {
0380: StringBuffer s = new StringBuffer();
0381: if (!c.isInner()) { // if c is not an inner class
0382: s.append(getClassName(c, full));
0383: } else {
0384: // c is an inner class, so include type params of outer.
0385: ClassSymbol encl = c.owner.enclClass();
0386: s.append(classToString(env, encl, full)).append('.')
0387: .append(c.name);
0388: }
0389: s.append(TypeMaker.typeParametersString(env, c, full));
0390: return s.toString();
0391: }
0392:
0393: /**
0394: * Is this class (or any enclosing class) generic? That is, does
0395: * it have type parameters?
0396: */
0397: static boolean isGeneric(ClassSymbol c) {
0398: return c.type.allparams().nonEmpty();
0399: }
0400:
0401: /**
0402: * Return the formal type parameters of this class or interface.
0403: * Return an empty array if there are none.
0404: */
0405: public TypeVariable[] typeParameters() {
0406: if (env.legacyDoclet) {
0407: return new TypeVariable[0];
0408: }
0409: TypeVariable res[] = new TypeVariable[type.getTypeArguments()
0410: .length()];
0411: TypeMaker.getTypes(env, type.getTypeArguments(), res);
0412: return res;
0413: }
0414:
0415: /**
0416: * Return the type parameter tags of this class or interface.
0417: */
0418: public ParamTag[] typeParamTags() {
0419: return (env.legacyDoclet) ? new ParamTag[0] : comment()
0420: .typeParamTags();
0421: }
0422:
0423: /**
0424: * Return the modifier string for this class. If it's an interface
0425: * exclude 'abstract' keyword from the modifier string
0426: */
0427: public String modifiers() {
0428: return Modifier.toString(modifierSpecifier());
0429: }
0430:
0431: public int modifierSpecifier() {
0432: int modifiers = getModifiers();
0433: return (isInterface() || isAnnotationType()) ? modifiers
0434: & ~Modifier.ABSTRACT : modifiers;
0435: }
0436:
0437: /**
0438: * Return the superclass of this class
0439: *
0440: * @return the ClassDocImpl for the superclass of this class, null
0441: * if there is no superclass.
0442: */
0443: public ClassDoc super class() {
0444: if (isInterface() || isAnnotationType())
0445: return null;
0446: if (tsym == env.syms.objectType.tsym)
0447: return null;
0448: ClassSymbol c = (ClassSymbol) env.types.super type(type).tsym;
0449: if (c == null || c == tsym)
0450: c = (ClassSymbol) env.syms.objectType.tsym;
0451: return env.getClassDoc(c);
0452: }
0453:
0454: /**
0455: * Return the superclass of this class. Return null if this is an
0456: * interface. A superclass is represented by either a
0457: * <code>ClassDoc</code> or a <code>ParameterizedType</code>.
0458: */
0459: public com.sun.javadoc.Type super classType() {
0460: if (isInterface() || isAnnotationType()
0461: || (tsym == env.syms.objectType.tsym))
0462: return null;
0463: Type sup = env.types.super type(type);
0464: return TypeMaker.getType(env, (sup != type) ? sup
0465: : env.syms.objectType);
0466: }
0467:
0468: /**
0469: * Test whether this class is a subclass of the specified class.
0470: *
0471: * @param cd the candidate superclass.
0472: * @return true if cd is a superclass of this class.
0473: */
0474: public boolean subclassOf(ClassDoc cd) {
0475: return tsym.isSubClass(((ClassDocImpl) cd).tsym, env.types);
0476: }
0477:
0478: /**
0479: * Return interfaces implemented by this class or interfaces
0480: * extended by this interface.
0481: *
0482: * @return An array of ClassDocImpl representing the interfaces.
0483: * Return an empty array if there are no interfaces.
0484: */
0485: public ClassDoc[] interfaces() {
0486: ListBuffer<ClassDocImpl> ta = new ListBuffer<ClassDocImpl>();
0487: for (Type t : env.types.interfaces(type)) {
0488: ta.append(env.getClassDoc((ClassSymbol) t.tsym));
0489: }
0490: //### Cache ta here?
0491: return ta.toArray(new ClassDocImpl[ta.length()]);
0492: }
0493:
0494: /**
0495: * Return interfaces implemented by this class or interfaces extended
0496: * by this interface. Includes only directly-declared interfaces, not
0497: * inherited interfaces.
0498: * Return an empty array if there are no interfaces.
0499: */
0500: public com.sun.javadoc.Type[] interfaceTypes() {
0501: //### Cache result here?
0502: return TypeMaker.getTypes(env, env.types.interfaces(type));
0503: }
0504:
0505: /**
0506: * Return fields in class.
0507: * @param filter include only the included fields if filter==true
0508: */
0509: public FieldDoc[] fields(boolean filter) {
0510: return fields(filter, false);
0511: }
0512:
0513: /**
0514: * Return included fields in class.
0515: */
0516: public FieldDoc[] fields() {
0517: return fields(true, false);
0518: }
0519:
0520: /**
0521: * Return the enum constants if this is an enum type.
0522: */
0523: public FieldDoc[] enumConstants() {
0524: return fields(false, true);
0525: }
0526:
0527: /**
0528: * Return fields in class.
0529: * @param filter if true, return only the included fields
0530: * @param enumConstants if true, return the enum constants instead
0531: */
0532: private FieldDoc[] fields(boolean filter, boolean enumConstants) {
0533: List<FieldDocImpl> fields = List.nil();
0534: for (Scope.Entry e = tsym.members().elems; e != null; e = e.sibling) {
0535: if (e.sym != null && e.sym.kind == VAR) {
0536: VarSymbol s = (VarSymbol) e.sym;
0537: boolean isEnum = ((s.flags() & Flags.ENUM) != 0)
0538: && !env.legacyDoclet;
0539: if (isEnum == enumConstants
0540: && (!filter || env.shouldDocument(s))) {
0541: fields = fields.prepend(env.getFieldDoc(s));
0542: }
0543: }
0544: }
0545: return fields.toArray(new FieldDocImpl[fields.length()]);
0546: }
0547:
0548: /**
0549: * Return methods in class.
0550: * This method is overridden by AnnotationTypeDocImpl.
0551: *
0552: * @param filter include only the included methods if filter==true
0553: * @return an array of MethodDocImpl for representing the visible
0554: * methods in this class. Does not include constructors.
0555: */
0556: public MethodDoc[] methods(boolean filter) {
0557: Name.Table names = tsym.name.table;
0558: List<MethodDocImpl> methods = List.nil();
0559: for (Scope.Entry e = tsym.members().elems; e != null; e = e.sibling) {
0560: if (e.sym != null && e.sym.kind == Kinds.MTH
0561: && e.sym.name != names.init) {
0562: MethodSymbol s = (MethodSymbol) e.sym;
0563: if (!filter || env.shouldDocument(s)) {
0564: methods = methods.prepend(env.getMethodDoc(s));
0565: }
0566: }
0567: }
0568: //### Cache methods here?
0569: return methods.toArray(new MethodDocImpl[methods.length()]);
0570: }
0571:
0572: /**
0573: * Return included methods in class.
0574: *
0575: * @return an array of MethodDocImpl for representing the visible
0576: * methods in this class. Does not include constructors.
0577: */
0578: public MethodDoc[] methods() {
0579: return methods(true);
0580: }
0581:
0582: /**
0583: * Return constructors in class.
0584: *
0585: * @param filter include only the included constructors if filter==true
0586: * @return an array of ConstructorDocImpl for representing the visible
0587: * constructors in this class.
0588: */
0589: public ConstructorDoc[] constructors(boolean filter) {
0590: Name.Table names = tsym.name.table;
0591: List<ConstructorDocImpl> constructors = List.nil();
0592: for (Scope.Entry e = tsym.members().elems; e != null; e = e.sibling) {
0593: if (e.sym != null && e.sym.kind == Kinds.MTH
0594: && e.sym.name == names.init) {
0595: MethodSymbol s = (MethodSymbol) e.sym;
0596: if (!filter || env.shouldDocument(s)) {
0597: constructors = constructors.prepend(env
0598: .getConstructorDoc(s));
0599: }
0600: }
0601: }
0602: //### Cache constructors here?
0603: return constructors.toArray(new ConstructorDocImpl[constructors
0604: .length()]);
0605: }
0606:
0607: /**
0608: * Return included constructors in class.
0609: *
0610: * @return an array of ConstructorDocImpl for representing the visible
0611: * constructors in this class.
0612: */
0613: public ConstructorDoc[] constructors() {
0614: return constructors(true);
0615: }
0616:
0617: /**
0618: * Adds all inner classes of this class, and their
0619: * inner classes recursively, to the list l.
0620: */
0621: void addAllClasses(ListBuffer<ClassDocImpl> l, boolean filtered) {
0622: try {
0623: if (isSynthetic())
0624: return;
0625: // sometimes synthetic classes are not marked synthetic
0626: if (!JavadocTool.isValidClassName(tsym.name.toString()))
0627: return;
0628: if (filtered && !env.shouldDocument(tsym))
0629: return;
0630: if (l.contains(this ))
0631: return;
0632: l.append(this );
0633: List<ClassDocImpl> more = List.nil();
0634: for (Scope.Entry e = tsym.members().elems; e != null; e = e.sibling) {
0635: if (e.sym != null && e.sym.kind == Kinds.TYP) {
0636: ClassSymbol s = (ClassSymbol) e.sym;
0637: ClassDocImpl c = env.getClassDoc(s);
0638: if (c.isSynthetic())
0639: continue;
0640: if (c != null)
0641: more = more.prepend(c);
0642: }
0643: }
0644: // this extra step preserves the ordering from oldjavadoc
0645: for (; more.nonEmpty(); more = more.tail) {
0646: more.head.addAllClasses(l, filtered);
0647: }
0648: } catch (CompletionFailure e) {
0649: // quietly ignore completion failures
0650: }
0651: }
0652:
0653: /**
0654: * Return inner classes within this class.
0655: *
0656: * @param filter include only the included inner classes if filter==true.
0657: * @return an array of ClassDocImpl for representing the visible
0658: * classes defined in this class. Anonymous and local classes
0659: * are not included.
0660: */
0661: public ClassDoc[] innerClasses(boolean filter) {
0662: ListBuffer<ClassDocImpl> innerClasses = new ListBuffer<ClassDocImpl>();
0663: for (Scope.Entry e = tsym.members().elems; e != null; e = e.sibling) {
0664: if (e.sym != null && e.sym.kind == Kinds.TYP) {
0665: ClassSymbol s = (ClassSymbol) e.sym;
0666: if ((s.flags_field & Flags.SYNTHETIC) != 0)
0667: continue;
0668: if (!filter || env.isVisible(s)) {
0669: innerClasses.prepend(env.getClassDoc(s));
0670: }
0671: }
0672: }
0673: //### Cache classes here?
0674: return innerClasses.toArray(new ClassDocImpl[innerClasses
0675: .length()]);
0676: }
0677:
0678: /**
0679: * Return included inner classes within this class.
0680: *
0681: * @return an array of ClassDocImpl for representing the visible
0682: * classes defined in this class. Anonymous and local classes
0683: * are not included.
0684: */
0685: public ClassDoc[] innerClasses() {
0686: return innerClasses(true);
0687: }
0688:
0689: /**
0690: * Find a class within the context of this class.
0691: * Search order: qualified name, in this class (inner),
0692: * in this package, in the class imports, in the package
0693: * imports.
0694: * Return the ClassDocImpl if found, null if not found.
0695: */
0696: //### The specified search order is not the normal rule the
0697: //### compiler would use. Leave as specified or change it?
0698: public ClassDoc findClass(String className) {
0699: ClassDoc searchResult = searchClass(className);
0700: if (searchResult == null) {
0701: ClassDocImpl enclosingClass = (ClassDocImpl) containingClass();
0702: //Expand search space to include enclosing class.
0703: while (enclosingClass != null
0704: && enclosingClass.containingClass() != null) {
0705: enclosingClass = (ClassDocImpl) enclosingClass
0706: .containingClass();
0707: }
0708: searchResult = enclosingClass == null ? null
0709: : enclosingClass.searchClass(className);
0710: }
0711: return searchResult;
0712: }
0713:
0714: private ClassDoc searchClass(String className) {
0715: Name.Table names = tsym.name.table;
0716:
0717: // search by qualified name first
0718: ClassDoc cd = env.lookupClass(className);
0719: if (cd != null) {
0720: return cd;
0721: }
0722:
0723: // search inner classes
0724: //### Add private entry point to avoid creating array?
0725: //### Replicate code in innerClasses here to avoid consing?
0726: ClassDoc innerClasses[] = innerClasses();
0727: for (int i = 0; i < innerClasses.length; i++) {
0728: if (innerClasses[i].name().equals(className) ||
0729: //### This is from original javadoc but it looks suspicious to me...
0730: //### I believe it is attempting to compensate for the confused
0731: //### convention of including the nested class qualifiers in the
0732: //### 'name' of the inner class, rather than the true simple name.
0733: innerClasses[i].name().endsWith(className)) {
0734: return innerClasses[i];
0735: } else {
0736: ClassDoc innercd = ((ClassDocImpl) innerClasses[i])
0737: .searchClass(className);
0738: if (innercd != null) {
0739: return innercd;
0740: }
0741: }
0742: }
0743:
0744: // check in this package
0745: cd = containingPackage().findClass(className);
0746: if (cd != null) {
0747: return cd;
0748: }
0749:
0750: // make sure that this symbol has been completed
0751: if (tsym.completer != null) {
0752: tsym.complete();
0753: }
0754:
0755: // search imports
0756:
0757: if (tsym.sourcefile != null) {
0758:
0759: //### This information is available only for source classes.
0760:
0761: Env<AttrContext> compenv = env.enter.getEnv(tsym);
0762: if (compenv == null)
0763: return null;
0764:
0765: Scope s = compenv.toplevel.namedImportScope;
0766: for (Scope.Entry e = s.lookup(names.fromString(className)); e.scope != null; e = e
0767: .next()) {
0768: if (e.sym.kind == Kinds.TYP) {
0769: ClassDoc c = env.getClassDoc((ClassSymbol) e.sym);
0770: return c;
0771: }
0772: }
0773:
0774: s = compenv.toplevel.starImportScope;
0775: for (Scope.Entry e = s.lookup(names.fromString(className)); e.scope != null; e = e
0776: .next()) {
0777: if (e.sym.kind == Kinds.TYP) {
0778: ClassDoc c = env.getClassDoc((ClassSymbol) e.sym);
0779: return c;
0780: }
0781: }
0782: }
0783:
0784: return null; // not found
0785: }
0786:
0787: private boolean hasParameterTypes(MethodSymbol method,
0788: String[] argTypes) {
0789:
0790: if (argTypes == null) {
0791: // wildcard
0792: return true;
0793: }
0794:
0795: int i = 0;
0796: List<Type> types = method.type.getParameterTypes();
0797:
0798: if (argTypes.length != types.length()) {
0799: return false;
0800: }
0801:
0802: for (Type t : types) {
0803: String argType = argTypes[i++];
0804: // For vararg method, "T..." matches type T[].
0805: if (i == argTypes.length) {
0806: argType = argType.replace("...", "[]");
0807: }
0808: if (!hasTypeName(env.types.erasure(t), argType)) { //###(gj)
0809: return false;
0810: }
0811: }
0812: return true;
0813: }
0814:
0815: // where
0816: private boolean hasTypeName(Type t, String name) {
0817: return name.equals(TypeMaker.getTypeName(t, true))
0818: || name.equals(TypeMaker.getTypeName(t, false))
0819: || (qualifiedName() + "." + name).equals(TypeMaker
0820: .getTypeName(t, true));
0821: }
0822:
0823: /**
0824: * Find a method in this class scope.
0825: * Search order: this class, interfaces, superclasses, outerclasses.
0826: * Note that this is not necessarily what the compiler would do!
0827: *
0828: * @param methodName the unqualified name to search for.
0829: * @param paramTypeArray the array of Strings for method parameter types.
0830: * @return the first MethodDocImpl which matches, null if not found.
0831: */
0832: public MethodDocImpl findMethod(String methodName,
0833: String[] paramTypes) {
0834: // Use hash table 'searched' to avoid searching same class twice.
0835: //### It is not clear how this could happen.
0836: return searchMethod(methodName, paramTypes,
0837: new HashSet<ClassDocImpl>());
0838: }
0839:
0840: private MethodDocImpl searchMethod(String methodName,
0841: String[] paramTypes, Set<ClassDocImpl> searched) {
0842: //### Note that this search is not necessarily what the compiler would do!
0843:
0844: ClassDocImpl cdi;
0845: MethodDocImpl mdi;
0846:
0847: if (searched.contains(this )) {
0848: return null;
0849: }
0850: searched.add(this );
0851:
0852: //DEBUG
0853: /*---------------------------------*
0854: System.out.print("searching " + this + " for " + methodName);
0855: if (paramTypes == null) {
0856: System.out.println("()");
0857: } else {
0858: System.out.print("(");
0859: for (int k=0; k < paramTypes.length; k++) {
0860: System.out.print(paramTypes[k]);
0861: if ((k + 1) < paramTypes.length) {
0862: System.out.print(", ");
0863: }
0864: }
0865: System.out.println(")");
0866: }
0867: *---------------------------------*/
0868:
0869: // search current class
0870: Name.Table names = tsym.name.table;
0871: Scope.Entry e = tsym.members().lookup(
0872: names.fromString(methodName));
0873:
0874: //### Using modifier filter here isn't really correct,
0875: //### but emulates the old behavior. Instead, we should
0876: //### apply the normal rules of visibility and inheritance.
0877:
0878: if (paramTypes == null) {
0879: // If no parameters specified, we are allowed to return
0880: // any method with a matching name. In practice, the old
0881: // code returned the first method, which is now the last!
0882: // In order to provide textually identical results, we
0883: // attempt to emulate the old behavior.
0884: MethodSymbol lastFound = null;
0885: for (; e.scope != null; e = e.next()) {
0886: if (e.sym.kind == Kinds.MTH) {
0887: //### Should intern methodName as Name.
0888: if (e.sym.name.toString().equals(methodName)) {
0889: lastFound = (MethodSymbol) e.sym;
0890: }
0891: }
0892: }
0893: if (lastFound != null) {
0894: return env.getMethodDoc(lastFound);
0895: }
0896: } else {
0897: for (; e.scope != null; e = e.next()) {
0898: if (e.sym != null && e.sym.kind == Kinds.MTH) {
0899: //### Should intern methodName as Name.
0900: if (hasParameterTypes((MethodSymbol) e.sym,
0901: paramTypes)) {
0902: return env.getMethodDoc((MethodSymbol) e.sym);
0903: }
0904: }
0905: }
0906: }
0907:
0908: //### If we found a MethodDoc above, but which did not pass
0909: //### the modifier filter, we should return failure here!
0910:
0911: // search superclass
0912: cdi = (ClassDocImpl) super class();
0913: if (cdi != null) {
0914: mdi = cdi.searchMethod(methodName, paramTypes, searched);
0915: if (mdi != null) {
0916: return mdi;
0917: }
0918: }
0919:
0920: // search interfaces
0921: ClassDoc intf[] = interfaces();
0922: for (int i = 0; i < intf.length; i++) {
0923: cdi = (ClassDocImpl) intf[i];
0924: mdi = cdi.searchMethod(methodName, paramTypes, searched);
0925: if (mdi != null) {
0926: return mdi;
0927: }
0928: }
0929:
0930: // search enclosing class
0931: cdi = (ClassDocImpl) containingClass();
0932: if (cdi != null) {
0933: mdi = cdi.searchMethod(methodName, paramTypes, searched);
0934: if (mdi != null) {
0935: return mdi;
0936: }
0937: }
0938:
0939: //###(gj) As a temporary measure until type variables are better
0940: //### handled, try again without the parameter types.
0941: //### This should most often find the right method, and occassionally
0942: //### find the wrong one.
0943: //if (paramTypes != null) {
0944: // return findMethod(methodName, null);
0945: //}
0946:
0947: return null;
0948: }
0949:
0950: /**
0951: * Find constructor in this class.
0952: *
0953: * @param constrName the unqualified name to search for.
0954: * @param paramTypeArray the array of Strings for constructor parameters.
0955: * @return the first ConstructorDocImpl which matches, null if not found.
0956: */
0957: public ConstructorDoc findConstructor(String constrName,
0958: String[] paramTypes) {
0959: Name.Table names = tsym.name.table;
0960: for (Scope.Entry e = tsym.members().lookup(
0961: names.fromString("<init>")); e.scope != null; e = e
0962: .next()) {
0963: if (e.sym.kind == Kinds.MTH) {
0964: if (hasParameterTypes((MethodSymbol) e.sym, paramTypes)) {
0965: return env.getConstructorDoc((MethodSymbol) e.sym);
0966: }
0967: }
0968: }
0969:
0970: //###(gj) As a temporary measure until type variables are better
0971: //### handled, try again without the parameter types.
0972: //### This will often find the right constructor, and occassionally
0973: //### find the wrong one.
0974: //if (paramTypes != null) {
0975: // return findConstructor(constrName, null);
0976: //}
0977:
0978: return null;
0979: }
0980:
0981: /**
0982: * Find a field in this class scope.
0983: * Search order: this class, outerclasses, interfaces,
0984: * superclasses. IMP: If see tag is defined in an inner class,
0985: * which extends a super class and if outerclass and the super
0986: * class have a visible field in common then Java compiler cribs
0987: * about the ambiguity, but the following code will search in the
0988: * above given search order.
0989: *
0990: * @param fieldName the unqualified name to search for.
0991: * @return the first FieldDocImpl which matches, null if not found.
0992: */
0993: public FieldDoc findField(String fieldName) {
0994: return searchField(fieldName, new HashSet<ClassDocImpl>());
0995: }
0996:
0997: private FieldDocImpl searchField(String fieldName,
0998: Set<ClassDocImpl> searched) {
0999: Name.Table names = tsym.name.table;
1000: if (searched.contains(this )) {
1001: return null;
1002: }
1003: searched.add(this );
1004:
1005: for (Scope.Entry e = tsym.members().lookup(
1006: names.fromString(fieldName)); e.scope != null; e = e
1007: .next()) {
1008: if (e.sym.kind == Kinds.VAR) {
1009: //### Should intern fieldName as Name.
1010: return env.getFieldDoc((VarSymbol) e.sym);
1011: }
1012: }
1013:
1014: //### If we found a FieldDoc above, but which did not pass
1015: //### the modifier filter, we should return failure here!
1016:
1017: ClassDocImpl cdi = (ClassDocImpl) containingClass();
1018: if (cdi != null) {
1019: FieldDocImpl fdi = cdi.searchField(fieldName, searched);
1020: if (fdi != null) {
1021: return fdi;
1022: }
1023: }
1024:
1025: // search superclass
1026: cdi = (ClassDocImpl) super class();
1027: if (cdi != null) {
1028: FieldDocImpl fdi = cdi.searchField(fieldName, searched);
1029: if (fdi != null) {
1030: return fdi;
1031: }
1032: }
1033:
1034: // search interfaces
1035: ClassDoc intf[] = interfaces();
1036: for (int i = 0; i < intf.length; i++) {
1037: cdi = (ClassDocImpl) intf[i];
1038: FieldDocImpl fdi = cdi.searchField(fieldName, searched);
1039: if (fdi != null) {
1040: return fdi;
1041: }
1042: }
1043:
1044: return null;
1045: }
1046:
1047: /**
1048: * Get the list of classes declared as imported.
1049: * These are called "single-type-import declarations" in the JLS.
1050: * This method is deprecated in the ClassDoc interface.
1051: *
1052: * @return an array of ClassDocImpl representing the imported classes.
1053: *
1054: * @deprecated Import declarations are implementation details that
1055: * should not be exposed here. In addition, not all imported
1056: * classes are imported through single-type-import declarations.
1057: */
1058: @Deprecated
1059: public ClassDoc[] importedClasses() {
1060: // information is not available for binary classfiles
1061: if (tsym.sourcefile == null)
1062: return new ClassDoc[0];
1063:
1064: ListBuffer<ClassDocImpl> importedClasses = new ListBuffer<ClassDocImpl>();
1065:
1066: Env<AttrContext> compenv = env.enter.getEnv(tsym);
1067: if (compenv == null)
1068: return new ClassDocImpl[0];
1069:
1070: Name asterisk = tsym.name.table.asterisk;
1071: for (JCTree t : compenv.toplevel.defs) {
1072: if (t.getTag() == JCTree.IMPORT) {
1073: JCTree imp = ((JCImport) t).qualid;
1074: if ((TreeInfo.name(imp) != asterisk)
1075: && (imp.type.tsym.kind & Kinds.TYP) != 0) {
1076: importedClasses.append(env
1077: .getClassDoc((ClassSymbol) imp.type.tsym));
1078: }
1079: }
1080: }
1081:
1082: return importedClasses.toArray(new ClassDocImpl[importedClasses
1083: .length()]);
1084: }
1085:
1086: /**
1087: * Get the list of packages declared as imported.
1088: * These are called "type-import-on-demand declarations" in the JLS.
1089: * This method is deprecated in the ClassDoc interface.
1090: *
1091: * @return an array of PackageDocImpl representing the imported packages.
1092: *
1093: * ###NOTE: the syntax supports importing all inner classes from a class as well.
1094: * @deprecated Import declarations are implementation details that
1095: * should not be exposed here. In addition, this method's
1096: * return type does not allow for all type-import-on-demand
1097: * declarations to be returned.
1098: */
1099: @Deprecated
1100: public PackageDoc[] importedPackages() {
1101: // information is not available for binary classfiles
1102: if (tsym.sourcefile == null)
1103: return new PackageDoc[0];
1104:
1105: ListBuffer<PackageDocImpl> importedPackages = new ListBuffer<PackageDocImpl>();
1106:
1107: //### Add the implicit "import java.lang.*" to the result
1108: Name.Table names = tsym.name.table;
1109: importedPackages.append(env.getPackageDoc(env.reader
1110: .enterPackage(names.java_lang)));
1111:
1112: Env<AttrContext> compenv = env.enter.getEnv(tsym);
1113: if (compenv == null)
1114: return new PackageDocImpl[0];
1115:
1116: for (JCTree t : compenv.toplevel.defs) {
1117: if (t.getTag() == JCTree.IMPORT) {
1118: JCTree imp = ((JCImport) t).qualid;
1119: if (TreeInfo.name(imp) == names.asterisk) {
1120: JCFieldAccess sel = (JCFieldAccess) imp;
1121: Symbol s = sel.selected.type.tsym;
1122: PackageDocImpl pdoc = env.getPackageDoc(s.packge());
1123: if (!importedPackages.contains(pdoc))
1124: importedPackages.append(pdoc);
1125: }
1126: }
1127: }
1128:
1129: return importedPackages
1130: .toArray(new PackageDocImpl[importedPackages.length()]);
1131: }
1132:
1133: /**
1134: * Return the type's dimension information.
1135: * Always return "", as this is not an array type.
1136: */
1137: public String dimension() {
1138: return "";
1139: }
1140:
1141: /**
1142: * Return this type as a class, which it already is.
1143: */
1144: public ClassDoc asClassDoc() {
1145: return this ;
1146: }
1147:
1148: /**
1149: * Return null (unless overridden), as this is not an annotation type.
1150: */
1151: public AnnotationTypeDoc asAnnotationTypeDoc() {
1152: return null;
1153: }
1154:
1155: /**
1156: * Return null, as this is not a class instantiation.
1157: */
1158: public ParameterizedType asParameterizedType() {
1159: return null;
1160: }
1161:
1162: /**
1163: * Return null, as this is not a type variable.
1164: */
1165: public TypeVariable asTypeVariable() {
1166: return null;
1167: }
1168:
1169: /**
1170: * Return null, as this is not a wildcard type.
1171: */
1172: public WildcardType asWildcardType() {
1173: return null;
1174: }
1175:
1176: /**
1177: * Return false, as this is not a primitive type.
1178: */
1179: public boolean isPrimitive() {
1180: return false;
1181: }
1182:
1183: //--- Serialization ---
1184:
1185: //### These methods ignore modifier filter.
1186:
1187: /**
1188: * Return true if this class implements <code>java.io.Serializable</code>.
1189: *
1190: * Since <code>java.io.Externalizable</code> extends
1191: * <code>java.io.Serializable</code>,
1192: * Externalizable objects are also Serializable.
1193: */
1194: public boolean isSerializable() {
1195: try {
1196: return env.types.isSubtype(type, env.syms.serializableType);
1197: } catch (CompletionFailure ex) {
1198: // quietly ignore completion failures
1199: return false;
1200: }
1201: }
1202:
1203: /**
1204: * Return true if this class implements
1205: * <code>java.io.Externalizable</code>.
1206: */
1207: public boolean isExternalizable() {
1208: try {
1209: return env.types
1210: .isSubtype(type, env.externalizableSym.type);
1211: } catch (CompletionFailure ex) {
1212: // quietly ignore completion failures
1213: return false;
1214: }
1215: }
1216:
1217: /**
1218: * Return the serialization methods for this class.
1219: *
1220: * @return an array of <code>MethodDocImpl</code> that represents
1221: * the serialization methods for this class.
1222: */
1223: public MethodDoc[] serializationMethods() {
1224: if (serializedForm == null) {
1225: serializedForm = new SerializedForm(env, tsym, this );
1226: }
1227: //### Clone this?
1228: return serializedForm.methods();
1229: }
1230:
1231: /**
1232: * Return the Serializable fields of class.<p>
1233: *
1234: * Return either a list of default fields documented by
1235: * <code>serial</code> tag<br>
1236: * or return a single <code>FieldDoc</code> for
1237: * <code>serialPersistentField</code> member.
1238: * There should be a <code>serialField</code> tag for
1239: * each Serializable field defined by an <code>ObjectStreamField</code>
1240: * array component of <code>serialPersistentField</code>.
1241: *
1242: * @returns an array of <code>FieldDoc</code> for the Serializable fields
1243: * of this class.
1244: *
1245: * @see #definesSerializableFields()
1246: * @see SerialFieldTagImpl
1247: */
1248: public FieldDoc[] serializableFields() {
1249: if (serializedForm == null) {
1250: serializedForm = new SerializedForm(env, tsym, this );
1251: }
1252: //### Clone this?
1253: return serializedForm.fields();
1254: }
1255:
1256: /**
1257: * Return true if Serializable fields are explicitly defined with
1258: * the special class member <code>serialPersistentFields</code>.
1259: *
1260: * @see #serializableFields()
1261: * @see SerialFieldTagImpl
1262: */
1263: public boolean definesSerializableFields() {
1264: if (!isSerializable() || isExternalizable()) {
1265: return false;
1266: } else {
1267: if (serializedForm == null) {
1268: serializedForm = new SerializedForm(env, tsym, this );
1269: }
1270: //### Clone this?
1271: return serializedForm.definesSerializableFields();
1272: }
1273: }
1274:
1275: /**
1276: * Determine if a class is a RuntimeException.
1277: * <p>
1278: * Used only by ThrowsTagImpl.
1279: */
1280: boolean isRuntimeException() {
1281: return tsym.isSubClass(env.syms.runtimeExceptionType.tsym,
1282: env.types);
1283: }
1284:
1285: /**
1286: * Return the source position of the entity, or null if
1287: * no position is available.
1288: */
1289: public SourcePosition position() {
1290: if (tsym.sourcefile == null)
1291: return null;
1292: return SourcePositionImpl.make(tsym.sourcefile.toString(),
1293: (tree == null) ? Position.NOPOS : tree.pos, lineMap);
1294: }
1295: }
|