src/share/classes/com/sun/tools/javadoc/ClassDocImpl.java

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

mercurial