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;
+ }
+}