/* * Copyright (c) 1997, 2006, Oracle and/or its affiliates. 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. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.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.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); 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. * <pre> * Examples: * for java.util.Hashtable * return Hashtable * for java.util.Map.Entry * return Map.Entry * </pre> */ public String name() { return getClassName(tsym, false); } /** * Return the qualified class name as a String. * <pre> * Example: * for java.util.Hashtable * return java.util.Hashtable * if no qualifier, just return flat name * </pre> */ public String qualifiedName() { return getClassName(tsym, true); } /** * Return unqualified name of type excluding any dimension information. * <p> * For example, a two dimensional array of String returns 'String'. */ public String typeName() { return name(); } /** * Return qualified name of type excluding any dimension information. *<p> * For example, a two dimensional array of String * returns 'java.lang.String'. */ public String qualifiedTypeName() { return qualifiedName(); } /** * Return the simple name of this type. */ public String simpleTypeName() { return tsym.name.toString(); } /** * Return the qualified name and any type parameters. * Each parameter is a type variable with optional bounds. */ public String toString() { return classToString(env, tsym, true); } /** * Return the class name as a string. If "full" is true the name is * qualified, otherwise it is qualified by its enclosing class(es) only. */ static String getClassName(ClassSymbol c, boolean full) { if (full) { return c.getQualifiedName().toString(); } else { String n = ""; for ( ; c != null; c = c.owner.enclClass()) { n = c.name + (n.equals("") ? "" : ".") + n; } return n; } } /** * Return the class name with any type parameters as a string. * Each parameter is a type variable with optional bounds. * If "full" is true all names are qualified, otherwise they are * qualified by their enclosing class(es) only. */ static String classToString(DocEnv env, ClassSymbol c, boolean full) { StringBuffer s = new StringBuffer(); if (!c.isInner()) { // if c is not an inner class s.append(getClassName(c, full)); } else { // c is an inner class, so include type params of outer. ClassSymbol encl = c.owner.enclClass(); s.append(classToString(env, encl, full)) .append('.') .append(c.name); } s.append(TypeMaker.typeParametersString(env, c, full)); return s.toString(); } /** * Is this class (or any enclosing class) generic? That is, does * it have type parameters? */ static boolean isGeneric(ClassSymbol c) { return c.type.allparams().nonEmpty(); } /** * Return the formal type parameters of this class or interface. * Return an empty array if there are none. */ public TypeVariable[] typeParameters() { if (env.legacyDoclet) { return new TypeVariable[0]; } TypeVariable res[] = new TypeVariable[type.getTypeArguments().length()]; TypeMaker.getTypes(env, type.getTypeArguments(), res); return res; } /** * Return the type parameter tags of this class or interface. */ public ParamTag[] typeParamTags() { return (env.legacyDoclet) ? new ParamTag[0] : comment().typeParamTags(); } /** * Return the modifier string for this class. If it's an interface * exclude 'abstract' keyword from the modifier string */ public String modifiers() { return Modifier.toString(modifierSpecifier()); } public int modifierSpecifier() { int modifiers = getModifiers(); return (isInterface() || isAnnotationType()) ? modifiers & ~Modifier.ABSTRACT : modifiers; } /** * Return the superclass of this class * * @return the ClassDocImpl for the superclass of this class, null * if there is no superclass. */ public ClassDoc superclass() { if (isInterface() || isAnnotationType()) return null; if (tsym == env.syms.objectType.tsym) return null; ClassSymbol c = (ClassSymbol)env.types.supertype(type).tsym; if (c == null || c == tsym) c = (ClassSymbol)env.syms.objectType.tsym; return env.getClassDoc(c); } /** * Return the superclass of this class. Return null if this is an * interface. A superclass is represented by either a * <code>ClassDoc</code> or a <code>ParameterizedType</code>. */ public com.sun.javadoc.Type superclassType() { if (isInterface() || isAnnotationType() || (tsym == env.syms.objectType.tsym)) return null; Type sup = env.types.supertype(type); return TypeMaker.getType(env, (sup != type) ? sup : env.syms.objectType); } /** * Test whether this class is a subclass of the specified class. * * @param cd the candidate superclass. * @return true if cd is a superclass of this class. */ public boolean subclassOf(ClassDoc cd) { return tsym.isSubClass(((ClassDocImpl)cd).tsym, env.types); } /** * Return interfaces implemented by this class or interfaces * extended by this interface. * * @return An array of ClassDocImpl representing the interfaces. * Return an empty array if there are no interfaces. */ public ClassDoc[] interfaces() { ListBuffer<ClassDocImpl> ta = new ListBuffer<ClassDocImpl>(); for (Type t : env.types.interfaces(type)) { ta.append(env.getClassDoc((ClassSymbol)t.tsym)); } //### Cache ta here? return ta.toArray(new ClassDocImpl[ta.length()]); } /** * Return interfaces implemented by this class or interfaces extended * by this interface. Includes only directly-declared interfaces, not * inherited interfaces. * Return an empty array if there are no interfaces. */ public com.sun.javadoc.Type[] interfaceTypes() { //### Cache result here? return TypeMaker.getTypes(env, env.types.interfaces(type)); } /** * Return fields in class. * @param filter include only the included fields if filter==true */ public FieldDoc[] fields(boolean filter) { return fields(filter, false); } /** * Return included fields in class. */ public FieldDoc[] fields() { return fields(true, false); } /** * Return the enum constants if this is an enum type. */ public FieldDoc[] enumConstants() { return fields(false, true); } /** * Return fields in class. * @param filter if true, return only the included fields * @param enumConstants if true, return the enum constants instead */ private FieldDoc[] fields(boolean filter, boolean enumConstants) { List<FieldDocImpl> fields = List.nil(); for (Scope.Entry e = tsym.members().elems; e != null; e = e.sibling) { if (e.sym != null && e.sym.kind == VAR) { VarSymbol s = (VarSymbol)e.sym; boolean isEnum = ((s.flags() & Flags.ENUM) != 0) && !env.legacyDoclet; if (isEnum == enumConstants && (!filter || env.shouldDocument(s))) { fields = fields.prepend(env.getFieldDoc(s)); } } } return fields.toArray(new FieldDocImpl[fields.length()]); } /** * Return methods in class. * This method is overridden by AnnotationTypeDocImpl. * * @param filter include only the included methods if filter==true * @return an array of MethodDocImpl for representing the visible * methods in this class. Does not include constructors. */ public MethodDoc[] methods(boolean filter) { Name.Table names = tsym.name.table; List<MethodDocImpl> methods = List.nil(); for (Scope.Entry e = tsym.members().elems; e != null; e = e.sibling) { if (e.sym != null && e.sym.kind == Kinds.MTH && e.sym.name != names.init) { MethodSymbol s = (MethodSymbol)e.sym; if (!filter || env.shouldDocument(s)) { methods = methods.prepend(env.getMethodDoc(s)); } } } //### Cache methods here? return methods.toArray(new MethodDocImpl[methods.length()]); } /** * Return included methods in class. * * @return an array of MethodDocImpl for representing the visible * methods in this class. Does not include constructors. */ public MethodDoc[] methods() { return methods(true); } /** * Return constructors in class. * * @param filter include only the included constructors if filter==true * @return an array of ConstructorDocImpl for representing the visible * constructors in this class. */ public ConstructorDoc[] constructors(boolean filter) { Name.Table names = tsym.name.table; List<ConstructorDocImpl> constructors = List.nil(); for (Scope.Entry e = tsym.members().elems; e != null; e = e.sibling) { if (e.sym != null && e.sym.kind == Kinds.MTH && e.sym.name == names.init) { MethodSymbol s = (MethodSymbol)e.sym; if (!filter || env.shouldDocument(s)) { constructors = constructors.prepend(env.getConstructorDoc(s)); } } } //### Cache constructors here? return constructors.toArray(new ConstructorDocImpl[constructors.length()]); } /** * Return included constructors in class. * * @return an array of ConstructorDocImpl for representing the visible * constructors in this class. */ public ConstructorDoc[] constructors() { return constructors(true); } /** * Adds all inner classes of this class, and their * inner classes recursively, to the list l. */ void addAllClasses(ListBuffer<ClassDocImpl> l, boolean filtered) { try { if (isSynthetic()) return; // sometimes synthetic classes are not marked synthetic if (!JavadocTool.isValidClassName(tsym.name.toString())) return; if (filtered && !env.shouldDocument(tsym)) return; if (l.contains(this)) return; l.append(this); List<ClassDocImpl> more = List.nil(); for (Scope.Entry e = tsym.members().elems; e != null; e = e.sibling) { if (e.sym != null && e.sym.kind == Kinds.TYP) { ClassSymbol s = (ClassSymbol)e.sym; ClassDocImpl c = env.getClassDoc(s); if (c.isSynthetic()) continue; if (c != null) more = more.prepend(c); } } // this extra step preserves the ordering from oldjavadoc for (; more.nonEmpty(); more=more.tail) { more.head.addAllClasses(l, filtered); } } catch (CompletionFailure e) { // quietly ignore completion failures } } /** * Return inner classes within this class. * * @param filter include only the included inner classes if filter==true. * @return an array of ClassDocImpl for representing the visible * classes defined in this class. Anonymous and local classes * are not included. */ public ClassDoc[] innerClasses(boolean filter) { ListBuffer<ClassDocImpl> innerClasses = new ListBuffer<ClassDocImpl>(); for (Scope.Entry e = tsym.members().elems; e != null; e = e.sibling) { if (e.sym != null && e.sym.kind == Kinds.TYP) { ClassSymbol s = (ClassSymbol)e.sym; if ((s.flags_field & Flags.SYNTHETIC) != 0) continue; if (!filter || env.isVisible(s)) { innerClasses.prepend(env.getClassDoc(s)); } } } //### Cache classes here? return innerClasses.toArray(new ClassDocImpl[innerClasses.length()]); } /** * Return included inner classes within this class. * * @return an array of ClassDocImpl for representing the visible * classes defined in this class. Anonymous and local classes * are not included. */ public ClassDoc[] innerClasses() { return innerClasses(true); } /** * Find a class within the context of this class. * Search order: qualified name, in this class (inner), * in this package, in the class imports, in the package * imports. * Return the ClassDocImpl if found, null if not found. */ //### The specified search order is not the normal rule the //### compiler would use. Leave as specified or change it? public ClassDoc findClass(String className) { ClassDoc searchResult = searchClass(className); if (searchResult == null) { ClassDocImpl enclosingClass = (ClassDocImpl)containingClass(); //Expand search space to include enclosing class. while (enclosingClass != null && enclosingClass.containingClass() != null) { enclosingClass = (ClassDocImpl)enclosingClass.containingClass(); } searchResult = enclosingClass == null ? null : enclosingClass.searchClass(className); } return searchResult; } private ClassDoc searchClass(String className) { Name.Table names = tsym.name.table; // search by qualified name first ClassDoc cd = env.lookupClass(className); if (cd != null) { return cd; } // search inner classes //### Add private entry point to avoid creating array? //### Replicate code in innerClasses here to avoid consing? ClassDoc innerClasses[] = innerClasses(); for (int i = 0; i < innerClasses.length; i++) { if (innerClasses[i].name().equals(className) || //### This is from original javadoc but it looks suspicious to me... //### I believe it is attempting to compensate for the confused //### convention of including the nested class qualifiers in the //### 'name' of the inner class, rather than the true simple name. innerClasses[i].name().endsWith(className)) { return innerClasses[i]; } else { ClassDoc innercd = ((ClassDocImpl) innerClasses[i]).searchClass(className); if (innercd != null) { return innercd; } } } // check in this package cd = containingPackage().findClass(className); if (cd != null) { return cd; } // make sure that this symbol has been completed if (tsym.completer != null) { tsym.complete(); } // search imports if (tsym.sourcefile != null) { //### This information is available only for source classes. Env<AttrContext> compenv = env.enter.getEnv(tsym); if (compenv == null) return null; Scope s = compenv.toplevel.namedImportScope; for (Scope.Entry e = s.lookup(names.fromString(className)); e.scope != null; e = e.next()) { if (e.sym.kind == Kinds.TYP) { ClassDoc c = env.getClassDoc((ClassSymbol)e.sym); return c; } } s = compenv.toplevel.starImportScope; for (Scope.Entry e = s.lookup(names.fromString(className)); e.scope != null; e = e.next()) { if (e.sym.kind == Kinds.TYP) { ClassDoc c = env.getClassDoc((ClassSymbol)e.sym); return c; } } } return null; // not found } private boolean hasParameterTypes(MethodSymbol method, String[] argTypes) { if (argTypes == null) { // wildcard return true; } int i = 0; List<Type> types = method.type.getParameterTypes(); if (argTypes.length != types.length()) { return false; } for (Type t : types) { String argType = argTypes[i++]; // For vararg method, "T..." matches type T[]. if (i == argTypes.length) { argType = argType.replace("...", "[]"); } if (!hasTypeName(env.types.erasure(t), argType)) { //###(gj) return false; } } return true; } // where private boolean hasTypeName(Type t, String name) { return name.equals(TypeMaker.getTypeName(t, true)) || name.equals(TypeMaker.getTypeName(t, false)) || (qualifiedName() + "." + name).equals(TypeMaker.getTypeName(t, true)); } /** * Find a method in this class scope. * Search order: this class, interfaces, superclasses, outerclasses. * Note that this is not necessarily what the compiler would do! * * @param methodName the unqualified name to search for. * @param paramTypeArray the array of Strings for method parameter types. * @return the first MethodDocImpl which matches, null if not found. */ public MethodDocImpl findMethod(String methodName, String[] paramTypes) { // Use hash table 'searched' to avoid searching same class twice. //### It is not clear how this could happen. return searchMethod(methodName, paramTypes, new HashSet<ClassDocImpl>()); } private MethodDocImpl searchMethod(String methodName, String[] paramTypes, Set<ClassDocImpl> searched) { //### Note that this search is not necessarily what the compiler would do! ClassDocImpl cdi; MethodDocImpl mdi; if (searched.contains(this)) { return null; } searched.add(this); //DEBUG /*---------------------------------* System.out.print("searching " + this + " for " + methodName); if (paramTypes == null) { System.out.println("()"); } else { System.out.print("("); for (int k=0; k < paramTypes.length; k++) { System.out.print(paramTypes[k]); if ((k + 1) < paramTypes.length) { System.out.print(", "); } } System.out.println(")"); } *---------------------------------*/ // search current class Name.Table names = tsym.name.table; Scope.Entry e = tsym.members().lookup(names.fromString(methodName)); //### Using modifier filter here isn't really correct, //### but emulates the old behavior. Instead, we should //### apply the normal rules of visibility and inheritance. if (paramTypes == null) { // If no parameters specified, we are allowed to return // any method with a matching name. In practice, the old // code returned the first method, which is now the last! // In order to provide textually identical results, we // attempt to emulate the old behavior. MethodSymbol lastFound = null; for (; e.scope != null; e = e.next()) { if (e.sym.kind == Kinds.MTH) { //### Should intern methodName as Name. if (e.sym.name.toString().equals(methodName)) { lastFound = (MethodSymbol)e.sym; } } } if (lastFound != null) { return env.getMethodDoc(lastFound); } } else { for (; e.scope != null; e = e.next()) { if (e.sym != null && e.sym.kind == Kinds.MTH) { //### Should intern methodName as Name. if (hasParameterTypes((MethodSymbol)e.sym, paramTypes)) { return env.getMethodDoc((MethodSymbol)e.sym); } } } } //### If we found a MethodDoc above, but which did not pass //### the modifier filter, we should return failure here! // search superclass cdi = (ClassDocImpl)superclass(); if (cdi != null) { mdi = cdi.searchMethod(methodName, paramTypes, searched); if (mdi != null) { return mdi; } } // search interfaces ClassDoc intf[] = interfaces(); for (int i = 0; i < intf.length; i++) { cdi = (ClassDocImpl)intf[i]; mdi = cdi.searchMethod(methodName, paramTypes, searched); if (mdi != null) { return mdi; } } // search enclosing class cdi = (ClassDocImpl)containingClass(); if (cdi != null) { mdi = cdi.searchMethod(methodName, paramTypes, searched); if (mdi != null) { return mdi; } } //###(gj) As a temporary measure until type variables are better //### handled, try again without the parameter types. //### This should most often find the right method, and occassionally //### find the wrong one. //if (paramTypes != null) { // return findMethod(methodName, null); //} return null; } /** * Find constructor in this class. * * @param constrName the unqualified name to search for. * @param paramTypeArray the array of Strings for constructor parameters. * @return the first ConstructorDocImpl which matches, null if not found. */ public ConstructorDoc findConstructor(String constrName, String[] paramTypes) { Name.Table names = tsym.name.table; for (Scope.Entry e = tsym.members().lookup(names.fromString("<init>")); e.scope != null; e = e.next()) { if (e.sym.kind == Kinds.MTH) { if (hasParameterTypes((MethodSymbol)e.sym, paramTypes)) { return env.getConstructorDoc((MethodSymbol)e.sym); } } } //###(gj) As a temporary measure until type variables are better //### handled, try again without the parameter types. //### This will often find the right constructor, and occassionally //### find the wrong one. //if (paramTypes != null) { // return findConstructor(constrName, null); //} return null; } /** * Find a field in this class scope. * Search order: this class, outerclasses, interfaces, * superclasses. IMP: If see tag is defined in an inner class, * which extends a super class and if outerclass and the super * class have a visible field in common then Java compiler cribs * about the ambiguity, but the following code will search in the * above given search order. * * @param fieldName the unqualified name to search for. * @return the first FieldDocImpl which matches, null if not found. */ public FieldDoc findField(String fieldName) { return searchField(fieldName, new HashSet<ClassDocImpl>()); } private FieldDocImpl searchField(String fieldName, Set<ClassDocImpl> searched) { Name.Table names = tsym.name.table; if (searched.contains(this)) { return null; } searched.add(this); for (Scope.Entry e = tsym.members().lookup(names.fromString(fieldName)); e.scope != null; e = e.next()) { if (e.sym.kind == Kinds.VAR) { //### Should intern fieldName as Name. return env.getFieldDoc((VarSymbol)e.sym); } } //### If we found a FieldDoc above, but which did not pass //### the modifier filter, we should return failure here! ClassDocImpl cdi = (ClassDocImpl)containingClass(); if (cdi != null) { FieldDocImpl fdi = cdi.searchField(fieldName, searched); if (fdi != null) { return fdi; } } // search superclass cdi = (ClassDocImpl)superclass(); if (cdi != null) { FieldDocImpl fdi = cdi.searchField(fieldName, searched); if (fdi != null) { return fdi; } } // search interfaces ClassDoc intf[] = interfaces(); for (int i = 0; i < intf.length; i++) { cdi = (ClassDocImpl)intf[i]; FieldDocImpl fdi = cdi.searchField(fieldName, searched); if (fdi != null) { return fdi; } } return null; } /** * Get the list of classes declared as imported. * These are called "single-type-import declarations" in the JLS. * This method is deprecated in the ClassDoc interface. * * @return an array of ClassDocImpl representing the imported classes. * * @deprecated Import declarations are implementation details that * should not be exposed here. In addition, not all imported * classes are imported through single-type-import declarations. */ @Deprecated public ClassDoc[] importedClasses() { // information is not available for binary classfiles if (tsym.sourcefile == null) return new ClassDoc[0]; ListBuffer<ClassDocImpl> importedClasses = new ListBuffer<ClassDocImpl>(); Env<AttrContext> compenv = env.enter.getEnv(tsym); if (compenv == null) return new ClassDocImpl[0]; Name asterisk = tsym.name.table.asterisk; for (JCTree t : compenv.toplevel.defs) { if (t.getTag() == JCTree.IMPORT) { JCTree imp = ((JCImport) t).qualid; if ((TreeInfo.name(imp) != asterisk) && (imp.type.tsym.kind & Kinds.TYP) != 0) { importedClasses.append( env.getClassDoc((ClassSymbol)imp.type.tsym)); } } } return importedClasses.toArray(new ClassDocImpl[importedClasses.length()]); } /** * Get the list of packages declared as imported. * These are called "type-import-on-demand declarations" in the JLS. * This method is deprecated in the ClassDoc interface. * * @return an array of PackageDocImpl representing the imported packages. * * ###NOTE: the syntax supports importing all inner classes from a class as well. * @deprecated Import declarations are implementation details that * should not be exposed here. In addition, this method's * return type does not allow for all type-import-on-demand * declarations to be returned. */ @Deprecated public PackageDoc[] importedPackages() { // information is not available for binary classfiles if (tsym.sourcefile == null) return new PackageDoc[0]; ListBuffer<PackageDocImpl> importedPackages = new ListBuffer<PackageDocImpl>(); //### Add the implicit "import java.lang.*" to the result Name.Table names = tsym.name.table; importedPackages.append(env.getPackageDoc(env.reader.enterPackage(names.java_lang))); Env<AttrContext> compenv = env.enter.getEnv(tsym); if (compenv == null) return new PackageDocImpl[0]; for (JCTree t : compenv.toplevel.defs) { if (t.getTag() == JCTree.IMPORT) { JCTree imp = ((JCImport) t).qualid; if (TreeInfo.name(imp) == names.asterisk) { JCFieldAccess sel = (JCFieldAccess)imp; Symbol s = sel.selected.type.tsym; PackageDocImpl pdoc = env.getPackageDoc(s.packge()); if (!importedPackages.contains(pdoc)) importedPackages.append(pdoc); } } } return importedPackages.toArray(new PackageDocImpl[importedPackages.length()]); } /** * Return the type's dimension information. * Always return "", as this is not an array type. */ public String dimension() { return ""; } /** * Return this type as a class, which it already is. */ public ClassDoc asClassDoc() { return this; } /** * Return null (unless overridden), as this is not an annotation type. */ public AnnotationTypeDoc asAnnotationTypeDoc() { return null; } /** * Return null, as this is not a class instantiation. */ public ParameterizedType asParameterizedType() { return null; } /** * Return null, as this is not a type variable. */ public TypeVariable asTypeVariable() { return null; } /** * Return null, as this is not a wildcard type. */ public WildcardType asWildcardType() { return null; } /** * Return false, as this is not a primitive type. */ public boolean isPrimitive() { return false; } //--- Serialization --- //### These methods ignore modifier filter. /** * Return true if this class implements <code>java.io.Serializable</code>. * * Since <code>java.io.Externalizable</code> extends * <code>java.io.Serializable</code>, * Externalizable objects are also Serializable. */ public boolean isSerializable() { try { return env.types.isSubtype(type, env.syms.serializableType); } catch (CompletionFailure ex) { // quietly ignore completion failures return false; } } /** * Return true if this class implements * <code>java.io.Externalizable</code>. */ public boolean isExternalizable() { try { return env.types.isSubtype(type, env.externalizableSym.type); } catch (CompletionFailure ex) { // quietly ignore completion failures return false; } } /** * Return the serialization methods for this class. * * @return an array of <code>MethodDocImpl</code> that represents * the serialization methods for this class. */ public MethodDoc[] serializationMethods() { if (serializedForm == null) { serializedForm = new SerializedForm(env, tsym, this); } //### Clone this? return serializedForm.methods(); } /** * Return the Serializable fields of class.<p> * * Return either a list of default fields documented by * <code>serial</code> tag<br> * or return a single <code>FieldDoc</code> for * <code>serialPersistentField</code> member. * There should be a <code>serialField</code> tag for * each Serializable field defined by an <code>ObjectStreamField</code> * array component of <code>serialPersistentField</code>. * * @returns an array of <code>FieldDoc</code> for the Serializable fields * of this class. * * @see #definesSerializableFields() * @see SerialFieldTagImpl */ public FieldDoc[] serializableFields() { if (serializedForm == null) { serializedForm = new SerializedForm(env, tsym, this); } //### Clone this? return serializedForm.fields(); } /** * Return true if Serializable fields are explicitly defined with * the special class member <code>serialPersistentFields</code>. * * @see #serializableFields() * @see SerialFieldTagImpl */ public boolean definesSerializableFields() { if (!isSerializable() || isExternalizable()) { return false; } else { if (serializedForm == null) { serializedForm = new SerializedForm(env, tsym, this); } //### Clone this? return serializedForm.definesSerializableFields(); } } /** * Determine if a class is a RuntimeException. * <p> * Used only by ThrowsTagImpl. */ boolean isRuntimeException() { return tsym.isSubClass(env.syms.runtimeExceptionType.tsym, env.types); } /** * Return the source position of the entity, or null if * no position is available. */ public SourcePosition position() { if (tsym.sourcefile == null) return null; return SourcePositionImpl.make(tsym.sourcefile, (tree==null) ? Position.NOPOS : tree.pos, lineMap); } }