Home | History | Annotate | Download | only in doclava
      1 /*
      2  * Copyright (C) 2011 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.parser.JavaLexer;
     20 import com.google.doclava.parser.JavaParser;
     21 
     22 import org.antlr.runtime.ANTLRFileStream;
     23 import org.antlr.runtime.CommonToken;
     24 import org.antlr.runtime.CommonTokenStream;
     25 import org.antlr.runtime.RecognitionException;
     26 import org.antlr.runtime.debug.ParseTreeBuilder;
     27 import org.antlr.runtime.tree.ParseTree;
     28 import org.antlr.runtime.tree.Tree;
     29 
     30 import java.io.IOException;
     31 import java.util.ArrayList;
     32 import java.util.HashMap;
     33 import java.util.HashSet;
     34 import java.util.Iterator;
     35 
     36 /**
     37  * InfoBuilder parses an individual file and builds Doclava
     38  * objects out of the data within the file. This data is
     39  * stored within a global cache for later use.
     40  */
     41 public class InfoBuilder {
     42     private PackageInfo mPackage;
     43     private ArrayList<String> mImports;
     44     private HashSet<String> mClassNames;
     45     private String mFilename; // TODO - remove this eventually
     46     private ClassInfo mRootClass;
     47 
     48     public InfoBuilder(String filename) {
     49         mImports = new ArrayList<String>();
     50         mImports.add("java.lang.*"); // should allow us to resolve this properly, eventually
     51                                      // alternatively, we could add everything from java.lang.*
     52                                      // but that would probably be too brittle
     53         mClassNames = new HashSet<String>();
     54         mFilename = filename;
     55     }
     56 
     57     @Override
     58     public String toString() {
     59         return mFilename;
     60     }
     61 
     62     public void parseFile() {
     63         JavaLexer lex;
     64         try {
     65             lex = new JavaLexer(new ANTLRFileStream(mFilename, "UTF8"));
     66 
     67             CommonTokenStream tokens = new CommonTokenStream(lex);
     68 
     69             // create the ParseTreeBuilder to build a parse tree
     70             // much easier to parse than ASTs
     71             ParseTreeBuilder builder = new ParseTreeBuilder("compilationUnit");
     72             JavaParser g = new JavaParser(tokens, builder);
     73 
     74             g.compilationUnit();
     75             ParseTree tree = builder.getTree();
     76 
     77             lex = null;
     78             tokens = null;
     79             builder = null;
     80             g = null;
     81 
     82             parseFile(tree);
     83 
     84         } catch (IOException e1) {
     85             e1.printStackTrace();
     86         } catch (RecognitionException e) {
     87             e.printStackTrace();
     88         }
     89     }
     90 
     91     public static void resolve() {
     92         Caches.resolve();
     93     }
     94 
     95     // All of the print functions exist for debugging alone.
     96     public void printStuff() {
     97         System.out.println(mPackage.name() + "\n");
     98 
     99         printList(mImports);
    100 
    101         Caches.printResolutions();
    102     }
    103 
    104     private void printList(ArrayList<String> list) {
    105         for (String value : list) {
    106             System.out.println(value);
    107         }
    108 
    109         System.out.println();
    110     }
    111 
    112     public static void printClassInfo(ClassInfo cl) {
    113         System.out.print("Class: " + cl.toString());
    114 
    115         printTypeVariables(cl.type());
    116 
    117         System.out.println();
    118 
    119         System.out.println(cl.comment().mText);
    120 
    121         if (!cl.annotations().isEmpty()) {
    122             System.out.println("\nAnnotations:");
    123             printAnnotations(cl.annotations());
    124         }
    125 
    126         if (cl.superclass() != null) {
    127             System.out.print("Superclass: " + cl.superclass().qualifiedName());
    128             printTypeVariables(cl.superclassType());
    129             System.out.println();
    130         }
    131 
    132         if (!cl.realInterfaces().isEmpty()) {
    133             System.out.println("\nInterfaces Implemented:");
    134             Iterator<TypeInfo> it = cl.realInterfaceTypes().iterator();
    135             for (ClassInfo cls : cl.realInterfaces()) {
    136                 TypeInfo outerType = it.next();
    137                 if (cls == null) {
    138                     System.out.print(outerType.simpleTypeName());
    139                 } else {
    140                     System.out.print(cls.qualifiedName());
    141                 }
    142 
    143                 printTypeVariables(outerType);
    144 
    145                 System.out.println();
    146             }
    147 
    148             System.out.println();
    149         }
    150 
    151         if (!cl.allSelfFields().isEmpty()) {
    152             System.out.println("\nFields:");
    153             for (FieldInfo f : cl.allSelfFields()) {
    154                 if (f != cl.allSelfFields().get(0)) {
    155                     System.out.println();
    156                 }
    157                 System.out.println(f.comment().mText);
    158 
    159                 printAnnotations(f.annotations());
    160                 printTypeName(f.type());
    161 
    162                 System.out.print(" " + f.name());
    163 
    164                 if (f.constantValue() != null) {
    165                     System.out.println(": " + f.constantValue());
    166                 } else if (f.hasValue()) {
    167                     System.out.println(": has some value");
    168                 } else {
    169                     System.out.println();
    170                 }
    171             }
    172 
    173             System.out.println();
    174         }
    175 
    176         if (cl.enumConstants() != null && !cl.enumConstants().isEmpty()) {
    177             System.out.println("\nEnum Constants:");
    178             for (FieldInfo f : cl.enumConstants()) {
    179                 if (f != cl.enumConstants().get(0)) {
    180                     System.out.println();
    181                 }
    182                 System.out.println(f.comment().mText);
    183                 printAnnotations(f.annotations());
    184                 System.out.print(f.type().simpleTypeName() + " " + f.name());
    185 
    186                 if (f.constantValue() != null) {
    187                     System.out.println(": " + f.constantValue());
    188                 } else {
    189                     System.out.println();
    190                 }
    191             }
    192 
    193             System.out.println();
    194         }
    195 
    196         if (!cl.allConstructors().isEmpty()) {
    197             System.out.println("\nConstructors:");
    198             for (MethodInfo m : cl.allConstructors()) {
    199                 if (m != cl.allConstructors().get(0)) {
    200                     System.out.println();
    201                 }
    202 
    203                 System.out.println(m.comment().mText);
    204 
    205                 printAnnotations(m.annotations());
    206                 if (m.getTypeParameters() != null) {
    207                     printTypeVariableList(m.getTypeParameters());
    208                     System.out.print(" ");
    209                 }
    210 
    211                 System.out.println(m.name() + m.flatSignature());
    212             }
    213 
    214             System.out.println();
    215         }
    216 
    217         if (!cl.allSelfMethods().isEmpty()) {
    218             System.out.println("\nMethods:");
    219             for (MethodInfo m : cl.allSelfMethods()) {
    220                 if (m != cl.allSelfMethods().get(0)) {
    221                     System.out.println();
    222                 }
    223 
    224                 System.out.println(m.comment().mText);
    225                 printAnnotations(m.annotations());
    226                 if (m.getTypeParameters() != null) {
    227                     printTypeVariableList(m.getTypeParameters());
    228                     System.out.print(" ");
    229                 }
    230 
    231                 printTypeName(m.returnType());
    232 
    233                 System.out.print(" " + m.name() + m.flatSignature());
    234 
    235                 if (m.thrownExceptions() != null && !m.thrownExceptions().isEmpty()) {
    236                     System.out.print(" throws ");
    237                     for (ClassInfo c : m.thrownExceptions()) {
    238                         if (c != m.thrownExceptions().get(0)) {
    239                             System.out.print(", ");
    240                         }
    241 
    242                         System.out.print(c.name());
    243                     }
    244                 }
    245 
    246                 System.out.println();
    247             }
    248 
    249             System.out.println();
    250         }
    251 
    252         if (!cl.annotationElements().isEmpty()) {
    253             System.out.println("\nAnnotation Elements:");
    254 
    255             for (MethodInfo m : cl.annotationElements()) {
    256                 if (m != cl.annotationElements().get(0)) {
    257                     System.out.println();
    258                 }
    259 
    260                 System.out.println(m.comment().mText);
    261                 printAnnotations(m.annotations());
    262                 printTypeName(m.returnType());
    263 
    264                 System.out.print(" " + m.name() + m.flatSignature());
    265 
    266                 if (m.defaultAnnotationElementValue() != null) {
    267                     System.out.print(" default " +
    268                             m.defaultAnnotationElementValue().valueString());
    269                 }
    270 
    271                 System.out.println();
    272             }
    273 
    274             System.out.println();
    275         }
    276 
    277         if (cl.innerClasses() != null && !cl.innerClasses().isEmpty()) {
    278             System.out.println("\nInner Classes:");
    279             for (ClassInfo c : cl.innerClasses()) {
    280                 printClassInfo(c);
    281             }
    282         }
    283     }
    284 
    285     private static void printTypeName(TypeInfo type) {
    286         System.out.print(type.simpleTypeName());
    287 
    288         if (type.extendsBounds() != null && !type.extendsBounds().isEmpty()) {
    289             System.out.print(" extends ");
    290             for (TypeInfo t : type.extendsBounds()) {
    291                 if (t != type.extendsBounds().get(0)) {
    292                     System.out.print(" & ");
    293                 }
    294                 printTypeName(t);
    295             }
    296         }
    297 
    298         if (type.superBounds() != null && !type.superBounds().isEmpty()) {
    299             System.out.print(" super ");
    300             for (TypeInfo t : type.superBounds()) {
    301                 if (t != type.superBounds().get(0)) {
    302                     System.out.print(" & ");
    303                 }
    304                 printTypeName(t);
    305             }
    306         }
    307 
    308         printTypeVariables(type);
    309 
    310         if (type.dimension() != null) {
    311             System.out.print(type.dimension());
    312         }
    313     }
    314 
    315     private static void printAnnotations(ArrayList<AnnotationInstanceInfo> annotations) {
    316         for (AnnotationInstanceInfo i : annotations) {
    317             System.out.println(i);
    318         }
    319     }
    320 
    321     private static void printTypeVariables(TypeInfo type) {
    322         printTypeVariableList(type.typeArguments());
    323     }
    324 
    325     private static void printTypeVariableList(ArrayList<TypeInfo> typeList) {
    326         if (typeList != null && !typeList.isEmpty()) {
    327             System.out.print("<");
    328             for (TypeInfo type : typeList) {
    329                 if (type != typeList.get(0)) {
    330                     System.out.print(", ");
    331                 }
    332                 printTypeName(type);
    333             }
    334             System.out.print(">");
    335         }
    336     }
    337 
    338     /**
    339      * Parses the file represented by the ParseTree.
    340      * @param tree A ParseTree of the file to parse.
    341      */
    342     private void parseFile(ParseTree tree) {
    343         if (tree.payload != null) {
    344             String payload = tree.payload.toString();
    345 
    346             // first pass at ignore method blocks
    347             if ("block".equals(payload) ||
    348                     "blockStatement".equals(payload) ||
    349                     "explicitConstructorInvocation".equals(payload)) {
    350                 tree = null;
    351                 return;
    352             }
    353 
    354             // parse package of file
    355             if ("packageDeclaration".equals(payload)) {
    356                 mPackage = buildPackage(tree);
    357                 return;
    358             // parse imports
    359             } else if ("importDeclaration".equals(payload)) {
    360                 mImports.add(buildImport(tree));
    361                 return;
    362             // classes
    363             } else if ("normalClassDeclaration".equals(payload)) {
    364                 buildClass(tree, null);
    365                 return;
    366             // enums
    367             }  else if ("enumDeclaration".equals(payload)) {
    368                 buildEnum(tree, null);
    369                 return;
    370             // interfaces
    371             } else if ("normalInterfaceDeclaration".equals(payload)) {
    372                 buildInterface(tree, null);
    373                 return;
    374             // annotations
    375             } else if ("annotationTypeDeclaration".equals(payload)) {
    376                 buildAnnotationDeclaration(tree, null);
    377                 return;
    378             }
    379         }
    380 
    381         // if we're not at the end, recurse down the tree
    382         for (int i = 0; i < tree.getChildCount(); i++) {
    383             parseFile((ParseTree) tree.getChild(i));
    384         }
    385     }
    386 
    387     /**
    388      * Parses a packageDeclaration in the tree. This function should only be called once per file.
    389      * @param tree The tree to parse. packageDeclaration should be the root value.
    390      * @return a PackageInfo representing the package in which this file exists.
    391      */
    392     private PackageInfo buildPackage(ParseTree tree) {
    393         for (int i = 0; i < tree.getChildCount(); i++) {
    394             ParseTree child = (ParseTree) tree.getChild(i);
    395 
    396             if (child.payload != null && "qualifiedName".equals(child.payload.toString())) {
    397                 String packageName = buildQualifiedName(child);
    398 
    399                 // return package because we might be creating packages for other classes
    400                 return Caches.obtainPackage(packageName);
    401             }
    402         }
    403 
    404         return null;
    405     }
    406 
    407     /**
    408      * Parses a qualifiedName, returning it as a String.
    409      * @param tree The tree to parse. qualifiedName should be the root value.
    410      * @return
    411      */
    412     private static String buildQualifiedName(ParseTree tree) {
    413         StringBuilder packageName = new StringBuilder();
    414 
    415         for (int j = 0; j < tree.getChildCount(); j++) {
    416             packageName.append(tree.getChild(j).toString());
    417         }
    418 
    419         return packageName.toString();
    420     }
    421 
    422     /**
    423      * Builds a string representing an import declaration.
    424      * @param tree The tree to parse. importDeclaration should be the root value.
    425      * @return a String version of the import.
    426      */
    427     private String buildImport(ParseTree tree) {
    428         StringBuilder theImport = new StringBuilder();
    429         for (int i = 1; i < tree.getChildCount(); i++) {
    430             String part = tree.getChild(i).toString();
    431 
    432             if ((i == 1 && "static".equals(part))
    433                     || (i == tree.getChildCount()-1 && ";".equals(part))) {
    434                 continue;
    435             }
    436 
    437             theImport.append(part);
    438         }
    439 
    440         return theImport.toString();
    441     }
    442 
    443     /**
    444      * Builds a ClassInfo for a normalClassDeclaration.
    445      * @param tree The tree to parse. normalClassDeclaration should be the root value.
    446      * @param containingClass The class that contains the class that will be built.
    447      * This value should be null if this class is a root class in the file.
    448      * @return A ClassInfo that contains all of the information about the class.
    449      */
    450     private ClassInfo buildClass(ParseTree tree, ClassInfo containingClass) {
    451         CommentAndPosition commentAndPosition = parseCommentAndPosition(tree);
    452         Modifiers modifiers = new Modifiers(this);
    453         ClassInfo cls = null;
    454 
    455         @SuppressWarnings("unchecked")
    456         Iterator<ParseTree> it = (Iterator<ParseTree>) tree.getChildren().iterator();
    457         ParseTree child = it.next();
    458 
    459         // parse modifiers
    460         modifiers.parseModifiers(child);
    461 
    462         it.next();
    463         child = it.next();
    464 
    465         // parse class name
    466         cls = buildClassName(child, containingClass, modifiers,
    467                 commentAndPosition.getCommentText(),
    468                 commentAndPosition.getPosition(),
    469                 ClassType.ORDINARY);
    470 
    471         child = it.next();
    472 
    473         // handle generics
    474         if ("typeParameters".equals(child.toString())) {
    475             cls.type().setTypeArguments(buildTypeVariables(child));
    476             child = it.next();
    477 
    478         }
    479 
    480         // handle extends
    481         if ("extends".equals(child.toString())) {
    482             child = it.next();
    483 
    484             TypeInfo type = buildType(child);
    485             cls.setSuperclassType(type);
    486 
    487             // if ClassInfo is null, we need to add a resolution
    488             if (type.asClassInfo() == null) {
    489                 addFutureResolution(cls, "superclassQualifiedName", type.simpleTypeName(), this);
    490             }
    491 
    492             cls.setSuperClass(type.asClassInfo());
    493 
    494             child = it.next();
    495         }
    496 
    497         // TODO - do I have to make java.lang.Object the superclass if there is none otherwise?
    498 
    499         // handle implements
    500         if ("implements".equals(child.toString())) {
    501             child = it.next();
    502 
    503             parseInterfaces(child, cls);
    504 
    505             child = it.next();
    506         }
    507 
    508         // finally, parse the body
    509         buildClassBody(child, cls);
    510 
    511         return cls;
    512     }
    513 
    514     /**
    515      * Parses the list of interfaces that the class implements.
    516      * Should only be called if the implements keyword is found.
    517      * @param tree The tree to parse. typeList should be the root element.
    518      * @param cls The class that implements these interfaces.
    519      */
    520     private void parseInterfaces(ParseTree tree, ClassInfo cls) {
    521         for (Object o : tree.getChildren()) {
    522             if ("type".equals(o.toString())) {
    523                 TypeInfo type = buildType((ParseTree) o);
    524                 cls.addInterfaceType(type);
    525 
    526                 // if ClassInfo is null, we need to add a resolution
    527                 if (type.asClassInfo() == null) {
    528                     addFutureResolution(cls, "interfaceQualifiedName", type.simpleTypeName(), this);
    529                 }
    530 
    531                 cls.addInterface(type.asClassInfo());
    532             }
    533         }
    534     }
    535 
    536     /**
    537      * ClassType exists solely to tell buildClassName which type of ClassInfo is being built.
    538      */
    539     private enum ClassType {
    540         ENUM, INTERFACE, ANNOTATION, ORDINARY
    541     }
    542 
    543     /**
    544      * Parses the class name from the declaration. Also initializes the class.
    545      * @param tree Position of the tree where the name of the class resides.
    546      * @param containingClass Class that this class is contained within.
    547      * <tt>null</tt> if this class is the root class.
    548      * @param modifiers Contains all the modifiers of this class.
    549      * @param commentText Javadoc comment of this class.
    550      * @param position Position of the class.
    551      * @param classType Type of class being instantiated.
    552      * @return the ClassInfo being initialized.
    553      */
    554     private ClassInfo buildClassName(ParseTree tree, ClassInfo containingClass, Modifiers modifiers,
    555             String commentText, SourcePositionInfo position, ClassType classType) {
    556         String qualifiedClassName = null;
    557         boolean isOrdinaryClass = true;
    558         boolean isException = false;
    559         boolean isError = false;
    560         boolean isIncluded = false;
    561         boolean isPrimitive = false;
    562         boolean isEnum = false;
    563         boolean isInterface = false;
    564         boolean isAnnotation = false;
    565 
    566         // set appropriate flags based on ClassType
    567         switch (classType) {
    568             case ENUM:
    569                 isEnum = true;
    570                 break;
    571             case INTERFACE:
    572                 isInterface = true;
    573                 break;
    574             case ANNOTATION:
    575                 isAnnotation = true;
    576                 break;
    577         }
    578 
    579         String qualifiedTypeName = null;
    580         ClassInfo cls = null;
    581 
    582         // changes the name based upon whether this is the root class or an inner class
    583         if (containingClass == null) {
    584             qualifiedClassName = mPackage.name() + "." + tree.toString();
    585         } else {
    586             qualifiedClassName = containingClass.qualifiedName() + "." + tree.toString();
    587         }
    588 
    589         qualifiedTypeName = new String(qualifiedClassName);
    590 
    591         // add the name to mClassNames so that we can use it to resolve usages of this class
    592         mClassNames.add(qualifiedClassName);
    593 
    594         // get the class from the cache and initialize it
    595         cls = Caches.obtainClass(qualifiedClassName);
    596         cls.initialize(commentText, position,
    597                 modifiers.isPublic(), modifiers.isProtected(),
    598                 modifiers.isPackagePrivate(), modifiers.isPrivate(),
    599                 modifiers.isStatic(), isInterface, modifiers.isAbstract(),
    600                 isOrdinaryClass, isException, isError, isEnum, isAnnotation,
    601                 modifiers.isFinal(), isIncluded, qualifiedTypeName, isPrimitive,
    602                 modifiers.getAnnotations());
    603 
    604         cls.setContainingClass(containingClass);
    605         cls.setContainingPackage(mPackage);
    606 
    607         if (containingClass == null) {
    608             mRootClass = cls;
    609         }
    610 
    611         // create an set a TypeInfo for this class
    612         TypeInfo type = new TypeInfo(false, null, cls.name(), qualifiedTypeName, cls);
    613         cls.setTypeInfo(type);
    614 
    615         return cls;
    616     }
    617 
    618     /**
    619      * Parses the body of a class.
    620      * @param tree The tree to parse. classBody should be the root value.
    621      * @param cls
    622      */
    623     private void buildClassBody(ParseTree tree, ClassInfo cls) {
    624         for (Object o : tree.getChildren()) {
    625             ParseTree child = (ParseTree) o;
    626 
    627             // skip all of the cruft that isn't a declaration
    628             if (!"classBodyDeclaration".equals(child.toString())) {
    629                 continue;
    630             }
    631 
    632             // get to an actual definition
    633             ParseTree member = (ParseTree) child.getChild(0).getChild(0);
    634 
    635             // ignores static initializers
    636             if (member == null) {
    637                 continue;
    638             }
    639 
    640             // field
    641             if ("fieldDeclaration".equals(member.toString())) {
    642                 for (FieldInfo f : buildFields(member, cls)) {
    643                     cls.addField(f);
    644                 }
    645             // method and constructor
    646             } else if ("methodDeclaration".equals(member.toString())) {
    647                 MethodInfo method = buildMethod(member, cls, false);
    648 
    649                 if (method.kind().equals("constructor")) {
    650                     cls.addConstructor(method);
    651                 } else {
    652                     cls.addMethod(method);
    653                 }
    654             // classes and enums
    655             } else if ("classDeclaration".equals(member.toString())) {
    656                 Object tmp = member.getChild(0);
    657 
    658                 if ("normalClassDeclaration".equals(tmp.toString())) {
    659                     cls.addInnerClass(buildClass((ParseTree) tmp, cls));
    660                 } else if ("enumDeclaration".equals(tmp.toString())) {
    661                     cls.addInnerClass(buildEnum((ParseTree) tmp, cls));
    662                 }
    663             // interfaces and annotations
    664             } else if ("interfaceDeclaration".equals(member.toString())) {
    665                 Object tmp = member.getChild(0);
    666 
    667                 if ("normalInterfaceDeclaration".equals(tmp.toString())) {
    668                     cls.addInnerClass(buildInterface((ParseTree) tmp, cls));
    669                 } else if ("annotationTypeDeclaration".equals(tmp.toString())) {
    670                     cls.addInnerClass(buildAnnotationDeclaration((ParseTree) tmp, cls));
    671                 }
    672             }
    673         }
    674     }
    675 
    676     /**
    677      * Builds one or more FieldInfos for the field declared in this class.
    678      * @param tree The tree to parse. fieldDeclaration should be the root value.
    679      * @param containingClass The ClassInfo in which this field is contained.
    680      * @return A list of FieldInfos for this field declaration.
    681      */
    682     private ArrayList<FieldInfo> buildFields(ParseTree tree, ClassInfo containingClass) {
    683         ArrayList<FieldInfo> fields = new ArrayList<FieldInfo>();
    684         Modifiers modifiers = new Modifiers(this);
    685         CommentAndPosition commentAndPosition = parseCommentAndPosition(tree);
    686         String name = null;
    687         Object constantValue = null;
    688         TypeInfo type = null;
    689         boolean hasValue = false;
    690 
    691         @SuppressWarnings("unchecked")
    692         Iterator<ParseTree> it = (Iterator<ParseTree>) tree.getChildren().iterator();
    693         ParseTree child = it.next();
    694 
    695         // modifiers
    696         modifiers.parseModifiers(child);
    697         child = it.next();
    698 
    699         // parse the type of this field
    700         type = buildType(child);
    701 
    702         child = it.next();
    703 
    704         // parse the variable declarators
    705         boolean firstType = true;
    706         while (!";".equals(child.toString())) {
    707             if ("variableDeclarator".equals(child.toString())) {
    708                 TypeInfo newType;
    709                 if (firstType) {
    710                     firstType = false;
    711                     newType = type;
    712                 } else {
    713                     newType = new TypeInfo(type.isPrimitive(), type.dimension(),
    714                             type.simpleTypeName(), type.qualifiedTypeName(), type.asClassInfo());
    715                     newType.setBounds(type.superBounds(), type.extendsBounds());
    716                     newType.setIsWildcard(type.isWildcard());
    717                     newType.setIsTypeVariable(type.isTypeVariable());
    718                     newType.setTypeArguments(type.typeArguments());
    719                 }
    720                 name = child.getChild(0).toString();
    721 
    722                 // if we have a value for the field and/or dimensions
    723                 if (child.getChildCount() > 1) {
    724                     int j = 1;
    725                     ParseTree tmp = (ParseTree) child.getChild(j++);
    726 
    727                     // if we have dimensions in the wrong place
    728                     if ("[".equals(tmp.toString())) {
    729                         StringBuilder builder = new StringBuilder();
    730 
    731                         do {
    732                             builder.append(tmp.toString());
    733                             tmp = (ParseTree) child.getChild(j++);
    734                         } while (j < child.getChildCount() && !"=".equals(tmp.toString()));
    735 
    736                         newType.setDimension(builder.toString());
    737                     }
    738 
    739                     // get value if it exists
    740                     if (j < child.getChildCount()) {
    741                         // get to variableInitializer
    742                         do {
    743                             tmp = (ParseTree) child.getChild(j++);
    744                         } while (!"variableInitializer".equals(tmp.toString()));
    745 
    746                         // get the constantValue
    747                         constantValue = parseExpression(tmp);
    748                     }
    749 
    750                     hasValue = true;
    751                 }
    752 
    753                 FieldInfo field = new FieldInfo(name, containingClass, containingClass,
    754                         modifiers.isPublic(), modifiers.isProtected(),
    755                         modifiers.isPackagePrivate(), modifiers.isPrivate(),
    756                         modifiers.isFinal(), modifiers.isStatic(), modifiers.isTransient(),
    757                         modifiers.isVolatile(), modifiers.isSynthetic(),
    758                         newType, commentAndPosition.getCommentText(), constantValue,
    759                         commentAndPosition.getPosition(), modifiers.getAnnotations());
    760                 field.setHasValue(hasValue);
    761                 fields.add(field);
    762             }
    763 
    764             child = it.next();
    765         }
    766 
    767         return fields;
    768     }
    769 
    770     /**
    771      * Parses an expression in the ParseTree to get a constant value.
    772      * @param tree the place in the tree to get the constant value.
    773      * @return the constant value.
    774      */
    775     private static Object parseExpression(ParseTree tree) {
    776         Object constantValue = null;
    777         StringBuilder builder = new StringBuilder();
    778 
    779         while (!"primary".equals(tree.toString())) {
    780             if (tree.getChildCount() > 1) {
    781                 if ("unaryExpression".equals(tree.toString()) ||
    782                         "unaryExpressionNotPlusMinus".equals(tree.toString())) {
    783                     if ("selector".equals(tree.getChild(1).toString())) {
    784                         return constantValue;
    785                     }
    786 
    787                     builder.append(tree.getChild(0));
    788                     tree = (ParseTree) tree.getChild(1);
    789                 } else if ("arrayInitializer".equals(tree.toString())) {
    790                     // TODO - do we wanna parse arrays or just skip it
    791                     return constantValue;
    792                 } else {
    793                     return constantValue;
    794                 }
    795             } else if ("castExpression".equals(tree.toString())) {
    796                 tree = (ParseTree) tree.getChild(tree.getChildCount()-1);
    797             } else {
    798                 tree = (ParseTree) tree.getChild(0);
    799             }
    800         }
    801 
    802         if ("literal".equals(tree.getChild(0).toString())) {
    803             constantValue = builder.append(tree.getChild(0).getChild(0).toString()).toString();
    804         } else if (tree.getChildCount() > 1) {
    805             for (Object o : tree.getChildren()) {
    806                 builder.append(o.toString());
    807             }
    808 
    809             constantValue = builder.toString();
    810         }
    811 
    812         return constantValue;
    813     }
    814 
    815     /**
    816      * Builds  TypeInfo. Requires that tree points to "type" in the ParseTree.
    817      * @param tree The tree to parse. type should be the root value.
    818      * @return A TypeInfo for this type.
    819      */
    820     private TypeInfo buildType(ParseTree tree) {
    821         boolean isPrimitive = false;
    822         String dimension = null;
    823         String simpleTypeName = null;
    824         String qualifiedTypeName = null;
    825         ClassInfo cl = null;
    826         boolean addResolution = false;
    827         ArrayList<TypeInfo> typeArguments = null;
    828 
    829         // parse primitive types - very easy
    830         if ("primitiveType".equals(tree.getChild(0).toString())) {
    831             isPrimitive = true;
    832 
    833             simpleTypeName = tree.getChild(0).getChild(0).toString();
    834             qualifiedTypeName = simpleTypeName;
    835         // any non-primitives
    836         } else {
    837             StringBuilder builder = new StringBuilder();
    838 
    839             // get the full name of the type
    840             for (Object namePart : ((ParseTree) tree.getChild(0)).getChildren()) {
    841                 // if we get to typeArguments, aka generics, parse that and bale out
    842                 // of building the name
    843                 if ("typeArguments".equals(namePart.toString())) {
    844                     typeArguments = buildTypeVariables((ParseTree) namePart);
    845                     break;
    846                 }
    847 
    848                 builder.append(namePart.toString());
    849             }
    850 
    851             // get simple and qualified name
    852             simpleTypeName = builder.toString();
    853             StringBuilder qualifiedTypeNameBuilder = new StringBuilder();
    854             boolean isGeneric = resolveQualifiedName(simpleTypeName,
    855                     qualifiedTypeNameBuilder, this);
    856             qualifiedTypeName = qualifiedTypeNameBuilder.toString();
    857 
    858             // if we couldn't figure out the qualified name
    859             // tell us we need to resolve this
    860             // can't add the resolution until the TypeInfo has been created
    861             if ("".equals(qualifiedTypeName)) {
    862                 addResolution = true;
    863             // otherwise, if the name is not a generic, get the class that this Type refers to
    864             } else if (!isGeneric) {
    865                 cl = Caches.obtainClass(qualifiedTypeName);
    866             }
    867         }
    868 
    869         // get the dimensions of this type
    870         dimension = getDimensions(tree);
    871 
    872         TypeInfo type = new TypeInfo(isPrimitive, dimension, simpleTypeName, qualifiedTypeName, cl);
    873         type.setTypeArguments(typeArguments);
    874 
    875         if (addResolution) {
    876             addFutureResolution(type, "class", simpleTypeName, this);
    877         }
    878 
    879         return type;
    880     }
    881 
    882     /**
    883      * Processes the type variables of a class that contains generics.
    884      * @param tree Root of the type parameters.
    885      * @param cls Class in which these type variables are contained.
    886      */
    887     private ArrayList<TypeInfo> buildTypeVariables(ParseTree tree) {
    888         ArrayList<TypeInfo> typeVariables = new ArrayList<TypeInfo>();
    889         ArrayList<TypeInfo> superBounds = new ArrayList<TypeInfo>();
    890         ArrayList<TypeInfo> extendsBounds = new ArrayList<TypeInfo>();
    891 
    892         for (Object o : tree.getChildren()) {
    893             // if we're not dealing with a type, skip
    894             // basically gets rid of commas and lessthan and greater than signs
    895             if (!o.toString().equals("typeParameter") &&
    896                     !o.toString().equals("typeArgument")) {
    897                 continue;
    898             }
    899 
    900             ParseTree typeParameter = (ParseTree) o;
    901 
    902             TypeInfo type;
    903             // if we have a typeArgument and it is not a wildcard
    904             if ("typeArgument".equals(typeParameter.toString()) &&
    905                     !"?".equals(typeParameter.getChild(0).toString())) {
    906                 type = buildType((ParseTree) typeParameter.getChild(0));
    907             } else {
    908                 // otherwise, we have a wildcard or parameter
    909                 // which can be more vague because of generics
    910                 String name = typeParameter.getChild(0).toString();
    911 
    912                 type = new TypeInfo(false, null, name, name, null);
    913                 if ("?".equals(name)) {
    914                     type.setIsWildcard(true);
    915                 } else {
    916                     // add generic
    917                     mClassNames.add(name);
    918                 }
    919             }
    920 
    921             // if we have an extends or super on our type variable
    922             if (typeParameter.getChildCount() > 1) {
    923                 ParseTree value = (ParseTree) typeParameter.getChild(1);
    924 
    925                 if ("extends".equals(value.toString())) {
    926                     value = (ParseTree) typeParameter.getChild(2);
    927 
    928                     // wildcard extends
    929                     if ("type".equals(value.toString())) {
    930                         extendsBounds.add(buildType(value));
    931                     // all other extends
    932                     } else {
    933                         // will have to handle stuff with typeBound - multiple types
    934                         for (Object obj : value.getChildren()) {
    935                             if ("type".equals(obj.toString())) {
    936                                 extendsBounds.add(buildType((ParseTree) obj));
    937                             }
    938                         }
    939                     }
    940                 } else if ("super".equals(value.toString())) {
    941                     superBounds.add(buildType((ParseTree) typeParameter.getChild(2)));
    942                 }
    943             }
    944 
    945             type.setIsTypeVariable(true);
    946             type.setBounds(superBounds, extendsBounds);
    947             typeVariables.add(type);
    948         }
    949 
    950         return typeVariables;
    951     }
    952 
    953     /**
    954      * Builds a MethodInfo for methods, constructors and annotation elements.
    955      * @param tree The tree to parse. methodDeclaration, interfaceMethodDeclaration
    956      * or annotationMethodDeclaration should be the root value.
    957      * @param containingClass the class in which this method exists.
    958      * @param isAnnotation true if the class is an annotation element
    959      * @return the MethodInfo
    960      */
    961     private MethodInfo buildMethod(ParseTree tree, ClassInfo containingClass,
    962             boolean isAnnotation) {
    963         Modifiers modifiers = new Modifiers(this);
    964         CommentAndPosition commentAndPosition = parseCommentAndPosition(tree);
    965 
    966         String name = null;
    967         StringBuilder flatSignature = new StringBuilder().append('(');
    968         ArrayList<TypeInfo> typeParameters = null;
    969         ArrayList<ParameterInfo> parameters = new ArrayList<ParameterInfo>();
    970         ArrayList<ClassInfo> thrownExceptions = new ArrayList<ClassInfo>();
    971         TypeInfo returnType = null;
    972         boolean isAnnotationElement = false;
    973         boolean isVarArg = false;
    974         String kind = "method"; // annotationElement, method, or constructor
    975         AnnotationValueInfo elementValue = null;
    976         ArrayList<Resolution> pendingResolutions = new ArrayList<Resolution>();
    977 
    978         @SuppressWarnings("unchecked")
    979         Iterator<ParseTree> it = (Iterator<ParseTree>) tree.getChildren().iterator();
    980         ParseTree child = it.next();
    981 
    982         modifiers.parseModifiers(child);
    983 
    984         child = it.next();
    985 
    986         // generics stuff
    987         if ("typeParameters".equals(child.toString())) {
    988             typeParameters = buildTypeVariables(child);
    989             child = it.next();
    990         }
    991 
    992         // handle returnType if we're not in a constructor
    993         if ("type".equals(child.toString())) {
    994             returnType = buildType(child);
    995             child = it.next();
    996         } else if ("void".equals(child.toString())) {
    997             returnType = new TypeInfo(true, null, "void", "void", null);
    998             child = it.next();
    999         }
   1000 
   1001         // this is the method name
   1002         name = child.toString();
   1003 
   1004         if (name.equals(containingClass.name())) {
   1005             kind = "constructor";
   1006         }
   1007 
   1008         // probably don't need this check any longer since I unrolled the loop
   1009 //        if (isConstructorOrMethodName(child)) {
   1010 //            // this is the method name
   1011 //            name = child.toString();
   1012 //
   1013 //            if (name.equals(containingClass.name())) {
   1014 //                kind = "constructor";
   1015 //            }
   1016 //        }
   1017 
   1018         child = it.next();
   1019 
   1020         // method parameters
   1021         if ("formalParameters".equals(child.toString())) {
   1022             isVarArg = buildMethodParameters(child, parameters, flatSignature);
   1023         } else {
   1024             child = it.next();
   1025         }
   1026 
   1027         child = it.next();
   1028         flatSignature.append(')');
   1029 
   1030         // handle exception throwing
   1031         if ("throws".equals(child.toString())) {
   1032             child = it.next();
   1033 
   1034             for (Object o : child.getChildren()) {
   1035                 if (",".equals(o.toString())) {
   1036                     continue;
   1037                 }
   1038 
   1039                 // get the name of the exception, resolve it and add it to the list
   1040                 // unless we can't, in which case, add a resolution
   1041                 String exceptionName = buildQualifiedName(((ParseTree) o));
   1042                 StringBuilder exceptionQualifiedName = new StringBuilder();
   1043                 boolean isGeneric = resolveQualifiedName(exceptionName,
   1044                         exceptionQualifiedName, this);
   1045 
   1046                 if ("".equals(exceptionQualifiedName.toString())) {
   1047                     pendingResolutions.add(new Resolution("thrownException", exceptionName, null));
   1048                 } else if (!isGeneric) {
   1049                     thrownExceptions.add(Caches.obtainClass(exceptionQualifiedName.toString()));
   1050                 }
   1051             }
   1052         // handle default values for annotation elements
   1053         } else if ("default".equals(child.toString())) {
   1054             child = it.next();
   1055 
   1056             elementValue = buildElementValue(child, this);
   1057             child = it.next();
   1058         }
   1059 
   1060         if (isAnnotation) {
   1061             kind = "annotationElement";
   1062         }
   1063 
   1064         // Here we set signature, overridden method to null because
   1065         // MethodInfo figures these values out later on
   1066         MethodInfo method =  new MethodInfo(commentAndPosition.getCommentText(), typeParameters,
   1067                 name, null, containingClass, containingClass, modifiers.isPublic(),
   1068                 modifiers.isProtected(), modifiers.isPackagePrivate(),
   1069                 modifiers.isPrivate(), modifiers.isFinal(),
   1070                 modifiers.isStatic(), modifiers.isSynthetic(),
   1071                 modifiers.isAbstract(), modifiers.isSynchronized(),
   1072                 false, isAnnotationElement, kind, flatSignature.toString(),
   1073                 null, returnType, parameters, thrownExceptions,
   1074                 commentAndPosition.getPosition(), modifiers.getAnnotations());
   1075 
   1076         method.setVarargs(isVarArg);
   1077         method.init(elementValue);
   1078 
   1079         for (Resolution r : pendingResolutions) {
   1080             addFutureResolution(method, r.getVariable(), r.getValue(), this);
   1081         }
   1082 
   1083         return method;
   1084     }
   1085 
   1086     /**
   1087      * Build the method parameters.
   1088      * @param tree The tree to parse. formalParamaters should be the root value.
   1089      * @param parameters List to put the method ParamaterInfos into.
   1090      * @param flatSignature Pass in a StringBuilder with "(" in it to build the
   1091      * flatSignature of the MethodInfo
   1092      * @return true if the Method has a VarArgs parameter. false otherwise.
   1093      */
   1094     private boolean buildMethodParameters(ParseTree tree,
   1095                                     ArrayList<ParameterInfo> parameters,
   1096                                     StringBuilder flatSignature) {
   1097         boolean isVarArg = false;
   1098         for (Object obj : tree.getChildren()) {
   1099             ParseTree child = (ParseTree) obj;
   1100 
   1101             if ("formalParameterDecls".equals(child.toString())) {
   1102                 for (Object formalParam : child.getChildren()) {
   1103                     ParseTree param = (ParseTree) formalParam;
   1104                     TypeInfo type = null;
   1105 
   1106                     if (param.getChildCount() == 0) {
   1107                         continue;
   1108                     }
   1109 
   1110                     @SuppressWarnings("unchecked")
   1111                     Iterator<ParseTree> it = (Iterator<ParseTree>) param.getChildren().iterator();
   1112 
   1113                     ParseTree paramPart = it.next();
   1114 
   1115                     if ("variableModifiers".equals(paramPart.toString())) {
   1116                         // TODO - handle variable modifiers - final, etc
   1117                     }
   1118 
   1119                     paramPart = it.next();
   1120 
   1121                     type = buildType(paramPart);
   1122 
   1123                     buildSignatureForType(flatSignature, type);
   1124 
   1125                     if (param != child.getChildren().get(child.getChildCount()-1)) {
   1126                         flatSignature.append(", ");
   1127                     }
   1128 
   1129                     paramPart = it.next();
   1130 
   1131                     if ("...".equals(paramPart.toString())) {
   1132                         isVarArg = true;
   1133                         // thank you varargs for only being the last parameter
   1134                         // you make life so much nicer
   1135                         flatSignature.append("...");
   1136                         paramPart = it.next();
   1137                     }
   1138 
   1139                     String name = paramPart.toString();
   1140 
   1141                     CommentAndPosition commentAndPosition = new CommentAndPosition();
   1142                     commentAndPosition.setPosition(paramPart);
   1143 
   1144                     parameters.add(new ParameterInfo(name, type.qualifiedTypeName(), type,
   1145                             isVarArg, commentAndPosition.getPosition()));
   1146                 }
   1147             }
   1148         }
   1149 
   1150         return isVarArg;
   1151     }
   1152 
   1153     /**
   1154      * Builds a StringBuilder representing the Type, including type arguments.
   1155      * @param builder StringBuilder in which the Type will be placed.
   1156      * @param type the TypeInfo to turn into a String.
   1157      */
   1158     private void buildSignatureForType(StringBuilder builder, TypeInfo type) {
   1159         // simple name
   1160         builder.append(type.simpleTypeName());
   1161 
   1162         // generics
   1163         if (type.typeArguments() != null && !type.typeArguments().isEmpty()) {
   1164             builder.append('<');
   1165             for (TypeInfo inner : type.typeArguments()) {
   1166                 if (inner != type.typeArguments().get(0)) {
   1167                     builder.append(", ");
   1168                 }
   1169 
   1170                 // recurse
   1171                 buildSignatureForType(builder, inner);
   1172             }
   1173             builder.append('>');
   1174         }
   1175     }
   1176 
   1177     /**
   1178      * Builds a ClassInfo for an enum.
   1179      * @param tree The tree to parse. enumDeclaration should be the root value.
   1180      * @param containingClass ClassInfo that contains the enum declaration.
   1181      * null if the enum is a root class.
   1182      * @return the enum as a ClassInfo
   1183      */
   1184     private ClassInfo buildEnum(ParseTree tree, ClassInfo containingClass) {
   1185         CommentAndPosition commentAndPosition = parseCommentAndPosition(tree);
   1186         Modifiers modifiers = new Modifiers(this);
   1187         ClassInfo cls = null;
   1188 
   1189         @SuppressWarnings("unchecked")
   1190         Iterator<ParseTree> it = (Iterator<ParseTree>) tree.getChildren().iterator();
   1191 
   1192         ParseTree child = it.next();
   1193 
   1194         modifiers.parseModifiers(child);
   1195 
   1196         child = it.next();
   1197         child = it.next();
   1198 
   1199         cls = buildClassName(child, containingClass, modifiers,
   1200                 commentAndPosition.getCommentText(),
   1201                 commentAndPosition.getPosition(), ClassType.ENUM);
   1202 
   1203         child = it.next();
   1204 
   1205         // handle implements
   1206         if ("implements".equals(child.toString())) {
   1207             child = it.next();
   1208 
   1209             parseInterfaces(child, cls);
   1210 
   1211             child = it.next();
   1212         }
   1213 
   1214         buildEnumBody(child, cls);
   1215 
   1216         return cls;
   1217     }
   1218 
   1219     /**
   1220      * Parses the body of an enum.
   1221      * @param tree The tree to parse. enumBody should be the root value.
   1222      * @param containingClass ClassInfo to which this enum body pertains.
   1223      */
   1224     private void buildEnumBody(ParseTree tree, ClassInfo containingClass) {
   1225         for (Object o : tree.getChildren()) {
   1226             ParseTree child = (ParseTree) o;
   1227 
   1228             if ("enumConstants".equals(child.toString())) {
   1229                 for (Object o2 : child.getChildren()) {
   1230                     ParseTree tmp = (ParseTree) o2;
   1231 
   1232                     if ("enumConstant".equals(tmp.toString())) {
   1233                         containingClass.addEnumConstant(buildEnumConstant(tmp, containingClass));
   1234                     }
   1235                 }
   1236             } else if ("enumBodyDeclarations".equals(child.toString())) {
   1237                 buildClassBody(child, containingClass);
   1238             }
   1239         }
   1240         return;
   1241     }
   1242 
   1243     /**
   1244      * Builds an enum constant.
   1245      * @param tree The tree to parse. enumConstant should be the root value.
   1246      * @param containingClass ClassInfo to which this enum constant pertains.
   1247      * @return
   1248      */
   1249     private FieldInfo buildEnumConstant(ParseTree tree, ClassInfo containingClass) {
   1250         @SuppressWarnings("unchecked")
   1251         Iterator<ParseTree> it = (Iterator<ParseTree>) tree.getChildren().iterator();
   1252         ParseTree child = it.next();
   1253 
   1254         Modifiers modifiers = new Modifiers(this);
   1255         if ("annotations".equals(child.toString())) {
   1256             modifiers.parseModifiers(child);
   1257             child = it.next();
   1258         }
   1259 
   1260         String name = child.toString();
   1261         CommentAndPosition commentAndPosition = new CommentAndPosition();
   1262         commentAndPosition.setCommentText(child);
   1263         commentAndPosition.setPosition(child);
   1264         Object constantValue = null;
   1265 
   1266         // get constantValue if it exists
   1267         if (it.hasNext()) {
   1268             child = it.next();
   1269 
   1270             // if we have an expressionList
   1271             if (child.getChildCount() == 3) {
   1272                 StringBuilder builder = new StringBuilder();
   1273                 child = (ParseTree) child.getChild(1); // get the middle child
   1274 
   1275                 for (Object o : child.getChildren()) {
   1276                     if ("expression".equals(o.toString())) {
   1277                         builder.append(parseExpression((ParseTree) o));
   1278 
   1279                         if (o != child.getChild(child.getChildCount()-1)) {
   1280                             builder.append(", ");
   1281                         }
   1282                     }
   1283                 }
   1284 
   1285                 constantValue = builder.toString();
   1286             }
   1287         }
   1288 
   1289         return new FieldInfo(name, containingClass, containingClass, containingClass.isPublic(),
   1290         containingClass.isProtected(), containingClass.isPackagePrivate(),
   1291         containingClass.isPrivate(), containingClass.isFinal(),
   1292         containingClass.isStatic(), false, false, false,
   1293         containingClass.type(), commentAndPosition.getCommentText(),
   1294         constantValue, commentAndPosition.getPosition(),
   1295         modifiers.getAnnotations());
   1296     }
   1297 
   1298     /**
   1299      * Builds a ClassInfo for an interface.
   1300      * @param tree The tree to parse. normalInterfaceDeclaration should be the root value.
   1301      * @param containingClass ClassInfo that contains the interface declaration.
   1302      * null if the interface is a root class.
   1303      * @return a ClassInfo representing the interface.
   1304      */
   1305     private ClassInfo buildInterface(ParseTree tree, ClassInfo containingClass) {
   1306         @SuppressWarnings("unchecked")
   1307         Iterator<ParseTree> it = (Iterator<ParseTree>) tree.getChildren().iterator();
   1308         ParseTree child = it.next();
   1309 
   1310         // parse modifiers and get comment and position
   1311         Modifiers modifiers = new Modifiers(this);
   1312         modifiers.parseModifiers(child);
   1313         CommentAndPosition commentAndPosition = parseCommentAndPosition(tree);
   1314 
   1315         it.next();
   1316         child = it.next();
   1317 
   1318         // get class name
   1319         ClassInfo iface = buildClassName(child, containingClass, modifiers,
   1320                 commentAndPosition.getCommentText(),
   1321                 commentAndPosition.getPosition(), ClassType.INTERFACE);
   1322 
   1323         child = it.next();
   1324 
   1325         // parse generics if they exist
   1326         if ("typeParameters".equals(child.toString())) {
   1327             iface.type().setTypeArguments(buildTypeVariables(child));
   1328             child = it.next();
   1329         }
   1330 
   1331         // parse interfaces implemented by this interface
   1332         if ("extends".equals(child.toString())) {
   1333             child = it.next();
   1334 
   1335             parseInterfaces(child, iface);
   1336 
   1337             child = it.next();
   1338         }
   1339 
   1340         // finally, build the body of the interface
   1341         buildInterfaceBody(child, iface);
   1342 
   1343         return iface;
   1344     }
   1345 
   1346     /**
   1347      * Parses the body of the interface, adding it to iface.
   1348      * @param tree The tree to parse. interfaceBody should be the root value.
   1349      * @param iface ClassInfo that will contain all of the interface body.
   1350      */
   1351     private void buildInterfaceBody(ParseTree tree, ClassInfo iface) {
   1352         for (Object o : tree.getChildren()) {
   1353             if (!o.toString().equals("interfaceBodyDeclaration")) {
   1354                 continue;
   1355             }
   1356 
   1357             ParseTree child = (ParseTree) ((ParseTree) o).getChild(0);
   1358 
   1359             if (";".equals(child.toString())) {
   1360                 continue;
   1361             }
   1362 
   1363             // field
   1364             if ("interfaceFieldDeclaration".equals(child.toString())) {
   1365                 for (FieldInfo f : buildFields(child, iface)) {
   1366                     iface.addField(f);
   1367                 }
   1368             // method
   1369             } else if ("interfaceMethodDeclaration".equals(child.toString())) {
   1370                 iface.addMethod(buildMethod(child, iface, false));
   1371             // inner class
   1372             } else if ("normalClassDeclaration".equals(child.getChild(0).toString())) {
   1373                 iface.addInnerClass(buildClass((ParseTree) child.getChild(0), iface));
   1374             // inner enum
   1375             } else if ("enumDeclaration".equals(child.getChild(0).toString())) {
   1376                 iface.addInnerClass(buildEnum((ParseTree) child.getChild(0), iface));
   1377             // inner interface
   1378             } else if ("normalInterfaceDeclaration".equals(child.getChild(0).toString())) {
   1379                 iface.addInnerClass(buildInterface((ParseTree) child.getChild(0), iface));
   1380             // inner annotation
   1381             } else if ("annotationTypeDeclaration".equals(child.getChild(0).toString())) {
   1382                 iface.addInnerClass(buildAnnotationDeclaration(
   1383                         (ParseTree) child.getChild(0), iface));
   1384             }
   1385         }
   1386     }
   1387 
   1388     /**
   1389      * Builds a ClassInfo of an annotation declaration.
   1390      * @param tree The tree to parse. annotationTypeDeclaration should be the root value.
   1391      * @param containingClass The class that contains this annotation.
   1392      * null if this is a root annotation.
   1393      * @return the ClassInfo of the annotation declaration.
   1394      */
   1395     private ClassInfo buildAnnotationDeclaration(ParseTree tree, ClassInfo containingClass) {
   1396         @SuppressWarnings("unchecked")
   1397         Iterator<ParseTree> it = (Iterator<ParseTree>) tree.getChildren().iterator();
   1398         ParseTree child = it.next();
   1399 
   1400         // get comment and position
   1401         CommentAndPosition commentAndPosition = parseCommentAndPosition(tree);
   1402 
   1403         // modifiers
   1404         Modifiers modifiers = new Modifiers(this);
   1405         modifiers.parseModifiers(child);
   1406 
   1407         // three calls to next to skip over @, interface and then
   1408         // make child = the name of this annotation
   1409         it.next();
   1410         it.next();
   1411         child = it.next();
   1412 
   1413         // build class name and initialize the class
   1414         ClassInfo annotation = buildClassName(child, containingClass, modifiers,
   1415                 commentAndPosition.getCommentText(),
   1416                 commentAndPosition.getPosition(), ClassType.INTERFACE);
   1417 
   1418         child = it.next();
   1419 
   1420         // build annotation body
   1421         buildAnnotationBody(child, annotation);
   1422 
   1423         return annotation;
   1424     }
   1425 
   1426     /**
   1427      * Parses the body of the annotation declaration.
   1428      * @param tree The tree to parse. annotationTypeBody should be the root value.
   1429      * @param annotation the Classinfo in which the annotation elements should be added.
   1430      */
   1431     private void buildAnnotationBody(ParseTree tree, ClassInfo annotation) {
   1432         for (Object o : tree.getChildren()) {
   1433             if (!"annotationTypeElementDeclaration".equals(o.toString())) {
   1434                 continue;
   1435             }
   1436 
   1437             ParseTree child = (ParseTree) ((ParseTree) o).getChild(0);
   1438 
   1439             // annotation fields
   1440             if ("interfaceFieldDeclaration".equals(child.toString())) {
   1441                 for (FieldInfo f : buildFields(child, annotation)) {
   1442                     annotation.addField(f);
   1443                 }
   1444             // annotation methods
   1445             } else if ("annotationMethodDeclaration".equals(child.toString())) {
   1446                 annotation.addAnnotationElement(buildMethod(child, annotation, true));
   1447             // inner class
   1448             } else if ("normalClassDeclaration".equals(child.toString())) {
   1449                 annotation.addInnerClass(buildClass((ParseTree) child, annotation));
   1450             // enum
   1451             } else if ("enumDeclaration".equals(child.toString())) {
   1452                 annotation.addInnerClass(buildEnum((ParseTree) child, annotation));
   1453             // inner interface
   1454             } else if ("normalInterfaceDeclaration".equals(child.toString())) {
   1455                 annotation.addInnerClass(buildInterface((ParseTree) child, annotation));
   1456             // inner annotation
   1457             } else if ("annotationTypeDeclaration".equals(child.toString())) {
   1458                 annotation.addInnerClass(buildAnnotationDeclaration(
   1459                         (ParseTree) child, annotation));
   1460             }
   1461         }
   1462     }
   1463 
   1464     /**
   1465      * Build an annotation instance.
   1466      * @param tree The tree to parse. annotation should be the root value.
   1467      * @param builder InfoBuilder of this file.
   1468      * @return The AnnotationInstanceInfo being parsed.
   1469      */
   1470     private static AnnotationInstanceInfo buildAnnotationInstance(ParseTree tree,
   1471             InfoBuilder builder) {
   1472         @SuppressWarnings("unchecked")
   1473         Iterator<ParseTree> it = (Iterator<ParseTree>) tree.getChildren().iterator();
   1474 
   1475         AnnotationInstanceInfo annotationInstance = new AnnotationInstanceInfo();
   1476 
   1477         it.next();
   1478 
   1479         // parse the name, get its full version, and then get the ClassInfo of it, if possible.
   1480         String name = InfoBuilder.buildQualifiedName(it.next());
   1481         StringBuilder qualifiedNameBuilder = new StringBuilder();
   1482         resolveQualifiedName(name, qualifiedNameBuilder, builder);
   1483 
   1484         if ("".equals(qualifiedNameBuilder.toString())) {
   1485             addFutureResolution(annotationInstance, "annotationTypeName", name, builder);
   1486             annotationInstance.setSimpleAnnotationName(name); // TODO - remove once we've completed the parser
   1487         } else { // can't have generics here so we won't do a test
   1488             annotationInstance.setClass(Caches.obtainClass(qualifiedNameBuilder.toString()));
   1489         }
   1490 
   1491         // at this point, the annotation is either finished or we have more work to do
   1492         if (!it.hasNext()) {
   1493             return annotationInstance;
   1494         }
   1495 
   1496         it.next();
   1497         ParseTree child = it.next();
   1498 
   1499         // parse elementValue pairs
   1500         if ("elementValuePairs".equals(child.toString())) {
   1501             for (Object o : child.getChildren()) {
   1502                 if (!"elementValuePair".equals(o.toString())) {
   1503                     continue;
   1504                 }
   1505 
   1506                 ParseTree inner = (ParseTree) o;
   1507                 MethodInfo element = null;
   1508                 String methodName = inner.getChild(0).toString();
   1509 
   1510                 // try and look up the MethodInfo for this annotation, if possible
   1511                 if (annotationInstance.type() != null) {
   1512                     for (MethodInfo m : annotationInstance.type().annotationElements()) {
   1513                         if (methodName.equals(m.name()) ||
   1514                                 annotationInstance.type().annotationElements().size() == 1) {
   1515                             element = m;
   1516                             break;
   1517                         }
   1518                     }
   1519                 }
   1520 
   1521                 // go to elementValue
   1522                 AnnotationValueInfo info = buildElementValue(
   1523                         (ParseTree) inner.getChild(2), builder);
   1524 
   1525                 if (element == null) {
   1526                     addFutureResolution(info, "element", methodName, builder);
   1527                     info.setAnnotationInstanceName(name);
   1528                 } else {
   1529                     info.setElement(element);
   1530                 }
   1531 
   1532                 annotationInstance.addElementValue(info);
   1533             }
   1534         // parse element value
   1535         } else if ("elementValue".equals(child.toString())) {
   1536             annotationInstance.addElementValue(buildElementValue(child, builder));
   1537         }
   1538 
   1539         return annotationInstance;
   1540     }
   1541 
   1542     /**
   1543      * Builds the value of the annotation element.
   1544      * @param tree The tree to parse. elementValue should be the root value.
   1545      * @param builder InfoBuilder of this file.
   1546      * @return AnnotationValueInfo representing the elementValue.
   1547      */
   1548     private static AnnotationValueInfo buildElementValue(ParseTree tree, InfoBuilder builder) {
   1549         AnnotationValueInfo elementValue = new AnnotationValueInfo();
   1550         Object value = null;
   1551 
   1552         // parse some stuff
   1553         String str = tree.getChild(0).toString();
   1554         if ("conditionalExpression".equals(str)) {
   1555             value = parseExpression((ParseTree) tree.getChild(0));
   1556         } else if ("annotation".equals(str)) {
   1557             value = InfoBuilder.buildAnnotationInstance((ParseTree) tree.getChild(0), builder);
   1558         } else if ("elementValueArrayInitializer".equals(str)) {
   1559             ParseTree child = (ParseTree) tree.getChild(0);
   1560             ArrayList<AnnotationValueInfo> values = new ArrayList<AnnotationValueInfo>();
   1561             for (Object o : child.getChildren()) {
   1562                 if ("elementValue".equals(o.toString())) {
   1563                     values.add(buildElementValue((ParseTree) o, builder));
   1564                 }
   1565             }
   1566 
   1567             value = values;
   1568         }
   1569 
   1570         elementValue.init(value);
   1571 
   1572         return elementValue;
   1573     }
   1574 
   1575     /**
   1576      * Get the dimensions of the type, as a String.
   1577      * @param tree The tree to parse. type should be the root value.
   1578      * @return A String of the dimensions of the type.
   1579      */
   1580     private String getDimensions(ParseTree tree) {
   1581         // we only have dimensions if the count is not 1
   1582         if (tree.getChildCount() == 1) {
   1583             return null;
   1584         }
   1585 
   1586         StringBuilder builder = new StringBuilder();
   1587 
   1588         for (int i = 1; i < tree.getChildCount(); i++) {
   1589             builder.append(((ParseTree) tree.getChild(i)).toString());
   1590         }
   1591 
   1592         return builder.toString();
   1593     }
   1594 
   1595     /**
   1596      * When we have data that we can't yet parse, save it for later.
   1597      * @param resolvable Resolvable to which the data refers.
   1598      * @param variable Variable in the document to which the data refers;
   1599      * @param value Value for the variable
   1600      * @param builder The InfoBuilder of this file
   1601      */
   1602     private static void addFutureResolution(Resolvable resolvable, String variable,
   1603             String value, InfoBuilder builder) {
   1604         resolvable.addResolution(new Resolution(variable, value, builder));
   1605 
   1606         Caches.addResolvableToCache(resolvable);
   1607     }
   1608 
   1609     /**
   1610      * Turns a short name of a class into the qualified name of a class.
   1611      * StringBuilder will contain an empty string if not found.
   1612      * @param name the abbreviated name of the class
   1613      * @param qualifiedClassName the qualified name that will be set if found.
   1614      * Unchanged if not found.
   1615      * @param builder InfoBuilder with all of the file specific information necessary
   1616      * to properly resolve the name.
   1617      * @return a boolean is returned that will be true if the type is a generic. false otherwise.
   1618      */
   1619     public static boolean resolveQualifiedName(String name,
   1620                                                 StringBuilder qualifiedClassName,
   1621                                                 InfoBuilder builder) {
   1622         // steps to figure out a class's real name
   1623         // check class(es) in this file
   1624 
   1625         // trying something out. let's see how this works
   1626         if (name.indexOf('.') != -1) {
   1627             qualifiedClassName.append(name);
   1628             return false;
   1629         }
   1630 
   1631         // TODO - search since we're now a HashSet
   1632         for (String className : builder.getClassNames()) {
   1633             int beginIndex = className.lastIndexOf(".") + 1;
   1634 
   1635             if (className.substring(beginIndex).equals(name)) {
   1636                 qualifiedClassName.append(className);
   1637                 return qualifiedClassName.toString().equals(name);
   1638             }
   1639         }
   1640 
   1641         // check package
   1642         ClassInfo potentialClass = builder.getPackage().getClass(name);
   1643 
   1644         if (potentialClass != null) {
   1645             qualifiedClassName.append(potentialClass.qualifiedName());
   1646             return qualifiedClassName.toString().equals(name);
   1647         }
   1648 
   1649         potentialClass = null;
   1650 
   1651         String potentialName = null;
   1652         // check superclass and interfaces for type
   1653         if (builder.getRootClass() != null) {
   1654             potentialName = resolveQualifiedNameInInheritedClass(name, builder.getRootClass(),
   1655                     builder.getRootClass().containingPackage().name());
   1656         }
   1657 
   1658         if (potentialName != null) {
   1659             qualifiedClassName.append(potentialName);
   1660             return false;
   1661         }
   1662 
   1663 
   1664         // check class imports - ie, java.lang.String;
   1665         ArrayList<String> packagesToCheck = new ArrayList<String>();
   1666         for (String imp : builder.getImports()) {
   1667             // +1 to get rid of off by 1 error
   1668             String endOfName = imp.substring(imp.lastIndexOf('.') + 1);
   1669             if (endOfName.equals(name) || (name.indexOf('.') != -1 &&
   1670                                            endOfName.equals(
   1671                                                    name.substring(0, name.lastIndexOf('.'))))) {
   1672                 qualifiedClassName.append(imp);
   1673                 return qualifiedClassName.toString().equals(name);
   1674             } else if (endOfName.equals("*")) {
   1675                 // add package to check
   1676                 packagesToCheck.add(imp.substring(0, imp.lastIndexOf('.')));
   1677             } else {
   1678                 // check inner classes
   1679                 ClassInfo cl = Caches.obtainClass(imp);
   1680                 String possibleName = resolveQualifiedInnerName(cl.qualifiedName() + "." + name,
   1681                         cl);
   1682                 if (possibleName != null) {
   1683                     qualifiedClassName.append(possibleName);
   1684                     return false;
   1685                 }
   1686             }
   1687         }
   1688 
   1689         // check package imports - ie, java.lang.*;
   1690         for (String packageName : packagesToCheck) {
   1691             PackageInfo pkg = Caches.obtainPackage(packageName);
   1692 
   1693             ClassInfo cls = pkg.getClass(name);
   1694 
   1695             if (cls != null && name.equals(cls.name())) {
   1696                 qualifiedClassName.append(cls.qualifiedName());
   1697                 return qualifiedClassName.toString().equals(name);
   1698             }
   1699         }
   1700         //     including import's inner classes...
   1701         // check package of imports...
   1702 
   1703         // TODO - remove
   1704         // FROM THE JAVADOC VERSION OF THIS FUNCTION
   1705         // Find the specified class or interface within the context of this class doc.
   1706         // Search order: 1) qualified name, 2) nested in this class or interface,
   1707         // 3) in this package, 4) in the class imports, 5) in the package imports.
   1708         // Return the ClassDoc if found, null if not found.
   1709 
   1710         return false;
   1711     }
   1712 
   1713     private static String resolveQualifiedNameInInheritedClass(String name, ClassInfo cl,
   1714             String originalPackage) {
   1715         ArrayList<ClassInfo> classesToCheck = new ArrayList<ClassInfo>();
   1716         if (cl != null) {
   1717             // if we're in a new package only, check it
   1718             if (cl.containingPackage() != null &&
   1719                     !originalPackage.equals(cl.containingPackage().name())) {
   1720                 // check for new class
   1721                 ClassInfo cls = cl.containingPackage().getClass(name);
   1722 
   1723                 if (cls != null && name.equals(cls.name())) {
   1724                     return cls.name();
   1725                 }
   1726             }
   1727 
   1728             if (cl.realSuperclass() != null) {
   1729                 classesToCheck.add(cl.realSuperclass());
   1730             }
   1731 
   1732             if (cl.realInterfaces() != null) {
   1733                 for (ClassInfo iface : cl.realInterfaces()) {
   1734                     classesToCheck.add(iface);
   1735                 }
   1736             }
   1737 
   1738             for (ClassInfo cls : classesToCheck) {
   1739                 String potential = resolveQualifiedNameInInheritedClass(name, cls, originalPackage);
   1740 
   1741                 if (potential != null) {
   1742                     return potential;
   1743                 }
   1744             }
   1745         }
   1746         return null;
   1747     }
   1748 
   1749     private static String resolveQualifiedInnerName(String possibleQualifiedName, ClassInfo cl) {
   1750         if (cl.innerClasses() == null) {
   1751             return null;
   1752         }
   1753 
   1754         for (ClassInfo inner : cl.innerClasses()) {
   1755             if (possibleQualifiedName.equals(inner.qualifiedName())) {
   1756                 return possibleQualifiedName;
   1757             }
   1758 
   1759             String name = resolveQualifiedInnerName(possibleQualifiedName + "." + inner.name(),
   1760                     inner);
   1761 
   1762             if (name != null) {
   1763                 return name;
   1764             }
   1765         }
   1766 
   1767         return null;
   1768     }
   1769 
   1770     /**
   1771      * Parses the tree, looking for the comment and position.
   1772      * @param tree The tree to parse.
   1773      * @return a CommentAndPosition object containing the comment and position of the element.
   1774      */
   1775     private CommentAndPosition parseCommentAndPosition(ParseTree tree) {
   1776         Tree child = tree.getChild(0).getChild(0);
   1777 
   1778         // three options (modifiers with annotations, modifiers w/o annotations, no modifiers)
   1779         // if there are no modifiers, use tree.getChild(1)
   1780         // otherwise, dive as deep as possible into modifiers to get to the comment and position.
   1781         child = ("<epsilon>".equals(child.toString())) ? tree.getChild(1) : child;
   1782 
   1783         while (child.getChildCount() > 0) {
   1784             child = child.getChild(0);
   1785         }
   1786 
   1787         CommentAndPosition cAndP = new CommentAndPosition();
   1788         cAndP.setCommentText((ParseTree) child);
   1789         cAndP.setPosition((ParseTree) child);
   1790         return cAndP;
   1791     }
   1792 
   1793     /**
   1794      * Private class to facilitate passing the comment and position out of a function.
   1795      */
   1796     private class CommentAndPosition {
   1797         public String getCommentText() {
   1798             return mCommentText;
   1799         }
   1800 
   1801         /**
   1802          * Parses the tree to get the commentText and set that value.
   1803          * @param tree The tree to parse. Should be pointing to the node containing the comment.
   1804          */
   1805         public void setCommentText(ParseTree tree) {
   1806             if (tree.hiddenTokens != null && !tree.hiddenTokens.isEmpty()) {
   1807                 mCommentText = ((CommonToken) tree.hiddenTokens.get(0)).getText();
   1808 
   1809                 if (mCommentText != null) {
   1810                     return;
   1811                 }
   1812             }
   1813 
   1814             mCommentText = "";
   1815         }
   1816 
   1817         public SourcePositionInfo getPosition() {
   1818             return mPosition;
   1819         }
   1820 
   1821         /**
   1822          * Parses the tree to get the SourcePositionInfo of the node.
   1823          * @param tree The tree to parse. Should be pointing to the node containing the position.
   1824          */
   1825         public void setPosition(ParseTree tree) {
   1826           CommonToken token = (CommonToken) tree.payload;
   1827 
   1828           int line = token.getLine();
   1829           int column = token.getCharPositionInLine();
   1830           String fileName = ((ANTLRFileStream) token.getInputStream()).getSourceName();
   1831 
   1832           mPosition = new SourcePositionInfo(fileName, line, column);
   1833         }
   1834 
   1835         private String mCommentText;
   1836         private SourcePositionInfo mPosition;
   1837     }
   1838 
   1839     /**
   1840      * Private class to handle all the possible modifiers to a class/interface/field/anything else.
   1841      */
   1842     private class Modifiers {
   1843         private boolean mIsPublic = false;
   1844         private boolean mIsProtected = false;
   1845         private boolean mIsPackagePrivate = true;
   1846         private boolean mIsPrivate = false;
   1847         private boolean mIsStatic = false;
   1848         private boolean mIsAbstract = false;
   1849         private boolean mIsFinal = false;
   1850         private boolean mIsTransient = false;
   1851         private boolean mIsVolatile = false;
   1852         private boolean mIsSynthetic = false;
   1853         private boolean mIsSynchronized = false;
   1854         private boolean mIsStrictfp = false;
   1855         private InfoBuilder mBuilder;
   1856         private ArrayList<AnnotationInstanceInfo> mAnnotations;
   1857 
   1858         public Modifiers(InfoBuilder builder) {
   1859             mAnnotations = new ArrayList<AnnotationInstanceInfo>();
   1860             mBuilder = builder;
   1861         }
   1862 
   1863         /**
   1864          * Parses all of the modifiers of any declaration, including annotations.
   1865          * @param tree
   1866          */
   1867         public void parseModifiers(ParseTree tree) {
   1868             for (Object child : tree.getChildren()) {
   1869                 String modifier = child.toString();
   1870 
   1871                 if ("public".equals(modifier)) {
   1872                     mIsPublic = true;
   1873                     mIsPackagePrivate = false;
   1874                 } else if ("protected".equals(modifier)) {
   1875                     mIsProtected = true;
   1876                     mIsPackagePrivate = false;
   1877                 } else if ("private".equals(modifier)) {
   1878                     mIsPrivate = true;
   1879                     mIsPackagePrivate = false;
   1880                 } else if ("static".equals(modifier)) {
   1881                     mIsStatic = true;
   1882                 } else if ("abstract".equals(modifier)) {
   1883                     mIsAbstract = true;
   1884                 } else if ("final".equals(modifier)) {
   1885                     mIsFinal = true;
   1886                 } else if ("transient".equals(modifier)) {
   1887                     mIsTransient = true;
   1888                 } else if ("volatile".equals(modifier)) {
   1889                     mIsVolatile = true;
   1890                 } else if ("synthetic".equals(modifier)) {
   1891                     mIsSynthetic = true;
   1892                 } else if ("synchronized".equals(modifier)) {
   1893                     mIsSynchronized = true;
   1894                 }  else if ("strictfp".equals(modifier)) {
   1895                     mIsStrictfp = true;
   1896                 } else if ("annotation".equals(modifier)) {
   1897                     mAnnotations.add(buildAnnotationInstance((ParseTree) child, mBuilder));
   1898                 }
   1899             }
   1900         }
   1901 
   1902         public boolean isPublic() {
   1903             return mIsPublic;
   1904         }
   1905 
   1906         public boolean isProtected() {
   1907             return mIsProtected;
   1908         }
   1909 
   1910         public boolean isPackagePrivate() {
   1911             return mIsPackagePrivate;
   1912         }
   1913 
   1914         public boolean isPrivate() {
   1915             return mIsPrivate;
   1916         }
   1917 
   1918         public boolean isStatic() {
   1919             return mIsStatic;
   1920         }
   1921 
   1922         public boolean isAbstract() {
   1923             return mIsAbstract;
   1924         }
   1925 
   1926         public boolean isFinal() {
   1927             return mIsFinal;
   1928         }
   1929 
   1930         public boolean isTransient() {
   1931             return mIsTransient;
   1932         }
   1933 
   1934         public boolean isVolatile() {
   1935             return mIsVolatile;
   1936         }
   1937 
   1938         public boolean isSynthetic() {
   1939             return mIsSynthetic;
   1940         }
   1941 
   1942         public boolean isSynchronized() {
   1943             return mIsSynchronized;
   1944         }
   1945 
   1946         @SuppressWarnings("unused")
   1947         public boolean isStrictfp() {
   1948             return mIsStrictfp;
   1949         }
   1950 
   1951         public ArrayList<AnnotationInstanceInfo> getAnnotations() {
   1952             return mAnnotations;
   1953         }
   1954     };
   1955 
   1956 
   1957     /**
   1958      * Singleton class to store all of the global data amongst every InfoBuilder.
   1959      */
   1960     public static class Caches {
   1961         private static HashMap<String, PackageInfo> mPackages
   1962                                         = new HashMap<String, PackageInfo>();
   1963         private static HashMap<String, ClassInfo> mClasses
   1964                                         = new HashMap<String, ClassInfo>();
   1965         private static HashSet<Resolvable> mInfosToResolve
   1966                                         = new HashSet<Resolvable>();
   1967 
   1968         public static PackageInfo obtainPackage(String packageName) {
   1969             PackageInfo pkg = mPackages.get(packageName);
   1970 
   1971             if (pkg == null) {
   1972                 pkg = new PackageInfo(packageName);
   1973                 mPackages.put(packageName, pkg);
   1974             }
   1975 
   1976             return pkg;
   1977         }
   1978 
   1979         /**
   1980          * Gets the ClassInfo from the master list or creates a new one if it does not exist.
   1981          * @param qualifiedClassName Qualified name of the ClassInfo to obtain.
   1982          * @return the ClassInfo
   1983          */
   1984         public static ClassInfo obtainClass(String qualifiedClassName) {
   1985             ClassInfo cls = mClasses.get(qualifiedClassName);
   1986 
   1987             if (cls == null) {
   1988                 cls = new ClassInfo(qualifiedClassName);
   1989                 mClasses.put(cls.qualifiedName(), cls);
   1990             }
   1991 
   1992             return cls;
   1993         }
   1994 
   1995         /**
   1996          * Gets the ClassInfo from the master list or returns null if it does not exist.
   1997          * @param qualifiedClassName Qualified name of the ClassInfo to obtain.
   1998          * @return the ClassInfo or null, if the ClassInfo does not exist.
   1999          */
   2000         public static ClassInfo getClass(String qualifiedClassName) {
   2001             return mClasses.get(qualifiedClassName);
   2002         }
   2003 
   2004         public static void addResolvableToCache(Resolvable resolvable) {
   2005             mInfosToResolve.add(resolvable);
   2006         }
   2007 
   2008         public static void printResolutions() {
   2009             if (mInfosToResolve.isEmpty()) {
   2010                 System.out.println("We've resolved everything.");
   2011                 return;
   2012             }
   2013 
   2014             for (Resolvable r : mInfosToResolve) {
   2015                 r.printResolutions();
   2016                 System.out.println();
   2017             }
   2018         }
   2019 
   2020         public static void resolve() {
   2021             HashSet<Resolvable> resolveList = mInfosToResolve;
   2022             mInfosToResolve = new HashSet<Resolvable>();
   2023 
   2024             for (Resolvable r : resolveList) {
   2025                 // if we could not resolve everything in this class
   2026                 if (!r.resolveResolutions()) {
   2027                     mInfosToResolve.add(r);
   2028                 }
   2029 
   2030                 System.out.println();
   2031             }
   2032         }
   2033     }
   2034 
   2035     public PackageInfo getPackage() {
   2036         return mPackage;
   2037     }
   2038 
   2039     public ArrayList<String> getImports() {
   2040         return mImports;
   2041     }
   2042 
   2043     public HashSet<String> getClassNames() {
   2044         return mClassNames;
   2045     }
   2046 
   2047     public ClassInfo getRootClass() {
   2048         return mRootClass;
   2049     }
   2050 }
   2051