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