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