• 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.ExecutableMemberDoc;
27 import com.sun.javadoc.FieldDoc;
28 import com.sun.javadoc.MemberDoc;
29 import com.sun.javadoc.MethodDoc;
30 import com.sun.javadoc.PackageDoc;
31 import com.sun.javadoc.ParamTag;
32 import com.sun.javadoc.Parameter;
33 import com.sun.javadoc.RootDoc;
34 import com.sun.javadoc.SeeTag;
35 import com.sun.javadoc.SourcePosition;
36 import com.sun.javadoc.Tag;
37 import com.sun.javadoc.ThrowsTag;
38 import com.sun.javadoc.Type;
39 
40 import java.util.ArrayList;
41 import java.util.Arrays;
42 import java.util.Collection;
43 import java.util.HashMap;
44 import java.util.HashSet;
45 import java.util.List;
46 import java.util.Map;
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 = m.position();
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   private static Cache mMethods = new Cache() {
451     @Override
452     protected Object make(Object o) {
453       if (o instanceof AnnotationTypeElementDoc) {
454         AnnotationTypeElementDoc m = (AnnotationTypeElementDoc) o;
455         MethodInfo result =
456             new MethodInfo(m.getRawCommentText(),
457                     new ArrayList<TypeInfo>(Arrays.asList(
458                             Converter.convertTypes(m.typeParameters()))),
459                     m.name(), m.signature(), Converter.obtainClass(m.containingClass()),
460                     Converter.obtainClass(m.containingClass()), m.isPublic(), m.isProtected(), m
461                     .isPackagePrivate(), m.isPrivate(), m.isFinal(), m.isStatic(), m.isSynthetic(),
462                     m.isAbstract(), m.isSynchronized(), m.isNative(), m.isDefault(), true,
463                     "annotationElement", m.flatSignature(),
464                     Converter.obtainMethod(m.overriddenMethod()),
465                     Converter.obtainType(m.returnType()),
466                     new ArrayList<ParameterInfo>(Arrays.asList(
467                             Converter.convertParameters(m.parameters(), m))),
468                     new ArrayList<ClassInfo>(Arrays.asList(Converter.convertClasses(
469                             m.thrownExceptions()))), Converter.convertSourcePosition(m.position()),
470                     new ArrayList<AnnotationInstanceInfo>(Arrays.asList(
471                             Converter.convertAnnotationInstances(m.annotations()))));
472         result.setVarargs(m.isVarArgs());
473         result.init(Converter.obtainAnnotationValue(m.defaultValue(), result));
474         return result;
475       } else if (o instanceof MethodDoc) {
476         MethodDoc m = (MethodDoc) o;
477         final ClassInfo containingClass = Converter.obtainClass(m.containingClass());
478 
479         // The containing class is final, so it is implied that every method is final as well.
480         // No need to apply 'final' to each method.
481         final boolean isMethodFinal = m.isFinal() && !containingClass.isFinal();
482         MethodInfo result =
483             new MethodInfo(m.getRawCommentText(),
484                     new ArrayList<TypeInfo>(Arrays.asList(
485                             Converter.convertTypes(m.typeParameters()))), m.name(), m.signature(),
486                     containingClass, containingClass, m.isPublic(), m.isProtected(),
487                     m.isPackagePrivate(), m.isPrivate(), isMethodFinal, m.isStatic(),
488                     m.isSynthetic(), m.isAbstract(), m.isSynchronized(), m.isNative(),
489                     m.isDefault(), false, "method", m.flatSignature(),
490                     Converter.obtainMethod(m.overriddenMethod()),
491                     Converter.obtainType(m.returnType()),
492                     new ArrayList<ParameterInfo>(Arrays.asList(
493                             Converter.convertParameters(m.parameters(), m))),
494                     new ArrayList<ClassInfo>(Arrays.asList(
495                             Converter.convertClasses(m.thrownExceptions()))),
496                     Converter.convertSourcePosition(m.position()),
497                     new ArrayList<AnnotationInstanceInfo>(Arrays.asList(
498                             Converter.convertAnnotationInstances(m.annotations()))));
499         result.setVarargs(m.isVarArgs());
500         result.init(null);
501         return result;
502       } else {
503         ConstructorDoc m = (ConstructorDoc) o;
504         // Workaround for a JavaDoc behavior change introduced in OpenJDK 8 that breaks
505         // links in documentation and the content of API files like current.txt.
506         // http://b/18051133.
507         String name = m.name();
508         ClassDoc containingClass = m.containingClass();
509         if (containingClass.containingClass() != null) {
510           // This should detect the new behavior and be bypassed otherwise.
511           if (!name.contains(".")) {
512             // Constructors of inner classes do not contain the name of the enclosing class
513             // with OpenJDK 8. This simulates the old behavior:
514             name = containingClass.name();
515           }
516         }
517         // End of workaround.
518         MethodInfo result =
519             new MethodInfo(m.getRawCommentText(), new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(m.typeParameters()))),
520                 name, m.signature(), Converter.obtainClass(m.containingClass()), Converter
521                 .obtainClass(m.containingClass()), m.isPublic(), m.isProtected(), m
522                 .isPackagePrivate(), m.isPrivate(), m.isFinal(), m.isStatic(), m.isSynthetic(),
523                 false, m.isSynchronized(), m.isNative(), false/*isDefault*/, false, "constructor", m.flatSignature(),
524                 null, null, new ArrayList<ParameterInfo>(Arrays.asList(Converter.convertParameters(m.parameters(), m))),
525                 new ArrayList<ClassInfo>(Arrays.asList(Converter.convertClasses(m.thrownExceptions()))), Converter.convertSourcePosition(m
526                     .position()), new ArrayList<AnnotationInstanceInfo>(Arrays.asList(Converter.convertAnnotationInstances(m.annotations()))));
527         result.setVarargs(m.isVarArgs());
528         result.init(null);
529         return result;
530       }
531     }
532   };
533 
534 
convertFields(FieldDoc[] fields)535   private static FieldInfo[] convertFields(FieldDoc[] fields) {
536     if (fields == null) return null;
537     ArrayList<FieldInfo> out = new ArrayList<FieldInfo>();
538     int N = fields.length;
539     for (int i = 0; i < N; i++) {
540       FieldInfo f = Converter.obtainField(fields[i]);
541       if (f.checkLevel()) {
542         out.add(f);
543       }
544     }
545     return out.toArray(new FieldInfo[out.size()]);
546   }
547 
obtainField(FieldDoc o)548   private static FieldInfo obtainField(FieldDoc o) {
549     return (FieldInfo) mFields.obtain(o);
550   }
551 
obtainField(ConstructorDoc o)552   private static FieldInfo obtainField(ConstructorDoc o) {
553     return (FieldInfo) mFields.obtain(o);
554   }
555 
556   private static Cache mFields = new Cache() {
557     @Override
558     protected Object make(Object o) {
559       FieldDoc f = (FieldDoc) o;
560       return new FieldInfo(f.name(), Converter.obtainClass(f.containingClass()), Converter
561           .obtainClass(f.containingClass()), f.isPublic(), f.isProtected(), f.isPackagePrivate(), f
562           .isPrivate(), f.isFinal(), f.isStatic(), f.isTransient(), f.isVolatile(),
563           f.isSynthetic(), Converter.obtainType(f.type()), f.getRawCommentText(),
564           f.constantValue(), Converter.convertSourcePosition(f.position()),
565           new ArrayList<AnnotationInstanceInfo>(Arrays.asList(Converter
566               .convertAnnotationInstances(f.annotations()))));
567     }
568   };
569 
obtainPackage(PackageDoc o)570   private static PackageInfo obtainPackage(PackageDoc o) {
571     return (PackageInfo) mPackagees.obtain(o);
572   }
573 
574   private static Cache mPackagees = new Cache() {
575     @Override
576     protected Object make(Object o) {
577       PackageDoc p = (PackageDoc) o;
578       return new PackageInfo(p, p.name(), Converter.convertSourcePosition(p.position()));
579     }
580   };
581 
obtainType(Type o)582   private static TypeInfo obtainType(Type o) {
583     return (TypeInfo) mTypes.obtain(o);
584   }
585 
586   private static Cache mTypes = new Cache() {
587     @Override
588     protected Object make(Object o) {
589       Type t = (Type) o;
590       String simpleTypeName;
591       if (t instanceof ClassDoc) {
592         simpleTypeName = ((ClassDoc) t).name();
593       } else {
594         simpleTypeName = t.simpleTypeName();
595       }
596       TypeInfo ti =
597           new TypeInfo(t.isPrimitive(), t.dimension(), simpleTypeName, t.qualifiedTypeName(),
598               Converter.obtainClass(t.asClassDoc()));
599       return ti;
600     }
601 
602     @Override
603     protected void made(Object o, Object r) {
604       Type t = (Type) o;
605       TypeInfo ti = (TypeInfo) r;
606       if (t.asParameterizedType() != null) {
607         ti.setTypeArguments(new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(t.asParameterizedType().typeArguments()))));
608       } else if (t instanceof ClassDoc) {
609         ti.setTypeArguments(new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(((ClassDoc) t).typeParameters()))));
610       } else if (t.asTypeVariable() != null) {
611         ti.setBounds(null, new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes((t.asTypeVariable().bounds())))));
612         ti.setIsTypeVariable(true);
613       } else if (t.asWildcardType() != null) {
614         ti.setIsWildcard(true);
615         ti.setBounds(new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(t.asWildcardType().superBounds()))),
616                 new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(t.asWildcardType().extendsBounds()))));
617       }
618     }
619 
620     @Override
621     protected Object keyFor(Object o) {
622       Type t = (Type) o;
623       while (t.asAnnotatedType() != null) {
624         t = t.asAnnotatedType().underlyingType();
625       }
626       String keyString = t.getClass().getName() + "/" + t.toString() + "/";
627       if (t.asParameterizedType() != null) {
628         keyString += t.asParameterizedType().toString() + "/";
629         if (t.asParameterizedType().typeArguments() != null) {
630           for (Type ty : t.asParameterizedType().typeArguments()) {
631             keyString += ty.toString() + "/";
632           }
633         }
634       } else {
635         keyString += "NoParameterizedType//";
636       }
637       if (t.asTypeVariable() != null) {
638         keyString += t.asTypeVariable().toString() + "/";
639         if (t.asTypeVariable().bounds() != null) {
640           for (Type ty : t.asTypeVariable().bounds()) {
641             keyString += ty.toString() + "/";
642           }
643         }
644       } else {
645         keyString += "NoTypeVariable//";
646       }
647       if (t.asWildcardType() != null) {
648         keyString += t.asWildcardType().toString() + "/";
649         if (t.asWildcardType().superBounds() != null) {
650           for (Type ty : t.asWildcardType().superBounds()) {
651             keyString += ty.toString() + "/";
652           }
653         }
654         if (t.asWildcardType().extendsBounds() != null) {
655           for (Type ty : t.asWildcardType().extendsBounds()) {
656             keyString += ty.toString() + "/";
657           }
658         }
659       } else {
660         keyString += "NoWildCardType//";
661       }
662 
663       return keyString;
664     }
665   };
666 
obtainTypeFromString(String type)667   public static TypeInfo obtainTypeFromString(String type) {
668     return (TypeInfo) mTypesFromString.obtain(type);
669   }
670 
671   private static final Cache mTypesFromString = new Cache() {
672     @Override
673     protected Object make(Object o) {
674       String name = (String) o;
675       return new TypeInfo(name);
676     }
677 
678     @Override
679     protected void made(Object o, Object r) {
680 
681     }
682 
683     @Override
684     protected Object keyFor(Object o) {
685       return o;
686     }
687   };
688 
obtainMember(MemberDoc o)689   private static MemberInfo obtainMember(MemberDoc o) {
690     return (MemberInfo) mMembers.obtain(o);
691   }
692 
693   private static Cache mMembers = new Cache() {
694     @Override
695     protected Object make(Object o) {
696       if (o instanceof MethodDoc) {
697         return Converter.obtainMethod((MethodDoc) o);
698       } else if (o instanceof ConstructorDoc) {
699         return Converter.obtainMethod((ConstructorDoc) o);
700       } else if (o instanceof FieldDoc) {
701         return Converter.obtainField((FieldDoc) o);
702       } else {
703         return null;
704       }
705     }
706   };
707 
convertAnnotationInstances(AnnotationDesc[] orig)708   private static AnnotationInstanceInfo[] convertAnnotationInstances(AnnotationDesc[] orig) {
709     int len = orig.length;
710     AnnotationInstanceInfo[] out = new AnnotationInstanceInfo[len];
711     for (int i = 0; i < len; i++) {
712       out[i] = Converter.obtainAnnotationInstance(orig[i]);
713     }
714     return out;
715   }
716 
717 
obtainAnnotationInstance(AnnotationDesc o)718   private static AnnotationInstanceInfo obtainAnnotationInstance(AnnotationDesc o) {
719     return (AnnotationInstanceInfo) mAnnotationInstances.obtain(o);
720   }
721 
722   private static Cache mAnnotationInstances = new Cache() {
723     @Override
724     protected Object make(Object o) {
725       AnnotationDesc a = (AnnotationDesc) o;
726       ClassInfo annotationType = Converter.obtainClass(a.annotationType());
727       AnnotationDesc.ElementValuePair[] ev = a.elementValues();
728       AnnotationValueInfo[] elementValues = new AnnotationValueInfo[ev.length];
729       for (int i = 0; i < ev.length; i++) {
730         elementValues[i] =
731             obtainAnnotationValue(ev[i].value(), Converter.obtainMethod(ev[i].element()));
732       }
733       return new AnnotationInstanceInfo(annotationType, elementValues);
734     }
735   };
736 
737 
738   private abstract static class Cache {
put(Object key, Object value)739     void put(Object key, Object value) {
740       mCache.put(key, value);
741     }
742 
obtain(Object o)743     Object obtain(Object o) {
744       if (o == null) {
745         return null;
746       }
747       Object k = keyFor(o);
748       Object r = mCache.get(k);
749       if (r == null) {
750         r = make(o);
751         mCache.put(k, r);
752         made(o, r);
753       }
754       return r;
755     }
756 
757     protected HashMap<Object, Object> mCache = new HashMap<Object, Object>();
758 
make(Object o)759     protected abstract Object make(Object o);
760 
made(Object o, Object r)761     protected void made(Object o, Object r) {}
762 
keyFor(Object o)763     protected Object keyFor(Object o) {
764       return o;
765     }
766 
all()767     Collection<?> all() {
768       return null;
769     }
770   }
771 
772   // annotation values
773   private static HashMap<AnnotationValue, AnnotationValueInfo> mAnnotationValues =
774       new HashMap<AnnotationValue, AnnotationValueInfo>();
775   private static HashSet<AnnotationValue> mAnnotationValuesNeedingInit =
776       new HashSet<AnnotationValue>();
777 
obtainAnnotationValue(AnnotationValue o, MethodInfo element)778   private static AnnotationValueInfo obtainAnnotationValue(AnnotationValue o, MethodInfo element) {
779     if (o == null) {
780       return null;
781     }
782     AnnotationValueInfo v = mAnnotationValues.get(o);
783     if (v != null) return v;
784     v = new AnnotationValueInfo(element);
785     mAnnotationValues.put(o, v);
786     if (mAnnotationValuesNeedingInit != null) {
787       mAnnotationValuesNeedingInit.add(o);
788     } else {
789       initAnnotationValue(o, v);
790     }
791     return v;
792   }
793 
initAnnotationValue(AnnotationValue o, AnnotationValueInfo v)794   private static void initAnnotationValue(AnnotationValue o, AnnotationValueInfo v) {
795     Object orig = o.value();
796     Object converted;
797     if (orig instanceof Type) {
798       // class literal
799       converted = Converter.obtainType((Type) orig);
800     } else if (orig instanceof FieldDoc) {
801       // enum constant
802       converted = Converter.obtainField((FieldDoc) orig);
803     } else if (orig instanceof AnnotationDesc) {
804       // annotation instance
805       converted = Converter.obtainAnnotationInstance((AnnotationDesc) orig);
806     } else if (orig instanceof AnnotationValue[]) {
807       AnnotationValue[] old = (AnnotationValue[]) orig;
808       ArrayList<AnnotationValueInfo> values = new ArrayList<AnnotationValueInfo>();
809       for (int i = 0; i < old.length; i++) {
810         values.add(Converter.obtainAnnotationValue(old[i], null));
811       }
812       converted = values;
813     } else {
814       converted = orig;
815     }
816     v.init(converted);
817   }
818 
finishAnnotationValueInit()819   private static void finishAnnotationValueInit() {
820     int depth = 0;
821     while (mAnnotationValuesNeedingInit.size() > 0) {
822       HashSet<AnnotationValue> set = mAnnotationValuesNeedingInit;
823       mAnnotationValuesNeedingInit = new HashSet<AnnotationValue>();
824       for (AnnotationValue o : set) {
825         AnnotationValueInfo v = mAnnotationValues.get(o);
826         initAnnotationValue(o, v);
827       }
828       depth++;
829     }
830     mAnnotationValuesNeedingInit = null;
831   }
832 }
833