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