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 
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.HashMap;
43 import java.util.HashSet;
44 import java.util.List;
45 
46 public class Converter {
47   private static RootDoc root;
48 
makeInfo(RootDoc r)49   public static void makeInfo(RootDoc r) {
50     root = r;
51 
52     // create the objects
53     ClassDoc[] classes = getClasses(r);
54     for (ClassDoc c : classes) {
55       Converter.obtainClass(c);
56     }
57 
58     ArrayList<ClassInfo> classesNeedingInit2 = new ArrayList<ClassInfo>();
59 
60     int i;
61     // fill in the fields that reference other classes
62     while (mClassesNeedingInit.size() > 0) {
63       i = mClassesNeedingInit.size() - 1;
64       ClassNeedingInit clni = mClassesNeedingInit.get(i);
65       mClassesNeedingInit.remove(i);
66 
67       initClass(clni.c, clni.cl);
68       classesNeedingInit2.add(clni.cl);
69     }
70     mClassesNeedingInit = null;
71     for (ClassInfo cl : classesNeedingInit2) {
72       cl.init2();
73     }
74 
75     finishAnnotationValueInit();
76 
77     // fill in the "root" stuff
78     mRootClasses = Converter.convertClasses(classes);
79   }
80 
getClasses(RootDoc r)81   private static ClassDoc[] getClasses(RootDoc r) {
82     ClassDoc[] classDocs = r.classes();
83     ArrayList<ClassDoc> filtered = new ArrayList<ClassDoc>(classDocs.length);
84     for (ClassDoc c : classDocs) {
85       if (c.position() != null) {
86         // Work around a javadoc bug in Java 7: We sometimes spuriously
87         // receive duplicate top level ClassDocs with null positions and no type
88         // information. Ignore them, since every ClassDoc must have a non null
89         // position.
90 
91         filtered.add(c);
92       }
93     }
94 
95     ClassDoc[] filteredArray = new ClassDoc[filtered.size()];
96     filtered.toArray(filteredArray);
97     return filteredArray;
98   }
99 
100   private static ClassInfo[] mRootClasses;
101 
rootClasses()102   public static ClassInfo[] rootClasses() {
103     return mRootClasses;
104   }
105 
allClasses()106   public static ClassInfo[] allClasses() {
107     return (ClassInfo[]) mClasses.all();
108   }
109 
110   private static final MethodDoc[] EMPTY_METHOD_DOC = new MethodDoc[0];
111 
initClass(ClassDoc c, ClassInfo cl)112   private static void initClass(ClassDoc c, ClassInfo cl) {
113     MethodDoc[] annotationElements;
114     if (c instanceof AnnotationTypeDoc) {
115       annotationElements = ((AnnotationTypeDoc) c).elements();
116     } else {
117       annotationElements = EMPTY_METHOD_DOC;
118     }
119     cl.init(Converter.obtainType(c),
120             new ArrayList<ClassInfo>(Arrays.asList(Converter.convertClasses(c.interfaces()))),
121             new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(c.interfaceTypes()))),
122             new ArrayList<ClassInfo>(Arrays.asList(Converter.convertClasses(c.innerClasses()))),
123             new ArrayList<MethodInfo>(Arrays.asList(
124                     Converter.convertMethods(c.constructors(false)))),
125             new ArrayList<MethodInfo>(Arrays.asList(Converter.convertMethods(c.methods(false)))),
126             new ArrayList<MethodInfo>(Arrays.asList(Converter.convertMethods(annotationElements))),
127             new ArrayList<FieldInfo>(Arrays.asList(Converter.convertFields(c.fields(false)))),
128             new ArrayList<FieldInfo>(Arrays.asList(Converter.convertFields(c.enumConstants()))),
129             Converter.obtainPackage(c.containingPackage()),
130             Converter.obtainClass(c.containingClass()),
131             Converter.obtainClass(c.superclass()), Converter.obtainType(c.superclassType()),
132             new ArrayList<AnnotationInstanceInfo>(Arrays.asList(
133                     Converter.convertAnnotationInstances(c.annotations()))));
134 
135     cl.setHiddenMethods(
136             new ArrayList<MethodInfo>(Arrays.asList(Converter.getHiddenMethods(c.methods(false)))));
137     cl.setRemovedMethods(
138             new ArrayList<MethodInfo>(Arrays.asList(Converter.getRemovedMethods(c.methods(false)))));
139 
140     cl.setRemovedSelfMethods(
141         new ArrayList<MethodInfo>(Converter.convertAllMethods(c.methods(false))));
142     cl.setRemovedConstructors(
143         new ArrayList<MethodInfo>(Converter.convertAllMethods(c.constructors(false))));
144     cl.setRemovedSelfFields(
145         new ArrayList<FieldInfo>(Converter.convertAllFields(c.fields(false))));
146     cl.setRemovedEnumConstants(
147         new ArrayList<FieldInfo>(Converter.convertAllFields(c.enumConstants())));
148 
149     cl.setNonWrittenConstructors(
150             new ArrayList<MethodInfo>(Arrays.asList(Converter.convertNonWrittenConstructors(
151                     c.constructors(false)))));
152     cl.init3(
153             new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(c.typeParameters()))),
154             new ArrayList<ClassInfo>(Arrays.asList(
155                     Converter.convertClasses(c.innerClasses(false)))));
156   }
157 
obtainClass(String className)158   public static ClassInfo obtainClass(String className) {
159     return Converter.obtainClass(root.classNamed(className));
160   }
161 
obtainPackage(String packageName)162   public static PackageInfo obtainPackage(String packageName) {
163     return Converter.obtainPackage(root.packageNamed(packageName));
164   }
165 
convertTag(Tag tag)166   private static TagInfo convertTag(Tag tag) {
167     return new TextTagInfo(tag.name(), tag.kind(), tag.text(),
168             Converter.convertSourcePosition(tag.position()));
169   }
170 
convertThrowsTag(ThrowsTag tag, ContainerInfo base)171   private static ThrowsTagInfo convertThrowsTag(ThrowsTag tag, ContainerInfo base) {
172     return new ThrowsTagInfo(tag.name(), tag.text(), tag.kind(), Converter.obtainClass(tag
173         .exception()), tag.exceptionComment(), base, Converter
174         .convertSourcePosition(tag.position()));
175   }
176 
convertParamTag(ParamTag tag, ContainerInfo base)177   private static ParamTagInfo convertParamTag(ParamTag tag, ContainerInfo base) {
178     return new ParamTagInfo(tag.name(), tag.kind(), tag.text(), tag.isTypeParameter(), tag
179         .parameterComment(), tag.parameterName(), base, Converter.convertSourcePosition(tag
180         .position()));
181   }
182 
convertSeeTag(SeeTag tag, ContainerInfo base)183   private static SeeTagInfo convertSeeTag(SeeTag tag, ContainerInfo base) {
184     return new SeeTagInfo(tag.name(), tag.kind(), tag.text(), base, Converter
185         .convertSourcePosition(tag.position()));
186   }
187 
convertSourcePosition(SourcePosition sp)188   private static SourcePositionInfo convertSourcePosition(SourcePosition sp) {
189     if (sp == null) {
190       return null;
191     }
192     return new SourcePositionInfo(sp.file().toString(), sp.line(), sp.column());
193   }
194 
convertTags(Tag[] tags, ContainerInfo base)195   public static TagInfo[] convertTags(Tag[] tags, ContainerInfo base) {
196     int len = tags.length;
197     TagInfo[] out = TagInfo.getArray(len);
198     for (int i = 0; i < len; i++) {
199       Tag t = tags[i];
200       /*
201        * System.out.println("Tag name='" + t.name() + "' kind='" + t.kind() + "'");
202        */
203       if (t instanceof SeeTag) {
204         out[i] = Converter.convertSeeTag((SeeTag) t, base);
205       } else if (t instanceof ThrowsTag) {
206         out[i] = Converter.convertThrowsTag((ThrowsTag) t, base);
207       } else if (t instanceof ParamTag) {
208         out[i] = Converter.convertParamTag((ParamTag) t, base);
209       } else {
210         out[i] = Converter.convertTag(t);
211       }
212     }
213     return out;
214   }
215 
convertClasses(ClassDoc[] classes)216   public static ClassInfo[] convertClasses(ClassDoc[] classes) {
217     if (classes == null) return null;
218     int N = classes.length;
219     ClassInfo[] result = new ClassInfo[N];
220     for (int i = 0; i < N; i++) {
221       result[i] = Converter.obtainClass(classes[i]);
222     }
223     return result;
224   }
225 
convertParameter(Parameter p, SourcePosition pos, boolean isVarArg)226   private static ParameterInfo convertParameter(Parameter p, SourcePosition pos, boolean isVarArg) {
227     if (p == null) return null;
228     ParameterInfo pi =
229         new ParameterInfo(p.name(), p.typeName(), Converter.obtainType(p.type()), isVarArg,
230           Converter.convertSourcePosition(pos));
231     return pi;
232   }
233 
convertParameters(Parameter[] p, ExecutableMemberDoc m)234   private static ParameterInfo[] convertParameters(Parameter[] p, ExecutableMemberDoc m) {
235     SourcePosition pos = m.position();
236     int len = p.length;
237     ParameterInfo[] q = new ParameterInfo[len];
238     for (int i = 0; i < len; i++) {
239       boolean isVarArg = (m.isVarArgs() && i == len - 1);
240       q[i] = Converter.convertParameter(p[i], pos, isVarArg);
241     }
242     return q;
243   }
244 
convertTypes(Type[] p)245   private static TypeInfo[] convertTypes(Type[] p) {
246     if (p == null) return null;
247     int len = p.length;
248     TypeInfo[] q = new TypeInfo[len];
249     for (int i = 0; i < len; i++) {
250       q[i] = Converter.obtainType(p[i]);
251     }
252     return q;
253   }
254 
Converter()255   private Converter() {}
256 
257   private static class ClassNeedingInit {
ClassNeedingInit(ClassDoc c, ClassInfo cl)258     ClassNeedingInit(ClassDoc c, ClassInfo cl) {
259       this.c = c;
260       this.cl = cl;
261     }
262 
263     ClassDoc c;
264     ClassInfo cl;
265   }
266 
267   private static ArrayList<ClassNeedingInit> mClassesNeedingInit =
268       new ArrayList<ClassNeedingInit>();
269 
obtainClass(ClassDoc o)270   static ClassInfo obtainClass(ClassDoc o) {
271     return (ClassInfo) mClasses.obtain(o);
272   }
273 
274   private static Cache mClasses = new Cache() {
275     @Override
276     protected Object make(Object o) {
277       ClassDoc c = (ClassDoc) o;
278       ClassInfo cl =
279           new ClassInfo(c, c.getRawCommentText(), Converter.convertSourcePosition(c.position()), c
280               .isPublic(), c.isProtected(), c.isPackagePrivate(), c.isPrivate(), c.isStatic(), c
281               .isInterface(), c.isAbstract(), c.isOrdinaryClass(), c.isException(), c.isError(), c
282               .isEnum(), (c instanceof AnnotationTypeDoc), c.isFinal(), c.isIncluded(), c.name(), c
283               .qualifiedName(), c.qualifiedTypeName(), c.isPrimitive());
284       if (mClassesNeedingInit != null) {
285         mClassesNeedingInit.add(new ClassNeedingInit(c, cl));
286       }
287       return cl;
288     }
289 
290     @Override
291     protected void made(Object o, Object r) {
292       if (mClassesNeedingInit == null) {
293         initClass((ClassDoc) o, (ClassInfo) r);
294         ((ClassInfo) r).init2();
295       }
296     }
297 
298     @Override
299     ClassInfo[] all() {
300       return mCache.values().toArray(new ClassInfo[mCache.size()]);
301     }
302   };
303 
getHiddenMethods(MethodDoc[] methods)304   private static MethodInfo[] getHiddenMethods(MethodDoc[] methods) {
305     if (methods == null) return null;
306     ArrayList<MethodInfo> hiddenMethods = new ArrayList<MethodInfo>();
307     for (MethodDoc method : methods) {
308       MethodInfo methodInfo = Converter.obtainMethod(method);
309       if (methodInfo.isHidden()) {
310         hiddenMethods.add(methodInfo);
311       }
312     }
313 
314     return hiddenMethods.toArray(new MethodInfo[hiddenMethods.size()]);
315   }
316 
317   // Gets the removed methods regardless of access levels
getRemovedMethods(MethodDoc[] methods)318   private static MethodInfo[] getRemovedMethods(MethodDoc[] methods) {
319     if (methods == null) return null;
320     ArrayList<MethodInfo> removedMethods = new ArrayList<MethodInfo>();
321     for (MethodDoc method : methods) {
322       MethodInfo methodInfo = Converter.obtainMethod(method);
323       if (methodInfo.isRemoved()) {
324         removedMethods.add(methodInfo);
325       }
326     }
327 
328     return removedMethods.toArray(new MethodInfo[removedMethods.size()]);
329   }
330 
331   /**
332    * Converts FieldDoc[] into List<FieldInfo>. No filtering is done.
333    */
convertAllFields(FieldDoc[] fields)334   private static List<FieldInfo> convertAllFields(FieldDoc[] fields) {
335     if (fields == null) return null;
336     List<FieldInfo> allFields = new ArrayList<FieldInfo>();
337 
338     for (FieldDoc field : fields) {
339       FieldInfo fieldInfo = Converter.obtainField(field);
340       allFields.add(fieldInfo);
341     }
342 
343     return allFields;
344   }
345 
346   /**
347    * Converts ExecutableMemberDoc[] into List<MethodInfo>. No filtering is done.
348    */
convertAllMethods(ExecutableMemberDoc[] methods)349   private static List<MethodInfo> convertAllMethods(ExecutableMemberDoc[] methods) {
350     if (methods == null) return null;
351     List<MethodInfo> allMethods = new ArrayList<MethodInfo>();
352     for (ExecutableMemberDoc method : methods) {
353       MethodInfo methodInfo = Converter.obtainMethod(method);
354       allMethods.add(methodInfo);
355     }
356     return allMethods;
357   }
358 
359   /**
360    * Convert MethodDoc[] or ConstructorDoc[] into MethodInfo[].
361    * Also filters according to the -private, -public option,
362    * because the filtering doesn't seem to be working in the ClassDoc.constructors(boolean) call.
363    */
convertMethods(ExecutableMemberDoc[] methods)364   private static MethodInfo[] convertMethods(ExecutableMemberDoc[] methods) {
365     if (methods == null) return null;
366     List<MethodInfo> filteredMethods = new ArrayList<MethodInfo>();
367     for (ExecutableMemberDoc method : methods) {
368       MethodInfo methodInfo = Converter.obtainMethod(method);
369       if (methodInfo.checkLevel()) {
370         filteredMethods.add(methodInfo);
371       }
372     }
373 
374     return filteredMethods.toArray(new MethodInfo[filteredMethods.size()]);
375   }
376 
convertNonWrittenConstructors(ConstructorDoc[] methods)377   private static MethodInfo[] convertNonWrittenConstructors(ConstructorDoc[] methods) {
378     if (methods == null) return null;
379     ArrayList<MethodInfo> ctors = new ArrayList<MethodInfo>();
380     for (ConstructorDoc method : methods) {
381       MethodInfo methodInfo = Converter.obtainMethod(method);
382       if (!methodInfo.checkLevel()) {
383         ctors.add(methodInfo);
384       }
385     }
386 
387     return ctors.toArray(new MethodInfo[ctors.size()]);
388   }
389 
obtainMethod(E o)390   private static <E extends ExecutableMemberDoc> MethodInfo obtainMethod(E o) {
391     return (MethodInfo) mMethods.obtain(o);
392   }
393 
394   private static Cache mMethods = new Cache() {
395     @Override
396     protected Object make(Object o) {
397       if (o instanceof AnnotationTypeElementDoc) {
398         AnnotationTypeElementDoc m = (AnnotationTypeElementDoc) o;
399         MethodInfo result =
400             new MethodInfo(m.getRawCommentText(),
401                     new ArrayList<TypeInfo>(Arrays.asList(
402                             Converter.convertTypes(m.typeParameters()))),
403                     m.name(), m.signature(), Converter.obtainClass(m.containingClass()),
404                     Converter.obtainClass(m.containingClass()), m.isPublic(), m.isProtected(), m
405                     .isPackagePrivate(), m.isPrivate(), m.isFinal(), m.isStatic(), m.isSynthetic(),
406                     m.isAbstract(), m.isSynchronized(), m.isNative(), true, "annotationElement",
407                     m.flatSignature(), Converter.obtainMethod(m.overriddenMethod()),
408                     Converter.obtainType(m.returnType()),
409                     new ArrayList<ParameterInfo>(Arrays.asList(
410                             Converter.convertParameters(m.parameters(), m))),
411                     new ArrayList<ClassInfo>(Arrays.asList(Converter.convertClasses(
412                             m.thrownExceptions()))), Converter.convertSourcePosition(m.position()),
413                     new ArrayList<AnnotationInstanceInfo>(Arrays.asList(
414                             Converter.convertAnnotationInstances(m.annotations()))));
415         result.setVarargs(m.isVarArgs());
416         result.init(Converter.obtainAnnotationValue(m.defaultValue(), result));
417         return result;
418       } else if (o instanceof MethodDoc) {
419         MethodDoc m = (MethodDoc) o;
420         MethodInfo result =
421             new MethodInfo(m.getRawCommentText(),
422                     new ArrayList<TypeInfo>(Arrays.asList(
423                             Converter.convertTypes(m.typeParameters()))), m.name(), m.signature(),
424                     Converter.obtainClass(m.containingClass()),
425                     Converter.obtainClass(m.containingClass()), m.isPublic(), m.isProtected(),
426                     m.isPackagePrivate(), m.isPrivate(), m.isFinal(), m.isStatic(), m.isSynthetic(),
427                     m.isAbstract(), m.isSynchronized(), m.isNative(), false, "method",
428                     m.flatSignature(), Converter.obtainMethod(m.overriddenMethod()),
429                     Converter.obtainType(m.returnType()),
430                     new ArrayList<ParameterInfo>(Arrays.asList(
431                             Converter.convertParameters(m.parameters(), m))),
432                     new ArrayList<ClassInfo>(Arrays.asList(
433                             Converter.convertClasses(m.thrownExceptions()))),
434                     Converter.convertSourcePosition(m.position()),
435                     new ArrayList<AnnotationInstanceInfo>(Arrays.asList(
436                             Converter.convertAnnotationInstances(m.annotations()))));
437         result.setVarargs(m.isVarArgs());
438         result.init(null);
439         return result;
440       } else {
441         ConstructorDoc m = (ConstructorDoc) o;
442         MethodInfo result =
443             new MethodInfo(m.getRawCommentText(), new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(m.typeParameters()))), m
444                 .name(), m.signature(), Converter.obtainClass(m.containingClass()), Converter
445                 .obtainClass(m.containingClass()), m.isPublic(), m.isProtected(), m
446                 .isPackagePrivate(), m.isPrivate(), m.isFinal(), m.isStatic(), m.isSynthetic(),
447                 false, m.isSynchronized(), m.isNative(), false, "constructor", m.flatSignature(),
448                 null, null, new ArrayList<ParameterInfo>(Arrays.asList(Converter.convertParameters(m.parameters(), m))),
449                 new ArrayList<ClassInfo>(Arrays.asList(Converter.convertClasses(m.thrownExceptions()))), Converter.convertSourcePosition(m
450                     .position()), new ArrayList<AnnotationInstanceInfo>(Arrays.asList(Converter.convertAnnotationInstances(m.annotations()))));
451         result.setVarargs(m.isVarArgs());
452         result.init(null);
453         return result;
454       }
455     }
456   };
457 
458 
convertFields(FieldDoc[] fields)459   private static FieldInfo[] convertFields(FieldDoc[] fields) {
460     if (fields == null) return null;
461     ArrayList<FieldInfo> out = new ArrayList<FieldInfo>();
462     int N = fields.length;
463     for (int i = 0; i < N; i++) {
464       FieldInfo f = Converter.obtainField(fields[i]);
465       if (f.checkLevel()) {
466         out.add(f);
467       }
468     }
469     return out.toArray(new FieldInfo[out.size()]);
470   }
471 
obtainField(FieldDoc o)472   private static FieldInfo obtainField(FieldDoc o) {
473     return (FieldInfo) mFields.obtain(o);
474   }
475 
obtainField(ConstructorDoc o)476   private static FieldInfo obtainField(ConstructorDoc o) {
477     return (FieldInfo) mFields.obtain(o);
478   }
479 
480   private static Cache mFields = new Cache() {
481     @Override
482     protected Object make(Object o) {
483       FieldDoc f = (FieldDoc) o;
484       return new FieldInfo(f.name(), Converter.obtainClass(f.containingClass()), Converter
485           .obtainClass(f.containingClass()), f.isPublic(), f.isProtected(), f.isPackagePrivate(), f
486           .isPrivate(), f.isFinal(), f.isStatic(), f.isTransient(), f.isVolatile(),
487           f.isSynthetic(), Converter.obtainType(f.type()), f.getRawCommentText(),
488           f.constantValue(), Converter.convertSourcePosition(f.position()),
489           new ArrayList<AnnotationInstanceInfo>(Arrays.asList(Converter
490               .convertAnnotationInstances(f.annotations()))));
491     }
492   };
493 
obtainPackage(PackageDoc o)494   private static PackageInfo obtainPackage(PackageDoc o) {
495     return (PackageInfo) mPackagees.obtain(o);
496   }
497 
498   private static Cache mPackagees = new Cache() {
499     @Override
500     protected Object make(Object o) {
501       PackageDoc p = (PackageDoc) o;
502       return new PackageInfo(p, p.name(), Converter.convertSourcePosition(p.position()));
503     }
504   };
505 
obtainType(Type o)506   private static TypeInfo obtainType(Type o) {
507     return (TypeInfo) mTypes.obtain(o);
508   }
509 
510   private static Cache mTypes = new Cache() {
511     @Override
512     protected Object make(Object o) {
513       Type t = (Type) o;
514       String simpleTypeName;
515       if (t instanceof ClassDoc) {
516         simpleTypeName = ((ClassDoc) t).name();
517       } else {
518         simpleTypeName = t.simpleTypeName();
519       }
520       TypeInfo ti =
521           new TypeInfo(t.isPrimitive(), t.dimension(), simpleTypeName, t.qualifiedTypeName(),
522               Converter.obtainClass(t.asClassDoc()));
523       return ti;
524     }
525 
526     @Override
527     protected void made(Object o, Object r) {
528       Type t = (Type) o;
529       TypeInfo ti = (TypeInfo) r;
530       if (t.asParameterizedType() != null) {
531         ti.setTypeArguments(new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(t.asParameterizedType().typeArguments()))));
532       } else if (t instanceof ClassDoc) {
533         ti.setTypeArguments(new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(((ClassDoc) t).typeParameters()))));
534       } else if (t.asTypeVariable() != null) {
535         ti.setBounds(null, new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes((t.asTypeVariable().bounds())))));
536         ti.setIsTypeVariable(true);
537       } else if (t.asWildcardType() != null) {
538         ti.setIsWildcard(true);
539         ti.setBounds(new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(t.asWildcardType().superBounds()))),
540                 new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(t.asWildcardType().extendsBounds()))));
541       }
542     }
543 
544     @Override
545     protected Object keyFor(Object o) {
546       Type t = (Type) o;
547       String keyString = o.getClass().getName() + "/" + o.toString() + "/";
548       if (t.asParameterizedType() != null) {
549         keyString += t.asParameterizedType().toString() + "/";
550         if (t.asParameterizedType().typeArguments() != null) {
551           for (Type ty : t.asParameterizedType().typeArguments()) {
552             keyString += ty.toString() + "/";
553           }
554         }
555       } else {
556         keyString += "NoParameterizedType//";
557       }
558       if (t.asTypeVariable() != null) {
559         keyString += t.asTypeVariable().toString() + "/";
560         if (t.asTypeVariable().bounds() != null) {
561           for (Type ty : t.asTypeVariable().bounds()) {
562             keyString += ty.toString() + "/";
563           }
564         }
565       } else {
566         keyString += "NoTypeVariable//";
567       }
568       if (t.asWildcardType() != null) {
569         keyString += t.asWildcardType().toString() + "/";
570         if (t.asWildcardType().superBounds() != null) {
571           for (Type ty : t.asWildcardType().superBounds()) {
572             keyString += ty.toString() + "/";
573           }
574         }
575         if (t.asWildcardType().extendsBounds() != null) {
576           for (Type ty : t.asWildcardType().extendsBounds()) {
577             keyString += ty.toString() + "/";
578           }
579         }
580       } else {
581         keyString += "NoWildCardType//";
582       }
583 
584       return keyString;
585     }
586   };
587 
obtainTypeFromString(String type)588   public static TypeInfo obtainTypeFromString(String type) {
589     return (TypeInfo) mTypesFromString.obtain(type);
590   }
591 
592   private static final Cache mTypesFromString = new Cache() {
593     @Override
594     protected Object make(Object o) {
595       String name = (String) o;
596       return new TypeInfo(name);
597     }
598 
599     @Override
600     protected void made(Object o, Object r) {
601 
602     }
603 
604     @Override
605     protected Object keyFor(Object o) {
606       return o;
607     }
608   };
609 
obtainMember(MemberDoc o)610   private static MemberInfo obtainMember(MemberDoc o) {
611     return (MemberInfo) mMembers.obtain(o);
612   }
613 
614   private static Cache mMembers = new Cache() {
615     @Override
616     protected Object make(Object o) {
617       if (o instanceof MethodDoc) {
618         return Converter.obtainMethod((MethodDoc) o);
619       } else if (o instanceof ConstructorDoc) {
620         return Converter.obtainMethod((ConstructorDoc) o);
621       } else if (o instanceof FieldDoc) {
622         return Converter.obtainField((FieldDoc) o);
623       } else {
624         return null;
625       }
626     }
627   };
628 
convertAnnotationInstances(AnnotationDesc[] orig)629   private static AnnotationInstanceInfo[] convertAnnotationInstances(AnnotationDesc[] orig) {
630     int len = orig.length;
631     AnnotationInstanceInfo[] out = new AnnotationInstanceInfo[len];
632     for (int i = 0; i < len; i++) {
633       out[i] = Converter.obtainAnnotationInstance(orig[i]);
634     }
635     return out;
636   }
637 
638 
obtainAnnotationInstance(AnnotationDesc o)639   private static AnnotationInstanceInfo obtainAnnotationInstance(AnnotationDesc o) {
640     return (AnnotationInstanceInfo) mAnnotationInstances.obtain(o);
641   }
642 
643   private static Cache mAnnotationInstances = new Cache() {
644     @Override
645     protected Object make(Object o) {
646       AnnotationDesc a = (AnnotationDesc) o;
647       ClassInfo annotationType = Converter.obtainClass(a.annotationType());
648       AnnotationDesc.ElementValuePair[] ev = a.elementValues();
649       AnnotationValueInfo[] elementValues = new AnnotationValueInfo[ev.length];
650       for (int i = 0; i < ev.length; i++) {
651         elementValues[i] =
652             obtainAnnotationValue(ev[i].value(), Converter.obtainMethod(ev[i].element()));
653       }
654       return new AnnotationInstanceInfo(annotationType, elementValues);
655     }
656   };
657 
658 
659   private abstract static class Cache {
put(Object key, Object value)660     void put(Object key, Object value) {
661       mCache.put(key, value);
662     }
663 
obtain(Object o)664     Object obtain(Object o) {
665       if (o == null) {
666         return null;
667       }
668       Object k = keyFor(o);
669       Object r = mCache.get(k);
670       if (r == null) {
671         r = make(o);
672         mCache.put(k, r);
673         made(o, r);
674       }
675       return r;
676     }
677 
678     protected HashMap<Object, Object> mCache = new HashMap<Object, Object>();
679 
make(Object o)680     protected abstract Object make(Object o);
681 
made(Object o, Object r)682     protected void made(Object o, Object r) {}
683 
keyFor(Object o)684     protected Object keyFor(Object o) {
685       return o;
686     }
687 
all()688     Object[] all() {
689       return null;
690     }
691   }
692 
693   // annotation values
694   private static HashMap<AnnotationValue, AnnotationValueInfo> mAnnotationValues =
695       new HashMap<AnnotationValue, AnnotationValueInfo>();
696   private static HashSet<AnnotationValue> mAnnotationValuesNeedingInit =
697       new HashSet<AnnotationValue>();
698 
obtainAnnotationValue(AnnotationValue o, MethodInfo element)699   private static AnnotationValueInfo obtainAnnotationValue(AnnotationValue o, MethodInfo element) {
700     if (o == null) {
701       return null;
702     }
703     AnnotationValueInfo v = mAnnotationValues.get(o);
704     if (v != null) return v;
705     v = new AnnotationValueInfo(element);
706     mAnnotationValues.put(o, v);
707     if (mAnnotationValuesNeedingInit != null) {
708       mAnnotationValuesNeedingInit.add(o);
709     } else {
710       initAnnotationValue(o, v);
711     }
712     return v;
713   }
714 
initAnnotationValue(AnnotationValue o, AnnotationValueInfo v)715   private static void initAnnotationValue(AnnotationValue o, AnnotationValueInfo v) {
716     Object orig = o.value();
717     Object converted;
718     if (orig instanceof Type) {
719       // class literal
720       converted = Converter.obtainType((Type) orig);
721     } else if (orig instanceof FieldDoc) {
722       // enum constant
723       converted = Converter.obtainField((FieldDoc) orig);
724     } else if (orig instanceof AnnotationDesc) {
725       // annotation instance
726       converted = Converter.obtainAnnotationInstance((AnnotationDesc) orig);
727     } else if (orig instanceof AnnotationValue[]) {
728       AnnotationValue[] old = (AnnotationValue[]) orig;
729       ArrayList<AnnotationValueInfo> values = new ArrayList<AnnotationValueInfo>();
730       for (int i = 0; i < old.length; i++) {
731         values.add(Converter.obtainAnnotationValue(old[i], null));
732       }
733       converted = values;
734     } else {
735       converted = orig;
736     }
737     v.init(converted);
738   }
739 
finishAnnotationValueInit()740   private static void finishAnnotationValueInit() {
741     int depth = 0;
742     while (mAnnotationValuesNeedingInit.size() > 0) {
743       HashSet<AnnotationValue> set = mAnnotationValuesNeedingInit;
744       mAnnotationValuesNeedingInit = new HashSet<AnnotationValue>();
745       for (AnnotationValue o : set) {
746         AnnotationValueInfo v = mAnnotationValues.get(o);
747         initAnnotationValue(o, v);
748       }
749       depth++;
750     }
751     mAnnotationValuesNeedingInit = null;
752   }
753 }
754