1package annotations.io; 2 3import java.util.ArrayDeque; 4import java.util.ArrayList; 5import java.util.Deque; 6import java.util.HashMap; 7import java.util.List; 8import java.util.Map; 9 10import javax.lang.model.element.Name; 11 12import com.google.common.collect.BiMap; 13import com.google.common.collect.HashBiMap; 14import com.sun.source.tree.AnnotatedTypeTree; 15import com.sun.source.tree.AnnotationTree; 16import com.sun.source.tree.ArrayAccessTree; 17import com.sun.source.tree.ArrayTypeTree; 18import com.sun.source.tree.AssertTree; 19import com.sun.source.tree.AssignmentTree; 20import com.sun.source.tree.BinaryTree; 21import com.sun.source.tree.BlockTree; 22import com.sun.source.tree.CaseTree; 23import com.sun.source.tree.CatchTree; 24import com.sun.source.tree.ClassTree; 25import com.sun.source.tree.CompilationUnitTree; 26import com.sun.source.tree.CompoundAssignmentTree; 27import com.sun.source.tree.ConditionalExpressionTree; 28import com.sun.source.tree.DoWhileLoopTree; 29import com.sun.source.tree.EnhancedForLoopTree; 30import com.sun.source.tree.ExpressionStatementTree; 31import com.sun.source.tree.ExpressionTree; 32import com.sun.source.tree.ForLoopTree; 33import com.sun.source.tree.IfTree; 34import com.sun.source.tree.InstanceOfTree; 35import com.sun.source.tree.IntersectionTypeTree; 36import com.sun.source.tree.LabeledStatementTree; 37import com.sun.source.tree.LambdaExpressionTree; 38import com.sun.source.tree.MemberReferenceTree; 39import com.sun.source.tree.MemberSelectTree; 40import com.sun.source.tree.MethodInvocationTree; 41import com.sun.source.tree.MethodTree; 42import com.sun.source.tree.ModifiersTree; 43import com.sun.source.tree.NewArrayTree; 44import com.sun.source.tree.NewClassTree; 45import com.sun.source.tree.ParameterizedTypeTree; 46import com.sun.source.tree.ParenthesizedTree; 47import com.sun.source.tree.ReturnTree; 48import com.sun.source.tree.SwitchTree; 49import com.sun.source.tree.SynchronizedTree; 50import com.sun.source.tree.ThrowTree; 51import com.sun.source.tree.Tree; 52import com.sun.source.tree.Tree.Kind; 53import com.sun.source.tree.TryTree; 54import com.sun.source.tree.TypeCastTree; 55import com.sun.source.tree.TypeParameterTree; 56import com.sun.source.tree.UnaryTree; 57import com.sun.source.tree.UnionTypeTree; 58import com.sun.source.tree.VariableTree; 59import com.sun.source.tree.WhileLoopTree; 60import com.sun.source.tree.WildcardTree; 61import com.sun.source.util.SimpleTreeVisitor; 62import com.sun.source.util.TreePath; 63import com.sun.tools.javac.code.Symbol.ClassSymbol; 64import com.sun.tools.javac.tree.JCTree; 65 66import annotations.util.JVMNames; 67import annotations.util.coll.WrapperMap; 68 69/** 70 * Cache of {@code ASTPath} data for the nodes of a compilation unit tree. 71 * 72 * @author dbro 73 */ 74public class ASTIndex extends WrapperMap<Tree, ASTRecord> { 75 // single-item cache 76 private static Tree cachedRoot = null; 77 private static Map<Tree, ASTRecord> cachedIndex = null; 78 private static final int EXPECTED_SIZE = 128; 79 80 private final CompilationUnitTree cut; 81 private final Map<String, Map<String, List<String>>> formals; 82 83 /** 84 * Maps source trees in compilation unit to corresponding AST paths. 85 * 86 * @param root compilation unit to be indexed 87 * @return map of trees in compilation unit to AST paths 88 */ 89 public static Map<Tree, ASTRecord> indexOf(CompilationUnitTree root) { 90 if (cachedRoot == null || !cachedRoot.equals(root)) { 91 cachedRoot = root; 92 cachedIndex = new ASTIndex(root); 93 } 94 return cachedIndex; 95 } 96 97 private ASTIndex(CompilationUnitTree root) { 98 super(HashBiMap.<Tree, ASTRecord>create(EXPECTED_SIZE)); 99 cut = root; 100 formals = new HashMap<String, Map<String, List<String>>>(); 101 102 // The visitor implementation is slightly complicated by the 103 // inclusion of information from both parent and child nodes in each 104 // ASTEntry. The pattern for most node types is to call save() and 105 // saveAll() as needed to handle the node's descendants and finally 106 // to invoke defaultAction() to save the entry for the current node. 107 // (If the JVM could take advantage of tail recursion, it would be 108 // better to save the current node's entry first, at a small cost to 109 // the clarity of the code.) 110 cut.accept(new SimpleTreeVisitor<Void, ASTRecord>() { 111 Deque<Integer> counters = new ArrayDeque<Integer>(); 112 String inMethod = null; 113 114 private void save(Tree node, ASTRecord rec, 115 Kind kind, String sel) { 116 if (node != null) { 117 node.accept(this, rec.extend(kind, sel)); 118 } 119 } 120 121 private void save(Tree node, ASTRecord rec, 122 Kind kind, String sel, int arg) { 123 if (node != null) { 124 node.accept(this, rec.extend(kind, sel, arg)); 125 } 126 } 127 128 private void saveAll(Iterable<? extends Tree> nodes, 129 ASTRecord rec, Kind kind, String sel) { 130 if (nodes != null) { 131 int i = 0; 132 for (Tree node : nodes) { 133 save(node, rec, kind, sel, i++); 134 } 135 } 136 } 137 138 private void saveClass(ClassTree node) { 139 String className = 140 ((JCTree.JCClassDecl) node).sym.flatname.toString(); 141 ASTRecord rec = 142 new ASTRecord(cut, className, null, null, ASTPath.empty()); 143 counters.push(0); 144 node.accept(this, rec); 145 counters.pop(); 146 } 147 148 @Override 149 public Void defaultAction(Tree node, ASTRecord rec) { 150 switch (node.getKind()) { 151 case BREAK: 152 case COMPILATION_UNIT: 153 case CONTINUE: 154 case IMPORT: 155 case MODIFIERS: 156 break; // not handled 157 default: 158 put(node, rec); 159 } 160 return null; 161 } 162 163 @Override 164 public Void visitAnnotatedType(AnnotatedTypeTree node, 165 ASTRecord rec) { 166 Kind kind = node.getKind(); 167 saveAll(node.getAnnotations(), rec, kind, ASTPath.ANNOTATION); 168 save(node.getUnderlyingType(), rec, kind, ASTPath.UNDERLYING_TYPE); 169 return defaultAction(node, rec); 170 } 171 172 @Override 173 public Void visitAnnotation(AnnotationTree node, 174 ASTRecord rec) { 175 Kind kind = node.getKind(); 176 save(node.getAnnotationType(), rec, kind, ASTPath.TYPE); 177 saveAll(node.getArguments(), rec, kind, ASTPath.ARGUMENT); 178 return defaultAction(node, rec); 179 } 180 181 @Override 182 public Void visitMethodInvocation(MethodInvocationTree node, 183 ASTRecord rec) { 184 Kind kind = node.getKind(); 185 saveAll(node.getTypeArguments(), rec, kind, ASTPath.TYPE_ARGUMENT); 186 save(node.getMethodSelect(), rec, kind, ASTPath.METHOD_SELECT); 187 saveAll(node.getArguments(), rec, kind, ASTPath.ARGUMENT); 188 return defaultAction(node, rec); 189 } 190 191 @Override 192 public Void visitAssert(AssertTree node, ASTRecord rec) { 193 Kind kind = node.getKind(); 194 save(node.getCondition(), rec, kind, ASTPath.CONDITION); 195 save(node.getDetail(), rec, kind, ASTPath.DETAIL); 196 return defaultAction(node, rec); 197 } 198 199 @Override 200 public Void visitAssignment(AssignmentTree node, ASTRecord rec) { 201 Kind kind = node.getKind(); 202 save(node.getExpression(), rec, kind, ASTPath.EXPRESSION); 203 save(node.getVariable(), rec, kind, ASTPath.VARIABLE); 204 return defaultAction(node, rec); 205 } 206 207 @Override 208 public Void visitCompoundAssignment(CompoundAssignmentTree node, 209 ASTRecord rec) { 210 Kind kind = node.getKind(); 211 save(node.getExpression(), rec, kind, ASTPath.EXPRESSION); 212 save(node.getVariable(), rec, kind, ASTPath.VARIABLE); 213 return defaultAction(node, rec); 214 } 215 216 @Override 217 public Void visitBinary(BinaryTree node, ASTRecord rec) { 218 Kind kind = node.getKind(); 219 save(node.getLeftOperand(), rec, kind, ASTPath.LEFT_OPERAND); 220 save(node.getRightOperand(), rec, kind, ASTPath.RIGHT_OPERAND); 221 return defaultAction(node, rec); 222 } 223 224 @Override 225 public Void visitBlock(BlockTree node, ASTRecord rec) { 226 Iterable<? extends Tree> nodes = node.getStatements(); 227 if (nodes != null) { 228 int i = 0; 229 for (Tree stmt : nodes) { 230 if (ASTPath.isClassEquiv(stmt.getKind())) { 231 saveClass((ClassTree) stmt); 232 } else { 233 save(stmt, rec, node.getKind(), ASTPath.STATEMENT, i); 234 } 235 ++i; 236 } 237 } 238 return defaultAction(node, rec); 239 } 240 241 @Override 242 public Void visitCase(CaseTree node, ASTRecord rec) { 243 Kind kind = node.getKind(); 244 save(node.getExpression(), rec, kind, ASTPath.EXPRESSION); 245 saveAll(node.getStatements(), rec, kind, ASTPath.STATEMENT); 246 return defaultAction(node, rec); 247 } 248 249 @Override 250 public Void visitCatch(CatchTree node, ASTRecord rec) { 251 Kind kind = node.getKind(); 252 save(node.getBlock(), rec, kind, ASTPath.BLOCK); 253 save(node.getParameter(), rec, kind, ASTPath.PARAMETER); 254 return defaultAction(node, rec); 255 } 256 257 @Override 258 public Void visitClass(ClassTree node, ASTRecord rec) { 259 Kind kind = Tree.Kind.CLASS; // use for all class-equivalent kinds 260 int i = 0; 261 formals.put(rec.className, new HashMap<String, List<String>>()); 262 if (node.getSimpleName().length() > 0) { 263 // don't save exts/impls for anonymous inner class 264 save(node.getExtendsClause(), rec, kind, ASTPath.BOUND, -1); 265 saveAll(node.getImplementsClause(), rec, kind, ASTPath.BOUND); 266 } 267 saveAll(node.getTypeParameters(), rec, kind, ASTPath.TYPE_PARAMETER); 268 for (Tree member : node.getMembers()) { 269 if (member.getKind() == Tree.Kind.BLOCK) { 270 save(member, rec, kind, ASTPath.INITIALIZER, i++); 271 } else if (ASTPath.isClassEquiv(member.getKind())) { 272 String className = 273 ((JCTree.JCClassDecl) member).sym.flatname.toString(); 274 member.accept(this, 275 new ASTRecord(cut, className, null, null, ASTPath.empty())); 276 } else { 277 member.accept(this, rec); 278 } 279 } 280 return defaultAction(node, rec); 281 } 282 283 @Override 284 public Void visitConditionalExpression(ConditionalExpressionTree node, 285 ASTRecord rec) { 286 Kind kind = node.getKind(); 287 save(node.getCondition(), rec, kind, ASTPath.CONDITION); 288 save(node.getFalseExpression(), rec, kind, ASTPath.FALSE_EXPRESSION); 289 save(node.getTrueExpression(), rec, kind, ASTPath.TRUE_EXPRESSION); 290 return defaultAction(node, rec); 291 } 292 293 @Override 294 public Void visitDoWhileLoop(DoWhileLoopTree node, 295 ASTRecord rec) { 296 Kind kind = node.getKind(); 297 save(node.getCondition(), rec, kind, ASTPath.CONDITION); 298 save(node.getStatement(), rec, kind, ASTPath.STATEMENT); 299 return defaultAction(node, rec); 300 } 301 302 @Override 303 public Void visitExpressionStatement(ExpressionStatementTree node, 304 ASTRecord rec) { 305 save(node.getExpression(), rec, node.getKind(), ASTPath.EXPRESSION); 306 return defaultAction(node, rec); 307 } 308 309 @Override 310 public Void visitEnhancedForLoop(EnhancedForLoopTree node, 311 ASTRecord rec) { 312 Kind kind = node.getKind(); 313 save(node.getVariable(), rec, kind, ASTPath.VARIABLE); 314 save(node.getExpression(), rec, kind, ASTPath.EXPRESSION); 315 save(node.getStatement(), rec, kind, ASTPath.STATEMENT); 316 return defaultAction(node, rec); 317 } 318 319 @Override 320 public Void visitForLoop(ForLoopTree node, ASTRecord rec) { 321 Kind kind = node.getKind(); 322 saveAll(node.getInitializer(), rec, kind, ASTPath.INITIALIZER); 323 save(node.getCondition(), rec, kind, ASTPath.CONDITION); 324 save(node.getStatement(), rec, kind, ASTPath.STATEMENT); 325 saveAll(node.getUpdate(), rec, kind, ASTPath.UPDATE); 326 return defaultAction(node, rec); 327 } 328 329 @Override 330 public Void visitIf(IfTree node, ASTRecord rec) { 331 Kind kind = node.getKind(); 332 save(node.getCondition(), rec, kind, ASTPath.CONDITION); 333 save(node.getThenStatement(), rec, kind, ASTPath.THEN_STATEMENT); 334 save(node.getElseStatement(), rec, kind, ASTPath.ELSE_STATEMENT); 335 return defaultAction(node, rec); 336 } 337 338 @Override 339 public Void visitArrayAccess(ArrayAccessTree node, 340 ASTRecord rec) { 341 Kind kind = node.getKind(); 342 save(node.getExpression(), rec, kind, ASTPath.EXPRESSION); 343 save(node.getIndex(), rec, kind, ASTPath.INDEX); 344 return defaultAction(node, rec); 345 } 346 347 @Override 348 public Void visitLabeledStatement(LabeledStatementTree node, 349 ASTRecord rec) { 350 save(node.getStatement(), rec, node.getKind(), ASTPath.STATEMENT); 351 return defaultAction(node, rec); 352 } 353 354 @Override 355 public Void visitMethod(MethodTree node, ASTRecord rec) { 356 Kind kind = node.getKind(); 357 Tree rcvr = node.getReceiverParameter(); 358 ModifiersTree mods = node.getModifiers(); 359 List<? extends Tree> params = node.getParameters(); 360 String outMethod = inMethod; 361 inMethod = JVMNames.getJVMMethodName(node); 362 rec = new ASTRecord(cut, rec.className, inMethod, null, 363 ASTPath.empty()); 364 if (mods != null) { 365 save(mods, rec, kind, ASTPath.MODIFIERS); 366 } 367 if (rcvr != null) { 368 rcvr.accept(this, rec.extend(kind, ASTPath.PARAMETER, -1)); 369 } 370 if (params != null && !params.isEmpty()) { 371 Map<String, List<String>> map = formals.get(rec.className); 372 List<String> names = new ArrayList<String>(params.size()); 373 int i = 0; 374 map.put(inMethod, names); 375 for (Tree param : params) { 376 if (param != null) { 377 names.add(((VariableTree) param).getName().toString()); 378 param.accept(this, 379 rec.extend(Tree.Kind.METHOD, ASTPath.PARAMETER, i++)); 380 } 381 } 382 } 383 save(node.getReturnType(), rec, kind, ASTPath.TYPE); 384 saveAll(node.getTypeParameters(), rec, kind, ASTPath.TYPE_PARAMETER); 385 // save(node.getReceiverParameter(), rec, kind, ASTPath.PARAMETER, -1); 386 // saveAll(node.getParameters(), rec, kind, ASTPath.PARAMETER); 387 saveAll(node.getThrows(), rec, kind, ASTPath.THROWS); 388 save(node.getBody(), rec, kind, ASTPath.BODY); 389 inMethod = outMethod; 390 return defaultAction(node, rec); 391 } 392 393 @Override 394 public Void visitModifiers(ModifiersTree node, ASTRecord rec) { 395 Kind kind = node.getKind(); 396 saveAll(node.getAnnotations(), rec, kind, ASTPath.ANNOTATION); 397 return defaultAction(node, rec); 398 } 399 400 @Override 401 public Void visitNewArray(NewArrayTree node, ASTRecord rec) { 402 Kind kind = node.getKind(); 403 Tree type = node.getType(); 404 int n = node.getDimensions().size(); 405 do { 406 save(type, rec, kind, ASTPath.TYPE, n); 407 } while (--n > 0); 408 saveAll(node.getDimensions(), rec, kind, ASTPath.DIMENSION); 409 saveAll(node.getInitializers(), rec, kind, ASTPath.INITIALIZER); 410 return defaultAction(node, rec); 411 } 412 413 @Override 414 public Void visitNewClass(NewClassTree node, ASTRecord rec) { 415 JCTree.JCClassDecl classBody = 416 (JCTree.JCClassDecl) node.getClassBody(); 417 Kind kind = node.getKind(); 418 save(node.getEnclosingExpression(), rec, kind, 419 ASTPath.ENCLOSING_EXPRESSION); 420 saveAll(node.getTypeArguments(), rec, kind, ASTPath.TYPE_ARGUMENT); 421 save(node.getIdentifier(), rec, kind, ASTPath.IDENTIFIER); 422 saveAll(node.getArguments(), rec, kind, ASTPath.ARGUMENT); 423 if (classBody != null) { 424 Name name = classBody.getSimpleName(); 425 String className = null; 426 if (name == null || name.toString().isEmpty()) { 427 int i = counters.pop(); 428 counters.push(++i); 429 className = rec.className + "$" + i; 430 } else { 431 ClassSymbol sym = ((JCTree.JCClassDecl) classBody).sym; 432 String s = sym == null ? "" : sym.toString(); 433 if (s.startsWith("<anonymous ")) { 434 int i = counters.pop(); 435 counters.push(++i); 436 className = s.substring(11, s.length()-1); 437 } else { 438 className = rec.className + "$" + name; 439 } 440 } 441 counters.push(0); 442 classBody.accept(this, 443 new ASTRecord(cut, className, null, null, ASTPath.empty())); 444 counters.pop(); 445 } 446 return defaultAction(node, rec); 447 } 448 449 @Override 450 public Void visitLambdaExpression(LambdaExpressionTree node, 451 ASTRecord rec) { 452 Kind kind = node.getKind(); 453 String outMethod = inMethod; 454 Iterable<? extends Tree> nodes = node.getParameters(); 455 if (nodes != null) { 456 int i = 0; 457 for (Tree t : nodes) { 458 ASTRecord newRec = rec.extend(kind, ASTPath.PARAMETER, i++); 459 Tree.Kind newKind = t.getKind(); 460 if (newKind == Tree.Kind.VARIABLE) { 461 VariableTree vt = (VariableTree) t; 462 save(vt.getType(), newRec, newKind, ASTPath.TYPE); 463 save(vt.getInitializer(), newRec, newKind, ASTPath.INITIALIZER); 464 defaultAction(vt, newRec); 465 } else { 466 t.accept(this, rec.extend(kind, ASTPath.PARAMETER, i++)); 467 } 468 } 469 } 470 save(node.getBody(), rec, kind, ASTPath.BODY); 471 inMethod = outMethod; 472 return defaultAction(node, rec); 473 } 474 475 @Override 476 public Void visitParenthesized(ParenthesizedTree node, 477 ASTRecord rec) { 478 save(node.getExpression(), rec, node.getKind(), ASTPath.EXPRESSION); 479 return defaultAction(node, rec); 480 } 481 482 @Override 483 public Void visitReturn(ReturnTree node, ASTRecord rec) { 484 save(node.getExpression(), rec, node.getKind(), ASTPath.EXPRESSION); 485 return defaultAction(node, rec); 486 } 487 488 @Override 489 public Void visitMemberSelect(MemberSelectTree node, 490 ASTRecord rec) { 491 save(node.getExpression(), rec, node.getKind(), ASTPath.EXPRESSION); 492 return defaultAction(node, rec); 493 } 494 495 @Override 496 public Void visitMemberReference(MemberReferenceTree node, 497 ASTRecord rec) { 498 Kind kind = node.getKind(); 499 save(node.getQualifierExpression(), rec, kind, 500 ASTPath.QUALIFIER_EXPRESSION); 501 saveAll(node.getTypeArguments(), rec, kind, ASTPath.TYPE_ARGUMENT); 502 return defaultAction(node, rec); 503 } 504 505 @Override 506 public Void visitSwitch(SwitchTree node, ASTRecord rec) { 507 Kind kind = node.getKind(); 508 save(node.getExpression(), rec, kind, ASTPath.EXPRESSION); 509 saveAll(node.getCases(), rec, kind, ASTPath.CASE); 510 return defaultAction(node, rec); 511 } 512 513 @Override 514 public Void visitSynchronized(SynchronizedTree node, 515 ASTRecord rec) { 516 Kind kind = node.getKind(); 517 save(node.getExpression(), rec, kind, ASTPath.EXPRESSION); 518 save(node.getBlock(), rec, kind, ASTPath.BLOCK); 519 return defaultAction(node, rec); 520 } 521 522 @Override 523 public Void visitThrow(ThrowTree node, ASTRecord rec) { 524 save(node.getExpression(), rec, node.getKind(), ASTPath.EXPRESSION); 525 return defaultAction(node, rec); 526 } 527 528 @Override 529 public Void visitCompilationUnit(CompilationUnitTree node, 530 ASTRecord rec) { 531 for (Tree tree : node.getTypeDecls()) { 532 if (ASTPath.isClassEquiv(tree.getKind())) { 533 saveClass((ClassTree) tree); 534 } 535 } 536 return null; 537 } 538 539 @Override 540 public Void visitTry(TryTree node, ASTRecord rec) { 541 Kind kind = node.getKind(); 542 saveAll(node.getResources(), rec, kind, ASTPath.RESOURCE); 543 save(node.getBlock(), rec, kind, ASTPath.BLOCK); 544 saveAll(node.getCatches(), rec, kind, ASTPath.CATCH); 545 save(node.getFinallyBlock(), rec, kind, ASTPath.FINALLY_BLOCK); 546 return defaultAction(node, rec); 547 } 548 549 @Override 550 public Void visitParameterizedType(ParameterizedTypeTree node, 551 ASTRecord rec) { 552 Kind kind = node.getKind(); 553 save(node.getType(), rec, kind, ASTPath.TYPE); 554 saveAll(node.getTypeArguments(), rec, kind, ASTPath.TYPE_ARGUMENT); 555 return defaultAction(node, rec); 556 } 557 558 @Override 559 public Void visitUnionType(UnionTypeTree node, ASTRecord rec) { 560 saveAll(node.getTypeAlternatives(), rec, node.getKind(), 561 ASTPath.TYPE_ALTERNATIVE); 562 return defaultAction(node, rec); 563 } 564 565 @Override 566 public Void visitIntersectionType(IntersectionTypeTree node, 567 ASTRecord rec) { 568 saveAll(node.getBounds(), rec, node.getKind(), ASTPath.BOUND); 569 return defaultAction(node, rec); 570 } 571 572 @Override 573 public Void visitArrayType(ArrayTypeTree node, ASTRecord rec) { 574 save(node.getType(), rec, node.getKind(), ASTPath.TYPE); 575 return defaultAction(node, rec); 576 } 577 578 @Override 579 public Void visitTypeCast(TypeCastTree node, ASTRecord rec) { 580 Kind kind = node.getKind(); 581 save(node.getType(), rec, kind, ASTPath.TYPE); 582 save(node.getExpression(), rec, kind, ASTPath.EXPRESSION); 583 return defaultAction(node, rec); 584 } 585 586 @Override 587 public Void visitTypeParameter(TypeParameterTree node, 588 ASTRecord rec) { 589 saveAll(node.getBounds(), rec, node.getKind(), ASTPath.BOUND); 590 return defaultAction(node, rec); 591 } 592 593 @Override 594 public Void visitInstanceOf(InstanceOfTree node, ASTRecord rec) { 595 Kind kind = node.getKind(); 596 save(node.getExpression(), rec, kind, ASTPath.EXPRESSION); 597 save(node.getType(), rec, kind, ASTPath.TYPE); 598 return defaultAction(node, rec); 599 } 600 601 @Override 602 public Void visitUnary(UnaryTree node, ASTRecord rec) { 603 save(node.getExpression(), rec, node.getKind(), ASTPath.EXPRESSION); 604 return defaultAction(node, rec); 605 } 606 607 @Override 608 public Void visitVariable(VariableTree node, ASTRecord rec) { 609 Kind kind = node.getKind(); 610 if (rec.methodName == null) { // member field 611 rec = new ASTRecord(cut, rec.className, rec.methodName, 612 ((VariableTree) node).getName().toString(), rec.astPath); 613 } 614 save(node.getType(), rec, kind, ASTPath.TYPE); 615 save(node.getInitializer(), rec, kind, ASTPath.INITIALIZER); 616 return defaultAction(node, rec); 617 } 618 619 @Override 620 public Void visitWhileLoop(WhileLoopTree node, ASTRecord rec) { 621 Kind kind = node.getKind(); 622 save(node.getCondition(), rec, kind, ASTPath.CONDITION); 623 save(node.getStatement(), rec, kind, ASTPath.STATEMENT); 624 return defaultAction(node, rec); 625 } 626 627 @Override 628 public Void visitWildcard(WildcardTree node, ASTRecord rec) { 629 save(node.getBound(), rec, node.getKind(), ASTPath.BOUND); 630 return defaultAction(node, rec); 631 } 632 }, null); 633 } 634 635 public static ASTRecord getASTPath(CompilationUnitTree cut, Tree node) { 636 return indexOf(cut).get(node); 637 } 638 639 public static TreePath getTreePath(CompilationUnitTree cut, ASTRecord rec) { 640 Tree node = getNode(cut, rec); 641 return node == null ? null : TreePath.getPath(cut, node); 642 } 643 644 public static Tree getNode(CompilationUnitTree cut, ASTRecord rec) { 645 Map<Tree, ASTRecord> fwdIndex = ((ASTIndex) indexOf(cut)).back; 646 Map<ASTRecord, Tree> revIndex = 647 ((BiMap<Tree, ASTRecord>) fwdIndex).inverse(); 648 ExpressionTree et = cut.getPackageName(); 649 String pkg = et == null ? "" : et.toString(); 650 if (!pkg.isEmpty() && rec.className.indexOf('.') < 0) { 651 rec = new ASTRecord(cut, pkg + "." + rec.className, 652 rec.methodName, rec.varName, rec.astPath); 653 } 654 return revIndex.get(rec); 655 } 656 657 public static String getParameterName(CompilationUnitTree cut, 658 String className, String methodName, int index) { 659 try { 660 ASTIndex ai = (ASTIndex) ASTIndex.indexOf(cut); 661 return ai.formals.get(className).get(methodName).get(index); 662 } catch (NullPointerException ex) { 663 return null; 664 } 665 } 666 667 public static Integer getParameterIndex(CompilationUnitTree cut, 668 String className, String methodName, String varName) { 669 if (cut != null && className != null 670 && methodName != null && varName != null) { 671 // if it's already a number, return it 672 try { 673 return Integer.valueOf(varName); 674 } catch (NumberFormatException ex) {} 675 // otherwise, look through parameter list for string 676 try { 677 ASTIndex ai = (ASTIndex) ASTIndex.indexOf(cut); 678 List<String> names = 679 ai.formals.get(className).get(methodName); 680 int i = 0; 681 for (String name : names) { 682 if (varName.equals(name)) { return i; } 683 ++i; 684 } 685 } catch (NullPointerException ex) {} 686 } 687 // not found 688 return null; 689 } 690 691 @Override 692 public String toString() { 693 StringBuilder sb = new StringBuilder(); 694 for (Map.Entry<Tree, ASTRecord> entry : entrySet()) { 695 sb.append(entry.getKey().toString().replaceAll("\\s+", " ")) 696 .append(" # ").append(entry.getValue()).append("\n"); 697 } 698 return sb.toString(); 699 } 700} 701