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