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