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