Thu, 18 Apr 2013 20:00:14 -0700
8012656: cache frequently used name strings for DocImpl classes
Reviewed-by: darcy
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 if (name == null) {
345 name = getClassName(tsym, false);
346 }
347 return name;
348 }
350 private String name;
352 /**
353 * Return the qualified class name as a String.
354 * <pre>
355 * Example:
356 * for java.util.Hashtable
357 * return java.util.Hashtable
358 * if no qualifier, just return flat name
359 * </pre>
360 */
361 public String qualifiedName() {
362 if (qualifiedName == null) {
363 qualifiedName = getClassName(tsym, true);
364 }
365 return qualifiedName;
366 }
368 private String qualifiedName;
370 /**
371 * Return unqualified name of type excluding any dimension information.
372 * <p>
373 * For example, a two dimensional array of String returns 'String'.
374 */
375 public String typeName() {
376 return name();
377 }
379 /**
380 * Return qualified name of type excluding any dimension information.
381 *<p>
382 * For example, a two dimensional array of String
383 * returns 'java.lang.String'.
384 */
385 public String qualifiedTypeName() {
386 return qualifiedName();
387 }
389 /**
390 * Return the simple name of this type.
391 */
392 public String simpleTypeName() {
393 if (simpleTypeName == null) {
394 simpleTypeName = tsym.name.toString();
395 }
396 return simpleTypeName;
397 }
399 private String simpleTypeName;
401 /**
402 * Return the qualified name and any type parameters.
403 * Each parameter is a type variable with optional bounds.
404 */
405 @Override
406 public String toString() {
407 return classToString(env, tsym, true);
408 }
410 /**
411 * Return the class name as a string. If "full" is true the name is
412 * qualified, otherwise it is qualified by its enclosing class(es) only.
413 */
414 static String getClassName(ClassSymbol c, boolean full) {
415 if (full) {
416 return c.getQualifiedName().toString();
417 } else {
418 String n = "";
419 for ( ; c != null; c = c.owner.enclClass()) {
420 n = c.name + (n.equals("") ? "" : ".") + n;
421 }
422 return n;
423 }
424 }
426 /**
427 * Return the class name with any type parameters as a string.
428 * Each parameter is a type variable with optional bounds.
429 * If "full" is true all names are qualified, otherwise they are
430 * qualified by their enclosing class(es) only.
431 */
432 static String classToString(DocEnv env, ClassSymbol c, boolean full) {
433 StringBuilder s = new StringBuilder();
434 if (!c.isInner()) { // if c is not an inner class
435 s.append(getClassName(c, full));
436 } else {
437 // c is an inner class, so include type params of outer.
438 ClassSymbol encl = c.owner.enclClass();
439 s.append(classToString(env, encl, full))
440 .append('.')
441 .append(c.name);
442 }
443 s.append(TypeMaker.typeParametersString(env, c, full));
444 return s.toString();
445 }
447 /**
448 * Is this class (or any enclosing class) generic? That is, does
449 * it have type parameters?
450 */
451 static boolean isGeneric(ClassSymbol c) {
452 return c.type.allparams().nonEmpty();
453 }
455 /**
456 * Return the formal type parameters of this class or interface.
457 * Return an empty array if there are none.
458 */
459 public TypeVariable[] typeParameters() {
460 if (env.legacyDoclet) {
461 return new TypeVariable[0];
462 }
463 TypeVariable res[] = new TypeVariable[type.getTypeArguments().length()];
464 TypeMaker.getTypes(env, type.getTypeArguments(), res);
465 return res;
466 }
468 /**
469 * Return the type parameter tags of this class or interface.
470 */
471 public ParamTag[] typeParamTags() {
472 return (env.legacyDoclet)
473 ? new ParamTag[0]
474 : comment().typeParamTags();
475 }
477 /**
478 * Return the modifier string for this class. If it's an interface
479 * exclude 'abstract' keyword from the modifier string
480 */
481 @Override
482 public String modifiers() {
483 return Modifier.toString(modifierSpecifier());
484 }
486 @Override
487 public int modifierSpecifier() {
488 int modifiers = getModifiers();
489 return (isInterface() || isAnnotationType())
490 ? modifiers & ~Modifier.ABSTRACT
491 : modifiers;
492 }
494 /**
495 * Return the superclass of this class
496 *
497 * @return the ClassDocImpl for the superclass of this class, null
498 * if there is no superclass.
499 */
500 public ClassDoc superclass() {
501 if (isInterface() || isAnnotationType()) return null;
502 if (tsym == env.syms.objectType.tsym) return null;
503 ClassSymbol c = (ClassSymbol)env.types.supertype(type).tsym;
504 if (c == null || c == tsym) c = (ClassSymbol)env.syms.objectType.tsym;
505 return env.getClassDoc(c);
506 }
508 /**
509 * Return the superclass of this class. Return null if this is an
510 * interface. A superclass is represented by either a
511 * <code>ClassDoc</code> or a <code>ParameterizedType</code>.
512 */
513 public com.sun.javadoc.Type superclassType() {
514 if (isInterface() || isAnnotationType() ||
515 (tsym == env.syms.objectType.tsym))
516 return null;
517 Type sup = env.types.supertype(type);
518 return TypeMaker.getType(env,
519 (sup != type) ? sup : env.syms.objectType);
520 }
522 /**
523 * Test whether this class is a subclass of the specified class.
524 *
525 * @param cd the candidate superclass.
526 * @return true if cd is a superclass of this class.
527 */
528 public boolean subclassOf(ClassDoc cd) {
529 return tsym.isSubClass(((ClassDocImpl)cd).tsym, env.types);
530 }
532 /**
533 * Return interfaces implemented by this class or interfaces
534 * extended by this interface.
535 *
536 * @return An array of ClassDocImpl representing the interfaces.
537 * Return an empty array if there are no interfaces.
538 */
539 public ClassDoc[] interfaces() {
540 ListBuffer<ClassDocImpl> ta = new ListBuffer<ClassDocImpl>();
541 for (Type t : env.types.interfaces(type)) {
542 ta.append(env.getClassDoc((ClassSymbol)t.tsym));
543 }
544 //### Cache ta here?
545 return ta.toArray(new ClassDocImpl[ta.length()]);
546 }
548 /**
549 * Return interfaces implemented by this class or interfaces extended
550 * by this interface. Includes only directly-declared interfaces, not
551 * inherited interfaces.
552 * Return an empty array if there are no interfaces.
553 */
554 public com.sun.javadoc.Type[] interfaceTypes() {
555 //### Cache result here?
556 return TypeMaker.getTypes(env, env.types.interfaces(type));
557 }
559 /**
560 * Return fields in class.
561 * @param filter include only the included fields if filter==true
562 */
563 public FieldDoc[] fields(boolean filter) {
564 return fields(filter, false);
565 }
567 /**
568 * Return included fields in class.
569 */
570 public FieldDoc[] fields() {
571 return fields(true, false);
572 }
574 /**
575 * Return the enum constants if this is an enum type.
576 */
577 public FieldDoc[] enumConstants() {
578 return fields(false, true);
579 }
581 /**
582 * Return fields in class.
583 * @param filter if true, return only the included fields
584 * @param enumConstants if true, return the enum constants instead
585 */
586 private FieldDoc[] fields(boolean filter, boolean enumConstants) {
587 List<FieldDocImpl> fields = List.nil();
588 for (Scope.Entry e = tsym.members().elems; e != null; e = e.sibling) {
589 if (e.sym != null && e.sym.kind == VAR) {
590 VarSymbol s = (VarSymbol)e.sym;
591 boolean isEnum = ((s.flags() & Flags.ENUM) != 0) &&
592 !env.legacyDoclet;
593 if (isEnum == enumConstants &&
594 (!filter || env.shouldDocument(s))) {
595 fields = fields.prepend(env.getFieldDoc(s));
596 }
597 }
598 }
599 return fields.toArray(new FieldDocImpl[fields.length()]);
600 }
602 /**
603 * Return methods in class.
604 * This method is overridden by AnnotationTypeDocImpl.
605 *
606 * @param filter include only the included methods if filter==true
607 * @return an array of MethodDocImpl for representing the visible
608 * methods in this class. Does not include constructors.
609 */
610 public MethodDoc[] methods(boolean filter) {
611 Names names = tsym.name.table.names;
612 List<MethodDocImpl> methods = List.nil();
613 for (Scope.Entry e = tsym.members().elems; e != null; e = e.sibling) {
614 if (e.sym != null &&
615 e.sym.kind == Kinds.MTH && e.sym.name != names.init) {
616 MethodSymbol s = (MethodSymbol)e.sym;
617 if (!filter || env.shouldDocument(s)) {
618 methods = methods.prepend(env.getMethodDoc(s));
619 }
620 }
621 }
622 //### Cache methods here?
623 return methods.toArray(new MethodDocImpl[methods.length()]);
624 }
626 /**
627 * Return included methods in class.
628 *
629 * @return an array of MethodDocImpl for representing the visible
630 * methods in this class. Does not include constructors.
631 */
632 public MethodDoc[] methods() {
633 return methods(true);
634 }
636 /**
637 * Return constructors in class.
638 *
639 * @param filter include only the included constructors if filter==true
640 * @return an array of ConstructorDocImpl for representing the visible
641 * constructors in this class.
642 */
643 public ConstructorDoc[] constructors(boolean filter) {
644 Names names = tsym.name.table.names;
645 List<ConstructorDocImpl> constructors = List.nil();
646 for (Scope.Entry e = tsym.members().elems; e != null; e = e.sibling) {
647 if (e.sym != null &&
648 e.sym.kind == Kinds.MTH && e.sym.name == names.init) {
649 MethodSymbol s = (MethodSymbol)e.sym;
650 if (!filter || env.shouldDocument(s)) {
651 constructors = constructors.prepend(env.getConstructorDoc(s));
652 }
653 }
654 }
655 //### Cache constructors here?
656 return constructors.toArray(new ConstructorDocImpl[constructors.length()]);
657 }
659 /**
660 * Return included constructors in class.
661 *
662 * @return an array of ConstructorDocImpl for representing the visible
663 * constructors in this class.
664 */
665 public ConstructorDoc[] constructors() {
666 return constructors(true);
667 }
669 /**
670 * Adds all inner classes of this class, and their
671 * inner classes recursively, to the list l.
672 */
673 void addAllClasses(ListBuffer<ClassDocImpl> l, boolean filtered) {
674 try {
675 if (isSynthetic()) return;
676 // sometimes synthetic classes are not marked synthetic
677 if (!JavadocTool.isValidClassName(tsym.name.toString())) return;
678 if (filtered && !env.shouldDocument(tsym)) return;
679 if (l.contains(this)) return;
680 l.append(this);
681 List<ClassDocImpl> more = List.nil();
682 for (Scope.Entry e = tsym.members().elems; e != null;
683 e = e.sibling) {
684 if (e.sym != null && e.sym.kind == Kinds.TYP) {
685 ClassSymbol s = (ClassSymbol)e.sym;
686 ClassDocImpl c = env.getClassDoc(s);
687 if (c.isSynthetic()) continue;
688 if (c != null) more = more.prepend(c);
689 }
690 }
691 // this extra step preserves the ordering from oldjavadoc
692 for (; more.nonEmpty(); more=more.tail) {
693 more.head.addAllClasses(l, filtered);
694 }
695 } catch (CompletionFailure e) {
696 // quietly ignore completion failures
697 }
698 }
700 /**
701 * Return inner classes within this class.
702 *
703 * @param filter include only the included inner classes if filter==true.
704 * @return an array of ClassDocImpl for representing the visible
705 * classes defined in this class. Anonymous and local classes
706 * are not included.
707 */
708 public ClassDoc[] innerClasses(boolean filter) {
709 ListBuffer<ClassDocImpl> innerClasses = new ListBuffer<ClassDocImpl>();
710 for (Scope.Entry e = tsym.members().elems; e != null; e = e.sibling) {
711 if (e.sym != null && e.sym.kind == Kinds.TYP) {
712 ClassSymbol s = (ClassSymbol)e.sym;
713 if ((s.flags_field & Flags.SYNTHETIC) != 0) continue;
714 if (!filter || env.isVisible(s)) {
715 innerClasses.prepend(env.getClassDoc(s));
716 }
717 }
718 }
719 //### Cache classes here?
720 return innerClasses.toArray(new ClassDocImpl[innerClasses.length()]);
721 }
723 /**
724 * Return included inner classes within this class.
725 *
726 * @return an array of ClassDocImpl for representing the visible
727 * classes defined in this class. Anonymous and local classes
728 * are not included.
729 */
730 public ClassDoc[] innerClasses() {
731 return innerClasses(true);
732 }
734 /**
735 * Find a class within the context of this class.
736 * Search order: qualified name, in this class (inner),
737 * in this package, in the class imports, in the package
738 * imports.
739 * Return the ClassDocImpl if found, null if not found.
740 */
741 //### The specified search order is not the normal rule the
742 //### compiler would use. Leave as specified or change it?
743 public ClassDoc findClass(String className) {
744 ClassDoc searchResult = searchClass(className);
745 if (searchResult == null) {
746 ClassDocImpl enclosingClass = (ClassDocImpl)containingClass();
747 //Expand search space to include enclosing class.
748 while (enclosingClass != null && enclosingClass.containingClass() != null) {
749 enclosingClass = (ClassDocImpl)enclosingClass.containingClass();
750 }
751 searchResult = enclosingClass == null ?
752 null : enclosingClass.searchClass(className);
753 }
754 return searchResult;
755 }
757 private ClassDoc searchClass(String className) {
758 Names names = tsym.name.table.names;
760 // search by qualified name first
761 ClassDoc cd = env.lookupClass(className);
762 if (cd != null) {
763 return cd;
764 }
766 // search inner classes
767 //### Add private entry point to avoid creating array?
768 //### Replicate code in innerClasses here to avoid consing?
769 for (ClassDoc icd : innerClasses()) {
770 if (icd.name().equals(className) ||
771 //### This is from original javadoc but it looks suspicious to me...
772 //### I believe it is attempting to compensate for the confused
773 //### convention of including the nested class qualifiers in the
774 //### 'name' of the inner class, rather than the true simple name.
775 icd.name().endsWith("." + className)) {
776 return icd;
777 } else {
778 ClassDoc innercd = ((ClassDocImpl) icd).searchClass(className);
779 if (innercd != null) {
780 return innercd;
781 }
782 }
783 }
785 // check in this package
786 cd = containingPackage().findClass(className);
787 if (cd != null) {
788 return cd;
789 }
791 // make sure that this symbol has been completed
792 if (tsym.completer != null) {
793 tsym.complete();
794 }
796 // search imports
798 if (tsym.sourcefile != null) {
800 //### This information is available only for source classes.
802 Env<AttrContext> compenv = env.enter.getEnv(tsym);
803 if (compenv == null) return null;
805 Scope s = compenv.toplevel.namedImportScope;
806 for (Scope.Entry e = s.lookup(names.fromString(className)); e.scope != null; e = e.next()) {
807 if (e.sym.kind == Kinds.TYP) {
808 ClassDoc c = env.getClassDoc((ClassSymbol)e.sym);
809 return c;
810 }
811 }
813 s = compenv.toplevel.starImportScope;
814 for (Scope.Entry e = s.lookup(names.fromString(className)); e.scope != null; e = e.next()) {
815 if (e.sym.kind == Kinds.TYP) {
816 ClassDoc c = env.getClassDoc((ClassSymbol)e.sym);
817 return c;
818 }
819 }
820 }
822 return null; // not found
823 }
826 private boolean hasParameterTypes(MethodSymbol method, String[] argTypes) {
828 if (argTypes == null) {
829 // wildcard
830 return true;
831 }
833 int i = 0;
834 List<Type> types = method.type.getParameterTypes();
836 if (argTypes.length != types.length()) {
837 return false;
838 }
840 for (Type t : types) {
841 String argType = argTypes[i++];
842 // For vararg method, "T..." matches type T[].
843 if (i == argTypes.length) {
844 argType = argType.replace("...", "[]");
845 }
846 if (!hasTypeName(env.types.erasure(t), argType)) { //###(gj)
847 return false;
848 }
849 }
850 return true;
851 }
852 // where
853 private boolean hasTypeName(Type t, String name) {
854 return
855 name.equals(TypeMaker.getTypeName(t, true))
856 ||
857 name.equals(TypeMaker.getTypeName(t, false))
858 ||
859 (qualifiedName() + "." + name).equals(TypeMaker.getTypeName(t, true));
860 }
864 /**
865 * Find a method in this class scope.
866 * Search order: this class, interfaces, superclasses, outerclasses.
867 * Note that this is not necessarily what the compiler would do!
868 *
869 * @param methodName the unqualified name to search for.
870 * @param paramTypes the array of Strings for method parameter types.
871 * @return the first MethodDocImpl which matches, null if not found.
872 */
873 public MethodDocImpl findMethod(String methodName, String[] paramTypes) {
874 // Use hash table 'searched' to avoid searching same class twice.
875 //### It is not clear how this could happen.
876 return searchMethod(methodName, paramTypes, new HashSet<ClassDocImpl>());
877 }
879 private MethodDocImpl searchMethod(String methodName,
880 String[] paramTypes, Set<ClassDocImpl> searched) {
881 //### Note that this search is not necessarily what the compiler would do!
883 Names names = tsym.name.table.names;
884 // do not match constructors
885 if (names.init.contentEquals(methodName)) {
886 return null;
887 }
889 ClassDocImpl cdi;
890 MethodDocImpl mdi;
892 if (searched.contains(this)) {
893 return null;
894 }
895 searched.add(this);
897 //DEBUG
898 /*---------------------------------*
899 System.out.print("searching " + this + " for " + methodName);
900 if (paramTypes == null) {
901 System.out.println("()");
902 } else {
903 System.out.print("(");
904 for (int k=0; k < paramTypes.length; k++) {
905 System.out.print(paramTypes[k]);
906 if ((k + 1) < paramTypes.length) {
907 System.out.print(", ");
908 }
909 }
910 System.out.println(")");
911 }
912 *---------------------------------*/
914 // search current class
915 Scope.Entry e = tsym.members().lookup(names.fromString(methodName));
917 //### Using modifier filter here isn't really correct,
918 //### but emulates the old behavior. Instead, we should
919 //### apply the normal rules of visibility and inheritance.
921 if (paramTypes == null) {
922 // If no parameters specified, we are allowed to return
923 // any method with a matching name. In practice, the old
924 // code returned the first method, which is now the last!
925 // In order to provide textually identical results, we
926 // attempt to emulate the old behavior.
927 MethodSymbol lastFound = null;
928 for (; e.scope != null; e = e.next()) {
929 if (e.sym.kind == Kinds.MTH) {
930 //### Should intern methodName as Name.
931 if (e.sym.name.toString().equals(methodName)) {
932 lastFound = (MethodSymbol)e.sym;
933 }
934 }
935 }
936 if (lastFound != null) {
937 return env.getMethodDoc(lastFound);
938 }
939 } else {
940 for (; e.scope != null; e = e.next()) {
941 if (e.sym != null &&
942 e.sym.kind == Kinds.MTH) {
943 //### Should intern methodName as Name.
944 if (hasParameterTypes((MethodSymbol)e.sym, paramTypes)) {
945 return env.getMethodDoc((MethodSymbol)e.sym);
946 }
947 }
948 }
949 }
951 //### If we found a MethodDoc above, but which did not pass
952 //### the modifier filter, we should return failure here!
954 // search superclass
955 cdi = (ClassDocImpl)superclass();
956 if (cdi != null) {
957 mdi = cdi.searchMethod(methodName, paramTypes, searched);
958 if (mdi != null) {
959 return mdi;
960 }
961 }
963 // search interfaces
964 ClassDoc intf[] = interfaces();
965 for (int i = 0; i < intf.length; i++) {
966 cdi = (ClassDocImpl)intf[i];
967 mdi = cdi.searchMethod(methodName, paramTypes, searched);
968 if (mdi != null) {
969 return mdi;
970 }
971 }
973 // search enclosing class
974 cdi = (ClassDocImpl)containingClass();
975 if (cdi != null) {
976 mdi = cdi.searchMethod(methodName, paramTypes, searched);
977 if (mdi != null) {
978 return mdi;
979 }
980 }
982 //###(gj) As a temporary measure until type variables are better
983 //### handled, try again without the parameter types.
984 //### This should most often find the right method, and occassionally
985 //### find the wrong one.
986 //if (paramTypes != null) {
987 // return findMethod(methodName, null);
988 //}
990 return null;
991 }
993 /**
994 * Find constructor in this class.
995 *
996 * @param constrName the unqualified name to search for.
997 * @param paramTypes the array of Strings for constructor parameters.
998 * @return the first ConstructorDocImpl which matches, null if not found.
999 */
1000 public ConstructorDoc findConstructor(String constrName,
1001 String[] paramTypes) {
1002 Names names = tsym.name.table.names;
1003 for (Scope.Entry e = tsym.members().lookup(names.fromString("<init>")); e.scope != null; e = e.next()) {
1004 if (e.sym.kind == Kinds.MTH) {
1005 if (hasParameterTypes((MethodSymbol)e.sym, paramTypes)) {
1006 return env.getConstructorDoc((MethodSymbol)e.sym);
1007 }
1008 }
1009 }
1011 //###(gj) As a temporary measure until type variables are better
1012 //### handled, try again without the parameter types.
1013 //### This will often find the right constructor, and occassionally
1014 //### find the wrong one.
1015 //if (paramTypes != null) {
1016 // return findConstructor(constrName, null);
1017 //}
1019 return null;
1020 }
1022 /**
1023 * Find a field in this class scope.
1024 * Search order: this class, outerclasses, interfaces,
1025 * superclasses. IMP: If see tag is defined in an inner class,
1026 * which extends a super class and if outerclass and the super
1027 * class have a visible field in common then Java compiler cribs
1028 * about the ambiguity, but the following code will search in the
1029 * above given search order.
1030 *
1031 * @param fieldName the unqualified name to search for.
1032 * @return the first FieldDocImpl which matches, null if not found.
1033 */
1034 public FieldDoc findField(String fieldName) {
1035 return searchField(fieldName, new HashSet<ClassDocImpl>());
1036 }
1038 private FieldDocImpl searchField(String fieldName, Set<ClassDocImpl> searched) {
1039 Names names = tsym.name.table.names;
1040 if (searched.contains(this)) {
1041 return null;
1042 }
1043 searched.add(this);
1045 for (Scope.Entry e = tsym.members().lookup(names.fromString(fieldName)); e.scope != null; e = e.next()) {
1046 if (e.sym.kind == Kinds.VAR) {
1047 //### Should intern fieldName as Name.
1048 return env.getFieldDoc((VarSymbol)e.sym);
1049 }
1050 }
1052 //### If we found a FieldDoc above, but which did not pass
1053 //### the modifier filter, we should return failure here!
1055 ClassDocImpl cdi = (ClassDocImpl)containingClass();
1056 if (cdi != null) {
1057 FieldDocImpl fdi = cdi.searchField(fieldName, searched);
1058 if (fdi != null) {
1059 return fdi;
1060 }
1061 }
1063 // search superclass
1064 cdi = (ClassDocImpl)superclass();
1065 if (cdi != null) {
1066 FieldDocImpl fdi = cdi.searchField(fieldName, searched);
1067 if (fdi != null) {
1068 return fdi;
1069 }
1070 }
1072 // search interfaces
1073 ClassDoc intf[] = interfaces();
1074 for (int i = 0; i < intf.length; i++) {
1075 cdi = (ClassDocImpl)intf[i];
1076 FieldDocImpl fdi = cdi.searchField(fieldName, searched);
1077 if (fdi != null) {
1078 return fdi;
1079 }
1080 }
1082 return null;
1083 }
1085 /**
1086 * Get the list of classes declared as imported.
1087 * These are called "single-type-import declarations" in the JLS.
1088 * This method is deprecated in the ClassDoc interface.
1089 *
1090 * @return an array of ClassDocImpl representing the imported classes.
1091 *
1092 * @deprecated Import declarations are implementation details that
1093 * should not be exposed here. In addition, not all imported
1094 * classes are imported through single-type-import declarations.
1095 */
1096 @Deprecated
1097 public ClassDoc[] importedClasses() {
1098 // information is not available for binary classfiles
1099 if (tsym.sourcefile == null) return new ClassDoc[0];
1101 ListBuffer<ClassDocImpl> importedClasses = new ListBuffer<ClassDocImpl>();
1103 Env<AttrContext> compenv = env.enter.getEnv(tsym);
1104 if (compenv == null) return new ClassDocImpl[0];
1106 Name asterisk = tsym.name.table.names.asterisk;
1107 for (JCTree t : compenv.toplevel.defs) {
1108 if (t.hasTag(IMPORT)) {
1109 JCTree imp = ((JCImport) t).qualid;
1110 if ((TreeInfo.name(imp) != asterisk) &&
1111 (imp.type.tsym.kind & Kinds.TYP) != 0) {
1112 importedClasses.append(
1113 env.getClassDoc((ClassSymbol)imp.type.tsym));
1114 }
1115 }
1116 }
1118 return importedClasses.toArray(new ClassDocImpl[importedClasses.length()]);
1119 }
1121 /**
1122 * Get the list of packages declared as imported.
1123 * These are called "type-import-on-demand declarations" in the JLS.
1124 * This method is deprecated in the ClassDoc interface.
1125 *
1126 * @return an array of PackageDocImpl representing the imported packages.
1127 *
1128 * ###NOTE: the syntax supports importing all inner classes from a class as well.
1129 * @deprecated Import declarations are implementation details that
1130 * should not be exposed here. In addition, this method's
1131 * return type does not allow for all type-import-on-demand
1132 * declarations to be returned.
1133 */
1134 @Deprecated
1135 public PackageDoc[] importedPackages() {
1136 // information is not available for binary classfiles
1137 if (tsym.sourcefile == null) return new PackageDoc[0];
1139 ListBuffer<PackageDocImpl> importedPackages = new ListBuffer<PackageDocImpl>();
1141 //### Add the implicit "import java.lang.*" to the result
1142 Names names = tsym.name.table.names;
1143 importedPackages.append(env.getPackageDoc(env.reader.enterPackage(names.java_lang)));
1145 Env<AttrContext> compenv = env.enter.getEnv(tsym);
1146 if (compenv == null) return new PackageDocImpl[0];
1148 for (JCTree t : compenv.toplevel.defs) {
1149 if (t.hasTag(IMPORT)) {
1150 JCTree imp = ((JCImport) t).qualid;
1151 if (TreeInfo.name(imp) == names.asterisk) {
1152 JCFieldAccess sel = (JCFieldAccess)imp;
1153 Symbol s = sel.selected.type.tsym;
1154 PackageDocImpl pdoc = env.getPackageDoc(s.packge());
1155 if (!importedPackages.contains(pdoc))
1156 importedPackages.append(pdoc);
1157 }
1158 }
1159 }
1161 return importedPackages.toArray(new PackageDocImpl[importedPackages.length()]);
1162 }
1164 /**
1165 * Return the type's dimension information.
1166 * Always return "", as this is not an array type.
1167 */
1168 public String dimension() {
1169 return "";
1170 }
1172 /**
1173 * Return this type as a class, which it already is.
1174 */
1175 public ClassDoc asClassDoc() {
1176 return this;
1177 }
1179 /**
1180 * Return null (unless overridden), as this is not an annotation type.
1181 */
1182 public AnnotationTypeDoc asAnnotationTypeDoc() {
1183 return null;
1184 }
1186 /**
1187 * Return null, as this is not a class instantiation.
1188 */
1189 public ParameterizedType asParameterizedType() {
1190 return null;
1191 }
1193 /**
1194 * Return null, as this is not a type variable.
1195 */
1196 public TypeVariable asTypeVariable() {
1197 return null;
1198 }
1200 /**
1201 * Return null, as this is not a wildcard type.
1202 */
1203 public WildcardType asWildcardType() {
1204 return null;
1205 }
1207 /**
1208 * Returns null, as this is not an annotated type.
1209 */
1210 public AnnotatedType asAnnotatedType() {
1211 return null;
1212 }
1214 /**
1215 * Return false, as this is not a primitive type.
1216 */
1217 public boolean isPrimitive() {
1218 return false;
1219 }
1221 //--- Serialization ---
1223 //### These methods ignore modifier filter.
1225 /**
1226 * Return true if this class implements <code>java.io.Serializable</code>.
1227 *
1228 * Since <code>java.io.Externalizable</code> extends
1229 * <code>java.io.Serializable</code>,
1230 * Externalizable objects are also Serializable.
1231 */
1232 public boolean isSerializable() {
1233 try {
1234 return env.types.isSubtype(type, env.syms.serializableType);
1235 } catch (CompletionFailure ex) {
1236 // quietly ignore completion failures
1237 return false;
1238 }
1239 }
1241 /**
1242 * Return true if this class implements
1243 * <code>java.io.Externalizable</code>.
1244 */
1245 public boolean isExternalizable() {
1246 try {
1247 return env.types.isSubtype(type, env.externalizableSym.type);
1248 } catch (CompletionFailure ex) {
1249 // quietly ignore completion failures
1250 return false;
1251 }
1252 }
1254 /**
1255 * Return the serialization methods for this class.
1256 *
1257 * @return an array of <code>MethodDocImpl</code> that represents
1258 * the serialization methods for this class.
1259 */
1260 public MethodDoc[] serializationMethods() {
1261 if (serializedForm == null) {
1262 serializedForm = new SerializedForm(env, tsym, this);
1263 }
1264 //### Clone this?
1265 return serializedForm.methods();
1266 }
1268 /**
1269 * Return the Serializable fields of class.<p>
1270 *
1271 * Return either a list of default fields documented by
1272 * <code>serial</code> tag<br>
1273 * or return a single <code>FieldDoc</code> for
1274 * <code>serialPersistentField</code> member.
1275 * There should be a <code>serialField</code> tag for
1276 * each Serializable field defined by an <code>ObjectStreamField</code>
1277 * array component of <code>serialPersistentField</code>.
1278 *
1279 * @returns an array of <code>FieldDoc</code> for the Serializable fields
1280 * of this class.
1281 *
1282 * @see #definesSerializableFields()
1283 * @see SerialFieldTagImpl
1284 */
1285 public FieldDoc[] serializableFields() {
1286 if (serializedForm == null) {
1287 serializedForm = new SerializedForm(env, tsym, this);
1288 }
1289 //### Clone this?
1290 return serializedForm.fields();
1291 }
1293 /**
1294 * Return true if Serializable fields are explicitly defined with
1295 * the special class member <code>serialPersistentFields</code>.
1296 *
1297 * @see #serializableFields()
1298 * @see SerialFieldTagImpl
1299 */
1300 public boolean definesSerializableFields() {
1301 if (!isSerializable() || isExternalizable()) {
1302 return false;
1303 } else {
1304 if (serializedForm == null) {
1305 serializedForm = new SerializedForm(env, tsym, this);
1306 }
1307 //### Clone this?
1308 return serializedForm.definesSerializableFields();
1309 }
1310 }
1312 /**
1313 * Determine if a class is a RuntimeException.
1314 * <p>
1315 * Used only by ThrowsTagImpl.
1316 */
1317 boolean isRuntimeException() {
1318 return tsym.isSubClass(env.syms.runtimeExceptionType.tsym, env.types);
1319 }
1321 /**
1322 * Return the source position of the entity, or null if
1323 * no position is available.
1324 */
1325 @Override
1326 public SourcePosition position() {
1327 if (tsym.sourcefile == null) return null;
1328 return SourcePositionImpl.make(tsym.sourcefile,
1329 (tree==null) ? Position.NOPOS : tree.pos,
1330 lineMap);
1331 }
1332 }