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