blob: a07f9905481403baa1b736b80ed117d4f1b78334 [file] [log] [blame]
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001/*
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
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -070019import com.google.doclava.parser.JavaLexer;
20import com.google.doclava.parser.JavaParser;
21
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -070022import org.antlr.runtime.ANTLRFileStream;
23import org.antlr.runtime.CommonToken;
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -070024import org.antlr.runtime.CommonTokenStream;
25import org.antlr.runtime.RecognitionException;
26import org.antlr.runtime.debug.ParseTreeBuilder;
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -070027import org.antlr.runtime.tree.ParseTree;
28import org.antlr.runtime.tree.Tree;
29
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -070030import java.io.IOException;
31import java.util.ArrayList;
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -070032import java.util.HashMap;
33import java.util.HashSet;
34import java.util.Iterator;
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -070035
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 {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -070042 private PackageInfo mPackage;
43 private ArrayList<String> mImports;
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -070044 private HashSet<String> mClassNames;
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -070045 private String mFilename; // TODO - remove this eventually
46 private ClassInfo mRootClass;
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -070047
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -070048 public InfoBuilder(String filename) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -070049 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
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -070053 mClassNames = new HashSet<String>();
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -070054 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();
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -070093 }
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
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700101 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
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700112 public static void printClassInfo(ClassInfo cl) {
113 System.out.print("Class: " + cl.toString());
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700114
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700115 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 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700147
148 System.out.println();
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700149 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700150
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700151 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);
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700158
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700159 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 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700171 }
172
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700173 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 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700191 }
192
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700193 System.out.println();
194 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700195
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700196 if (!cl.allConstructors().isEmpty()) {
197 System.out.println("\nConstructors:");
198 for (MethodInfo m : cl.allConstructors()) {
199 if (m != cl.allConstructors().get(0)) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700200 System.out.println();
201 }
202
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700203 System.out.println(m.comment().mText);
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700204
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700205 printAnnotations(m.annotations());
206 if (m.getTypeParameters() != null) {
207 printTypeVariableList(m.getTypeParameters());
208 System.out.print(" ");
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700209 }
210
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700211 System.out.println(m.name() + m.flatSignature());
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700212 }
213
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700214 System.out.println();
215 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700216
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700217 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();
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700222 }
223
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700224 System.out.println(m.comment().mText);
225 printAnnotations(m.annotations());
226 if (m.getTypeParameters() != null) {
227 printTypeVariableList(m.getTypeParameters());
228 System.out.print(" ");
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700229 }
230
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700231 printTypeName(m.returnType());
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700232
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700233 System.out.print(" " + m.name() + m.flatSignature());
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700234
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700235 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(", ");
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700240 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700241
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700242 System.out.print(c.name());
243 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700244 }
245
246 System.out.println();
247 }
248
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700249 System.out.println();
250 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700251
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700252 if (!cl.annotationElements().isEmpty()) {
253 System.out.println("\nAnnotation Elements:");
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700254
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700255 for (MethodInfo m : cl.annotationElements()) {
256 if (m != cl.annotationElements().get(0)) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700257 System.out.println();
258 }
259
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700260 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
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700271 System.out.println();
272 }
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700273
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 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700282 }
283 }
284
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700285 private static void printTypeName(TypeInfo type) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700286 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
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700315 private static void printAnnotations(ArrayList<AnnotationInstanceInfo> annotations) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700316 for (AnnotationInstanceInfo i : annotations) {
317 System.out.println(i);
318 }
319 }
320
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700321 private static void printTypeVariables(TypeInfo type) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700322 printTypeVariableList(type.typeArguments());
323 }
324
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700325 private static void printTypeVariableList(ArrayList<TypeInfo> typeList) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700326 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 */
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700342 private void parseFile(ParseTree tree) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700343 if (tree.payload != null) {
344 String payload = tree.payload.toString();
345
346 // first pass at ignore method blocks
347 if ("block".equals(payload) ||
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700348 "blockStatement".equals(payload) ||
349 "explicitConstructorInvocation".equals(payload)) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700350 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)) {
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700364 buildClass(tree, null);
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700365 return;
366 // enums
367 } else if ("enumDeclaration".equals(payload)) {
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700368 buildEnum(tree, null);
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700369 return;
370 // interfaces
371 } else if ("normalInterfaceDeclaration".equals(payload)) {
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700372 buildInterface(tree, null);
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700373 return;
374 // annotations
375 } else if ("annotationTypeDeclaration".equals(payload)) {
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700376 buildAnnotationDeclaration(tree, null);
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700377 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();
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700429 for (int i = 1; i < tree.getChildCount(); i++) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700430 String part = tree.getChild(i).toString();
431
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700432 if ((i == 1 && "static".equals(part))
433 || (i == tree.getChildCount()-1 && ";".equals(part))) {
434 continue;
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700435 }
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700436
437 theImport.append(part);
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700438 }
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) {
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700489 addFutureResolution(cls, "superclassQualifiedName", type.simpleTypeName(), this);
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700490 }
491
492 cls.setSuperClass(type.asClassInfo());
493
494 child = it.next();
495 }
496
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700497 // TODO - do I have to make java.lang.Object the superclass if there is none otherwise?
498
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700499 // 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) {
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700528 addFutureResolution(cls, "interfaceQualifiedName", type.simpleTypeName(), this);
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700529 }
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,
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700597 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());
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700603
604 cls.setContainingClass(containingClass);
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700605 cls.setContainingPackage(mPackage);
606
607 if (containingClass == null) {
608 mRootClass = cls;
609 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700610
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
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700635 // ignores static initializers
636 if (member == null) {
637 continue;
638 }
639
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700640 // field
641 if ("fieldDeclaration".equals(member.toString())) {
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700642 for (FieldInfo f : buildFields(member, cls)) {
643 cls.addField(f);
644 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700645 // 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())) {
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700659 cls.addInnerClass(buildClass((ParseTree) tmp, cls));
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700660 } else if ("enumDeclaration".equals(tmp.toString())) {
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700661 cls.addInnerClass(buildEnum((ParseTree) tmp, cls));
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700662 }
663 // interfaces and annotations
664 } else if ("interfaceDeclaration".equals(member.toString())) {
665 Object tmp = member.getChild(0);
666
667 if ("normalInterfaceDeclaration".equals(tmp.toString())) {
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700668 cls.addInnerClass(buildInterface((ParseTree) tmp, cls));
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700669 } else if ("annotationTypeDeclaration".equals(tmp.toString())) {
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700670 cls.addInnerClass(buildAnnotationDeclaration((ParseTree) tmp, cls));
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700671 }
672 }
673 }
674 }
675
676 /**
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700677 * Builds one or more FieldInfos for the field declared in this class.
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700678 * @param tree The tree to parse. fieldDeclaration should be the root value.
679 * @param containingClass The ClassInfo in which this field is contained.
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700680 * @return A list of FieldInfos for this field declaration.
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700681 */
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700682 private ArrayList<FieldInfo> buildFields(ParseTree tree, ClassInfo containingClass) {
683 ArrayList<FieldInfo> fields = new ArrayList<FieldInfo>();
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700684 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
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700705 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();
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700721
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700722 // 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++);
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700726
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700727 // if we have dimensions in the wrong place
728 if ("[".equals(tmp.toString())) {
729 StringBuilder builder = new StringBuilder();
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700730
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700731 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);
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700762 }
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700763
764 child = it.next();
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700765 }
766
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700767 return fields;
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700768 }
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())) {
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700780 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);
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700797 } 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) {
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700876 addFutureResolution(type, "class", simpleTypeName, this);
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700877 }
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") &&
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700896 !o.toString().equals("typeArgument")) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700897 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
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07001001 // this is the method name
1002 name = child.toString();
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001003
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07001004 if (name.equals(containingClass.name())) {
1005 kind = "constructor";
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001006 }
1007
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07001008 // 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
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001018 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())) {
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07001047 pendingResolutions.add(new Resolution("thrownException", exceptionName, null));
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001048 } 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) {
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07001080 addFutureResolution(method, r.getVariable(), r.getValue(), this);
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001081 }
1082
1083 return method;
1084 }
1085
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001086 /**
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
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07001110 @SuppressWarnings("unchecked")
1111 Iterator<ParseTree> it = (Iterator<ParseTree>) param.getChildren().iterator();
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001112
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07001113 ParseTree paramPart = it.next();
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001114
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07001115 if ("variableModifiers".equals(paramPart.toString())) {
1116 // TODO - handle variable modifiers - final, etc
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001117 }
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07001118
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()));
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001146 }
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
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07001203 child = it.next();
1204
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001205 // handle implements
1206 if ("implements".equals(child.toString())) {
1207 child = it.next();
1208
1209 parseInterfaces(child, cls);
1210
1211 child = it.next();
1212 }
1213
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001214 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) {
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07001250 @SuppressWarnings("unchecked")
1251 Iterator<ParseTree> it = (Iterator<ParseTree>) tree.getChildren().iterator();
1252 ParseTree child = it.next();
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001253
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07001254 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();
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001261 CommentAndPosition commentAndPosition = new CommentAndPosition();
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07001262 commentAndPosition.setCommentText(child);
1263 commentAndPosition.setPosition(child);
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001264 Object constantValue = null;
1265
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07001266 // 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 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001288
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(),
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07001295 modifiers.getAnnotations());
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001296 }
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
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07001359 if (";".equals(child.toString())) {
1360 continue;
1361 }
1362
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001363 // field
1364 if ("interfaceFieldDeclaration".equals(child.toString())) {
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07001365 for (FieldInfo f : buildFields(child, iface)) {
1366 iface.addField(f);
1367 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001368 // 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())) {
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07001441 for (FieldInfo f : buildFields(child, annotation)) {
1442 annotation.addField(f);
1443 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001444 // annotation methods
1445 } else if ("annotationMethodDeclaration".equals(child.toString())) {
1446 annotation.addAnnotationElement(buildMethod(child, annotation, true));
1447 // inner class
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07001448 } else if ("normalClassDeclaration".equals(child.toString())) {
1449 annotation.addInnerClass(buildClass((ParseTree) child, annotation));
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001450 // enum
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07001451 } else if ("enumDeclaration".equals(child.toString())) {
1452 annotation.addInnerClass(buildEnum((ParseTree) child, annotation));
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001453 // inner interface
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07001454 } else if ("normalInterfaceDeclaration".equals(child.toString())) {
1455 annotation.addInnerClass(buildInterface((ParseTree) child, annotation));
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001456 // inner annotation
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07001457 } else if ("annotationTypeDeclaration".equals(child.toString())) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001458 annotation.addInnerClass(buildAnnotationDeclaration(
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07001459 (ParseTree) child, annotation));
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001460 }
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())) {
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07001485 addFutureResolution(annotationInstance, "annotationTypeName", name, builder);
1486 annotationInstance.setSimpleAnnotationName(name); // TODO - remove once we've completed the parser
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001487 } 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) {
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07001512 for (MethodInfo m : annotationInstance.type().annotationElements()) {
1513 if (methodName.equals(m.name()) ||
1514 annotationInstance.type().annotationElements().size() == 1) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001515 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) {
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07001526 addFutureResolution(info, "element", methodName, builder);
1527 info.setAnnotationInstanceName(name);
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001528 } 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
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07001600 * @param builder The InfoBuilder of this file
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001601 */
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07001602 private static void addFutureResolution(Resolvable resolvable, String variable,
1603 String value, InfoBuilder builder) {
1604 resolvable.addResolution(new Resolution(variable, value, builder));
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001605
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 */
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07001619 public static boolean resolveQualifiedName(String name,
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001620 StringBuilder qualifiedClassName,
1621 InfoBuilder builder) {
1622 // steps to figure out a class's real name
1623 // check class(es) in this file
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07001624
1625 // trying something out. let's see how this works
1626 if (name.indexOf('.') != -1) {
1627 qualifiedClassName.append(name);
1628 return false;
1629 }
1630
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001631 // 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
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07001651 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
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001664 // 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('.')));
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07001677 } 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 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001686 }
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
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07001695 if (cls != null && name.equals(cls.name())) {
1696 qualifiedClassName.append(cls.qualifiedName());
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001697 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
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07001713 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
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001770 /**
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 */
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07001960 public static class Caches {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001961 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
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07001979 /**
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 */
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001984 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);
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07001990 }
1991
1992 return cls;
1993 }
1994
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07001995 /**
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
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07002004 public static void addResolvableToCache(Resolvable resolvable) {
2005 mInfosToResolve.add(resolvable);
2006 }
2007
2008 public static void printResolutions() {
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07002009 if (mInfosToResolve.isEmpty()) {
2010 System.out.println("We've resolved everything.");
2011 return;
2012 }
2013
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07002014 for (Resolvable r : mInfosToResolve) {
2015 r.printResolutions();
2016 System.out.println();
2017 }
2018 }
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07002019
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 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07002033 }
2034
2035 public PackageInfo getPackage() {
2036 return mPackage;
2037 }
2038
2039 public ArrayList<String> getImports() {
2040 return mImports;
2041 }
2042
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07002043 public HashSet<String> getClassNames() {
2044 return mClassNames;
2045 }
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -07002046
2047 public ClassInfo getRootClass() {
2048 return mRootClass;
2049 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -07002050}