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