Home | History | Annotate | Download | only in doclava
      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.sun.javadoc.*;
     20 import java.util.ArrayList;
     21 import java.util.Arrays;
     22 import java.util.HashMap;
     23 import java.util.HashSet;
     24 import java.util.ArrayList;
     25 
     26 public 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