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