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