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