Sat, 01 Dec 2007 00:00:00 +0000
Initial load
1 /*
2 * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
26 package com.sun.tools.javadoc;
28 import java.util.*;
30 import com.sun.javadoc.*;
32 import static com.sun.javadoc.LanguageVersion.*;
34 import com.sun.tools.javac.util.List;
35 import com.sun.tools.javac.util.ListBuffer;
36 import com.sun.tools.javac.util.Name;
37 import com.sun.tools.javac.util.Position;
39 import com.sun.tools.javac.code.Flags;
40 import com.sun.tools.javac.code.Kinds;
41 import com.sun.tools.javac.code.TypeTags;
42 import com.sun.tools.javac.code.Type;
43 import com.sun.tools.javac.code.Types;
44 import com.sun.tools.javac.code.Type.ClassType;
45 import com.sun.tools.javac.code.Scope;
46 import com.sun.tools.javac.code.Symbol;
47 import com.sun.tools.javac.code.Symbol.*;
49 import com.sun.tools.javac.comp.AttrContext;
50 import com.sun.tools.javac.comp.Env;
52 import com.sun.tools.javac.tree.JCTree;
53 import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
54 import com.sun.tools.javac.tree.JCTree.JCImport;
55 import com.sun.tools.javac.tree.JCTree.JCClassDecl;
56 import com.sun.tools.javac.tree.TreeInfo;
58 import static com.sun.tools.javac.code.Flags.*;
59 import static com.sun.tools.javac.code.Kinds.*;
60 import static com.sun.tools.javac.code.TypeTags.*;
62 import java.io.File;
63 import java.util.Set;
64 import java.util.HashSet;
65 import java.lang.reflect.Modifier;
67 /**
68 * Represents a java class and provides access to information
69 * about the class, the class' comment and tags, and the
70 * members of the class. A ClassDocImpl only exists if it was
71 * processed in this run of javadoc. References to classes
72 * which may or may not have been processed in this run are
73 * referred to using Type (which can be converted to ClassDocImpl,
74 * if possible).
75 *
76 * @see Type
77 *
78 * @since 1.2
79 * @author Robert Field
80 * @author Neal Gafter (rewrite)
81 * @author Scott Seligman (generics, enums, annotations)
82 */
84 public class ClassDocImpl extends ProgramElementDocImpl implements ClassDoc {
86 public final ClassType type; // protected->public for debugging
87 protected final ClassSymbol tsym;
89 boolean isIncluded = false; // Set in RootDocImpl
91 private SerializedForm serializedForm;
93 /**
94 * Constructor
95 */
96 public ClassDocImpl(DocEnv env, ClassSymbol sym) {
97 this(env, sym, null, null, null);
98 }
100 /**
101 * Constructor
102 */
103 public ClassDocImpl(DocEnv env, ClassSymbol sym, String documentation,
104 JCClassDecl tree, Position.LineMap lineMap) {
105 super(env, sym, documentation, tree, lineMap);
106 this.type = (ClassType)sym.type;
107 this.tsym = sym;
108 }
110 /**
111 * Returns the flags in terms of javac's flags
112 */
113 protected long getFlags() {
114 return getFlags(tsym);
115 }
117 /**
118 * Returns the flags of a ClassSymbol in terms of javac's flags
119 */
120 static long getFlags(ClassSymbol clazz) {
121 while (true) {
122 try {
123 return clazz.flags();
124 } catch (CompletionFailure ex) {
125 // quietly ignore completion failures
126 }
127 }
128 }
130 /**
131 * Is a ClassSymbol an annotation type?
132 */
133 static boolean isAnnotationType(ClassSymbol clazz) {
134 return (getFlags(clazz) & Flags.ANNOTATION) != 0;
135 }
137 /**
138 * Identify the containing class
139 */
140 protected ClassSymbol getContainingClass() {
141 return tsym.owner.enclClass();
142 }
144 /**
145 * Return true if this is a class, not an interface.
146 */
147 public boolean isClass() {
148 return !Modifier.isInterface(getModifiers());
149 }
151 /**
152 * Return true if this is a ordinary class,
153 * not an enumeration, exception, an error, or an interface.
154 */
155 public boolean isOrdinaryClass() {
156 if (isEnum() || isInterface() || isAnnotationType()) {
157 return false;
158 }
159 for (Type t = type; t.tag == TypeTags.CLASS; t = env.types.supertype(t)) {
160 if (t.tsym == env.syms.errorType.tsym ||
161 t.tsym == env.syms.exceptionType.tsym) {
162 return false;
163 }
164 }
165 return true;
166 }
168 /**
169 * Return true if this is an enumeration.
170 * (For legacy doclets, return false.)
171 */
172 public boolean isEnum() {
173 return (getFlags() & Flags.ENUM) != 0
174 &&
175 !env.legacyDoclet;
176 }
178 /**
179 * Return true if this is an interface, but not an annotation type.
180 * Overridden by AnnotationTypeDocImpl.
181 */
182 public boolean isInterface() {
183 return Modifier.isInterface(getModifiers());
184 }
186 /**
187 * Return true if this is an exception class
188 */
189 public boolean isException() {
190 if (isEnum() || isInterface() || isAnnotationType()) {
191 return false;
192 }
193 for (Type t = type; t.tag == TypeTags.CLASS; t = env.types.supertype(t)) {
194 if (t.tsym == env.syms.exceptionType.tsym) {
195 return true;
196 }
197 }
198 return false;
199 }
201 /**
202 * Return true if this is an error class
203 */
204 public boolean isError() {
205 if (isEnum() || isInterface() || isAnnotationType()) {
206 return false;
207 }
208 for (Type t = type; t.tag == TypeTags.CLASS; t = env.types.supertype(t)) {
209 if (t.tsym == env.syms.errorType.tsym) {
210 return true;
211 }
212 }
213 return false;
214 }
216 /**
217 * Return true if this is a throwable class
218 */
219 public boolean isThrowable() {
220 if (isEnum() || isInterface() || isAnnotationType()) {
221 return false;
222 }
223 for (Type t = type; t.tag == TypeTags.CLASS; t = env.types.supertype(t)) {
224 if (t.tsym == env.syms.throwableType.tsym) {
225 return true;
226 }
227 }
228 return false;
229 }
231 /**
232 * Return true if this class is abstract
233 */
234 public boolean isAbstract() {
235 return Modifier.isAbstract(getModifiers());
236 }
238 /**
239 * Returns true if this class was synthesized by the compiler.
240 */
241 public boolean isSynthetic() {
242 return (getFlags() & Flags.SYNTHETIC) != 0;
243 }
245 /**
246 * Return true if this class is included in the active set.
247 * A ClassDoc is included iff either it is specified on the
248 * commandline, or if it's containing package is specified
249 * on the command line, or if it is a member class of an
250 * included class.
251 */
253 public boolean isIncluded() {
254 if (isIncluded) {
255 return true;
256 }
257 if (env.shouldDocument(tsym)) {
258 // Class is nameable from top-level and
259 // the class and all enclosing classes
260 // pass the modifier filter.
261 if (containingPackage().isIncluded()) {
262 return isIncluded=true;
263 }
264 ClassDoc outer = containingClass();
265 if (outer != null && outer.isIncluded()) {
266 return isIncluded=true;
267 }
268 }
269 return false;
270 }
272 /**
273 * Return the package that this class is contained in.
274 */
275 public PackageDoc containingPackage() {
276 PackageDocImpl p = env.getPackageDoc(tsym.packge());
277 SourcePosition po = position();
278 if (po != null && p.setDocPath == false && p.zipDocPath == null) {
279 //Set the package path if possible
280 File packageDir = po.file().getParentFile();
281 if (packageDir != null
282 && (new File(packageDir, "package.html")).exists()) {
283 p.setDocPath(packageDir.getPath());
284 } else {
285 p.setDocPath(null);
286 }
287 }
288 return p;
289 }
291 /**
292 * Return the class name without package qualifier - but with
293 * enclosing class qualifier - as a String.
294 * <pre>
295 * Examples:
296 * for java.util.Hashtable
297 * return Hashtable
298 * for java.util.Map.Entry
299 * return Map.Entry
300 * </pre>
301 */
302 public String name() {
303 return getClassName(tsym, false);
304 }
306 /**
307 * Return the qualified class name as a String.
308 * <pre>
309 * Example:
310 * for java.util.Hashtable
311 * return java.util.Hashtable
312 * if no qualifier, just return flat name
313 * </pre>
314 */
315 public String qualifiedName() {
316 return getClassName(tsym, true);
317 }
319 /**
320 * Return unqualified name of type excluding any dimension information.
321 * <p>
322 * For example, a two dimensional array of String returns 'String'.
323 */
324 public String typeName() {
325 return name();
326 }
328 /**
329 * Return qualified name of type excluding any dimension information.
330 *<p>
331 * For example, a two dimensional array of String
332 * returns 'java.lang.String'.
333 */
334 public String qualifiedTypeName() {
335 return qualifiedName();
336 }
338 /**
339 * Return the simple name of this type.
340 */
341 public String simpleTypeName() {
342 return tsym.name.toString();
343 }
345 /**
346 * Return the qualified name and any type parameters.
347 * Each parameter is a type variable with optional bounds.
348 */
349 public String toString() {
350 return classToString(env, tsym, true);
351 }
353 /**
354 * Return the class name as a string. If "full" is true the name is
355 * qualified, otherwise it is qualified by its enclosing class(es) only.
356 */
357 static String getClassName(ClassSymbol c, boolean full) {
358 if (full) {
359 return c.getQualifiedName().toString();
360 } else {
361 String n = "";
362 for ( ; c != null; c = c.owner.enclClass()) {
363 n = c.name + (n.equals("") ? "" : ".") + n;
364 }
365 return n;
366 }
367 }
369 /**
370 * Return the class name with any type parameters as a string.
371 * Each parameter is a type variable with optional bounds.
372 * If "full" is true all names are qualified, otherwise they are
373 * qualified by their enclosing class(es) only.
374 */
375 static String classToString(DocEnv env, ClassSymbol c, boolean full) {
376 StringBuffer s = new StringBuffer();
377 if (!c.isInner()) { // if c is not an inner class
378 s.append(getClassName(c, full));
379 } else {
380 // c is an inner class, so include type params of outer.
381 ClassSymbol encl = c.owner.enclClass();
382 s.append(classToString(env, encl, full))
383 .append('.')
384 .append(c.name);
385 }
386 s.append(TypeMaker.typeParametersString(env, c, full));
387 return s.toString();
388 }
390 /**
391 * Is this class (or any enclosing class) generic? That is, does
392 * it have type parameters?
393 */
394 static boolean isGeneric(ClassSymbol c) {
395 return c.type.allparams().nonEmpty();
396 }
398 /**
399 * Return the formal type parameters of this class or interface.
400 * Return an empty array if there are none.
401 */
402 public TypeVariable[] typeParameters() {
403 if (env.legacyDoclet) {
404 return new TypeVariable[0];
405 }
406 TypeVariable res[] = new TypeVariable[type.getTypeArguments().length()];
407 TypeMaker.getTypes(env, type.getTypeArguments(), res);
408 return res;
409 }
411 /**
412 * Return the type parameter tags of this class or interface.
413 */
414 public ParamTag[] typeParamTags() {
415 return (env.legacyDoclet)
416 ? new ParamTag[0]
417 : comment().typeParamTags();
418 }
420 /**
421 * Return the modifier string for this class. If it's an interface
422 * exclude 'abstract' keyword from the modifier string
423 */
424 public String modifiers() {
425 return Modifier.toString(modifierSpecifier());
426 }
428 public int modifierSpecifier() {
429 int modifiers = getModifiers();
430 return (isInterface() || isAnnotationType())
431 ? modifiers & ~Modifier.ABSTRACT
432 : modifiers;
433 }
435 /**
436 * Return the superclass of this class
437 *
438 * @return the ClassDocImpl for the superclass of this class, null
439 * if there is no superclass.
440 */
441 public ClassDoc superclass() {
442 if (isInterface() || isAnnotationType()) return null;
443 if (tsym == env.syms.objectType.tsym) return null;
444 ClassSymbol c = (ClassSymbol)env.types.supertype(type).tsym;
445 if (c == null || c == tsym) c = (ClassSymbol)env.syms.objectType.tsym;
446 return env.getClassDoc(c);
447 }
449 /**
450 * Return the superclass of this class. Return null if this is an
451 * interface. A superclass is represented by either a
452 * <code>ClassDoc</code> or a <code>ParameterizedType</code>.
453 */
454 public com.sun.javadoc.Type superclassType() {
455 if (isInterface() || isAnnotationType() ||
456 (tsym == env.syms.objectType.tsym))
457 return null;
458 Type sup = env.types.supertype(type);
459 return TypeMaker.getType(env,
460 (sup != type) ? sup : env.syms.objectType);
461 }
463 /**
464 * Test whether this class is a subclass of the specified class.
465 *
466 * @param cd the candidate superclass.
467 * @return true if cd is a superclass of this class.
468 */
469 public boolean subclassOf(ClassDoc cd) {
470 return tsym.isSubClass(((ClassDocImpl)cd).tsym, env.types);
471 }
473 /**
474 * Return interfaces implemented by this class or interfaces
475 * extended by this interface.
476 *
477 * @return An array of ClassDocImpl representing the interfaces.
478 * Return an empty array if there are no interfaces.
479 */
480 public ClassDoc[] interfaces() {
481 ListBuffer<ClassDocImpl> ta = new ListBuffer<ClassDocImpl>();
482 for (Type t : env.types.interfaces(type)) {
483 ta.append(env.getClassDoc((ClassSymbol)t.tsym));
484 }
485 //### Cache ta here?
486 return ta.toArray(new ClassDocImpl[ta.length()]);
487 }
489 /**
490 * Return interfaces implemented by this class or interfaces extended
491 * by this interface. Includes only directly-declared interfaces, not
492 * inherited interfaces.
493 * Return an empty array if there are no interfaces.
494 */
495 public com.sun.javadoc.Type[] interfaceTypes() {
496 //### Cache result here?
497 return TypeMaker.getTypes(env, env.types.interfaces(type));
498 }
500 /**
501 * Return fields in class.
502 * @param filter include only the included fields if filter==true
503 */
504 public FieldDoc[] fields(boolean filter) {
505 return fields(filter, false);
506 }
508 /**
509 * Return included fields in class.
510 */
511 public FieldDoc[] fields() {
512 return fields(true, false);
513 }
515 /**
516 * Return the enum constants if this is an enum type.
517 */
518 public FieldDoc[] enumConstants() {
519 return fields(false, true);
520 }
522 /**
523 * Return fields in class.
524 * @param filter if true, return only the included fields
525 * @param enumConstants if true, return the enum constants instead
526 */
527 private FieldDoc[] fields(boolean filter, boolean enumConstants) {
528 List<FieldDocImpl> fields = List.nil();
529 for (Scope.Entry e = tsym.members().elems; e != null; e = e.sibling) {
530 if (e.sym != null && e.sym.kind == VAR) {
531 VarSymbol s = (VarSymbol)e.sym;
532 boolean isEnum = ((s.flags() & Flags.ENUM) != 0) &&
533 !env.legacyDoclet;
534 if (isEnum == enumConstants &&
535 (!filter || env.shouldDocument(s))) {
536 fields = fields.prepend(env.getFieldDoc(s));
537 }
538 }
539 }
540 return fields.toArray(new FieldDocImpl[fields.length()]);
541 }
543 /**
544 * Return methods in class.
545 * This method is overridden by AnnotationTypeDocImpl.
546 *
547 * @param filter include only the included methods if filter==true
548 * @return an array of MethodDocImpl for representing the visible
549 * methods in this class. Does not include constructors.
550 */
551 public MethodDoc[] methods(boolean filter) {
552 Name.Table names = tsym.name.table;
553 List<MethodDocImpl> methods = List.nil();
554 for (Scope.Entry e = tsym.members().elems; e != null; e = e.sibling) {
555 if (e.sym != null &&
556 e.sym.kind == Kinds.MTH && e.sym.name != names.init) {
557 MethodSymbol s = (MethodSymbol)e.sym;
558 if (!filter || env.shouldDocument(s)) {
559 methods = methods.prepend(env.getMethodDoc(s));
560 }
561 }
562 }
563 //### Cache methods here?
564 return methods.toArray(new MethodDocImpl[methods.length()]);
565 }
567 /**
568 * Return included methods in class.
569 *
570 * @return an array of MethodDocImpl for representing the visible
571 * methods in this class. Does not include constructors.
572 */
573 public MethodDoc[] methods() {
574 return methods(true);
575 }
577 /**
578 * Return constructors in class.
579 *
580 * @param filter include only the included constructors if filter==true
581 * @return an array of ConstructorDocImpl for representing the visible
582 * constructors in this class.
583 */
584 public ConstructorDoc[] constructors(boolean filter) {
585 Name.Table names = tsym.name.table;
586 List<ConstructorDocImpl> constructors = List.nil();
587 for (Scope.Entry e = tsym.members().elems; e != null; e = e.sibling) {
588 if (e.sym != null &&
589 e.sym.kind == Kinds.MTH && e.sym.name == names.init) {
590 MethodSymbol s = (MethodSymbol)e.sym;
591 if (!filter || env.shouldDocument(s)) {
592 constructors = constructors.prepend(env.getConstructorDoc(s));
593 }
594 }
595 }
596 //### Cache constructors here?
597 return constructors.toArray(new ConstructorDocImpl[constructors.length()]);
598 }
600 /**
601 * Return included constructors in class.
602 *
603 * @return an array of ConstructorDocImpl for representing the visible
604 * constructors in this class.
605 */
606 public ConstructorDoc[] constructors() {
607 return constructors(true);
608 }
610 /**
611 * Adds all inner classes of this class, and their
612 * inner classes recursively, to the list l.
613 */
614 void addAllClasses(ListBuffer<ClassDocImpl> l, boolean filtered) {
615 try {
616 if (isSynthetic()) return;
617 // sometimes synthetic classes are not marked synthetic
618 if (!JavadocTool.isValidClassName(tsym.name.toString())) return;
619 if (filtered && !env.shouldDocument(tsym)) return;
620 if (l.contains(this)) return;
621 l.append(this);
622 List<ClassDocImpl> more = List.nil();
623 for (Scope.Entry e = tsym.members().elems; e != null;
624 e = e.sibling) {
625 if (e.sym != null && e.sym.kind == Kinds.TYP) {
626 ClassSymbol s = (ClassSymbol)e.sym;
627 ClassDocImpl c = env.getClassDoc(s);
628 if (c.isSynthetic()) continue;
629 if (c != null) more = more.prepend(c);
630 }
631 }
632 // this extra step preserves the ordering from oldjavadoc
633 for (; more.nonEmpty(); more=more.tail) {
634 more.head.addAllClasses(l, filtered);
635 }
636 } catch (CompletionFailure e) {
637 // quietly ignore completion failures
638 }
639 }
641 /**
642 * Return inner classes within this class.
643 *
644 * @param filter include only the included inner classes if filter==true.
645 * @return an array of ClassDocImpl for representing the visible
646 * classes defined in this class. Anonymous and local classes
647 * are not included.
648 */
649 public ClassDoc[] innerClasses(boolean filter) {
650 ListBuffer<ClassDocImpl> innerClasses = new ListBuffer<ClassDocImpl>();
651 for (Scope.Entry e = tsym.members().elems; e != null; e = e.sibling) {
652 if (e.sym != null && e.sym.kind == Kinds.TYP) {
653 ClassSymbol s = (ClassSymbol)e.sym;
654 if ((s.flags_field & Flags.SYNTHETIC) != 0) continue;
655 if (!filter || env.isVisible(s)) {
656 innerClasses.prepend(env.getClassDoc(s));
657 }
658 }
659 }
660 //### Cache classes here?
661 return innerClasses.toArray(new ClassDocImpl[innerClasses.length()]);
662 }
664 /**
665 * Return included inner classes within this class.
666 *
667 * @return an array of ClassDocImpl for representing the visible
668 * classes defined in this class. Anonymous and local classes
669 * are not included.
670 */
671 public ClassDoc[] innerClasses() {
672 return innerClasses(true);
673 }
675 /**
676 * Find a class within the context of this class.
677 * Search order: qualified name, in this class (inner),
678 * in this package, in the class imports, in the package
679 * imports.
680 * Return the ClassDocImpl if found, null if not found.
681 */
682 //### The specified search order is not the normal rule the
683 //### compiler would use. Leave as specified or change it?
684 public ClassDoc findClass(String className) {
685 ClassDoc searchResult = searchClass(className);
686 if (searchResult == null) {
687 ClassDocImpl enclosingClass = (ClassDocImpl)containingClass();
688 //Expand search space to include enclosing class.
689 while (enclosingClass != null && enclosingClass.containingClass() != null) {
690 enclosingClass = (ClassDocImpl)enclosingClass.containingClass();
691 }
692 searchResult = enclosingClass == null ?
693 null : enclosingClass.searchClass(className);
694 }
695 return searchResult;
696 }
698 private ClassDoc searchClass(String className) {
699 Name.Table names = tsym.name.table;
701 // search by qualified name first
702 ClassDoc cd = env.lookupClass(className);
703 if (cd != null) {
704 return cd;
705 }
707 // search inner classes
708 //### Add private entry point to avoid creating array?
709 //### Replicate code in innerClasses here to avoid consing?
710 ClassDoc innerClasses[] = innerClasses();
711 for (int i = 0; i < innerClasses.length; i++) {
712 if (innerClasses[i].name().equals(className) ||
713 //### This is from original javadoc but it looks suspicious to me...
714 //### I believe it is attempting to compensate for the confused
715 //### convention of including the nested class qualifiers in the
716 //### 'name' of the inner class, rather than the true simple name.
717 innerClasses[i].name().endsWith(className)) {
718 return innerClasses[i];
719 } else {
720 ClassDoc innercd = ((ClassDocImpl) innerClasses[i]).searchClass(className);
721 if (innercd != null) {
722 return innercd;
723 }
724 }
725 }
727 // check in this package
728 cd = containingPackage().findClass(className);
729 if (cd != null) {
730 return cd;
731 }
733 // make sure that this symbol has been completed
734 if (tsym.completer != null) {
735 tsym.complete();
736 }
738 // search imports
740 if (tsym.sourcefile != null) {
742 //### This information is available only for source classes.
744 Env<AttrContext> compenv = env.enter.getEnv(tsym);
745 if (compenv == null) return null;
747 Scope s = compenv.toplevel.namedImportScope;
748 for (Scope.Entry e = s.lookup(names.fromString(className)); e.scope != null; e = e.next()) {
749 if (e.sym.kind == Kinds.TYP) {
750 ClassDoc c = env.getClassDoc((ClassSymbol)e.sym);
751 return c;
752 }
753 }
755 s = compenv.toplevel.starImportScope;
756 for (Scope.Entry e = s.lookup(names.fromString(className)); e.scope != null; e = e.next()) {
757 if (e.sym.kind == Kinds.TYP) {
758 ClassDoc c = env.getClassDoc((ClassSymbol)e.sym);
759 return c;
760 }
761 }
762 }
764 return null; // not found
765 }
768 private boolean hasParameterTypes(MethodSymbol method, String[] argTypes) {
770 if (argTypes == null) {
771 // wildcard
772 return true;
773 }
775 int i = 0;
776 List<Type> types = method.type.getParameterTypes();
778 if (argTypes.length != types.length()) {
779 return false;
780 }
782 for (Type t : types) {
783 String argType = argTypes[i++];
784 // For vararg method, "T..." matches type T[].
785 if (i == argTypes.length) {
786 argType = argType.replace("...", "[]");
787 }
788 if (!hasTypeName(env.types.erasure(t), argType)) { //###(gj)
789 return false;
790 }
791 }
792 return true;
793 }
794 // where
795 private boolean hasTypeName(Type t, String name) {
796 return
797 name.equals(TypeMaker.getTypeName(t, true))
798 ||
799 name.equals(TypeMaker.getTypeName(t, false))
800 ||
801 (qualifiedName() + "." + name).equals(TypeMaker.getTypeName(t, true));
802 }
806 /**
807 * Find a method in this class scope.
808 * Search order: this class, interfaces, superclasses, outerclasses.
809 * Note that this is not necessarily what the compiler would do!
810 *
811 * @param methodName the unqualified name to search for.
812 * @param paramTypeArray the array of Strings for method parameter types.
813 * @return the first MethodDocImpl which matches, null if not found.
814 */
815 public MethodDocImpl findMethod(String methodName, String[] paramTypes) {
816 // Use hash table 'searched' to avoid searching same class twice.
817 //### It is not clear how this could happen.
818 return searchMethod(methodName, paramTypes, new HashSet<ClassDocImpl>());
819 }
821 private MethodDocImpl searchMethod(String methodName,
822 String[] paramTypes, Set<ClassDocImpl> searched) {
823 //### Note that this search is not necessarily what the compiler would do!
825 ClassDocImpl cdi;
826 MethodDocImpl mdi;
828 if (searched.contains(this)) {
829 return null;
830 }
831 searched.add(this);
833 //DEBUG
834 /*---------------------------------*
835 System.out.print("searching " + this + " for " + methodName);
836 if (paramTypes == null) {
837 System.out.println("()");
838 } else {
839 System.out.print("(");
840 for (int k=0; k < paramTypes.length; k++) {
841 System.out.print(paramTypes[k]);
842 if ((k + 1) < paramTypes.length) {
843 System.out.print(", ");
844 }
845 }
846 System.out.println(")");
847 }
848 *---------------------------------*/
850 // search current class
851 Name.Table names = tsym.name.table;
852 Scope.Entry e = tsym.members().lookup(names.fromString(methodName));
854 //### Using modifier filter here isn't really correct,
855 //### but emulates the old behavior. Instead, we should
856 //### apply the normal rules of visibility and inheritance.
858 if (paramTypes == null) {
859 // If no parameters specified, we are allowed to return
860 // any method with a matching name. In practice, the old
861 // code returned the first method, which is now the last!
862 // In order to provide textually identical results, we
863 // attempt to emulate the old behavior.
864 MethodSymbol lastFound = null;
865 for (; e.scope != null; e = e.next()) {
866 if (e.sym.kind == Kinds.MTH) {
867 //### Should intern methodName as Name.
868 if (e.sym.name.toString().equals(methodName)) {
869 lastFound = (MethodSymbol)e.sym;
870 }
871 }
872 }
873 if (lastFound != null) {
874 return env.getMethodDoc(lastFound);
875 }
876 } else {
877 for (; e.scope != null; e = e.next()) {
878 if (e.sym != null &&
879 e.sym.kind == Kinds.MTH) {
880 //### Should intern methodName as Name.
881 if (hasParameterTypes((MethodSymbol)e.sym, paramTypes)) {
882 return env.getMethodDoc((MethodSymbol)e.sym);
883 }
884 }
885 }
886 }
888 //### If we found a MethodDoc above, but which did not pass
889 //### the modifier filter, we should return failure here!
891 // search superclass
892 cdi = (ClassDocImpl)superclass();
893 if (cdi != null) {
894 mdi = cdi.searchMethod(methodName, paramTypes, searched);
895 if (mdi != null) {
896 return mdi;
897 }
898 }
900 // search interfaces
901 ClassDoc intf[] = interfaces();
902 for (int i = 0; i < intf.length; i++) {
903 cdi = (ClassDocImpl)intf[i];
904 mdi = cdi.searchMethod(methodName, paramTypes, searched);
905 if (mdi != null) {
906 return mdi;
907 }
908 }
910 // search enclosing class
911 cdi = (ClassDocImpl)containingClass();
912 if (cdi != null) {
913 mdi = cdi.searchMethod(methodName, paramTypes, searched);
914 if (mdi != null) {
915 return mdi;
916 }
917 }
919 //###(gj) As a temporary measure until type variables are better
920 //### handled, try again without the parameter types.
921 //### This should most often find the right method, and occassionally
922 //### find the wrong one.
923 //if (paramTypes != null) {
924 // return findMethod(methodName, null);
925 //}
927 return null;
928 }
930 /**
931 * Find constructor in this class.
932 *
933 * @param constrName the unqualified name to search for.
934 * @param paramTypeArray the array of Strings for constructor parameters.
935 * @return the first ConstructorDocImpl which matches, null if not found.
936 */
937 public ConstructorDoc findConstructor(String constrName,
938 String[] paramTypes) {
939 Name.Table names = tsym.name.table;
940 for (Scope.Entry e = tsym.members().lookup(names.fromString("<init>")); e.scope != null; e = e.next()) {
941 if (e.sym.kind == Kinds.MTH) {
942 if (hasParameterTypes((MethodSymbol)e.sym, paramTypes)) {
943 return env.getConstructorDoc((MethodSymbol)e.sym);
944 }
945 }
946 }
948 //###(gj) As a temporary measure until type variables are better
949 //### handled, try again without the parameter types.
950 //### This will often find the right constructor, and occassionally
951 //### find the wrong one.
952 //if (paramTypes != null) {
953 // return findConstructor(constrName, null);
954 //}
956 return null;
957 }
959 /**
960 * Find a field in this class scope.
961 * Search order: this class, outerclasses, interfaces,
962 * superclasses. IMP: If see tag is defined in an inner class,
963 * which extends a super class and if outerclass and the super
964 * class have a visible field in common then Java compiler cribs
965 * about the ambiguity, but the following code will search in the
966 * above given search order.
967 *
968 * @param fieldName the unqualified name to search for.
969 * @return the first FieldDocImpl which matches, null if not found.
970 */
971 public FieldDoc findField(String fieldName) {
972 return searchField(fieldName, new HashSet<ClassDocImpl>());
973 }
975 private FieldDocImpl searchField(String fieldName, Set<ClassDocImpl> searched) {
976 Name.Table names = tsym.name.table;
977 if (searched.contains(this)) {
978 return null;
979 }
980 searched.add(this);
982 for (Scope.Entry e = tsym.members().lookup(names.fromString(fieldName)); e.scope != null; e = e.next()) {
983 if (e.sym.kind == Kinds.VAR) {
984 //### Should intern fieldName as Name.
985 return env.getFieldDoc((VarSymbol)e.sym);
986 }
987 }
989 //### If we found a FieldDoc above, but which did not pass
990 //### the modifier filter, we should return failure here!
992 ClassDocImpl cdi = (ClassDocImpl)containingClass();
993 if (cdi != null) {
994 FieldDocImpl fdi = cdi.searchField(fieldName, searched);
995 if (fdi != null) {
996 return fdi;
997 }
998 }
1000 // search superclass
1001 cdi = (ClassDocImpl)superclass();
1002 if (cdi != null) {
1003 FieldDocImpl fdi = cdi.searchField(fieldName, searched);
1004 if (fdi != null) {
1005 return fdi;
1006 }
1007 }
1009 // search interfaces
1010 ClassDoc intf[] = interfaces();
1011 for (int i = 0; i < intf.length; i++) {
1012 cdi = (ClassDocImpl)intf[i];
1013 FieldDocImpl fdi = cdi.searchField(fieldName, searched);
1014 if (fdi != null) {
1015 return fdi;
1016 }
1017 }
1019 return null;
1020 }
1022 /**
1023 * Get the list of classes declared as imported.
1024 * These are called "single-type-import declarations" in the JLS.
1025 * This method is deprecated in the ClassDoc interface.
1026 *
1027 * @return an array of ClassDocImpl representing the imported classes.
1028 *
1029 * @deprecated Import declarations are implementation details that
1030 * should not be exposed here. In addition, not all imported
1031 * classes are imported through single-type-import declarations.
1032 */
1033 @Deprecated
1034 public ClassDoc[] importedClasses() {
1035 // information is not available for binary classfiles
1036 if (tsym.sourcefile == null) return new ClassDoc[0];
1038 ListBuffer<ClassDocImpl> importedClasses = new ListBuffer<ClassDocImpl>();
1040 Env<AttrContext> compenv = env.enter.getEnv(tsym);
1041 if (compenv == null) return new ClassDocImpl[0];
1043 Name asterisk = tsym.name.table.asterisk;
1044 for (JCTree t : compenv.toplevel.defs) {
1045 if (t.getTag() == JCTree.IMPORT) {
1046 JCTree imp = ((JCImport) t).qualid;
1047 if ((TreeInfo.name(imp) != asterisk) &&
1048 (imp.type.tsym.kind & Kinds.TYP) != 0) {
1049 importedClasses.append(
1050 env.getClassDoc((ClassSymbol)imp.type.tsym));
1051 }
1052 }
1053 }
1055 return importedClasses.toArray(new ClassDocImpl[importedClasses.length()]);
1056 }
1058 /**
1059 * Get the list of packages declared as imported.
1060 * These are called "type-import-on-demand declarations" in the JLS.
1061 * This method is deprecated in the ClassDoc interface.
1062 *
1063 * @return an array of PackageDocImpl representing the imported packages.
1064 *
1065 * ###NOTE: the syntax supports importing all inner classes from a class as well.
1066 * @deprecated Import declarations are implementation details that
1067 * should not be exposed here. In addition, this method's
1068 * return type does not allow for all type-import-on-demand
1069 * declarations to be returned.
1070 */
1071 @Deprecated
1072 public PackageDoc[] importedPackages() {
1073 // information is not available for binary classfiles
1074 if (tsym.sourcefile == null) return new PackageDoc[0];
1076 ListBuffer<PackageDocImpl> importedPackages = new ListBuffer<PackageDocImpl>();
1078 //### Add the implicit "import java.lang.*" to the result
1079 Name.Table names = tsym.name.table;
1080 importedPackages.append(env.getPackageDoc(env.reader.enterPackage(names.java_lang)));
1082 Env<AttrContext> compenv = env.enter.getEnv(tsym);
1083 if (compenv == null) return new PackageDocImpl[0];
1085 for (JCTree t : compenv.toplevel.defs) {
1086 if (t.getTag() == JCTree.IMPORT) {
1087 JCTree imp = ((JCImport) t).qualid;
1088 if (TreeInfo.name(imp) == names.asterisk) {
1089 JCFieldAccess sel = (JCFieldAccess)imp;
1090 Symbol s = sel.selected.type.tsym;
1091 PackageDocImpl pdoc = env.getPackageDoc(s.packge());
1092 if (!importedPackages.contains(pdoc))
1093 importedPackages.append(pdoc);
1094 }
1095 }
1096 }
1098 return importedPackages.toArray(new PackageDocImpl[importedPackages.length()]);
1099 }
1101 /**
1102 * Return the type's dimension information.
1103 * Always return "", as this is not an array type.
1104 */
1105 public String dimension() {
1106 return "";
1107 }
1109 /**
1110 * Return this type as a class, which it already is.
1111 */
1112 public ClassDoc asClassDoc() {
1113 return this;
1114 }
1116 /**
1117 * Return null (unless overridden), as this is not an annotation type.
1118 */
1119 public AnnotationTypeDoc asAnnotationTypeDoc() {
1120 return null;
1121 }
1123 /**
1124 * Return null, as this is not a class instantiation.
1125 */
1126 public ParameterizedType asParameterizedType() {
1127 return null;
1128 }
1130 /**
1131 * Return null, as this is not a type variable.
1132 */
1133 public TypeVariable asTypeVariable() {
1134 return null;
1135 }
1137 /**
1138 * Return null, as this is not a wildcard type.
1139 */
1140 public WildcardType asWildcardType() {
1141 return null;
1142 }
1144 /**
1145 * Return false, as this is not a primitive type.
1146 */
1147 public boolean isPrimitive() {
1148 return false;
1149 }
1151 //--- Serialization ---
1153 //### These methods ignore modifier filter.
1155 /**
1156 * Return true if this class implements <code>java.io.Serializable</code>.
1157 *
1158 * Since <code>java.io.Externalizable</code> extends
1159 * <code>java.io.Serializable</code>,
1160 * Externalizable objects are also Serializable.
1161 */
1162 public boolean isSerializable() {
1163 try {
1164 return env.types.isSubtype(type, env.syms.serializableType);
1165 } catch (CompletionFailure ex) {
1166 // quietly ignore completion failures
1167 return false;
1168 }
1169 }
1171 /**
1172 * Return true if this class implements
1173 * <code>java.io.Externalizable</code>.
1174 */
1175 public boolean isExternalizable() {
1176 try {
1177 return env.types.isSubtype(type, env.externalizableSym.type);
1178 } catch (CompletionFailure ex) {
1179 // quietly ignore completion failures
1180 return false;
1181 }
1182 }
1184 /**
1185 * Return the serialization methods for this class.
1186 *
1187 * @return an array of <code>MethodDocImpl</code> that represents
1188 * the serialization methods for this class.
1189 */
1190 public MethodDoc[] serializationMethods() {
1191 if (serializedForm == null) {
1192 serializedForm = new SerializedForm(env, tsym, this);
1193 }
1194 //### Clone this?
1195 return serializedForm.methods();
1196 }
1198 /**
1199 * Return the Serializable fields of class.<p>
1200 *
1201 * Return either a list of default fields documented by
1202 * <code>serial</code> tag<br>
1203 * or return a single <code>FieldDoc</code> for
1204 * <code>serialPersistentField</code> member.
1205 * There should be a <code>serialField</code> tag for
1206 * each Serializable field defined by an <code>ObjectStreamField</code>
1207 * array component of <code>serialPersistentField</code>.
1208 *
1209 * @returns an array of <code>FieldDoc</code> for the Serializable fields
1210 * of this class.
1211 *
1212 * @see #definesSerializableFields()
1213 * @see SerialFieldTagImpl
1214 */
1215 public FieldDoc[] serializableFields() {
1216 if (serializedForm == null) {
1217 serializedForm = new SerializedForm(env, tsym, this);
1218 }
1219 //### Clone this?
1220 return serializedForm.fields();
1221 }
1223 /**
1224 * Return true if Serializable fields are explicitly defined with
1225 * the special class member <code>serialPersistentFields</code>.
1226 *
1227 * @see #serializableFields()
1228 * @see SerialFieldTagImpl
1229 */
1230 public boolean definesSerializableFields() {
1231 if (!isSerializable() || isExternalizable()) {
1232 return false;
1233 } else {
1234 if (serializedForm == null) {
1235 serializedForm = new SerializedForm(env, tsym, this);
1236 }
1237 //### Clone this?
1238 return serializedForm.definesSerializableFields();
1239 }
1240 }
1242 /**
1243 * Determine if a class is a RuntimeException.
1244 * <p>
1245 * Used only by ThrowsTagImpl.
1246 */
1247 boolean isRuntimeException() {
1248 return tsym.isSubClass(env.syms.runtimeExceptionType.tsym, env.types);
1249 }
1251 /**
1252 * Return the source position of the entity, or null if
1253 * no position is available.
1254 */
1255 public SourcePosition position() {
1256 if (tsym.sourcefile == null) return null;
1257 return SourcePositionImpl.make(tsym.sourcefile.toString(),
1258 (tree==null) ? Position.NOPOS : tree.pos,
1259 lineMap);
1260 }
1261 }