• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.doclava;
18 
19 import com.google.doclava.apicheck.ApiInfo;
20 import com.sun.javadoc.AnnotationDesc;
21 import com.sun.javadoc.AnnotationTypeDoc;
22 import com.sun.javadoc.AnnotationTypeElementDoc;
23 import com.sun.javadoc.AnnotationValue;
24 import com.sun.javadoc.ClassDoc;
25 import com.sun.javadoc.ConstructorDoc;
26 import com.sun.javadoc.Doc;
27 import com.sun.javadoc.ExecutableMemberDoc;
28 import com.sun.javadoc.FieldDoc;
29 import com.sun.javadoc.MemberDoc;
30 import com.sun.javadoc.MethodDoc;
31 import com.sun.javadoc.PackageDoc;
32 import com.sun.javadoc.ParamTag;
33 import com.sun.javadoc.Parameter;
34 import com.sun.javadoc.RootDoc;
35 import com.sun.javadoc.SeeTag;
36 import com.sun.javadoc.SourcePosition;
37 import com.sun.javadoc.Tag;
38 import com.sun.javadoc.ThrowsTag;
39 import com.sun.javadoc.Type;
40 
41 import java.util.ArrayList;
42 import java.util.Arrays;
43 import java.util.Collection;
44 import java.util.HashMap;
45 import java.util.HashSet;
46 import java.util.List;
47 
48 public class Converter {
49   private static RootDoc root;
50   private static List<ApiInfo> apis;
51 
makeInfo(RootDoc r)52   public static void makeInfo(RootDoc r) {
53     root = r;
54     apis = new ArrayList<>();
55 
56     // create the objects
57     ClassDoc[] classes = getClasses(r);
58     for (ClassDoc c : classes) {
59       Converter.obtainClass(c);
60     }
61 
62     ArrayList<ClassInfo> classesNeedingInit2 = new ArrayList<ClassInfo>();
63 
64     int i;
65     // fill in the fields that reference other classes
66     while (mClassesNeedingInit.size() > 0) {
67       i = mClassesNeedingInit.size() - 1;
68       ClassNeedingInit clni = mClassesNeedingInit.get(i);
69       mClassesNeedingInit.remove(i);
70 
71       initClass(clni.c, clni.cl);
72       classesNeedingInit2.add(clni.cl);
73     }
74     mClassesNeedingInit = null;
75     for (ClassInfo cl : classesNeedingInit2) {
76       cl.init2();
77     }
78 
79     finishAnnotationValueInit();
80 
81     // fill in the "root" stuff
82     mRootClasses = Converter.convertClasses(classes);
83   }
84 
85   /**
86    * Adds additional APIs to be available from calls to obtain() methods.
87    *
88    * @param api the APIs to add, must be non-{@code null}
89    */
addApiInfo(ApiInfo api)90   public static void addApiInfo(ApiInfo api) {
91     apis.add(api);
92   }
93 
getClasses(RootDoc r)94   private static ClassDoc[] getClasses(RootDoc r) {
95     ClassDoc[] classDocs = r.classes();
96     ArrayList<ClassDoc> filtered = new ArrayList<ClassDoc>(classDocs.length);
97     for (ClassDoc c : classDocs) {
98       if (c.position() != null) {
99         // Work around a javadoc bug in Java 7: We sometimes spuriously
100         // receive duplicate top level ClassDocs with null positions and no type
101         // information. Ignore them, since every ClassDoc must have a non null
102         // position.
103 
104         filtered.add(c);
105       }
106     }
107 
108     ClassDoc[] filteredArray = new ClassDoc[filtered.size()];
109     filtered.toArray(filteredArray);
110     return filteredArray;
111   }
112 
113   private static ClassInfo[] mRootClasses;
114 
rootClasses()115   public static Collection<ClassInfo> rootClasses() {
116     return Arrays.asList(mRootClasses);
117   }
118 
allClasses()119   public static Collection<ClassInfo> allClasses() {
120     return (Collection<ClassInfo>) mClasses.all();
121   }
122 
123   private static final MethodDoc[] EMPTY_METHOD_DOC = new MethodDoc[0];
124 
initClass(ClassDoc c, ClassInfo cl)125   private static void initClass(ClassDoc c, ClassInfo cl) {
126     MethodDoc[] annotationElements;
127     if (c instanceof AnnotationTypeDoc) {
128       annotationElements = ((AnnotationTypeDoc) c).elements();
129     } else {
130       annotationElements = EMPTY_METHOD_DOC;
131     }
132     cl.init(Converter.obtainType(c),
133             new ArrayList<ClassInfo>(Arrays.asList(Converter.convertClasses(c.interfaces()))),
134             new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(c.interfaceTypes()))),
135             new ArrayList<ClassInfo>(Arrays.asList(Converter.convertClasses(c.innerClasses()))),
136             new ArrayList<MethodInfo>(Arrays.asList(
137                     Converter.convertMethods(c.constructors(false)))),
138             new ArrayList<MethodInfo>(Arrays.asList(Converter.convertMethods(c.methods(false)))),
139             new ArrayList<MethodInfo>(Arrays.asList(Converter.convertMethods(annotationElements))),
140             new ArrayList<FieldInfo>(Arrays.asList(Converter.convertFields(c.fields(false)))),
141             new ArrayList<FieldInfo>(Arrays.asList(Converter.convertFields(c.enumConstants()))),
142             Converter.obtainPackage(c.containingPackage()),
143             Converter.obtainClass(c.containingClass()),
144             Converter.obtainClass(c.superclass()), Converter.obtainType(c.superclassType()),
145             new ArrayList<AnnotationInstanceInfo>(Arrays.asList(
146                     Converter.convertAnnotationInstances(c.annotations()))));
147 
148     cl.setHiddenMethods(
149             new ArrayList<MethodInfo>(Arrays.asList(Converter.getHiddenMethods(c.methods(false)))));
150     cl.setRemovedMethods(
151             new ArrayList<MethodInfo>(Arrays.asList(Converter.getRemovedMethods(c.methods(false)))));
152 
153     cl.setExhaustiveConstructors(
154         new ArrayList<MethodInfo>(Converter.convertAllMethods(c.constructors(false))));
155     cl.setExhaustiveMethods(
156         new ArrayList<MethodInfo>(Converter.convertAllMethods(c.methods(false))));
157     cl.setExhaustiveEnumConstants(
158         new ArrayList<FieldInfo>(Converter.convertAllFields(c.enumConstants())));
159     cl.setExhaustiveFields(
160         new ArrayList<FieldInfo>(Converter.convertAllFields(c.fields(false))));
161 
162     cl.setNonWrittenConstructors(
163             new ArrayList<MethodInfo>(Arrays.asList(Converter.convertNonWrittenConstructors(
164                     c.constructors(false)))));
165     cl.init3(
166             new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(c.typeParameters()))),
167             new ArrayList<ClassInfo>(Arrays.asList(
168                     Converter.convertClasses(c.innerClasses(false)))));
169   }
170 
171   /**
172    * Obtains a {@link ClassInfo} describing the specified class. If the class
173    * was not specified on the source path, this method will attempt to locate
174    * the class within the list of federated APIs.
175    *
176    * @param className the fully-qualified class name to search for
177    * @return info for the specified class, or {@code null} if not available
178    * @see #addApiInfo(ApiInfo)
179    */
obtainClass(String className)180   public static ClassInfo obtainClass(String className) {
181     ClassInfo result = Converter.obtainClass(root.classNamed(className));
182     if (result != null) {
183       return result;
184     }
185 
186     for (ApiInfo api : apis) {
187       result = api.findClass(className);
188       if (result != null) {
189         return result;
190       }
191     }
192 
193     return null;
194   }
195 
196   /**
197    * Obtains a {@link PackageInfo} describing the specified package. If the
198    * package was not specified on the source path, this method will attempt to
199    * locate the package within the list of federated APIs.
200    *
201    * @param packageName the fully-qualified package name to search for
202    * @return info for the specified package, or {@code null} if not available
203    * @see #addApiInfo(ApiInfo)
204    */
obtainPackage(String packageName)205   public static PackageInfo obtainPackage(String packageName) {
206     PackageInfo result = Converter.obtainPackage(root.packageNamed(packageName));
207     if (result != null) {
208       return result;
209     }
210 
211     for (ApiInfo api : apis) {
212       result = api.getPackages().get(packageName);
213       if (result != null) {
214         return result;
215       }
216     }
217 
218     return null;
219   }
220 
convertTag(Tag tag)221   private static TagInfo convertTag(Tag tag) {
222     return new TextTagInfo(tag.name(), tag.kind(), tag.text(),
223             Converter.convertSourcePosition(tag.position()));
224   }
225 
convertThrowsTag(ThrowsTag tag, ContainerInfo base)226   private static ThrowsTagInfo convertThrowsTag(ThrowsTag tag, ContainerInfo base) {
227     return new ThrowsTagInfo(tag.name(), tag.text(), tag.kind(), Converter.obtainClass(tag
228         .exception()), tag.exceptionComment(), base, Converter
229         .convertSourcePosition(tag.position()));
230   }
231 
convertParamTag(ParamTag tag, ContainerInfo base)232   private static ParamTagInfo convertParamTag(ParamTag tag, ContainerInfo base) {
233     return new ParamTagInfo(tag.name(), tag.kind(), tag.text(), tag.isTypeParameter(), tag
234         .parameterComment(), tag.parameterName(), base, Converter.convertSourcePosition(tag
235         .position()));
236   }
237 
convertSeeTag(SeeTag tag, ContainerInfo base)238   private static SeeTagInfo convertSeeTag(SeeTag tag, ContainerInfo base) {
239     return new SeeTagInfo(tag.name(), tag.kind(), tag.text(), base, Converter
240         .convertSourcePosition(tag.position()));
241   }
242 
convertSourcePosition(SourcePosition sp)243   private static SourcePositionInfo convertSourcePosition(SourcePosition sp) {
244     if (sp == null) {
245       return null;
246     }
247     return new SourcePositionInfo(sp.file().toString(), sp.line(), sp.column());
248   }
249 
convertTags(Tag[] tags, ContainerInfo base)250   public static TagInfo[] convertTags(Tag[] tags, ContainerInfo base) {
251     int len = tags.length;
252     TagInfo[] out = TagInfo.getArray(len);
253     for (int i = 0; i < len; i++) {
254       Tag t = tags[i];
255       /*
256        * System.out.println("Tag name='" + t.name() + "' kind='" + t.kind() + "'");
257        */
258       if (t instanceof SeeTag) {
259         out[i] = Converter.convertSeeTag((SeeTag) t, base);
260       } else if (t instanceof ThrowsTag) {
261         out[i] = Converter.convertThrowsTag((ThrowsTag) t, base);
262       } else if (t instanceof ParamTag) {
263         out[i] = Converter.convertParamTag((ParamTag) t, base);
264       } else {
265         out[i] = Converter.convertTag(t);
266       }
267     }
268     return out;
269   }
270 
convertClasses(ClassDoc[] classes)271   public static ClassInfo[] convertClasses(ClassDoc[] classes) {
272     if (classes == null) return null;
273     int N = classes.length;
274     ClassInfo[] result = new ClassInfo[N];
275     for (int i = 0; i < N; i++) {
276       result[i] = Converter.obtainClass(classes[i]);
277     }
278     return result;
279   }
280 
convertParameter(Parameter p, SourcePosition pos, boolean isVarArg)281   private static ParameterInfo convertParameter(Parameter p, SourcePosition pos, boolean isVarArg) {
282     if (p == null) return null;
283     ParameterInfo pi =
284         new ParameterInfo(p.name(), p.typeName(), Converter.obtainType(p.type()), isVarArg,
285           Converter.convertSourcePosition(pos),
286           Arrays.asList(Converter.convertAnnotationInstances(p.annotations())));
287     return pi;
288   }
289 
convertParameters(Parameter[] p, ExecutableMemberDoc m)290   private static ParameterInfo[] convertParameters(Parameter[] p, ExecutableMemberDoc m) {
291     SourcePosition pos = getPositionSafe(m);
292     int len = p.length;
293     ParameterInfo[] q = new ParameterInfo[len];
294     for (int i = 0; i < len; i++) {
295       boolean isVarArg = (m.isVarArgs() && i == len - 1);
296       q[i] = Converter.convertParameter(p[i], pos, isVarArg);
297     }
298     return q;
299   }
300 
convertTypes(Type[] p)301   private static TypeInfo[] convertTypes(Type[] p) {
302     if (p == null) return null;
303     int len = p.length;
304     TypeInfo[] q = new TypeInfo[len];
305     for (int i = 0; i < len; i++) {
306       q[i] = Converter.obtainType(p[i]);
307     }
308     return q;
309   }
310 
Converter()311   private Converter() {}
312 
313   private static class ClassNeedingInit {
ClassNeedingInit(ClassDoc c, ClassInfo cl)314     ClassNeedingInit(ClassDoc c, ClassInfo cl) {
315       this.c = c;
316       this.cl = cl;
317     }
318 
319     ClassDoc c;
320     ClassInfo cl;
321   }
322 
323   private static ArrayList<ClassNeedingInit> mClassesNeedingInit =
324       new ArrayList<ClassNeedingInit>();
325 
obtainClass(ClassDoc o)326   static ClassInfo obtainClass(ClassDoc o) {
327     return (ClassInfo) mClasses.obtain(o);
328   }
329 
330   private static Cache mClasses = new Cache() {
331     @Override
332     protected Object make(Object o) {
333       ClassDoc c = (ClassDoc) o;
334       ClassInfo cl =
335           new ClassInfo(c, c.getRawCommentText(), Converter.convertSourcePosition(c.position()), c
336               .isPublic(), c.isProtected(), c.isPackagePrivate(), c.isPrivate(), c.isStatic(), c
337               .isInterface(), c.isAbstract(), c.isOrdinaryClass(), c.isException(), c.isError(), c
338               .isEnum(), (c instanceof AnnotationTypeDoc), c.isFinal(), c.isIncluded(), c.name(), c
339               .qualifiedName(), c.qualifiedTypeName(), c.isPrimitive());
340       if (mClassesNeedingInit != null) {
341         mClassesNeedingInit.add(new ClassNeedingInit(c, cl));
342       }
343       return cl;
344     }
345 
346     @Override
347     protected void made(Object o, Object r) {
348       if (mClassesNeedingInit == null) {
349         initClass((ClassDoc) o, (ClassInfo) r);
350         ((ClassInfo) r).init2();
351       }
352     }
353 
354     @Override
355     Collection<?> all() {
356       return mCache.values();
357     }
358   };
359 
getHiddenMethods(MethodDoc[] methods)360   private static MethodInfo[] getHiddenMethods(MethodDoc[] methods) {
361     if (methods == null) return null;
362     ArrayList<MethodInfo> hiddenMethods = new ArrayList<MethodInfo>();
363     for (MethodDoc method : methods) {
364       MethodInfo methodInfo = Converter.obtainMethod(method);
365       if (methodInfo.isHidden()) {
366         hiddenMethods.add(methodInfo);
367       }
368     }
369 
370     return hiddenMethods.toArray(new MethodInfo[hiddenMethods.size()]);
371   }
372 
373   // Gets the removed methods regardless of access levels
getRemovedMethods(MethodDoc[] methods)374   private static MethodInfo[] getRemovedMethods(MethodDoc[] methods) {
375     if (methods == null) return null;
376     ArrayList<MethodInfo> removedMethods = new ArrayList<MethodInfo>();
377     for (MethodDoc method : methods) {
378       MethodInfo methodInfo = Converter.obtainMethod(method);
379       if (methodInfo.isRemoved()) {
380         removedMethods.add(methodInfo);
381       }
382     }
383 
384     return removedMethods.toArray(new MethodInfo[removedMethods.size()]);
385   }
386 
387   /**
388    * Converts FieldDoc[] into List<FieldInfo>. No filtering is done.
389    */
convertAllFields(FieldDoc[] fields)390   private static List<FieldInfo> convertAllFields(FieldDoc[] fields) {
391     if (fields == null) return null;
392     List<FieldInfo> allFields = new ArrayList<FieldInfo>();
393 
394     for (FieldDoc field : fields) {
395       FieldInfo fieldInfo = Converter.obtainField(field);
396       allFields.add(fieldInfo);
397     }
398 
399     return allFields;
400   }
401 
402   /**
403    * Converts ExecutableMemberDoc[] into List<MethodInfo>. No filtering is done.
404    */
convertAllMethods(ExecutableMemberDoc[] methods)405   private static List<MethodInfo> convertAllMethods(ExecutableMemberDoc[] methods) {
406     if (methods == null) return null;
407     List<MethodInfo> allMethods = new ArrayList<MethodInfo>();
408     for (ExecutableMemberDoc method : methods) {
409       MethodInfo methodInfo = Converter.obtainMethod(method);
410       allMethods.add(methodInfo);
411     }
412     return allMethods;
413   }
414 
415   /**
416    * Convert MethodDoc[] or ConstructorDoc[] into MethodInfo[].
417    * Also filters according to the -private, -public option,
418    * because the filtering doesn't seem to be working in the ClassDoc.constructors(boolean) call.
419    */
convertMethods(ExecutableMemberDoc[] methods)420   private static MethodInfo[] convertMethods(ExecutableMemberDoc[] methods) {
421     if (methods == null) return null;
422     List<MethodInfo> filteredMethods = new ArrayList<MethodInfo>();
423     for (ExecutableMemberDoc method : methods) {
424       MethodInfo methodInfo = Converter.obtainMethod(method);
425       if (methodInfo.checkLevel()) {
426         filteredMethods.add(methodInfo);
427       }
428     }
429 
430     return filteredMethods.toArray(new MethodInfo[filteredMethods.size()]);
431   }
432 
convertNonWrittenConstructors(ConstructorDoc[] methods)433   private static MethodInfo[] convertNonWrittenConstructors(ConstructorDoc[] methods) {
434     if (methods == null) return null;
435     ArrayList<MethodInfo> ctors = new ArrayList<MethodInfo>();
436     for (ConstructorDoc method : methods) {
437       MethodInfo methodInfo = Converter.obtainMethod(method);
438       if (!methodInfo.checkLevel()) {
439         ctors.add(methodInfo);
440       }
441     }
442 
443     return ctors.toArray(new MethodInfo[ctors.size()]);
444   }
445 
obtainMethod(E o)446   private static <E extends ExecutableMemberDoc> MethodInfo obtainMethod(E o) {
447     return (MethodInfo) mMethods.obtain(o);
448   }
449 
450   /**
451    * Returns the result of a call to {@code f.position()} but avoids a {@link NullPointerException}
452    * if {@code f.position()} throws a {@link NullPointerException} due to the {@link Doc} element
453    * being related to an annotation.
454    *
455    * <p>This is consistent with the Java runtime where references to annotations that do not exist
456    * on the classpath are allowed. This method is used, for example, to allow Doclava to process
457    * JARs in the classpath that were generated by the Kotlin compiler, without requiring the Kotlin
458    * stdlib to exist on the classpath when Doclava is executed.
459    */
getPositionSafe(Doc f)460   private static SourcePosition getPositionSafe(Doc f) {
461     try {
462       return f.position();
463     } catch (NullPointerException e) {
464       if (f.isAnnotationTypeElement() || mFieldDocElementsOnAnnotationClasses.contains(f)) {
465         return null;
466       }
467       throw e;
468     }
469   }
470 
471   private static Cache mMethods = new Cache() {
472     @Override
473     protected Object make(Object o) {
474       if (o instanceof AnnotationTypeElementDoc) {
475         AnnotationTypeElementDoc m = (AnnotationTypeElementDoc) o;
476         MethodInfo result =
477             new MethodInfo(m.getRawCommentText(),
478                     new ArrayList<TypeInfo>(Arrays.asList(
479                             Converter.convertTypes(m.typeParameters()))),
480                     m.name(), m.signature(), Converter.obtainClass(m.containingClass()),
481                     Converter.obtainClass(m.containingClass()), m.isPublic(), m.isProtected(), m
482                     .isPackagePrivate(), m.isPrivate(), m.isFinal(), m.isStatic(), m.isSynthetic(),
483                     m.isAbstract(), m.isSynchronized(), m.isNative(), m.isDefault(), true,
484                     "annotationElement", m.flatSignature(),
485                     Converter.obtainMethod(m.overriddenMethod()),
486                     Converter.obtainType(m.returnType()),
487                     new ArrayList<ParameterInfo>(Arrays.asList(
488                             Converter.convertParameters(m.parameters(), m))),
489                     new ArrayList<ClassInfo>(Arrays.asList(Converter.convertClasses(
490                             m.thrownExceptions()))), Converter.convertSourcePosition(getPositionSafe(m)),
491                     new ArrayList<AnnotationInstanceInfo>(Arrays.asList(
492                             Converter.convertAnnotationInstances(m.annotations()))));
493         result.setVarargs(m.isVarArgs());
494         result.init(Converter.obtainAnnotationValue(m.defaultValue(), result));
495         return result;
496       } else if (o instanceof MethodDoc) {
497         MethodDoc m = (MethodDoc) o;
498         final ClassInfo containingClass = Converter.obtainClass(m.containingClass());
499 
500         // The containing class is final, so it is implied that every method is final as well.
501         // No need to apply 'final' to each method.
502         final boolean isMethodFinal = m.isFinal() && !containingClass.isFinal();
503         MethodInfo result =
504             new MethodInfo(m.getRawCommentText(),
505                     new ArrayList<TypeInfo>(Arrays.asList(
506                             Converter.convertTypes(m.typeParameters()))), m.name(), m.signature(),
507                     containingClass, containingClass, m.isPublic(), m.isProtected(),
508                     m.isPackagePrivate(), m.isPrivate(), isMethodFinal, m.isStatic(),
509                     m.isSynthetic(), m.isAbstract(), m.isSynchronized(), m.isNative(),
510                     m.isDefault(), false, "method", m.flatSignature(),
511                     Converter.obtainMethod(m.overriddenMethod()),
512                     Converter.obtainType(m.returnType()),
513                     new ArrayList<ParameterInfo>(Arrays.asList(
514                             Converter.convertParameters(m.parameters(), m))),
515                     new ArrayList<ClassInfo>(Arrays.asList(
516                             Converter.convertClasses(m.thrownExceptions()))),
517                     Converter.convertSourcePosition(m.position()),
518                     new ArrayList<AnnotationInstanceInfo>(Arrays.asList(
519                             Converter.convertAnnotationInstances(m.annotations()))));
520         result.setVarargs(m.isVarArgs());
521         result.init(null);
522         return result;
523       } else {
524         ConstructorDoc m = (ConstructorDoc) o;
525         MethodInfo result =
526             new MethodInfo(m.getRawCommentText(), new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(m.typeParameters()))),
527                 m.name(), m.signature(), Converter.obtainClass(m.containingClass()), Converter
528                 .obtainClass(m.containingClass()), m.isPublic(), m.isProtected(), m
529                 .isPackagePrivate(), m.isPrivate(), m.isFinal(), m.isStatic(), m.isSynthetic(),
530                 false, m.isSynchronized(), m.isNative(), false/*isDefault*/, false, "constructor", m.flatSignature(),
531                 null, null, new ArrayList<ParameterInfo>(Arrays.asList(Converter.convertParameters(m.parameters(), m))),
532                 new ArrayList<ClassInfo>(Arrays.asList(Converter.convertClasses(m.thrownExceptions()))), Converter.convertSourcePosition(m
533                     .position()), new ArrayList<AnnotationInstanceInfo>(Arrays.asList(Converter.convertAnnotationInstances(m.annotations()))));
534         result.setVarargs(m.isVarArgs());
535         result.init(null);
536         return result;
537       }
538     }
539   };
540 
541 
convertFields(FieldDoc[] fields)542   private static FieldInfo[] convertFields(FieldDoc[] fields) {
543     if (fields == null) return null;
544     ArrayList<FieldInfo> out = new ArrayList<FieldInfo>();
545     int N = fields.length;
546     for (int i = 0; i < N; i++) {
547       FieldInfo f = Converter.obtainField(fields[i]);
548       if (f.checkLevel()) {
549         out.add(f);
550       }
551     }
552     return out.toArray(new FieldInfo[out.size()]);
553   }
554 
obtainField(FieldDoc o)555   private static FieldInfo obtainField(FieldDoc o) {
556     return (FieldInfo) mFields.obtain(o);
557   }
558 
obtainField(ConstructorDoc o)559   private static FieldInfo obtainField(ConstructorDoc o) {
560     return (FieldInfo) mFields.obtain(o);
561   }
562 
563   private static Cache mFields = new Cache() {
564     @Override
565     protected Object make(Object o) {
566       FieldDoc f = (FieldDoc) o;
567       return new FieldInfo(f.name(), Converter.obtainClass(f.containingClass()), Converter
568           .obtainClass(f.containingClass()), f.isPublic(), f.isProtected(), f.isPackagePrivate(), f
569           .isPrivate(), f.isFinal(), f.isStatic(), f.isTransient(), f.isVolatile(),
570           f.isSynthetic(), Converter.obtainType(f.type()), f.getRawCommentText(),
571           f.constantValue(), Converter.convertSourcePosition(getPositionSafe(f)),
572           new ArrayList<AnnotationInstanceInfo>(Arrays.asList(Converter
573               .convertAnnotationInstances(f.annotations()))));
574     }
575   };
576 
obtainPackage(PackageDoc o)577   private static PackageInfo obtainPackage(PackageDoc o) {
578     return (PackageInfo) mPackagees.obtain(o);
579   }
580 
581   private static Cache mPackagees = new Cache() {
582     @Override
583     protected Object make(Object o) {
584       PackageDoc p = (PackageDoc) o;
585       return new PackageInfo(p, p.name(), Converter.convertSourcePosition(p.position()));
586     }
587   };
588 
obtainType(Type o)589   private static TypeInfo obtainType(Type o) {
590     return (TypeInfo) mTypes.obtain(o);
591   }
592 
593   private static Cache mTypes = new Cache() {
594     @Override
595     protected Object make(Object o) {
596       Type t = (Type) o;
597       String simpleTypeName;
598       if (t instanceof ClassDoc) {
599         simpleTypeName = ((ClassDoc) t).name();
600       } else {
601         simpleTypeName = t.simpleTypeName();
602       }
603       TypeInfo ti =
604           new TypeInfo(t.isPrimitive(), t.dimension(), simpleTypeName, t.qualifiedTypeName(),
605               Converter.obtainClass(t.asClassDoc()));
606       return ti;
607     }
608 
609     @Override
610     protected void made(Object o, Object r) {
611       Type t = (Type) o;
612       TypeInfo ti = (TypeInfo) r;
613       if (t.asParameterizedType() != null) {
614         ti.setTypeArguments(new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(t.asParameterizedType().typeArguments()))));
615       } else if (t instanceof ClassDoc) {
616         ti.setTypeArguments(new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(((ClassDoc) t).typeParameters()))));
617       } else if (t.asTypeVariable() != null) {
618         ti.setBounds(null, new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes((t.asTypeVariable().bounds())))));
619         ti.setIsTypeVariable(true);
620       } else if (t.asWildcardType() != null) {
621         ti.setIsWildcard(true);
622         ti.setBounds(new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(t.asWildcardType().superBounds()))),
623                 new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(t.asWildcardType().extendsBounds()))));
624       }
625     }
626 
627     @Override
628     protected Object keyFor(Object o) {
629       Type t = (Type) o;
630       while (t.asAnnotatedType() != null) {
631         t = t.asAnnotatedType().underlyingType();
632       }
633       String keyString = t.getClass().getName() + "/" + t.toString() + "/";
634       if (t.asParameterizedType() != null) {
635         keyString += t.asParameterizedType().toString() + "/";
636         if (t.asParameterizedType().typeArguments() != null) {
637           for (Type ty : t.asParameterizedType().typeArguments()) {
638             keyString += ty.toString() + "/";
639           }
640         }
641       } else {
642         keyString += "NoParameterizedType//";
643       }
644       if (t.asTypeVariable() != null) {
645         keyString += t.asTypeVariable().toString() + "/";
646         if (t.asTypeVariable().bounds() != null) {
647           for (Type ty : t.asTypeVariable().bounds()) {
648             keyString += ty.toString() + "/";
649           }
650         }
651       } else {
652         keyString += "NoTypeVariable//";
653       }
654       if (t.asWildcardType() != null) {
655         keyString += t.asWildcardType().toString() + "/";
656         if (t.asWildcardType().superBounds() != null) {
657           for (Type ty : t.asWildcardType().superBounds()) {
658             keyString += ty.toString() + "/";
659           }
660         }
661         if (t.asWildcardType().extendsBounds() != null) {
662           for (Type ty : t.asWildcardType().extendsBounds()) {
663             keyString += ty.toString() + "/";
664           }
665         }
666       } else {
667         keyString += "NoWildCardType//";
668       }
669 
670       return keyString;
671     }
672   };
673 
obtainTypeFromString(String type)674   public static TypeInfo obtainTypeFromString(String type) {
675     return (TypeInfo) mTypesFromString.obtain(type);
676   }
677 
678   private static final Cache mTypesFromString = new Cache() {
679     @Override
680     protected Object make(Object o) {
681       String name = (String) o;
682       return new TypeInfo(name);
683     }
684 
685     @Override
686     protected void made(Object o, Object r) {
687 
688     }
689 
690     @Override
691     protected Object keyFor(Object o) {
692       return o;
693     }
694   };
695 
obtainMember(MemberDoc o)696   private static MemberInfo obtainMember(MemberDoc o) {
697     return (MemberInfo) mMembers.obtain(o);
698   }
699 
700   private static Cache mMembers = new Cache() {
701     @Override
702     protected Object make(Object o) {
703       if (o instanceof MethodDoc) {
704         return Converter.obtainMethod((MethodDoc) o);
705       } else if (o instanceof ConstructorDoc) {
706         return Converter.obtainMethod((ConstructorDoc) o);
707       } else if (o instanceof FieldDoc) {
708         return Converter.obtainField((FieldDoc) o);
709       } else {
710         return null;
711       }
712     }
713   };
714 
convertAnnotationInstances(AnnotationDesc[] orig)715   private static AnnotationInstanceInfo[] convertAnnotationInstances(AnnotationDesc[] orig) {
716     int len = orig.length;
717     AnnotationInstanceInfo[] out = new AnnotationInstanceInfo[len];
718     for (int i = 0; i < len; i++) {
719       out[i] = Converter.obtainAnnotationInstance(orig[i]);
720     }
721     return out;
722   }
723 
724 
obtainAnnotationInstance(AnnotationDesc o)725   private static AnnotationInstanceInfo obtainAnnotationInstance(AnnotationDesc o) {
726     return (AnnotationInstanceInfo) mAnnotationInstances.obtain(o);
727   }
728 
729   private static Cache mAnnotationInstances = new Cache() {
730     @Override
731     protected Object make(Object o) {
732       AnnotationDesc a = (AnnotationDesc) o;
733       ClassInfo annotationType = Converter.obtainClass(a.annotationType());
734       AnnotationDesc.ElementValuePair[] ev = a.elementValues();
735       AnnotationValueInfo[] elementValues = new AnnotationValueInfo[ev.length];
736       for (int i = 0; i < ev.length; i++) {
737         elementValues[i] =
738             obtainAnnotationValue(ev[i].value(), Converter.obtainMethod(ev[i].element()));
739       }
740       return new AnnotationInstanceInfo(annotationType, elementValues);
741     }
742   };
743 
744 
745   private abstract static class Cache {
put(Object key, Object value)746     void put(Object key, Object value) {
747       mCache.put(key, value);
748     }
749 
obtain(Object o)750     Object obtain(Object o) {
751       if (o == null) {
752         return null;
753       }
754       Object k = keyFor(o);
755       Object r = mCache.get(k);
756       if (r == null) {
757         r = make(o);
758         mCache.put(k, r);
759         made(o, r);
760       }
761       return r;
762     }
763 
764     protected HashMap<Object, Object> mCache = new HashMap<Object, Object>();
765 
make(Object o)766     protected abstract Object make(Object o);
767 
made(Object o, Object r)768     protected void made(Object o, Object r) {}
769 
keyFor(Object o)770     protected Object keyFor(Object o) {
771       return o;
772     }
773 
all()774     Collection<?> all() {
775       return null;
776     }
777   }
778 
779   // annotation values
780   private static HashMap<AnnotationValue, AnnotationValueInfo> mAnnotationValues =
781       new HashMap<AnnotationValue, AnnotationValueInfo>();
782   private static HashSet<AnnotationValue> mAnnotationValuesNeedingInit =
783       new HashSet<AnnotationValue>();
784   /**
785    * Stores the {@link FieldDoc} instances the are contained in annotation classes. This is used by
786    * {@link #getPositionSafe(Doc)} to allow {@link NullPointerException} for annotation classes to
787    * be ignored.
788    */
789   private static HashSet<FieldDoc> mFieldDocElementsOnAnnotationClasses = new HashSet<FieldDoc>();
790 
obtainAnnotationValue(AnnotationValue o, MethodInfo element)791   private static AnnotationValueInfo obtainAnnotationValue(AnnotationValue o, MethodInfo element) {
792     if (o == null) {
793       return null;
794     }
795     AnnotationValueInfo v = mAnnotationValues.get(o);
796     if (v != null) return v;
797     v = new AnnotationValueInfo(element);
798     mAnnotationValues.put(o, v);
799     if (o.value() instanceof FieldDoc) {
800       mFieldDocElementsOnAnnotationClasses.add((FieldDoc) o.value());
801     }
802     if (mAnnotationValuesNeedingInit != null) {
803       mAnnotationValuesNeedingInit.add(o);
804     } else {
805       initAnnotationValue(o, v);
806     }
807     return v;
808   }
809 
initAnnotationValue(AnnotationValue o, AnnotationValueInfo v)810   private static void initAnnotationValue(AnnotationValue o, AnnotationValueInfo v) {
811     Object orig = o.value();
812     Object converted;
813     if (orig instanceof Type) {
814       // class literal
815       converted = Converter.obtainType((Type) orig);
816     } else if (orig instanceof FieldDoc) {
817       // enum constant
818       converted = Converter.obtainField((FieldDoc) orig);
819     } else if (orig instanceof AnnotationDesc) {
820       // annotation instance
821       converted = Converter.obtainAnnotationInstance((AnnotationDesc) orig);
822     } else if (orig instanceof AnnotationValue[]) {
823       AnnotationValue[] old = (AnnotationValue[]) orig;
824       ArrayList<AnnotationValueInfo> values = new ArrayList<AnnotationValueInfo>();
825       for (int i = 0; i < old.length; i++) {
826         values.add(Converter.obtainAnnotationValue(old[i], null));
827       }
828       converted = values;
829     } else {
830       converted = orig;
831     }
832     v.init(converted);
833   }
834 
finishAnnotationValueInit()835   private static void finishAnnotationValueInit() {
836     int depth = 0;
837     while (mAnnotationValuesNeedingInit.size() > 0) {
838       HashSet<AnnotationValue> set = mAnnotationValuesNeedingInit;
839       mAnnotationValuesNeedingInit = new HashSet<AnnotationValue>();
840       for (AnnotationValue o : set) {
841         AnnotationValueInfo v = mAnnotationValues.get(o);
842         initAnnotationValue(o, v);
843       }
844       depth++;
845     }
846     mAnnotationValuesNeedingInit = null;
847   }
848 }
849