• 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 
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           Arrays.asList(Converter.convertAnnotationInstances(p.annotations())));
232     return pi;
233   }
234 
convertParameters(Parameter[] p, ExecutableMemberDoc m)235   private static ParameterInfo[] convertParameters(Parameter[] p, ExecutableMemberDoc m) {
236     SourcePosition pos = m.position();
237     int len = p.length;
238     ParameterInfo[] q = new ParameterInfo[len];
239     for (int i = 0; i < len; i++) {
240       boolean isVarArg = (m.isVarArgs() && i == len - 1);
241       q[i] = Converter.convertParameter(p[i], pos, isVarArg);
242     }
243     return q;
244   }
245 
convertTypes(Type[] p)246   private static TypeInfo[] convertTypes(Type[] p) {
247     if (p == null) return null;
248     int len = p.length;
249     TypeInfo[] q = new TypeInfo[len];
250     for (int i = 0; i < len; i++) {
251       q[i] = Converter.obtainType(p[i]);
252     }
253     return q;
254   }
255 
Converter()256   private Converter() {}
257 
258   private static class ClassNeedingInit {
ClassNeedingInit(ClassDoc c, ClassInfo cl)259     ClassNeedingInit(ClassDoc c, ClassInfo cl) {
260       this.c = c;
261       this.cl = cl;
262     }
263 
264     ClassDoc c;
265     ClassInfo cl;
266   }
267 
268   private static ArrayList<ClassNeedingInit> mClassesNeedingInit =
269       new ArrayList<ClassNeedingInit>();
270 
obtainClass(ClassDoc o)271   static ClassInfo obtainClass(ClassDoc o) {
272     return (ClassInfo) mClasses.obtain(o);
273   }
274 
275   private static Cache mClasses = new Cache() {
276     @Override
277     protected Object make(Object o) {
278       ClassDoc c = (ClassDoc) o;
279       ClassInfo cl =
280           new ClassInfo(c, c.getRawCommentText(), Converter.convertSourcePosition(c.position()), c
281               .isPublic(), c.isProtected(), c.isPackagePrivate(), c.isPrivate(), c.isStatic(), c
282               .isInterface(), c.isAbstract(), c.isOrdinaryClass(), c.isException(), c.isError(), c
283               .isEnum(), (c instanceof AnnotationTypeDoc), c.isFinal(), c.isIncluded(), c.name(), c
284               .qualifiedName(), c.qualifiedTypeName(), c.isPrimitive());
285       if (mClassesNeedingInit != null) {
286         mClassesNeedingInit.add(new ClassNeedingInit(c, cl));
287       }
288       return cl;
289     }
290 
291     @Override
292     protected void made(Object o, Object r) {
293       if (mClassesNeedingInit == null) {
294         initClass((ClassDoc) o, (ClassInfo) r);
295         ((ClassInfo) r).init2();
296       }
297     }
298 
299     @Override
300     ClassInfo[] all() {
301       return mCache.values().toArray(new ClassInfo[mCache.size()]);
302     }
303   };
304 
getHiddenMethods(MethodDoc[] methods)305   private static MethodInfo[] getHiddenMethods(MethodDoc[] methods) {
306     if (methods == null) return null;
307     ArrayList<MethodInfo> hiddenMethods = new ArrayList<MethodInfo>();
308     for (MethodDoc method : methods) {
309       MethodInfo methodInfo = Converter.obtainMethod(method);
310       if (methodInfo.isHidden()) {
311         hiddenMethods.add(methodInfo);
312       }
313     }
314 
315     return hiddenMethods.toArray(new MethodInfo[hiddenMethods.size()]);
316   }
317 
318   // Gets the removed methods regardless of access levels
getRemovedMethods(MethodDoc[] methods)319   private static MethodInfo[] getRemovedMethods(MethodDoc[] methods) {
320     if (methods == null) return null;
321     ArrayList<MethodInfo> removedMethods = new ArrayList<MethodInfo>();
322     for (MethodDoc method : methods) {
323       MethodInfo methodInfo = Converter.obtainMethod(method);
324       if (methodInfo.isRemoved()) {
325         removedMethods.add(methodInfo);
326       }
327     }
328 
329     return removedMethods.toArray(new MethodInfo[removedMethods.size()]);
330   }
331 
332   /**
333    * Converts FieldDoc[] into List<FieldInfo>. No filtering is done.
334    */
convertAllFields(FieldDoc[] fields)335   private static List<FieldInfo> convertAllFields(FieldDoc[] fields) {
336     if (fields == null) return null;
337     List<FieldInfo> allFields = new ArrayList<FieldInfo>();
338 
339     for (FieldDoc field : fields) {
340       FieldInfo fieldInfo = Converter.obtainField(field);
341       allFields.add(fieldInfo);
342     }
343 
344     return allFields;
345   }
346 
347   /**
348    * Converts ExecutableMemberDoc[] into List<MethodInfo>. No filtering is done.
349    */
convertAllMethods(ExecutableMemberDoc[] methods)350   private static List<MethodInfo> convertAllMethods(ExecutableMemberDoc[] methods) {
351     if (methods == null) return null;
352     List<MethodInfo> allMethods = new ArrayList<MethodInfo>();
353     for (ExecutableMemberDoc method : methods) {
354       MethodInfo methodInfo = Converter.obtainMethod(method);
355       allMethods.add(methodInfo);
356     }
357     return allMethods;
358   }
359 
360   /**
361    * Convert MethodDoc[] or ConstructorDoc[] into MethodInfo[].
362    * Also filters according to the -private, -public option,
363    * because the filtering doesn't seem to be working in the ClassDoc.constructors(boolean) call.
364    */
convertMethods(ExecutableMemberDoc[] methods)365   private static MethodInfo[] convertMethods(ExecutableMemberDoc[] methods) {
366     if (methods == null) return null;
367     List<MethodInfo> filteredMethods = new ArrayList<MethodInfo>();
368     for (ExecutableMemberDoc method : methods) {
369       MethodInfo methodInfo = Converter.obtainMethod(method);
370       if (methodInfo.checkLevel()) {
371         filteredMethods.add(methodInfo);
372       }
373     }
374 
375     return filteredMethods.toArray(new MethodInfo[filteredMethods.size()]);
376   }
377 
convertNonWrittenConstructors(ConstructorDoc[] methods)378   private static MethodInfo[] convertNonWrittenConstructors(ConstructorDoc[] methods) {
379     if (methods == null) return null;
380     ArrayList<MethodInfo> ctors = new ArrayList<MethodInfo>();
381     for (ConstructorDoc method : methods) {
382       MethodInfo methodInfo = Converter.obtainMethod(method);
383       if (!methodInfo.checkLevel()) {
384         ctors.add(methodInfo);
385       }
386     }
387 
388     return ctors.toArray(new MethodInfo[ctors.size()]);
389   }
390 
obtainMethod(E o)391   private static <E extends ExecutableMemberDoc> MethodInfo obtainMethod(E o) {
392     return (MethodInfo) mMethods.obtain(o);
393   }
394 
395   private static Cache mMethods = new Cache() {
396     @Override
397     protected Object make(Object o) {
398       if (o instanceof AnnotationTypeElementDoc) {
399         AnnotationTypeElementDoc m = (AnnotationTypeElementDoc) o;
400         MethodInfo result =
401             new MethodInfo(m.getRawCommentText(),
402                     new ArrayList<TypeInfo>(Arrays.asList(
403                             Converter.convertTypes(m.typeParameters()))),
404                     m.name(), m.signature(), Converter.obtainClass(m.containingClass()),
405                     Converter.obtainClass(m.containingClass()), m.isPublic(), m.isProtected(), m
406                     .isPackagePrivate(), m.isPrivate(), m.isFinal(), m.isStatic(), m.isSynthetic(),
407                     m.isAbstract(), m.isSynchronized(), m.isNative(), m.isDefault(), true,
408                     "annotationElement", m.flatSignature(),
409                     Converter.obtainMethod(m.overriddenMethod()),
410                     Converter.obtainType(m.returnType()),
411                     new ArrayList<ParameterInfo>(Arrays.asList(
412                             Converter.convertParameters(m.parameters(), m))),
413                     new ArrayList<ClassInfo>(Arrays.asList(Converter.convertClasses(
414                             m.thrownExceptions()))), Converter.convertSourcePosition(m.position()),
415                     new ArrayList<AnnotationInstanceInfo>(Arrays.asList(
416                             Converter.convertAnnotationInstances(m.annotations()))));
417         result.setVarargs(m.isVarArgs());
418         result.init(Converter.obtainAnnotationValue(m.defaultValue(), result));
419         return result;
420       } else if (o instanceof MethodDoc) {
421         MethodDoc m = (MethodDoc) o;
422         MethodInfo result =
423             new MethodInfo(m.getRawCommentText(),
424                     new ArrayList<TypeInfo>(Arrays.asList(
425                             Converter.convertTypes(m.typeParameters()))), m.name(), m.signature(),
426                     Converter.obtainClass(m.containingClass()),
427                     Converter.obtainClass(m.containingClass()), m.isPublic(), m.isProtected(),
428                     m.isPackagePrivate(), m.isPrivate(), m.isFinal(), m.isStatic(), m.isSynthetic(),
429                     m.isAbstract(), m.isSynchronized(), m.isNative(), m.isDefault(), false,
430                     "method", m.flatSignature(), Converter.obtainMethod(m.overriddenMethod()),
431                     Converter.obtainType(m.returnType()),
432                     new ArrayList<ParameterInfo>(Arrays.asList(
433                             Converter.convertParameters(m.parameters(), m))),
434                     new ArrayList<ClassInfo>(Arrays.asList(
435                             Converter.convertClasses(m.thrownExceptions()))),
436                     Converter.convertSourcePosition(m.position()),
437                     new ArrayList<AnnotationInstanceInfo>(Arrays.asList(
438                             Converter.convertAnnotationInstances(m.annotations()))));
439         result.setVarargs(m.isVarArgs());
440         result.init(null);
441         return result;
442       } else {
443         ConstructorDoc m = (ConstructorDoc) o;
444         // Workaround for a JavaDoc behavior change introduced in OpenJDK 8 that breaks
445         // links in documentation and the content of API files like current.txt.
446         // http://b/18051133.
447         String name = m.name();
448         ClassDoc containingClass = m.containingClass();
449         if (containingClass.containingClass() != null) {
450           // This should detect the new behavior and be bypassed otherwise.
451           if (!name.contains(".")) {
452             // Constructors of inner classes do not contain the name of the enclosing class
453             // with OpenJDK 8. This simulates the old behavior:
454             name = containingClass.name();
455           }
456         }
457         // End of workaround.
458         MethodInfo result =
459             new MethodInfo(m.getRawCommentText(), new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(m.typeParameters()))),
460                 name, m.signature(), Converter.obtainClass(m.containingClass()), Converter
461                 .obtainClass(m.containingClass()), m.isPublic(), m.isProtected(), m
462                 .isPackagePrivate(), m.isPrivate(), m.isFinal(), m.isStatic(), m.isSynthetic(),
463                 false, m.isSynchronized(), m.isNative(), false/*isDefault*/, false, "constructor", m.flatSignature(),
464                 null, null, new ArrayList<ParameterInfo>(Arrays.asList(Converter.convertParameters(m.parameters(), m))),
465                 new ArrayList<ClassInfo>(Arrays.asList(Converter.convertClasses(m.thrownExceptions()))), Converter.convertSourcePosition(m
466                     .position()), new ArrayList<AnnotationInstanceInfo>(Arrays.asList(Converter.convertAnnotationInstances(m.annotations()))));
467         result.setVarargs(m.isVarArgs());
468         result.init(null);
469         return result;
470       }
471     }
472   };
473 
474 
convertFields(FieldDoc[] fields)475   private static FieldInfo[] convertFields(FieldDoc[] fields) {
476     if (fields == null) return null;
477     ArrayList<FieldInfo> out = new ArrayList<FieldInfo>();
478     int N = fields.length;
479     for (int i = 0; i < N; i++) {
480       FieldInfo f = Converter.obtainField(fields[i]);
481       if (f.checkLevel()) {
482         out.add(f);
483       }
484     }
485     return out.toArray(new FieldInfo[out.size()]);
486   }
487 
obtainField(FieldDoc o)488   private static FieldInfo obtainField(FieldDoc o) {
489     return (FieldInfo) mFields.obtain(o);
490   }
491 
obtainField(ConstructorDoc o)492   private static FieldInfo obtainField(ConstructorDoc o) {
493     return (FieldInfo) mFields.obtain(o);
494   }
495 
496   private static Cache mFields = new Cache() {
497     @Override
498     protected Object make(Object o) {
499       FieldDoc f = (FieldDoc) o;
500       return new FieldInfo(f.name(), Converter.obtainClass(f.containingClass()), Converter
501           .obtainClass(f.containingClass()), f.isPublic(), f.isProtected(), f.isPackagePrivate(), f
502           .isPrivate(), f.isFinal(), f.isStatic(), f.isTransient(), f.isVolatile(),
503           f.isSynthetic(), Converter.obtainType(f.type()), f.getRawCommentText(),
504           f.constantValue(), Converter.convertSourcePosition(f.position()),
505           new ArrayList<AnnotationInstanceInfo>(Arrays.asList(Converter
506               .convertAnnotationInstances(f.annotations()))));
507     }
508   };
509 
obtainPackage(PackageDoc o)510   private static PackageInfo obtainPackage(PackageDoc o) {
511     return (PackageInfo) mPackagees.obtain(o);
512   }
513 
514   private static Cache mPackagees = new Cache() {
515     @Override
516     protected Object make(Object o) {
517       PackageDoc p = (PackageDoc) o;
518       return new PackageInfo(p, p.name(), Converter.convertSourcePosition(p.position()));
519     }
520   };
521 
obtainType(Type o)522   private static TypeInfo obtainType(Type o) {
523     return (TypeInfo) mTypes.obtain(o);
524   }
525 
526   private static Cache mTypes = new Cache() {
527     @Override
528     protected Object make(Object o) {
529       Type t = (Type) o;
530       String simpleTypeName;
531       if (t instanceof ClassDoc) {
532         simpleTypeName = ((ClassDoc) t).name();
533       } else {
534         simpleTypeName = t.simpleTypeName();
535       }
536       TypeInfo ti =
537           new TypeInfo(t.isPrimitive(), t.dimension(), simpleTypeName, t.qualifiedTypeName(),
538               Converter.obtainClass(t.asClassDoc()));
539       return ti;
540     }
541 
542     @Override
543     protected void made(Object o, Object r) {
544       Type t = (Type) o;
545       TypeInfo ti = (TypeInfo) r;
546       if (t.asParameterizedType() != null) {
547         ti.setTypeArguments(new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(t.asParameterizedType().typeArguments()))));
548       } else if (t instanceof ClassDoc) {
549         ti.setTypeArguments(new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(((ClassDoc) t).typeParameters()))));
550       } else if (t.asTypeVariable() != null) {
551         ti.setBounds(null, new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes((t.asTypeVariable().bounds())))));
552         ti.setIsTypeVariable(true);
553       } else if (t.asWildcardType() != null) {
554         ti.setIsWildcard(true);
555         ti.setBounds(new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(t.asWildcardType().superBounds()))),
556                 new ArrayList<TypeInfo>(Arrays.asList(Converter.convertTypes(t.asWildcardType().extendsBounds()))));
557       }
558     }
559 
560     @Override
561     protected Object keyFor(Object o) {
562       Type t = (Type) o;
563       String keyString = o.getClass().getName() + "/" + o.toString() + "/";
564       if (t.asParameterizedType() != null) {
565         keyString += t.asParameterizedType().toString() + "/";
566         if (t.asParameterizedType().typeArguments() != null) {
567           for (Type ty : t.asParameterizedType().typeArguments()) {
568             keyString += ty.toString() + "/";
569           }
570         }
571       } else {
572         keyString += "NoParameterizedType//";
573       }
574       if (t.asTypeVariable() != null) {
575         keyString += t.asTypeVariable().toString() + "/";
576         if (t.asTypeVariable().bounds() != null) {
577           for (Type ty : t.asTypeVariable().bounds()) {
578             keyString += ty.toString() + "/";
579           }
580         }
581       } else {
582         keyString += "NoTypeVariable//";
583       }
584       if (t.asWildcardType() != null) {
585         keyString += t.asWildcardType().toString() + "/";
586         if (t.asWildcardType().superBounds() != null) {
587           for (Type ty : t.asWildcardType().superBounds()) {
588             keyString += ty.toString() + "/";
589           }
590         }
591         if (t.asWildcardType().extendsBounds() != null) {
592           for (Type ty : t.asWildcardType().extendsBounds()) {
593             keyString += ty.toString() + "/";
594           }
595         }
596       } else {
597         keyString += "NoWildCardType//";
598       }
599 
600       return keyString;
601     }
602   };
603 
obtainTypeFromString(String type)604   public static TypeInfo obtainTypeFromString(String type) {
605     return (TypeInfo) mTypesFromString.obtain(type);
606   }
607 
608   private static final Cache mTypesFromString = new Cache() {
609     @Override
610     protected Object make(Object o) {
611       String name = (String) o;
612       return new TypeInfo(name);
613     }
614 
615     @Override
616     protected void made(Object o, Object r) {
617 
618     }
619 
620     @Override
621     protected Object keyFor(Object o) {
622       return o;
623     }
624   };
625 
obtainMember(MemberDoc o)626   private static MemberInfo obtainMember(MemberDoc o) {
627     return (MemberInfo) mMembers.obtain(o);
628   }
629 
630   private static Cache mMembers = new Cache() {
631     @Override
632     protected Object make(Object o) {
633       if (o instanceof MethodDoc) {
634         return Converter.obtainMethod((MethodDoc) o);
635       } else if (o instanceof ConstructorDoc) {
636         return Converter.obtainMethod((ConstructorDoc) o);
637       } else if (o instanceof FieldDoc) {
638         return Converter.obtainField((FieldDoc) o);
639       } else {
640         return null;
641       }
642     }
643   };
644 
convertAnnotationInstances(AnnotationDesc[] orig)645   private static AnnotationInstanceInfo[] convertAnnotationInstances(AnnotationDesc[] orig) {
646     int len = orig.length;
647     AnnotationInstanceInfo[] out = new AnnotationInstanceInfo[len];
648     for (int i = 0; i < len; i++) {
649       out[i] = Converter.obtainAnnotationInstance(orig[i]);
650     }
651     return out;
652   }
653 
654 
obtainAnnotationInstance(AnnotationDesc o)655   private static AnnotationInstanceInfo obtainAnnotationInstance(AnnotationDesc o) {
656     return (AnnotationInstanceInfo) mAnnotationInstances.obtain(o);
657   }
658 
659   private static Cache mAnnotationInstances = new Cache() {
660     @Override
661     protected Object make(Object o) {
662       AnnotationDesc a = (AnnotationDesc) o;
663       ClassInfo annotationType = Converter.obtainClass(a.annotationType());
664       AnnotationDesc.ElementValuePair[] ev = a.elementValues();
665       AnnotationValueInfo[] elementValues = new AnnotationValueInfo[ev.length];
666       for (int i = 0; i < ev.length; i++) {
667         elementValues[i] =
668             obtainAnnotationValue(ev[i].value(), Converter.obtainMethod(ev[i].element()));
669       }
670       return new AnnotationInstanceInfo(annotationType, elementValues);
671     }
672   };
673 
674 
675   private abstract static class Cache {
put(Object key, Object value)676     void put(Object key, Object value) {
677       mCache.put(key, value);
678     }
679 
obtain(Object o)680     Object obtain(Object o) {
681       if (o == null) {
682         return null;
683       }
684       Object k = keyFor(o);
685       Object r = mCache.get(k);
686       if (r == null) {
687         r = make(o);
688         mCache.put(k, r);
689         made(o, r);
690       }
691       return r;
692     }
693 
694     protected HashMap<Object, Object> mCache = new HashMap<Object, Object>();
695 
make(Object o)696     protected abstract Object make(Object o);
697 
made(Object o, Object r)698     protected void made(Object o, Object r) {}
699 
keyFor(Object o)700     protected Object keyFor(Object o) {
701       return o;
702     }
703 
all()704     Object[] all() {
705       return null;
706     }
707   }
708 
709   // annotation values
710   private static HashMap<AnnotationValue, AnnotationValueInfo> mAnnotationValues =
711       new HashMap<AnnotationValue, AnnotationValueInfo>();
712   private static HashSet<AnnotationValue> mAnnotationValuesNeedingInit =
713       new HashSet<AnnotationValue>();
714 
obtainAnnotationValue(AnnotationValue o, MethodInfo element)715   private static AnnotationValueInfo obtainAnnotationValue(AnnotationValue o, MethodInfo element) {
716     if (o == null) {
717       return null;
718     }
719     AnnotationValueInfo v = mAnnotationValues.get(o);
720     if (v != null) return v;
721     v = new AnnotationValueInfo(element);
722     mAnnotationValues.put(o, v);
723     if (mAnnotationValuesNeedingInit != null) {
724       mAnnotationValuesNeedingInit.add(o);
725     } else {
726       initAnnotationValue(o, v);
727     }
728     return v;
729   }
730 
initAnnotationValue(AnnotationValue o, AnnotationValueInfo v)731   private static void initAnnotationValue(AnnotationValue o, AnnotationValueInfo v) {
732     Object orig = o.value();
733     Object converted;
734     if (orig instanceof Type) {
735       // class literal
736       converted = Converter.obtainType((Type) orig);
737     } else if (orig instanceof FieldDoc) {
738       // enum constant
739       converted = Converter.obtainField((FieldDoc) orig);
740     } else if (orig instanceof AnnotationDesc) {
741       // annotation instance
742       converted = Converter.obtainAnnotationInstance((AnnotationDesc) orig);
743     } else if (orig instanceof AnnotationValue[]) {
744       AnnotationValue[] old = (AnnotationValue[]) orig;
745       ArrayList<AnnotationValueInfo> values = new ArrayList<AnnotationValueInfo>();
746       for (int i = 0; i < old.length; i++) {
747         values.add(Converter.obtainAnnotationValue(old[i], null));
748       }
749       converted = values;
750     } else {
751       converted = orig;
752     }
753     v.init(converted);
754   }
755 
finishAnnotationValueInit()756   private static void finishAnnotationValueInit() {
757     int depth = 0;
758     while (mAnnotationValuesNeedingInit.size() > 0) {
759       HashSet<AnnotationValue> set = mAnnotationValuesNeedingInit;
760       mAnnotationValuesNeedingInit = new HashSet<AnnotationValue>();
761       for (AnnotationValue o : set) {
762         AnnotationValueInfo v = mAnnotationValues.get(o);
763         initAnnotationValue(o, v);
764       }
765       depth++;
766     }
767     mAnnotationValuesNeedingInit = null;
768   }
769 }
770