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