• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2003, 2022, 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.google.doclava.javadoc;
27 
28 import com.google.doclava.annotation.Unused;
29 import com.google.doclava.annotation.Used;
30 import com.sun.javadoc.AnnotatedType;
31 import com.sun.javadoc.AnnotationTypeDoc;
32 import com.sun.javadoc.ClassDoc;
33 import com.sun.javadoc.ConstructorDoc;
34 import com.sun.javadoc.FieldDoc;
35 import com.sun.javadoc.MethodDoc;
36 import com.sun.javadoc.PackageDoc;
37 import com.sun.javadoc.ParamTag;
38 import com.sun.javadoc.ParameterizedType;
39 import com.sun.javadoc.Type;
40 import com.sun.javadoc.TypeVariable;
41 import com.sun.javadoc.WildcardType;
42 import com.sun.tools.javac.code.Symbol.ClassSymbol;
43 import com.sun.tools.javac.comp.AttrContext;
44 import com.sun.tools.javac.comp.Env;
45 import com.sun.tools.javac.code.Scope;
46 import com.sun.tools.javac.code.Symbol;
47 import com.sun.tools.javac.util.Names;
48 import javax.lang.model.element.ElementKind;
49 import javax.lang.model.element.ExecutableElement;
50 import javax.lang.model.element.NestingKind;
51 import javax.lang.model.element.TypeElement;
52 import javax.lang.model.element.VariableElement;
53 import javax.lang.model.type.TypeKind;
54 import javax.lang.model.type.TypeMirror;
55 import javax.lang.model.util.ElementFilter;
56 import jdk.javadoc.internal.tool.DocEnvImpl;
57 import jdk.javadoc.internal.tool.ToolEnvironment;
58 
59 import static com.sun.tools.javac.code.Kinds.Kind.*;
60 
61 class ClassDocImpl extends ProgramElementDocImpl<TypeElement> implements ClassDoc {
62 
63     protected final TypeElement typeElement;
64 
65     // Cached fields
66     private ConstructorDoc[] constructorsFiltered;
67     private ConstructorDoc[] constructorsAll;
68     private Type[] interfaceTypes;
69     private ClassDoc[] interfaces;
70     private TypeVariable[] typeParameters;
71     private MethodDoc[] methodsFiltered;
72     private MethodDoc[] methodsAll;
73     private FieldDoc[] fieldsFiltered;
74     private FieldDoc[] fieldsAll;
75     private FieldDoc[] enumConstants;
76     private ClassDoc[] innerClassesFiltered;
77     private ClassDoc[] innerClassesAll;
78 
ClassDocImpl(TypeElement c, Context context)79     protected ClassDocImpl(TypeElement c, Context context) {
80         super(c, context);
81         typeElement = c;
82 
83         if (c.getKind().isInterface()) {
84             reflectModifiers |= java.lang.reflect.Modifier.INTERFACE;
85         }
86     }
87 
create(TypeElement e, Context context)88     static ClassDocImpl create(TypeElement e, Context context) {
89         return context.caches.classes.computeIfAbsent(e, el -> new ClassDocImpl(el, context));
90     }
91 
92     @Override
93     @Unused(implemented = true)
modifiers()94     public String modifiers() {
95         return java.lang.reflect.Modifier.toString(modifierSpecifier());
96     }
97 
98     @Override
99     @Unused(implemented = true)
modifierSpecifier()100     public int modifierSpecifier() {
101         if (isInterface() || isAnnotationType()) {
102             return reflectModifiers & ~java.lang.reflect.Modifier.ABSTRACT;
103         }
104         return reflectModifiers;
105     }
106 
107     private Boolean isClass;
108 
109     @Override
110     @Unused(implemented = true)
isClass()111     public boolean isClass() {
112         if (isClass == null) {
113             isClass = typeElement.getKind().isClass();
114         }
115         return isClass;
116     }
117 
118     private Boolean isOrdinaryClass;
119 
120     @Override
121     @Used(implemented = true)
isOrdinaryClass()122     public boolean isOrdinaryClass() {
123         if (isOrdinaryClass == null) {
124             isOrdinaryClass = (!isEnum() &&
125                     !isInterface() &&
126                     !isAnnotationType() &&
127                     !isError() &&
128                     !isException()
129             );
130         }
131         return isOrdinaryClass;
132     }
133 
134     private Boolean isEnum;
135 
136     @Override
137     @Used(implemented = true)
isEnum()138     public boolean isEnum() {
139         if (isEnum == null) {
140             isEnum = (typeElement.getKind() == ElementKind.ENUM);
141         }
142         return isEnum;
143     }
144 
145     private Boolean isInterface;
146 
147     @Override
148     @Used(implemented = true)
isInterface()149     public boolean isInterface() {
150         if (isInterface == null) {
151             isInterface = (typeElement.getKind() == ElementKind.INTERFACE);
152         }
153         return isInterface;
154     }
155 
156     private Boolean isException;
157 
158     @Override
159     @Used(implemented = true)
isException()160     public boolean isException() {
161         if (isException == null) {
162             isException = context.docletElementUtils.isException(typeElement);
163         }
164         return isException;
165     }
166 
167     private Boolean isError;
168 
169     @Override
170     @Used(implemented = true)
isError()171     public boolean isError() {
172         if (isError == null) {
173             isError = context.docletElementUtils.isError(typeElement);
174         }
175         return isError;
176     }
177 
178     private String name;
179 
180     @Override
181     @Used(implemented = true)
name()182     public String name() {
183         if (name == null) {
184             name = context.docletElementUtils.getClassNameUntilNotNested(typeElement);
185         }
186         return name;
187     }
188 
189     private String qualifiedName;
190 
191     @Override
192     @Used(implemented = true)
qualifiedName()193     public String qualifiedName() {
194         if (qualifiedName == null) {
195             qualifiedName = typeElement.getQualifiedName().toString();
196         }
197         return qualifiedName;
198     }
199 
200     @Override
201     @Used(implemented = true)
isIncluded()202     public boolean isIncluded() {
203         return context.environment.isIncluded(typeElement);
204     }
205 
206     @Override
207     @Used(implemented = true)
isAbstract()208     public boolean isAbstract() {
209         return java.lang.reflect.Modifier.isAbstract(reflectModifiers);
210     }
211 
212     private Boolean isSerializable;
213 
214     @Override
215     @Used(implemented = true)
isSerializable()216     public boolean isSerializable() {
217         if (isSerializable == null) {
218             var serializable = context.environment.getElementUtils()
219                     .getTypeElement("java.io.Serializable").asType();
220             isSerializable = context.environment.getTypeUtils()
221                     .isSubtype(typeElement.asType(), serializable);
222         }
223         return isSerializable;
224     }
225 
226     private Boolean isExternalizable;
227 
228     @Override
229     @Unused(implemented = true)
isExternalizable()230     public boolean isExternalizable() {
231         if (isExternalizable == null) {
232             var externalizable = context.environment.getElementUtils()
233                     .getTypeElement("java.io.Externalizable").asType();
234             isExternalizable = context.environment.getTypeUtils()
235                     .isSubtype(typeElement.asType(), externalizable);
236         }
237         return isExternalizable;
238     }
239 
240     @Override
241     @Unused
serializationMethods()242     public MethodDoc[] serializationMethods() {
243         throw new UnsupportedOperationException("not yet implemented");
244     }
245 
246     @Override
247     @Unused
serializableFields()248     public FieldDoc[] serializableFields() {
249         throw new UnsupportedOperationException("not yet implemented");
250     }
251 
252     @Override
253     @Unused
definesSerializableFields()254     public boolean definesSerializableFields() {
255         throw new UnsupportedOperationException("not yet implemented");
256     }
257 
258     @Override
259     @Used(implemented = true)
superclass()260     public ClassDoc superclass() {
261         if (isInterface()) {
262             return null;
263         }
264         TypeMirror superclassMirror = typeElement.getSuperclass();
265         if (superclassMirror.getKind() == TypeKind.NONE) {
266             return null;
267         }
268         return TypeImpl.create(superclassMirror, context).asClassDoc();
269     }
270 
271     @Override
272     @Used(implemented = true)
superclassType()273     public Type superclassType() {
274         if (isInterface()) {
275             return null;
276         }
277         TypeMirror superclassMirror = typeElement.getSuperclass();
278         if (superclassMirror.getKind() == TypeKind.NONE) {
279             return null;
280         }
281         return TypeImpl.create(superclassMirror, context);
282     }
283 
284     @Override
285     @Unused(implemented = true)
subclassOf(ClassDoc cd)286     public boolean subclassOf(ClassDoc cd) {
287         TypeElement other = context.environment.getElementUtils()
288                 .getTypeElement(cd.qualifiedName());
289         if (isInterface()) {
290             return other.getQualifiedName().contentEquals("java.lang.Object");
291         }
292         return context.environment.getTypeUtils().isSubtype(typeElement.asType(), other.asType());
293     }
294 
295     @Override
296     @Used(implemented = true)
interfaces()297     public ClassDoc[] interfaces() {
298         if (interfaces == null) {
299             interfaces = typeElement.getInterfaces()
300                     .stream()
301                     .map(typeMirror -> {
302                         TypeElement asElement = (TypeElement) context.environment.getTypeUtils()
303                                 .asElement(typeMirror);
304                         return ClassDocImpl.create(asElement, context);
305                     })
306                     .toArray(ClassDoc[]::new);
307         }
308         return interfaces;
309     }
310 
311     @Override
312     @Used(implemented = true)
interfaceTypes()313     public Type[] interfaceTypes() {
314         if (interfaceTypes == null) {
315             interfaceTypes = typeElement.getInterfaces()
316                     .stream()
317                     .filter(typeMirror -> !typeMirror.getKind().equals(TypeKind.NONE))
318                     .map(typeMirror -> TypeImpl.create(typeMirror, context))
319                     .toArray(Type[]::new);
320         }
321         return interfaceTypes;
322     }
323 
324     @Override
325     @Used(implemented = true)
typeParameters()326     public TypeVariable[] typeParameters() {
327         if (typeParameters == null) {
328             typeParameters = typeElement.getTypeParameters()
329                     .stream()
330                     .map(tp -> {
331                         javax.lang.model.type.TypeVariable tv = (javax.lang.model.type.TypeVariable) tp.asType();
332                         return TypeVariableImpl.create(tv, context);
333                     })
334                     .toArray(TypeVariable[]::new);
335         }
336         return typeParameters;
337     }
338 
339     @Override
340     @Unused
typeParamTags()341     public ParamTag[] typeParamTags() {
342         throw new UnsupportedOperationException("not yet implemented");
343     }
344 
345     @Override
346     @Unused(implemented = true)
fields()347     public FieldDoc[] fields() {
348         if (fieldsAll == null) {
349             fieldsAll = getFields(true);
350         }
351         return fieldsAll;
352     }
353 
354     @Override
355     @Used(implemented = true)
fields(boolean filter)356     public FieldDoc[] fields(boolean filter) {
357         if (filter) {
358             if (fieldsFiltered == null) {
359                 fieldsFiltered = getFields(true);
360             }
361             return fieldsFiltered;
362         } else {
363             if (fieldsAll == null) {
364                 fieldsAll = getFields(false);
365             }
366             return fieldsAll;
367         }
368     }
369 
getFields(boolean filter)370     private FieldDoc[] getFields(boolean filter) {
371         return typeElement.getEnclosedElements()
372                 .stream()
373                 .filter(e -> e.getKind() == ElementKind.FIELD)
374                 .filter(field -> !filter || context.environment.isSelected(field))
375                 .map(field -> FieldDocImpl.create((VariableElement) field, context))
376                 .toArray(FieldDoc[]::new);
377     }
378 
379     @Override
380     @Used(implemented = true)
enumConstants()381     public FieldDoc[] enumConstants() {
382         if (enumConstants == null) {
383             enumConstants = typeElement.getEnclosedElements()
384                     .stream()
385                     .filter(e -> e.getKind() == ElementKind.ENUM_CONSTANT)
386                     .map(enumConstant -> FieldDocImpl.create((VariableElement) enumConstant,
387                             context))
388                     .toArray(FieldDoc[]::new);
389         }
390         return enumConstants;
391     }
392 
393     @Override
394     @Unused(implemented = true)
methods()395     public MethodDoc[] methods() {
396         if (methodsAll == null) {
397             methodsAll = getMethods(true);
398         }
399         return methodsAll;
400     }
401 
402     @Override
403     @Used(implemented = true)
methods(boolean filter)404     public MethodDoc[] methods(boolean filter) {
405         if (filter) {
406             if (methodsFiltered == null) {
407                 methodsFiltered = getMethods(true);
408             }
409             return methodsFiltered;
410         } else {
411             if (methodsAll == null) {
412                 methodsAll = getMethods(false);
413             }
414             return methodsAll;
415         }
416     }
417 
getMethods(boolean filter)418     private MethodDoc[] getMethods(boolean filter) {
419         return typeElement.getEnclosedElements()
420                 .stream()
421                 .filter(e -> e.getKind() == ElementKind.METHOD)
422                 .filter(method -> !filter || context.environment.isSelected(method))
423                 .map(method -> MethodDocImpl.create((ExecutableElement) method, context))
424                 .toArray(MethodDoc[]::new);
425     }
426 
427     @Override
428     @Unused(implemented = true)
constructors()429     public ConstructorDoc[] constructors() {
430         if (constructorsFiltered == null) {
431             constructorsFiltered = getConstructors(true);
432         }
433         return constructorsFiltered;
434     }
435 
436     @Override
437     @Used(implemented = true)
constructors(boolean filter)438     public ConstructorDoc[] constructors(boolean filter) {
439         if (filter) {
440             if (constructorsFiltered == null) {
441                 constructorsFiltered = getConstructors(true);
442             }
443             return constructorsFiltered;
444         } else {
445             if (constructorsAll == null) {
446                 constructorsAll = getConstructors(false);
447             }
448             return constructorsAll;
449         }
450     }
451 
getConstructors(boolean filter)452     private ConstructorDoc[] getConstructors(boolean filter) {
453         return typeElement.getEnclosedElements()
454                 .stream()
455                 .filter(e -> e.getKind() == ElementKind.CONSTRUCTOR)
456                 .filter(ctor -> !filter || context.environment.isSelected(ctor))
457                 .map(e -> ConstructorDocImpl.create((ExecutableElement) e, context))
458                 .toArray(ConstructorDoc[]::new);
459     }
460 
461     @Override
462     @Used(implemented = true)
innerClasses()463     public ClassDoc[] innerClasses() {
464         if (innerClassesFiltered == null) {
465             innerClassesFiltered = getInnerClasses(true);
466         }
467         return innerClassesFiltered;
468     }
469 
470     @Override
471     @Used(implemented = true)
innerClasses(boolean filter)472     public ClassDoc[] innerClasses(boolean filter) {
473         if (filter) {
474             return innerClasses();
475         } else {
476             if (innerClassesAll == null) {
477                 innerClassesAll = getInnerClasses(false);
478             }
479             return innerClassesAll;
480         }
481     }
482 
getInnerClasses(boolean filter)483     private ClassDoc[] getInnerClasses(boolean filter) {
484         return ElementFilter.typesIn(typeElement.getEnclosedElements())
485                 .stream()
486                 .filter(te -> te.getNestingKind() == NestingKind.MEMBER &&
487                         (te.getKind() == ElementKind.CLASS || te.getKind() == ElementKind.INTERFACE)
488                 )
489                 .filter(te -> !filter || context.environment.isSelected(te))
490                 .map(te -> ClassDocImpl.create(te, context))
491                 .toArray(ClassDoc[]::new);
492     }
493 
494     /**
495      * Note that this implementation does not search in sources!
496      *
497      * <p>
498      *
499      * {@inheritDoc}
500      *
501      * @implNote Does not search in sources.
502      */
503     @Override
504     @Used(implemented = true)
findClass(String className)505     public ClassDoc findClass(String className) {
506         ClassDoc result = searchClass(className);
507         if (result != null) {
508             return result;
509         }
510 
511         ClassDoc enclosing = containingClass();
512         while (enclosing != null && enclosing.containingClass() != null) {
513             enclosing = enclosing.containingClass();
514         }
515         if (enclosing == null) {
516             return null;
517         }
518         return ((ClassDocImpl) enclosing).searchClass(className);
519     }
520 
searchClass(String className)521     private ClassDoc searchClass(String className) {
522         TypeElement cls = context.environment.getElementUtils().getTypeElement(className);
523         if (cls != null) {
524             return ClassDocImpl.create(cls, context);
525         }
526 
527         for (ClassDoc nested : innerClasses()) {
528             if (nested.name().equals(className) || nested.name().endsWith("." + className)) {
529                 return nested;
530             } else {
531                 ClassDoc inNested = ((ClassDocImpl) nested).searchClass(className);
532                 if (inNested != null) {
533                     return inNested;
534                 }
535             }
536         }
537 
538         ClassDoc inPackage = containingPackage().findClass(className);
539         if (inPackage != null) {
540             return inPackage;
541         }
542 
543         //
544         if (! (typeElement instanceof ClassSymbol)) {
545             return null;
546         }
547         ClassSymbol tsym = (ClassSymbol)typeElement;
548         // make sure that this symbol has been completed
549         // TODO: do we need this anymore ?
550         if (tsym.completer != null) {
551             tsym.complete();
552         }
553 
554         // search imports
555         if (tsym.sourcefile != null) {
556 
557             ToolEnvironment toolEnv = ((DocEnvImpl)(context.environment)).toolEnv;
558             //### This information is available only for source classes.
559             Env<AttrContext> compenv = toolEnv.getEnv(tsym);
560             if (compenv == null) {
561                 return null;
562             }
563             Names names = tsym.name.table.names;
564             Scope s = compenv.toplevel.namedImportScope;
565             for (Symbol sym : s.getSymbolsByName(names.fromString(className))) {
566                 if (sym.kind == TYP) {
567                     return ClassDocImpl.create((TypeElement)sym, context);
568                 }
569             }
570 
571             s = compenv.toplevel.starImportScope;
572             for (Symbol sym : s.getSymbolsByName(names.fromString(className))) {
573                 if (sym.kind == TYP) {
574                     return ClassDocImpl.create((TypeElement)sym, context);
575                 }
576             }
577         }
578 
579         return null;
580     }
581 
582     @Override
583     @Unused
importedClasses()584     public ClassDoc[] importedClasses() {
585         throw new UnsupportedOperationException("not yet implemented");
586     }
587 
588     @Override
589     @Unused
importedPackages()590     public PackageDoc[] importedPackages() {
591         throw new UnsupportedOperationException("not yet implemented");
592     }
593 
594     @Override
595     @Unused(implemented = true)
typeName()596     public String typeName() {
597         return name();
598     }
599 
600     @Override
601     @Used(implemented = true)
qualifiedTypeName()602     public String qualifiedTypeName() {
603         return qualifiedName();
604     }
605 
606     private String simpleTypeName;
607 
608     @Override
609     @Used(implemented = true)
simpleTypeName()610     public String simpleTypeName() {
611         if (simpleTypeName == null) {
612             simpleTypeName = typeElement.getSimpleName().toString();
613         }
614         return simpleTypeName;
615     }
616 
617     @Override
618     @Used(implemented = true)
dimension()619     public String dimension() {
620         return "";
621     }
622 
623     @Override
624     @Used(implemented = true)
isPrimitive()625     public boolean isPrimitive() {
626         return false;
627     }
628 
629     @Override
630     @Used(implemented = true)
asClassDoc()631     public ClassDoc asClassDoc() {
632         return this;
633     }
634 
635     @Override
636     @Used(implemented = true)
asParameterizedType()637     public ParameterizedType asParameterizedType() {
638         return null;
639     }
640 
641     @Override
642     @Used(implemented = true)
asTypeVariable()643     public TypeVariable asTypeVariable() {
644         return null;
645     }
646 
647     @Override
648     @Used(implemented = true)
asWildcardType()649     public WildcardType asWildcardType() {
650         return null;
651     }
652 
653     @Override
654     @Used(implemented = true)
asAnnotatedType()655     public AnnotatedType asAnnotatedType() {
656         return null;
657     }
658 
659     @Override
660     @Unused(implemented = true)
asAnnotationTypeDoc()661     public AnnotationTypeDoc asAnnotationTypeDoc() {
662         return null;
663     }
664 
665     @Override
666     @Used(implemented = true)
getElementType()667     public Type getElementType() {
668         return null;
669     }
670 }
671