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