Modified old doclava to use LinkedLists instead of arrays

Functioning java parsing as well. Needs to be wired up into doclava proper.

Change-Id: Ifa38ca7312e6c23a8f663a9d0638afb9a29bf19d
diff --git a/src/com/google/doclava/InfoBuilder.java b/src/com/google/doclava/InfoBuilder.java
new file mode 100644
index 0000000..8c1b30e
--- /dev/null
+++ b/src/com/google/doclava/InfoBuilder.java
@@ -0,0 +1,1777 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.doclava;
+
+import org.antlr.runtime.ANTLRFileStream;
+import org.antlr.runtime.CommonToken;
+import org.antlr.runtime.tree.ParseTree;
+import org.antlr.runtime.tree.Tree;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.ArrayList;
+
+/**
+ * InfoBuilder parses an individual file and builds Doclava
+ * objects out of the data within the file. This data is
+ * stored within a global cache for later use.
+ */
+public class InfoBuilder {
+    // TODO - remove the unnecessary stuff here
+    private PackageInfo mPackage;
+    private ArrayList<String> mImports;
+    private ArrayList<ClassInfo> mClasses;
+    private HashSet<String> mClassNames;
+
+    public InfoBuilder() {
+        mImports = new ArrayList<String>();
+        mImports.add("java.lang.*"); // should allow us to resolve this properly, eventually
+                                     // alternatively, we could add everything from java.lang.*
+                                     // but that would probably be too brittle
+        mClasses = new ArrayList<ClassInfo>();
+        mClassNames = new HashSet<String>();
+    }
+
+    // All of the print functions exist for debugging alone.
+    public void printStuff() {
+        System.out.println(mPackage.name() + "\n");
+
+        printList(mImports);
+
+        printClassInfoList(mClasses);
+
+        Caches.printResolutions();
+    }
+
+    private void printList(ArrayList<String> list) {
+        for (String value : list) {
+            System.out.println(value);
+        }
+
+        System.out.println();
+    }
+
+    private void printClassInfoList(ArrayList<ClassInfo> list) {
+        for (ClassInfo value : list) {
+            System.out.print("Class: " + value.toString());
+
+            printTypeVariables(value.type());
+
+            System.out.println();
+
+            System.out.println(value.comment().mText);
+
+            if (!value.annotations().isEmpty()) {
+                System.out.println("\nAnnotations:");
+                printAnnotations(value.annotations());
+            }
+
+            if (value.superclass() != null) {
+                System.out.print("Superclass: " + value.superclass().qualifiedName());
+                printTypeVariables(value.superclassType());
+                System.out.println();
+            }
+
+            if (!value.realInterfaces().isEmpty()) {
+                System.out.println("\nInterfaces Implemented:");
+                Iterator<TypeInfo> it = value.realInterfaceTypes().iterator();
+                for (ClassInfo cls : value.realInterfaces()) {
+                    TypeInfo outerType = it.next();
+                    if (cls == null) {
+                        System.out.print(outerType.simpleTypeName());
+                    } else {
+                        System.out.print(cls.qualifiedName());
+                    }
+
+                    printTypeVariables(outerType);
+
+                    System.out.println();
+                }
+
+                System.out.println();
+            }
+
+            if (!value.allSelfFields().isEmpty()) {
+                System.out.println("\nFields:");
+                for (FieldInfo f : value.allSelfFields()) {
+                    if (f != value.allSelfFields().get(0)) {
+                        System.out.println();
+                    }
+                    System.out.println(f.comment().mText);
+
+                    printAnnotations(f.annotations());
+                    printTypeName(f.type());
+
+                    System.out.print(" " + f.name());
+
+                    if (f.constantValue() != null) {
+                        System.out.println(": " + f.constantValue());
+                    } else if (f.hasValue()) {
+                        System.out.println(": has some value");
+                    } else {
+                        System.out.println();
+                    }
+                }
+
+                System.out.println();
+            }
+
+            if (value.enumConstants() != null && !value.enumConstants().isEmpty()) {
+                System.out.println("\nEnum Constants:");
+                for (FieldInfo f : value.enumConstants()) {
+                    if (f != value.enumConstants().get(0)) {
+                        System.out.println();
+                    }
+                    System.out.println(f.comment().mText);
+                    printAnnotations(f.annotations());
+                    System.out.print(f.type().simpleTypeName() + " " + f.name());
+
+                    if (f.constantValue() != null) {
+                        System.out.println(": " + f.constantValue());
+                    } else {
+                        System.out.println();
+                    }
+                }
+
+                System.out.println();
+            }
+
+            if (!value.allConstructors().isEmpty()) {
+                System.out.println("\nConstructors:");
+                for (MethodInfo m : value.allConstructors()) {
+                    if (m != value.allConstructors().get(0)) {
+                        System.out.println();
+                    }
+
+                    System.out.println(m.comment().mText);
+
+                    printAnnotations(m.annotations());
+                    if (m.getTypeParameters() != null) {
+                        printTypeVariableList(m.getTypeParameters());
+                        System.out.print(" ");
+                    }
+
+                    System.out.println(m.name() + m.flatSignature());
+                }
+
+                System.out.println();
+            }
+
+            if (!value.allSelfMethods().isEmpty()) {
+                System.out.println("\nMethods:");
+                for (MethodInfo m : value.allSelfMethods()) {
+                    if (m != value.allSelfMethods().get(0)) {
+                        System.out.println();
+                    }
+
+                    System.out.println(m.comment().mText);
+                    printAnnotations(m.annotations());
+                    if (m.getTypeParameters() != null) {
+                        printTypeVariableList(m.getTypeParameters());
+                        System.out.print(" ");
+                    }
+
+                    printTypeName(m.returnType());
+
+                    System.out.print(" " + m.name() + m.flatSignature());
+
+                    if (m.thrownExceptions() != null && !m.thrownExceptions().isEmpty()) {
+                        System.out.print(" throws ");
+                        for (ClassInfo c : m.thrownExceptions()) {
+                            if (c != m.thrownExceptions().get(0)) {
+                                System.out.print(", ");
+                            }
+
+                            System.out.print(c.name());
+                        }
+                    }
+
+                    System.out.println();
+                }
+
+                System.out.println();
+            }
+
+            if (!value.annotationElements().isEmpty()) {
+                System.out.println("\nAnnotation Elements:");
+
+                for (MethodInfo m : value.annotationElements()) {
+                    if (m != value.annotationElements().get(0)) {
+                        System.out.println();
+                    }
+
+                    System.out.println(m.comment().mText);
+                    printAnnotations(m.annotations());
+                    printTypeName(m.returnType());
+
+                    System.out.print(" " + m.name() + m.flatSignature());
+
+                    if (m.defaultAnnotationElementValue() != null) {
+                        System.out.print(" default " +
+                                m.defaultAnnotationElementValue().valueString());
+                    }
+
+                    System.out.println();
+                }
+
+                System.out.println();
+            }
+        }
+    }
+
+    private void printTypeName(TypeInfo type) {
+        System.out.print(type.simpleTypeName());
+
+        if (type.extendsBounds() != null && !type.extendsBounds().isEmpty()) {
+            System.out.print(" extends ");
+            for (TypeInfo t : type.extendsBounds()) {
+                if (t != type.extendsBounds().get(0)) {
+                    System.out.print(" & ");
+                }
+                printTypeName(t);
+            }
+        }
+
+        if (type.superBounds() != null && !type.superBounds().isEmpty()) {
+            System.out.print(" super ");
+            for (TypeInfo t : type.superBounds()) {
+                if (t != type.superBounds().get(0)) {
+                    System.out.print(" & ");
+                }
+                printTypeName(t);
+            }
+        }
+
+        printTypeVariables(type);
+
+        if (type.dimension() != null) {
+            System.out.print(type.dimension());
+        }
+    }
+
+    private void printAnnotations(ArrayList<AnnotationInstanceInfo> annotations) {
+        for (AnnotationInstanceInfo i : annotations) {
+            System.out.println(i);
+        }
+    }
+
+    private void printTypeVariables(TypeInfo type) {
+        printTypeVariableList(type.typeArguments());
+    }
+
+    private void printTypeVariableList(ArrayList<TypeInfo> typeList) {
+        if (typeList != null && !typeList.isEmpty()) {
+            System.out.print("<");
+            for (TypeInfo type : typeList) {
+                if (type != typeList.get(0)) {
+                    System.out.print(", ");
+                }
+                printTypeName(type);
+            }
+            System.out.print(">");
+        }
+    }
+
+    /**
+     * Parses the file represented by the ParseTree.
+     * @param tree A ParseTree of the file to parse.
+     */
+    public void parseFile(ParseTree tree) {
+        if (tree.payload != null) {
+            String payload = tree.payload.toString();
+
+            // first pass at ignore method blocks
+            if ("block".equals(payload) ||
+                "blockStatement".equals(payload) ||
+                "explicitConstructorInvocation".equals(payload)) {
+                tree = null;
+                return;
+            }
+
+            // parse package of file
+            if ("packageDeclaration".equals(payload)) {
+                mPackage = buildPackage(tree);
+                return;
+            // parse imports
+            } else if ("importDeclaration".equals(payload)) {
+                mImports.add(buildImport(tree));
+                return;
+            // classes
+            } else if ("normalClassDeclaration".equals(payload)) {
+                mClasses.add(buildClass(tree, null));
+                return;
+            // enums
+            }  else if ("enumDeclaration".equals(payload)) {
+                mClasses.add(buildEnum(tree, null));
+                return;
+            // interfaces
+            } else if ("normalInterfaceDeclaration".equals(payload)) {
+                mClasses.add(buildInterface(tree, null));
+                return;
+            // annotations
+            } else if ("annotationTypeDeclaration".equals(payload)) {
+                mClasses.add(buildAnnotationDeclaration(tree, null));
+                return;
+            }
+        }
+
+        // if we're not at the end, recurse down the tree
+        for (int i = 0; i < tree.getChildCount(); i++) {
+            parseFile((ParseTree) tree.getChild(i));
+        }
+    }
+
+    /**
+     * Parses a packageDeclaration in the tree. This function should only be called once per file.
+     * @param tree The tree to parse. packageDeclaration should be the root value.
+     * @return a PackageInfo representing the package in which this file exists.
+     */
+    private PackageInfo buildPackage(ParseTree tree) {
+        for (int i = 0; i < tree.getChildCount(); i++) {
+            ParseTree child = (ParseTree) tree.getChild(i);
+
+            if (child.payload != null && "qualifiedName".equals(child.payload.toString())) {
+                String packageName = buildQualifiedName(child);
+
+                // return package because we might be creating packages for other classes
+                return Caches.obtainPackage(packageName);
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Parses a qualifiedName, returning it as a String.
+     * @param tree The tree to parse. qualifiedName should be the root value.
+     * @return
+     */
+    private static String buildQualifiedName(ParseTree tree) {
+        StringBuilder packageName = new StringBuilder();
+
+        for (int j = 0; j < tree.getChildCount(); j++) {
+            packageName.append(tree.getChild(j).toString());
+        }
+
+        return packageName.toString();
+    }
+
+    /**
+     * Builds a string representing an import declaration.
+     * @param tree The tree to parse. importDeclaration should be the root value.
+     * @return a String version of the import.
+     */
+    private String buildImport(ParseTree tree) {
+        StringBuilder theImport = new StringBuilder();
+        for (int i = 0; i < tree.getChildCount(); i++) {
+            String part = tree.getChild(i).toString();
+
+            if (!"import".equals(part) && !";".equals(part)) {
+                theImport.append(part);
+            }
+        }
+
+        return theImport.toString();
+    }
+
+    /**
+     * Builds a ClassInfo for a normalClassDeclaration.
+     * @param tree The tree to parse. normalClassDeclaration should be the root value.
+     * @param containingClass The class that contains the class that will be built.
+     * This value should be null if this class is a root class in the file.
+     * @return A ClassInfo that contains all of the information about the class.
+     */
+    private ClassInfo buildClass(ParseTree tree, ClassInfo containingClass) {
+        CommentAndPosition commentAndPosition = parseCommentAndPosition(tree);
+        Modifiers modifiers = new Modifiers(this);
+        ClassInfo cls = null;
+
+        @SuppressWarnings("unchecked")
+        Iterator<ParseTree> it = (Iterator<ParseTree>) tree.getChildren().iterator();
+        ParseTree child = it.next();
+
+        // parse modifiers
+        modifiers.parseModifiers(child);
+
+        it.next();
+        child = it.next();
+
+        // parse class name
+        cls = buildClassName(child, containingClass, modifiers,
+                commentAndPosition.getCommentText(),
+                commentAndPosition.getPosition(),
+                ClassType.ORDINARY);
+
+        child = it.next();
+
+        // handle generics
+        if ("typeParameters".equals(child.toString())) {
+            cls.type().setTypeArguments(buildTypeVariables(child));
+            child = it.next();
+
+        }
+
+        // handle extends
+        if ("extends".equals(child.toString())) {
+            child = it.next();
+
+            TypeInfo type = buildType(child);
+            cls.setSuperclassType(type);
+
+            // if ClassInfo is null, we need to add a resolution
+            if (type.asClassInfo() == null) {
+                addFutureResolution(cls, "superclassQualifiedName", type.simpleTypeName());
+            }
+
+            cls.setSuperClass(type.asClassInfo());
+
+            child = it.next();
+        }
+
+        // handle implements
+        if ("implements".equals(child.toString())) {
+            child = it.next();
+
+            parseInterfaces(child, cls);
+
+            child = it.next();
+        }
+
+        // finally, parse the body
+        buildClassBody(child, cls);
+
+        return cls;
+    }
+
+    /**
+     * Parses the list of interfaces that the class implements.
+     * Should only be called if the implements keyword is found.
+     * @param tree The tree to parse. typeList should be the root element.
+     * @param cls The class that implements these interfaces.
+     */
+    private void parseInterfaces(ParseTree tree, ClassInfo cls) {
+        for (Object o : tree.getChildren()) {
+            if ("type".equals(o.toString())) {
+                TypeInfo type = buildType((ParseTree) o);
+                cls.addInterfaceType(type);
+
+                // if ClassInfo is null, we need to add a resolution
+                if (type.asClassInfo() == null) {
+                    addFutureResolution(cls, "interfaceQualifiedName", type.simpleTypeName());
+                }
+
+                cls.addInterface(type.asClassInfo());
+            }
+        }
+    }
+
+    /**
+     * ClassType exists solely to tell buildClassName which type of ClassInfo is being built.
+     */
+    private enum ClassType {
+        ENUM, INTERFACE, ANNOTATION, ORDINARY
+    }
+
+    /**
+     * Parses the class name from the declaration. Also initializes the class.
+     * @param tree Position of the tree where the name of the class resides.
+     * @param containingClass Class that this class is contained within.
+     * <tt>null</tt> if this class is the root class.
+     * @param modifiers Contains all the modifiers of this class.
+     * @param commentText Javadoc comment of this class.
+     * @param position Position of the class.
+     * @param classType Type of class being instantiated.
+     * @return the ClassInfo being initialized.
+     */
+    private ClassInfo buildClassName(ParseTree tree, ClassInfo containingClass, Modifiers modifiers,
+            String commentText, SourcePositionInfo position, ClassType classType) {
+        String qualifiedClassName = null;
+        boolean isOrdinaryClass = true;
+        boolean isException = false;
+        boolean isError = false;
+        boolean isIncluded = false;
+        boolean isPrimitive = false;
+        boolean isEnum = false;
+        boolean isInterface = false;
+        boolean isAnnotation = false;
+
+        // set appropriate flags based on ClassType
+        switch (classType) {
+            case ENUM:
+                isEnum = true;
+                break;
+            case INTERFACE:
+                isInterface = true;
+                break;
+            case ANNOTATION:
+                isAnnotation = true;
+                break;
+        }
+
+        String qualifiedTypeName = null;
+        ClassInfo cls = null;
+
+        // changes the name based upon whether this is the root class or an inner class
+        if (containingClass == null) {
+            qualifiedClassName = mPackage.name() + "." + tree.toString();
+        } else {
+            qualifiedClassName = containingClass.qualifiedName() + "." + tree.toString();
+        }
+
+        qualifiedTypeName = new String(qualifiedClassName);
+
+        // add the name to mClassNames so that we can use it to resolve usages of this class
+        mClassNames.add(qualifiedClassName);
+
+        // get the class from the cache and initialize it
+        cls = Caches.obtainClass(qualifiedClassName);
+        cls.initialize(commentText, position,
+                       modifiers.isPublic(), modifiers.isProtected(),
+                       modifiers.isPackagePrivate(), modifiers.isPrivate(),
+                       modifiers.isStatic(), isInterface, modifiers.isAbstract(),
+                       isOrdinaryClass, isException, isError, isEnum, isAnnotation,
+                       modifiers.isFinal(), isIncluded, qualifiedTypeName, isPrimitive,
+                       modifiers.getAnnotations());
+
+        cls.setContainingClass(containingClass);
+
+        // create an set a TypeInfo for this class
+        TypeInfo type = new TypeInfo(false, null, cls.name(), qualifiedTypeName, cls);
+        cls.setTypeInfo(type);
+
+        return cls;
+    }
+
+    /**
+     * Parses the body of a class.
+     * @param tree The tree to parse. classBody should be the root value.
+     * @param cls
+     */
+    private void buildClassBody(ParseTree tree, ClassInfo cls) {
+        for (Object o : tree.getChildren()) {
+            ParseTree child = (ParseTree) o;
+
+            // skip all of the cruft that isn't a declaration
+            if (!"classBodyDeclaration".equals(child.toString())) {
+                continue;
+            }
+
+            // get to an actual definition
+            ParseTree member = (ParseTree) child.getChild(0).getChild(0);
+
+            // field
+            if ("fieldDeclaration".equals(member.toString())) {
+                cls.addField(buildField(member, cls));
+            // method and constructor
+            } else if ("methodDeclaration".equals(member.toString())) {
+                MethodInfo method = buildMethod(member, cls, false);
+
+                if (method.kind().equals("constructor")) {
+                    cls.addConstructor(method);
+                } else {
+                    cls.addMethod(method);
+                }
+            // classes and enums
+            } else if ("classDeclaration".equals(member.toString())) {
+                Object tmp = member.getChild(0);
+
+                if ("normalClassDeclaration".equals(tmp.toString())) {
+                    ClassInfo innerClass = buildClass((ParseTree) tmp, cls);
+                    cls.addInnerClass(innerClass);
+                    mClasses.add(innerClass);
+                } else if ("enumDeclaration".equals(tmp.toString())) {
+                    ClassInfo innerClass = buildEnum((ParseTree) tmp, cls);
+                    cls.addInnerClass(innerClass);
+                    mClasses.add(innerClass);
+                }
+            // interfaces and annotations
+            } else if ("interfaceDeclaration".equals(member.toString())) {
+                Object tmp = member.getChild(0);
+
+                if ("normalInterfaceDeclaration".equals(tmp.toString())) {
+                    ClassInfo innerClass = buildInterface((ParseTree) tmp, cls);
+                    cls.addInnerClass(innerClass);
+                    mClasses.add(innerClass);
+                } else if ("annotationTypeDeclaration".equals(tmp.toString())) {
+                    ClassInfo innerClass = buildAnnotationDeclaration((ParseTree) tmp, cls);
+                    cls.addInnerClass(innerClass);
+                    mClasses.add(innerClass);
+                }
+            }
+        }
+    }
+
+    /**
+     * Builds a FieldInfo for the field declared in this class.
+     * @param tree The tree to parse. fieldDeclaration should be the root value.
+     * @param containingClass The ClassInfo in which this field is contained.
+     * @return the FieldInfo for this field
+     */
+    private FieldInfo buildField(ParseTree tree, ClassInfo containingClass) {
+        Modifiers modifiers = new Modifiers(this);
+        CommentAndPosition commentAndPosition = parseCommentAndPosition(tree);
+        String name = null;
+        Object constantValue = null;
+        TypeInfo type = null;
+        boolean hasValue = false;
+
+        @SuppressWarnings("unchecked")
+        Iterator<ParseTree> it = (Iterator<ParseTree>) tree.getChildren().iterator();
+        ParseTree child = it.next();
+
+        // modifiers
+        modifiers.parseModifiers(child);
+        child = it.next();
+
+        // parse the type of this field
+        type = buildType(child);
+
+        child = it.next();
+
+        // parse the variable declarators
+        if ("variableDeclarator".equals(child.toString())) {
+            name = child.getChild(0).toString();
+
+            // if we have a value for the field
+            if (child.getChildCount() > 1) {
+                int j = 1;
+                ParseTree tmp = null;
+
+                // get to variableInitializer
+                do {
+                    tmp = (ParseTree) child.getChild(j++);
+                } while (!"variableInitializer".equals(tmp.toString()));
+
+                // get the constantValue
+                constantValue = parseExpression(tmp);
+                hasValue = true;
+            }
+        }
+
+        FieldInfo field = new FieldInfo(name, containingClass, containingClass,
+                modifiers.isPublic(), modifiers.isProtected(),
+                modifiers.isPackagePrivate(), modifiers.isPrivate(),
+                modifiers.isFinal(), modifiers.isStatic(), modifiers.isTransient(),
+                modifiers.isVolatile(), modifiers.isSynthetic(),
+                type, commentAndPosition.getCommentText(), constantValue,
+                commentAndPosition.getPosition(), modifiers.getAnnotations());
+        field.setHasValue(hasValue);
+
+        return field;
+    }
+
+    /**
+     * Parses an expression in the ParseTree to get a constant value.
+     * @param tree the place in the tree to get the constant value.
+     * @return the constant value.
+     */
+    private static Object parseExpression(ParseTree tree) {
+        Object constantValue = null;
+        StringBuilder builder = new StringBuilder();
+
+        while (!"primary".equals(tree.toString())) {
+            if ("unaryExpression".equals(tree.toString()) && tree.getChildCount() > 1) {
+                builder.append(tree.getChild(0));
+                tree = (ParseTree) tree.getChild(1);
+            } else {
+                tree = (ParseTree) tree.getChild(0);
+            }
+        }
+
+        if ("literal".equals(tree.getChild(0).toString())) {
+            constantValue = builder.append(tree.getChild(0).getChild(0).toString()).toString();
+        } else if (tree.getChildCount() > 1) {
+            for (Object o : tree.getChildren()) {
+                builder.append(o.toString());
+            }
+
+            constantValue = builder.toString();
+        }
+
+        return constantValue;
+    }
+
+    /**
+     * Builds  TypeInfo. Requires that tree points to "type" in the ParseTree.
+     * @param tree The tree to parse. type should be the root value.
+     * @return A TypeInfo for this type.
+     */
+    private TypeInfo buildType(ParseTree tree) {
+        boolean isPrimitive = false;
+        String dimension = null;
+        String simpleTypeName = null;
+        String qualifiedTypeName = null;
+        ClassInfo cl = null;
+        boolean addResolution = false;
+        ArrayList<TypeInfo> typeArguments = null;
+
+        // parse primitive types - very easy
+        if ("primitiveType".equals(tree.getChild(0).toString())) {
+            isPrimitive = true;
+
+            simpleTypeName = tree.getChild(0).getChild(0).toString();
+            qualifiedTypeName = simpleTypeName;
+        // any non-primitives
+        } else {
+            StringBuilder builder = new StringBuilder();
+
+            // get the full name of the type
+            for (Object namePart : ((ParseTree) tree.getChild(0)).getChildren()) {
+                // if we get to typeArguments, aka generics, parse that and bale out
+                // of building the name
+                if ("typeArguments".equals(namePart.toString())) {
+                    typeArguments = buildTypeVariables((ParseTree) namePart);
+                    break;
+                }
+
+                builder.append(namePart.toString());
+            }
+
+            // get simple and qualified name
+            simpleTypeName = builder.toString();
+            StringBuilder qualifiedTypeNameBuilder = new StringBuilder();
+            boolean isGeneric = resolveQualifiedName(simpleTypeName,
+                    qualifiedTypeNameBuilder, this);
+            qualifiedTypeName = qualifiedTypeNameBuilder.toString();
+
+            // if we couldn't figure out the qualified name
+            // tell us we need to resolve this
+            // can't add the resolution until the TypeInfo has been created
+            if ("".equals(qualifiedTypeName)) {
+                addResolution = true;
+            // otherwise, if the name is not a generic, get the class that this Type refers to
+            } else if (!isGeneric) {
+                cl = Caches.obtainClass(qualifiedTypeName);
+            }
+        }
+
+        // get the dimensions of this type
+        dimension = getDimensions(tree);
+
+        TypeInfo type = new TypeInfo(isPrimitive, dimension, simpleTypeName, qualifiedTypeName, cl);
+        type.setTypeArguments(typeArguments);
+
+        if (addResolution) {
+            addFutureResolution(type, "class", simpleTypeName);
+        }
+
+        return type;
+    }
+
+    /**
+     * Processes the type variables of a class that contains generics.
+     * @param tree Root of the type parameters.
+     * @param cls Class in which these type variables are contained.
+     */
+    private ArrayList<TypeInfo> buildTypeVariables(ParseTree tree) {
+        ArrayList<TypeInfo> typeVariables = new ArrayList<TypeInfo>();
+        ArrayList<TypeInfo> superBounds = new ArrayList<TypeInfo>();
+        ArrayList<TypeInfo> extendsBounds = new ArrayList<TypeInfo>();
+
+        for (Object o : tree.getChildren()) {
+            // if we're not dealing with a type, skip
+            // basically gets rid of commas and lessthan and greater than signs
+            if (!o.toString().equals("typeParameter") &&
+                !o.toString().equals("typeArgument")) {
+                continue;
+            }
+
+            ParseTree typeParameter = (ParseTree) o;
+
+            TypeInfo type;
+            // if we have a typeArgument and it is not a wildcard
+            if ("typeArgument".equals(typeParameter.toString()) &&
+                    !"?".equals(typeParameter.getChild(0).toString())) {
+                type = buildType((ParseTree) typeParameter.getChild(0));
+            } else {
+                // otherwise, we have a wildcard or parameter
+                // which can be more vague because of generics
+                String name = typeParameter.getChild(0).toString();
+
+                type = new TypeInfo(false, null, name, name, null);
+                if ("?".equals(name)) {
+                    type.setIsWildcard(true);
+                } else {
+                    // add generic
+                    mClassNames.add(name);
+                }
+            }
+
+            // if we have an extends or super on our type variable
+            if (typeParameter.getChildCount() > 1) {
+                ParseTree value = (ParseTree) typeParameter.getChild(1);
+
+                if ("extends".equals(value.toString())) {
+                    value = (ParseTree) typeParameter.getChild(2);
+
+                    // wildcard extends
+                    if ("type".equals(value.toString())) {
+                        extendsBounds.add(buildType(value));
+                    // all other extends
+                    } else {
+                        // will have to handle stuff with typeBound - multiple types
+                        for (Object obj : value.getChildren()) {
+                            if ("type".equals(obj.toString())) {
+                                extendsBounds.add(buildType((ParseTree) obj));
+                            }
+                        }
+                    }
+                } else if ("super".equals(value.toString())) {
+                    superBounds.add(buildType((ParseTree) typeParameter.getChild(2)));
+                }
+            }
+
+            type.setIsTypeVariable(true);
+            type.setBounds(superBounds, extendsBounds);
+            typeVariables.add(type);
+        }
+
+        return typeVariables;
+    }
+
+    /**
+     * Builds a MethodInfo for methods, constructors and annotation elements.
+     * @param tree The tree to parse. methodDeclaration, interfaceMethodDeclaration
+     * or annotationMethodDeclaration should be the root value.
+     * @param containingClass the class in which this method exists.
+     * @param isAnnotation true if the class is an annotation element
+     * @return the MethodInfo
+     */
+    private MethodInfo buildMethod(ParseTree tree, ClassInfo containingClass,
+            boolean isAnnotation) {
+        Modifiers modifiers = new Modifiers(this);
+        CommentAndPosition commentAndPosition = parseCommentAndPosition(tree);
+
+        String name = null;
+        StringBuilder flatSignature = new StringBuilder().append('(');
+        ArrayList<TypeInfo> typeParameters = null;
+        ArrayList<ParameterInfo> parameters = new ArrayList<ParameterInfo>();
+        ArrayList<ClassInfo> thrownExceptions = new ArrayList<ClassInfo>();
+        TypeInfo returnType = null;
+        boolean isAnnotationElement = false;
+        boolean isVarArg = false;
+        String kind = "method"; // annotationElement, method, or constructor
+        AnnotationValueInfo elementValue = null;
+        ArrayList<Resolution> pendingResolutions = new ArrayList<Resolution>();
+
+        @SuppressWarnings("unchecked")
+        Iterator<ParseTree> it = (Iterator<ParseTree>) tree.getChildren().iterator();
+        ParseTree child = it.next();
+
+        modifiers.parseModifiers(child);
+
+        child = it.next();
+
+        // generics stuff
+        if ("typeParameters".equals(child.toString())) {
+            typeParameters = buildTypeVariables(child);
+            child = it.next();
+        }
+
+        // handle returnType if we're not in a constructor
+        if ("type".equals(child.toString())) {
+            returnType = buildType(child);
+            child = it.next();
+        } else if ("void".equals(child.toString())) {
+            returnType = new TypeInfo(true, null, "void", "void", null);
+            child = it.next();
+        }
+
+        // probably don't need this check any longer since I unrolled the loop
+        if (isConstructorOrMethodName(child)) {
+            // this is the method name
+            name = child.toString();
+
+            if (name.equals(containingClass.name())) {
+                kind = "constructor";
+            }
+        }
+
+        child = it.next();
+
+        // method parameters
+        if ("formalParameters".equals(child.toString())) {
+            isVarArg = buildMethodParameters(child, parameters, flatSignature);
+        } else {
+            child = it.next();
+        }
+
+        child = it.next();
+        flatSignature.append(')');
+
+        // handle exception throwing
+        if ("throws".equals(child.toString())) {
+            child = it.next();
+
+            for (Object o : child.getChildren()) {
+                if (",".equals(o.toString())) {
+                    continue;
+                }
+
+                // get the name of the exception, resolve it and add it to the list
+                // unless we can't, in which case, add a resolution
+                String exceptionName = buildQualifiedName(((ParseTree) o));
+                StringBuilder exceptionQualifiedName = new StringBuilder();
+                boolean isGeneric = resolveQualifiedName(exceptionName,
+                        exceptionQualifiedName, this);
+
+                if ("".equals(exceptionQualifiedName.toString())) {
+                    pendingResolutions.add(new Resolution("thrownException", exceptionName));
+                } else if (!isGeneric) {
+                    thrownExceptions.add(Caches.obtainClass(exceptionQualifiedName.toString()));
+                }
+            }
+        // handle default values for annotation elements
+        } else if ("default".equals(child.toString())) {
+            child = it.next();
+
+            elementValue = buildElementValue(child, this);
+            child = it.next();
+        }
+
+        if (isAnnotation) {
+            kind = "annotationElement";
+        }
+
+        // Here we set signature, overridden method to null because
+        // MethodInfo figures these values out later on
+        MethodInfo method =  new MethodInfo(commentAndPosition.getCommentText(), typeParameters,
+                name, null, containingClass, containingClass, modifiers.isPublic(),
+                modifiers.isProtected(), modifiers.isPackagePrivate(),
+                modifiers.isPrivate(), modifiers.isFinal(),
+                modifiers.isStatic(), modifiers.isSynthetic(),
+                modifiers.isAbstract(), modifiers.isSynchronized(),
+                false, isAnnotationElement, kind, flatSignature.toString(),
+                null, returnType, parameters, thrownExceptions,
+                commentAndPosition.getPosition(), modifiers.getAnnotations());
+
+        method.setVarargs(isVarArg);
+        method.init(elementValue);
+
+        for (Resolution r : pendingResolutions) {
+            addFutureResolution(method, r.getVariable(), r.getValue());
+        }
+
+        return method;
+    }
+
+    private boolean isConstructorOrMethodName(ParseTree tree) {
+        String tmp = tree.toString();
+        return (!"{".equals(tmp) && !"}".equals(tmp) && !";".equals(tmp) &&
+                !"explicitConstructorInvocation".equals(tmp) &&
+                !"blockStatement".equals(tmp) && !"block".equals(tmp));
+    }
+
+    /**
+     * Build the method parameters.
+     * @param tree The tree to parse. formalParamaters should be the root value.
+     * @param parameters List to put the method ParamaterInfos into.
+     * @param flatSignature Pass in a StringBuilder with "(" in it to build the
+     * flatSignature of the MethodInfo
+     * @return true if the Method has a VarArgs parameter. false otherwise.
+     */
+    private boolean buildMethodParameters(ParseTree tree,
+                                    ArrayList<ParameterInfo> parameters,
+                                    StringBuilder flatSignature) {
+        boolean isVarArg = false;
+        for (Object obj : tree.getChildren()) {
+            ParseTree child = (ParseTree) obj;
+
+            if ("formalParameterDecls".equals(child.toString())) {
+                for (Object formalParam : child.getChildren()) {
+                    ParseTree param = (ParseTree) formalParam;
+                    TypeInfo type = null;
+
+                    if (param.getChildCount() == 0) {
+                        continue;
+                    }
+
+                    for (Object item : param.getChildren()) {
+                        ParseTree paramPart = (ParseTree) item;
+
+                        if ("variableModifiers".equals(item.toString())) {
+                            // TODO - handle variable modifiers - final, etc
+                        } else if ("type".equals(paramPart.toString())) {
+                            type = buildType(paramPart);
+
+                            buildSignatureForType(flatSignature, type);
+
+                            if (param != child.getChildren().get(child.getChildCount()-1)) {
+                                flatSignature.append(", ");
+                            }
+                        } else if ("...".equals(paramPart.toString())) {
+                            isVarArg = true;
+                            // thank you varargs for only being the last parameter
+                            // you make life so much nicer
+                            flatSignature.append("...");
+                        } else {
+                            String name = paramPart.toString();
+
+                            CommentAndPosition commentAndPosition = new CommentAndPosition();
+                            commentAndPosition.setPosition(paramPart);
+
+                            parameters.add(new ParameterInfo(name, type.qualifiedTypeName(), type,
+                                    isVarArg, commentAndPosition.getPosition()));
+                        }
+                    }
+                }
+            }
+        }
+
+        return isVarArg;
+    }
+
+    /**
+     * Builds a StringBuilder representing the Type, including type arguments.
+     * @param builder StringBuilder in which the Type will be placed.
+     * @param type the TypeInfo to turn into a String.
+     */
+    private void buildSignatureForType(StringBuilder builder, TypeInfo type) {
+        // simple name
+        builder.append(type.simpleTypeName());
+
+        // generics
+        if (type.typeArguments() != null && !type.typeArguments().isEmpty()) {
+            builder.append('<');
+            for (TypeInfo inner : type.typeArguments()) {
+                if (inner != type.typeArguments().get(0)) {
+                    builder.append(", ");
+                }
+
+                // recurse
+                buildSignatureForType(builder, inner);
+            }
+            builder.append('>');
+        }
+    }
+
+    /**
+     * Builds a ClassInfo for an enum.
+     * @param tree The tree to parse. enumDeclaration should be the root value.
+     * @param containingClass ClassInfo that contains the enum declaration.
+     * null if the enum is a root class.
+     * @return the enum as a ClassInfo
+     */
+    private ClassInfo buildEnum(ParseTree tree, ClassInfo containingClass) {
+        CommentAndPosition commentAndPosition = parseCommentAndPosition(tree);
+        Modifiers modifiers = new Modifiers(this);
+        ClassInfo cls = null;
+
+        @SuppressWarnings("unchecked")
+        Iterator<ParseTree> it = (Iterator<ParseTree>) tree.getChildren().iterator();
+
+        ParseTree child = it.next();
+
+        modifiers.parseModifiers(child);
+
+        child = it.next();
+        child = it.next();
+
+        cls = buildClassName(child, containingClass, modifiers,
+                commentAndPosition.getCommentText(),
+                commentAndPosition.getPosition(), ClassType.ENUM);
+
+        // handle implements
+        if ("implements".equals(child.toString())) {
+            child = it.next();
+
+            parseInterfaces(child, cls);
+
+            child = it.next();
+        }
+
+        child = it.next();
+        buildEnumBody(child, cls);
+
+        return cls;
+    }
+
+    /**
+     * Parses the body of an enum.
+     * @param tree The tree to parse. enumBody should be the root value.
+     * @param containingClass ClassInfo to which this enum body pertains.
+     */
+    private void buildEnumBody(ParseTree tree, ClassInfo containingClass) {
+        for (Object o : tree.getChildren()) {
+            ParseTree child = (ParseTree) o;
+
+            if ("enumConstants".equals(child.toString())) {
+                for (Object o2 : child.getChildren()) {
+                    ParseTree tmp = (ParseTree) o2;
+
+                    if ("enumConstant".equals(tmp.toString())) {
+                        containingClass.addEnumConstant(buildEnumConstant(tmp, containingClass));
+                    }
+                }
+            } else if ("enumBodyDeclarations".equals(child.toString())) {
+                buildClassBody(child, containingClass);
+            }
+        }
+        return;
+    }
+
+    /**
+     * Builds an enum constant.
+     * @param tree The tree to parse. enumConstant should be the root value.
+     * @param containingClass ClassInfo to which this enum constant pertains.
+     * @return
+     */
+    private FieldInfo buildEnumConstant(ParseTree tree, ClassInfo containingClass) {
+        tree = (ParseTree) tree.getChild(0);
+
+        String name = tree.toString();
+        CommentAndPosition commentAndPosition = new CommentAndPosition();
+        commentAndPosition.setCommentText(tree);
+        commentAndPosition.setPosition(tree);
+        Object constantValue = null;
+
+        // TODO - annotations and constantValue stuff
+
+        return new FieldInfo(name, containingClass, containingClass, containingClass.isPublic(),
+        containingClass.isProtected(), containingClass.isPackagePrivate(),
+        containingClass.isPrivate(), containingClass.isFinal(),
+        containingClass.isStatic(), false, false, false,
+        containingClass.type(), commentAndPosition.getCommentText(),
+        constantValue, commentAndPosition.getPosition(),
+        new ArrayList<AnnotationInstanceInfo>());
+    }
+
+    /**
+     * Builds a ClassInfo for an interface.
+     * @param tree The tree to parse. normalInterfaceDeclaration should be the root value.
+     * @param containingClass ClassInfo that contains the interface declaration.
+     * null if the interface is a root class.
+     * @return a ClassInfo representing the interface.
+     */
+    private ClassInfo buildInterface(ParseTree tree, ClassInfo containingClass) {
+        @SuppressWarnings("unchecked")
+        Iterator<ParseTree> it = (Iterator<ParseTree>) tree.getChildren().iterator();
+        ParseTree child = it.next();
+
+        // parse modifiers and get comment and position
+        Modifiers modifiers = new Modifiers(this);
+        modifiers.parseModifiers(child);
+        CommentAndPosition commentAndPosition = parseCommentAndPosition(tree);
+
+        it.next();
+        child = it.next();
+
+        // get class name
+        ClassInfo iface = buildClassName(child, containingClass, modifiers,
+                commentAndPosition.getCommentText(),
+                commentAndPosition.getPosition(), ClassType.INTERFACE);
+
+        child = it.next();
+
+        // parse generics if they exist
+        if ("typeParameters".equals(child.toString())) {
+            iface.type().setTypeArguments(buildTypeVariables(child));
+            child = it.next();
+        }
+
+        // parse interfaces implemented by this interface
+        if ("extends".equals(child.toString())) {
+            child = it.next();
+
+            parseInterfaces(child, iface);
+
+            child = it.next();
+        }
+
+        // finally, build the body of the interface
+        buildInterfaceBody(child, iface);
+
+        return iface;
+    }
+
+    /**
+     * Parses the body of the interface, adding it to iface.
+     * @param tree The tree to parse. interfaceBody should be the root value.
+     * @param iface ClassInfo that will contain all of the interface body.
+     */
+    private void buildInterfaceBody(ParseTree tree, ClassInfo iface) {
+        for (Object o : tree.getChildren()) {
+            if (!o.toString().equals("interfaceBodyDeclaration")) {
+                continue;
+            }
+
+            ParseTree child = (ParseTree) ((ParseTree) o).getChild(0);
+
+            // field
+            if ("interfaceFieldDeclaration".equals(child.toString())) {
+                iface.addField(buildField(child, iface));
+            // method
+            } else if ("interfaceMethodDeclaration".equals(child.toString())) {
+                iface.addMethod(buildMethod(child, iface, false));
+            // inner class
+            } else if ("normalClassDeclaration".equals(child.getChild(0).toString())) {
+                iface.addInnerClass(buildClass((ParseTree) child.getChild(0), iface));
+            // inner enum
+            } else if ("enumDeclaration".equals(child.getChild(0).toString())) {
+                iface.addInnerClass(buildEnum((ParseTree) child.getChild(0), iface));
+            // inner interface
+            } else if ("normalInterfaceDeclaration".equals(child.getChild(0).toString())) {
+                iface.addInnerClass(buildInterface((ParseTree) child.getChild(0), iface));
+            // inner annotation
+            } else if ("annotationTypeDeclaration".equals(child.getChild(0).toString())) {
+                iface.addInnerClass(buildAnnotationDeclaration(
+                        (ParseTree) child.getChild(0), iface));
+            }
+        }
+    }
+
+    /**
+     * Builds a ClassInfo of an annotation declaration.
+     * @param tree The tree to parse. annotationTypeDeclaration should be the root value.
+     * @param containingClass The class that contains this annotation.
+     * null if this is a root annotation.
+     * @return the ClassInfo of the annotation declaration.
+     */
+    private ClassInfo buildAnnotationDeclaration(ParseTree tree, ClassInfo containingClass) {
+        @SuppressWarnings("unchecked")
+        Iterator<ParseTree> it = (Iterator<ParseTree>) tree.getChildren().iterator();
+        ParseTree child = it.next();
+
+        // get comment and position
+        CommentAndPosition commentAndPosition = parseCommentAndPosition(tree);
+
+        // modifiers
+        Modifiers modifiers = new Modifiers(this);
+        modifiers.parseModifiers(child);
+
+        // three calls to next to skip over @, interface and then
+        // make child = the name of this annotation
+        it.next();
+        it.next();
+        child = it.next();
+
+        // build class name and initialize the class
+        ClassInfo annotation = buildClassName(child, containingClass, modifiers,
+                commentAndPosition.getCommentText(),
+                commentAndPosition.getPosition(), ClassType.INTERFACE);
+
+        child = it.next();
+
+        // build annotation body
+        buildAnnotationBody(child, annotation);
+
+        return annotation;
+    }
+
+    /**
+     * Parses the body of the annotation declaration.
+     * @param tree The tree to parse. annotationTypeBody should be the root value.
+     * @param annotation the Classinfo in which the annotation elements should be added.
+     */
+    private void buildAnnotationBody(ParseTree tree, ClassInfo annotation) {
+        for (Object o : tree.getChildren()) {
+            if (!"annotationTypeElementDeclaration".equals(o.toString())) {
+                continue;
+            }
+
+            ParseTree child = (ParseTree) ((ParseTree) o).getChild(0);
+
+            // annotation fields
+            if ("interfaceFieldDeclaration".equals(child.toString())) {
+                annotation.addField(buildField(child, annotation));
+            // annotation methods
+            } else if ("annotationMethodDeclaration".equals(child.toString())) {
+                annotation.addAnnotationElement(buildMethod(child, annotation, true));
+            // inner class
+            } else if ("normalClassDeclaration".equals(child.getChild(0).toString())) {
+                annotation.addInnerClass(buildClass((ParseTree) child.getChild(0), annotation));
+            // enum
+            } else if ("enumDeclaration".equals(child.getChild(0).toString())) {
+                annotation.addInnerClass(buildEnum((ParseTree) child.getChild(0), annotation));
+            // inner interface
+            } else if ("normalInterfaceDeclaration".equals(child.getChild(0).toString())) {
+                annotation.addInnerClass(buildInterface((ParseTree) child.getChild(0), annotation));
+            // inner annotation
+            } else if ("annotationTypeDeclaration".equals(child.getChild(0).toString())) {
+                annotation.addInnerClass(buildAnnotationDeclaration(
+                        (ParseTree) child.getChild(0), annotation));
+            }
+        }
+    }
+
+    /**
+     * Build an annotation instance.
+     * @param tree The tree to parse. annotation should be the root value.
+     * @param builder InfoBuilder of this file.
+     * @return The AnnotationInstanceInfo being parsed.
+     */
+    private static AnnotationInstanceInfo buildAnnotationInstance(ParseTree tree,
+            InfoBuilder builder) {
+        @SuppressWarnings("unchecked")
+        Iterator<ParseTree> it = (Iterator<ParseTree>) tree.getChildren().iterator();
+
+        AnnotationInstanceInfo annotationInstance = new AnnotationInstanceInfo();
+
+        it.next();
+
+        // parse the name, get its full version, and then get the ClassInfo of it, if possible.
+        String name = InfoBuilder.buildQualifiedName(it.next());
+        StringBuilder qualifiedNameBuilder = new StringBuilder();
+        resolveQualifiedName(name, qualifiedNameBuilder, builder);
+
+        if ("".equals(qualifiedNameBuilder.toString())) {
+            addFutureResolution(annotationInstance, "annotationTypeName", name);
+            annotationInstance.setClassName(name);
+        } else { // can't have generics here so we won't do a test
+            annotationInstance.setClass(Caches.obtainClass(qualifiedNameBuilder.toString()));
+        }
+
+        // at this point, the annotation is either finished or we have more work to do
+        if (!it.hasNext()) {
+            return annotationInstance;
+        }
+
+        it.next();
+        ParseTree child = it.next();
+
+        // parse elementValue pairs
+        if ("elementValuePairs".equals(child.toString())) {
+            for (Object o : child.getChildren()) {
+                if (!"elementValuePair".equals(o.toString())) {
+                    continue;
+                }
+
+                ParseTree inner = (ParseTree) o;
+                MethodInfo element = null;
+                String methodName = inner.getChild(0).toString();
+
+                // try and look up the MethodInfo for this annotation, if possible
+                if (annotationInstance.type() != null) {
+                    for (MethodInfo m : annotationInstance.type().allSelfMethods()) {
+                        if (methodName.equals(m.name())) {
+                            element = m;
+                            break;
+                        }
+                    }
+                }
+
+                // go to elementValue
+                AnnotationValueInfo info = buildElementValue(
+                        (ParseTree) inner.getChild(2), builder);
+
+                if (element == null) {
+                    addFutureResolution(info, "element", methodName);
+                } else {
+                    info.setElement(element);
+                }
+
+                annotationInstance.addElementValue(info);
+            }
+        // parse element value
+        } else if ("elementValue".equals(child.toString())) {
+            annotationInstance.addElementValue(buildElementValue(child, builder));
+        }
+
+        return annotationInstance;
+    }
+
+    /**
+     * Builds the value of the annotation element.
+     * @param tree The tree to parse. elementValue should be the root value.
+     * @param builder InfoBuilder of this file.
+     * @return AnnotationValueInfo representing the elementValue.
+     */
+    private static AnnotationValueInfo buildElementValue(ParseTree tree, InfoBuilder builder) {
+        AnnotationValueInfo elementValue = new AnnotationValueInfo();
+        Object value = null;
+
+        // parse some stuff
+        String str = tree.getChild(0).toString();
+        if ("conditionalExpression".equals(str)) {
+            value = parseExpression((ParseTree) tree.getChild(0));
+        } else if ("annotation".equals(str)) {
+            value = InfoBuilder.buildAnnotationInstance((ParseTree) tree.getChild(0), builder);
+        } else if ("elementValueArrayInitializer".equals(str)) {
+            ParseTree child = (ParseTree) tree.getChild(0);
+            ArrayList<AnnotationValueInfo> values = new ArrayList<AnnotationValueInfo>();
+            for (Object o : child.getChildren()) {
+                if ("elementValue".equals(o.toString())) {
+                    values.add(buildElementValue((ParseTree) o, builder));
+                }
+            }
+
+            value = values;
+        }
+
+        elementValue.init(value);
+
+        return elementValue;
+    }
+
+    /**
+     * Get the dimensions of the type, as a String.
+     * @param tree The tree to parse. type should be the root value.
+     * @return A String of the dimensions of the type.
+     */
+    private String getDimensions(ParseTree tree) {
+        // we only have dimensions if the count is not 1
+        if (tree.getChildCount() == 1) {
+            return null;
+        }
+
+        StringBuilder builder = new StringBuilder();
+
+        for (int i = 1; i < tree.getChildCount(); i++) {
+            builder.append(((ParseTree) tree.getChild(i)).toString());
+        }
+
+        return builder.toString();
+    }
+
+    /**
+     * When we have data that we can't yet parse, save it for later.
+     * @param resolvable Resolvable to which the data refers.
+     * @param variable Variable in the document to which the data refers;
+     * @param value Value for the variable
+     */
+    private static void addFutureResolution(Resolvable resolvable, String variable, String value) {
+        resolvable.addResolution(new Resolution(variable, value));
+
+        Caches.addResolvableToCache(resolvable);
+    }
+
+    /**
+     * Turns a short name of a class into the qualified name of a class.
+     * StringBuilder will contain an empty string if not found.
+     * @param name the abbreviated name of the class
+     * @param qualifiedClassName the qualified name that will be set if found.
+     * Unchanged if not found.
+     * @param builder InfoBuilder with all of the file specific information necessary
+     * to properly resolve the name.
+     * @return a boolean is returned that will be true if the type is a generic. false otherwise.
+     */
+    private static boolean resolveQualifiedName(String name,
+                                                StringBuilder qualifiedClassName,
+                                                InfoBuilder builder) {
+        // steps to figure out a class's real name
+        // check class(es) in this file
+        // TODO - search since we're now a HashSet
+        for (String className : builder.getClassNames()) {
+            int beginIndex = className.lastIndexOf(".") + 1;
+
+            if (className.substring(beginIndex).equals(name)) {
+                qualifiedClassName.append(className);
+                return qualifiedClassName.toString().equals(name);
+            }
+        }
+
+        // check package
+        ClassInfo potentialClass = builder.getPackage().getClass(name);
+
+        if (potentialClass != null) {
+            qualifiedClassName.append(potentialClass.qualifiedName());
+            return qualifiedClassName.toString().equals(name);
+        }
+
+        potentialClass = null;
+
+        // check class imports - ie, java.lang.String;
+        ArrayList<String> packagesToCheck = new ArrayList<String>();
+        for (String imp : builder.getImports()) {
+            // +1 to get rid of off by 1 error
+            String endOfName = imp.substring(imp.lastIndexOf('.') + 1);
+            if (endOfName.equals(name) || (name.indexOf('.') != -1 &&
+                                           endOfName.equals(
+                                                   name.substring(0, name.lastIndexOf('.'))))) {
+                qualifiedClassName.append(imp);
+                return qualifiedClassName.toString().equals(name);
+            } else if (endOfName.equals("*")) {
+                // add package to check
+                packagesToCheck.add(imp.substring(0, imp.lastIndexOf('.')));
+            }
+        }
+
+        // check package imports - ie, java.lang.*;
+        for (String packageName : packagesToCheck) {
+            PackageInfo pkg = Caches.obtainPackage(packageName);
+
+            ClassInfo cls = pkg.getClass(name);
+
+            if (cls != null && cls.name().equals(name)) {
+                qualifiedClassName.append(cls.qualifiedTypeName());
+                return qualifiedClassName.toString().equals(name);
+            }
+        }
+        //     including import's inner classes...
+        // check package of imports...
+
+        // TODO - remove
+        // FROM THE JAVADOC VERSION OF THIS FUNCTION
+        // Find the specified class or interface within the context of this class doc.
+        // Search order: 1) qualified name, 2) nested in this class or interface,
+        // 3) in this package, 4) in the class imports, 5) in the package imports.
+        // Return the ClassDoc if found, null if not found.
+
+        return false;
+    }
+
+    /**
+     * Parses the tree, looking for the comment and position.
+     * @param tree The tree to parse.
+     * @return a CommentAndPosition object containing the comment and position of the element.
+     */
+    private CommentAndPosition parseCommentAndPosition(ParseTree tree) {
+        Tree child = tree.getChild(0).getChild(0);
+
+        // three options (modifiers with annotations, modifiers w/o annotations, no modifiers)
+        // if there are no modifiers, use tree.getChild(1)
+        // otherwise, dive as deep as possible into modifiers to get to the comment and position.
+        child = ("<epsilon>".equals(child.toString())) ? tree.getChild(1) : child;
+
+        while (child.getChildCount() > 0) {
+            child = child.getChild(0);
+        }
+
+        CommentAndPosition cAndP = new CommentAndPosition();
+        cAndP.setCommentText((ParseTree) child);
+        cAndP.setPosition((ParseTree) child);
+        return cAndP;
+    }
+
+    /**
+     * Private class to facilitate passing the comment and position out of a function.
+     */
+    private class CommentAndPosition {
+        public String getCommentText() {
+            return mCommentText;
+        }
+
+        /**
+         * Parses the tree to get the commentText and set that value.
+         * @param tree The tree to parse. Should be pointing to the node containing the comment.
+         */
+        public void setCommentText(ParseTree tree) {
+            if (tree.hiddenTokens != null && !tree.hiddenTokens.isEmpty()) {
+                mCommentText = ((CommonToken) tree.hiddenTokens.get(0)).getText();
+
+                if (mCommentText != null) {
+                    return;
+                }
+            }
+
+            mCommentText = "";
+        }
+
+        public SourcePositionInfo getPosition() {
+            return mPosition;
+        }
+
+        /**
+         * Parses the tree to get the SourcePositionInfo of the node.
+         * @param tree The tree to parse. Should be pointing to the node containing the position.
+         */
+        public void setPosition(ParseTree tree) {
+          CommonToken token = (CommonToken) tree.payload;
+
+          int line = token.getLine();
+          int column = token.getCharPositionInLine();
+          String fileName = ((ANTLRFileStream) token.getInputStream()).getSourceName();
+
+          mPosition = new SourcePositionInfo(fileName, line, column);
+        }
+
+        private String mCommentText;
+        private SourcePositionInfo mPosition;
+    }
+
+    /**
+     * Private class to handle all the possible modifiers to a class/interface/field/anything else.
+     */
+    private class Modifiers {
+        private boolean mIsPublic = false;
+        private boolean mIsProtected = false;
+        private boolean mIsPackagePrivate = true;
+        private boolean mIsPrivate = false;
+        private boolean mIsStatic = false;
+        private boolean mIsAbstract = false;
+        private boolean mIsFinal = false;
+        private boolean mIsTransient = false;
+        private boolean mIsVolatile = false;
+        private boolean mIsSynthetic = false;
+        private boolean mIsSynchronized = false;
+        private boolean mIsStrictfp = false;
+        private InfoBuilder mBuilder;
+        private ArrayList<AnnotationInstanceInfo> mAnnotations;
+
+        public Modifiers(InfoBuilder builder) {
+            mAnnotations = new ArrayList<AnnotationInstanceInfo>();
+            mBuilder = builder;
+        }
+
+        /**
+         * Parses all of the modifiers of any declaration, including annotations.
+         * @param tree
+         */
+        public void parseModifiers(ParseTree tree) {
+            for (Object child : tree.getChildren()) {
+                String modifier = child.toString();
+
+                if ("public".equals(modifier)) {
+                    mIsPublic = true;
+                    mIsPackagePrivate = false;
+                } else if ("protected".equals(modifier)) {
+                    mIsProtected = true;
+                    mIsPackagePrivate = false;
+                } else if ("private".equals(modifier)) {
+                    mIsPrivate = true;
+                    mIsPackagePrivate = false;
+                } else if ("static".equals(modifier)) {
+                    mIsStatic = true;
+                } else if ("abstract".equals(modifier)) {
+                    mIsAbstract = true;
+                } else if ("final".equals(modifier)) {
+                    mIsFinal = true;
+                } else if ("transient".equals(modifier)) {
+                    mIsTransient = true;
+                } else if ("volatile".equals(modifier)) {
+                    mIsVolatile = true;
+                } else if ("synthetic".equals(modifier)) {
+                    mIsSynthetic = true;
+                } else if ("synchronized".equals(modifier)) {
+                    mIsSynchronized = true;
+                }  else if ("strictfp".equals(modifier)) {
+                    mIsStrictfp = true;
+                } else if ("annotation".equals(modifier)) {
+                    mAnnotations.add(buildAnnotationInstance((ParseTree) child, mBuilder));
+                }
+            }
+        }
+
+        public boolean isPublic() {
+            return mIsPublic;
+        }
+
+        public boolean isProtected() {
+            return mIsProtected;
+        }
+
+        public boolean isPackagePrivate() {
+            return mIsPackagePrivate;
+        }
+
+        public boolean isPrivate() {
+            return mIsPrivate;
+        }
+
+        public boolean isStatic() {
+            return mIsStatic;
+        }
+
+        public boolean isAbstract() {
+            return mIsAbstract;
+        }
+
+        public boolean isFinal() {
+            return mIsFinal;
+        }
+
+        public boolean isTransient() {
+            return mIsTransient;
+        }
+
+        public boolean isVolatile() {
+            return mIsVolatile;
+        }
+
+        public boolean isSynthetic() {
+            return mIsSynthetic;
+        }
+
+        public boolean isSynchronized() {
+            return mIsSynchronized;
+        }
+
+        @SuppressWarnings("unused")
+        public boolean isStrictfp() {
+            return mIsStrictfp;
+        }
+
+        public ArrayList<AnnotationInstanceInfo> getAnnotations() {
+            return mAnnotations;
+        }
+    };
+
+
+    /**
+     * Singleton class to store all of the global data amongst every InfoBuilder.
+     */
+    private static class Caches {
+        private static HashMap<String, PackageInfo> mPackages
+                                        = new HashMap<String, PackageInfo>();
+        private static HashMap<String, ClassInfo> mClasses
+                                        = new HashMap<String, ClassInfo>();
+        private static HashSet<Resolvable> mInfosToResolve
+                                        = new HashSet<Resolvable>();
+
+        public static PackageInfo obtainPackage(String packageName) {
+            PackageInfo pkg = mPackages.get(packageName);
+
+            if (pkg == null) {
+                pkg = new PackageInfo(packageName);
+                mPackages.put(packageName, pkg);
+            }
+
+            return pkg;
+        }
+
+        public static ClassInfo obtainClass(String qualifiedClassName) {
+            ClassInfo cls = mClasses.get(qualifiedClassName);
+
+            if (cls == null) {
+                cls = new ClassInfo(qualifiedClassName);
+                mClasses.put(cls.qualifiedName(), cls);
+                cls.setContainingPackage(Caches.obtainPackage(
+                        cls.qualifiedName().substring(0, cls.qualifiedName().lastIndexOf('.'))));
+            }
+
+            return cls;
+        }
+
+        public static void addResolvableToCache(Resolvable resolvable) {
+            mInfosToResolve.add(resolvable);
+        }
+
+        public static void printResolutions() {
+            for (Resolvable r : mInfosToResolve) {
+                r.printResolutions();
+                System.out.println();
+            }
+        }
+    }
+
+    public PackageInfo getPackage() {
+        return mPackage;
+    }
+
+    public ArrayList<String> getImports() {
+        return mImports;
+    }
+
+    public ArrayList<ClassInfo>  getClasses() {
+        return mClasses;
+    }
+
+    public HashSet<String> getClassNames() {
+        return mClassNames;
+    }
+}