1 package annotations.io; 2 3 import java.io.IOException; 4 import java.io.StreamTokenizer; 5 import java.io.StringReader; 6 import java.text.Collator; 7 import java.util.ArrayList; 8 import java.util.Arrays; 9 import java.util.Comparator; 10 import java.util.Deque; 11 import java.util.Iterator; 12 import java.util.LinkedList; 13 import java.util.List; 14 import java.util.NoSuchElementException; 15 16 import plume.ArraysMDE; 17 import annotations.util.PersistentStack; 18 19 import com.sun.source.tree.AnnotatedTypeTree; 20 import com.sun.source.tree.AnnotationTree; 21 import com.sun.source.tree.ArrayAccessTree; 22 import com.sun.source.tree.ArrayTypeTree; 23 import com.sun.source.tree.AssertTree; 24 import com.sun.source.tree.AssignmentTree; 25 import com.sun.source.tree.BinaryTree; 26 import com.sun.source.tree.BlockTree; 27 import com.sun.source.tree.CaseTree; 28 import com.sun.source.tree.CatchTree; 29 import com.sun.source.tree.ClassTree; 30 import com.sun.source.tree.CompilationUnitTree; 31 import com.sun.source.tree.CompoundAssignmentTree; 32 import com.sun.source.tree.ConditionalExpressionTree; 33 import com.sun.source.tree.DoWhileLoopTree; 34 import com.sun.source.tree.EnhancedForLoopTree; 35 import com.sun.source.tree.ExpressionStatementTree; 36 import com.sun.source.tree.ExpressionTree; 37 import com.sun.source.tree.ForLoopTree; 38 import com.sun.source.tree.IfTree; 39 import com.sun.source.tree.InstanceOfTree; 40 import com.sun.source.tree.LabeledStatementTree; 41 import com.sun.source.tree.LambdaExpressionTree; 42 import com.sun.source.tree.MemberReferenceTree; 43 import com.sun.source.tree.MemberSelectTree; 44 import com.sun.source.tree.MethodInvocationTree; 45 import com.sun.source.tree.MethodTree; 46 import com.sun.source.tree.NewArrayTree; 47 import com.sun.source.tree.NewClassTree; 48 import com.sun.source.tree.ParameterizedTypeTree; 49 import com.sun.source.tree.ParenthesizedTree; 50 import com.sun.source.tree.ReturnTree; 51 import com.sun.source.tree.StatementTree; 52 import com.sun.source.tree.SwitchTree; 53 import com.sun.source.tree.SynchronizedTree; 54 import com.sun.source.tree.ThrowTree; 55 import com.sun.source.tree.Tree; 56 import com.sun.source.tree.TryTree; 57 import com.sun.source.tree.TypeCastTree; 58 import com.sun.source.tree.UnaryTree; 59 import com.sun.source.tree.UnionTypeTree; 60 import com.sun.source.tree.VariableTree; 61 import com.sun.source.tree.WhileLoopTree; 62 import com.sun.source.tree.WildcardTree; 63 import com.sun.source.util.TreePath; 64 65 /** 66 * A path through the AST. 67 */ 68 public class ASTPath extends ConsStack<ASTPath.ASTEntry> 69 implements Comparable<ASTPath>, Iterable<ASTPath.ASTEntry> { 70 private static final ASTPath EMPTY = new ASTPath(); 71 private static final String[] typeSelectors = 72 { "bound", "identifier", "type", "typeAlternative", "typeArgument", 73 "typeParameter", "underlyingType" }; 74 75 // Constants for the various child selectors. 76 public static final String ANNOTATION = "annotation"; 77 public static final String ARGUMENT = "argument"; 78 public static final String BLOCK = "block"; 79 public static final String BODY = "body"; 80 public static final String BOUND = "bound"; 81 public static final String CASE = "case"; 82 public static final String CATCH = "catch"; 83 public static final String CLASS_BODY = "classBody"; 84 public static final String CONDITION = "condition"; 85 public static final String DETAIL = "detail"; 86 public static final String DIMENSION = "dimension"; 87 public static final String ELSE_STATEMENT = "elseStatement"; 88 public static final String ENCLOSING_EXPRESSION = "enclosingExpression"; 89 public static final String EXPRESSION = "expression"; 90 public static final String FALSE_EXPRESSION = "falseExpression"; 91 public static final String FINALLY_BLOCK = "finallyBlock"; 92 public static final String IDENTIFIER = "identifier"; 93 public static final String INDEX = "index"; 94 public static final String INITIALIZER = "initializer"; 95 public static final String LEFT_OPERAND = "leftOperand"; 96 public static final String METHOD_SELECT = "methodSelect"; 97 public static final String MODIFIERS = "modifiers"; 98 public static final String PARAMETER = "parameter"; 99 public static final String QUALIFIER_EXPRESSION = "qualifierExpression"; 100 public static final String RESOURCE = "resource"; 101 public static final String RIGHT_OPERAND = "rightOperand"; 102 public static final String STATEMENT = "statement"; 103 public static final String THEN_STATEMENT = "thenStatement"; 104 public static final String THROWS = "throws"; 105 public static final String TRUE_EXPRESSION = "trueExpression"; 106 public static final String TYPE = "type"; 107 public static final String TYPE_ALTERNATIVE = "typeAlternative"; 108 public static final String TYPE_ARGUMENT = "typeArgument"; 109 public static final String TYPE_PARAMETER = "typeParameter"; 110 public static final String UNDERLYING_TYPE = "underlyingType"; 111 public static final String UPDATE = "update"; 112 public static final String VARIABLE = "variable"; 113 114 /** 115 * A single entry in an AST path. 116 */ 117 public static class ASTEntry implements Comparable<ASTEntry> { 118 private Tree.Kind treeKind; 119 private String childSelector; 120 private Integer argument; 121 122 /** 123 * Constructs a new AST entry. For example, in the entry: 124 * <pre> 125 * {@code 126 * Block.statement 3 127 * }</pre> 128 * the tree kind is "Block", the child selector is "statement", and the 129 * argument is "3". 130 * 131 * @param treeKind the kind of this AST entry 132 * @param childSelector the child selector to this AST entry 133 * @param argument the argument 134 */ ASTEntry(Tree.Kind treeKind, String childSelector, Integer argument)135 public ASTEntry(Tree.Kind treeKind, String childSelector, Integer argument) { 136 this.treeKind = treeKind; 137 this.childSelector = childSelector; 138 this.argument = argument; 139 } 140 141 /** 142 * Constructs a new AST entry, without an argument. 143 * 144 * See {@link #ASTEntry(Tree.Kind, String, Integer)} for an example of the parameters. 145 * 146 * @param treeKind the kind of this AST entry 147 * @param childSelector the child selector to this AST entry 148 */ ASTEntry(Tree.Kind treeKind, String childSelector)149 public ASTEntry(Tree.Kind treeKind, String childSelector) { 150 this(treeKind, childSelector, null); 151 } 152 153 /** 154 * Gets the tree node equivalent kind of this AST entry. For example, in 155 * <pre> 156 * {@code 157 * Block.statement 3 158 * }</pre> 159 * "Block" is the tree kind. 160 * @return the tree kind 161 */ getTreeKind()162 public Tree.Kind getTreeKind() { 163 return treeKind; 164 } 165 166 /** 167 * Gets the child selector of this AST entry. For example, in 168 * <pre> 169 * {@code 170 * Block.statement 3 171 * }</pre> 172 * "statement" is the child selector. 173 * @return the child selector 174 */ getChildSelector()175 public String getChildSelector() { 176 return childSelector; 177 } 178 179 /** 180 * Determines if the given string is equal to this AST path entry's 181 * child selector. 182 * 183 * @param s the string to compare to 184 * @return {@code true} if the string matches the child selector, 185 * {@code false} otherwise. 186 */ childSelectorIs(String s)187 public boolean childSelectorIs(String s) { 188 return childSelector.equals(s); 189 } 190 191 /** 192 * Gets the argument of this AST entry. For example, in 193 * <pre> 194 * {@code 195 * Block.statement 3 196 * }</pre> 197 * "3" is the argument. 198 * @return the argument 199 * @throws IllegalStateException if this AST entry does not have an argument 200 */ getArgument()201 public int getArgument() { 202 if (argument >= (negativeAllowed() ? -1 : 0)) { 203 return argument; 204 } 205 throw new IllegalStateException("Value not set."); 206 } 207 208 /** 209 * Checks that this Entry has an argument. 210 * 211 * @return if this entry has an argument 212 */ hasArgument()213 public boolean hasArgument() { 214 return argument == null ? false 215 : argument >= 0 ? true 216 : negativeAllowed(); 217 } 218 219 // argument < 0 valid for two cases negativeAllowed()220 private boolean negativeAllowed() { 221 switch (treeKind) { 222 case CLASS: 223 return childSelectorIs(ASTPath.BOUND); 224 case METHOD: 225 return childSelectorIs(ASTPath.PARAMETER); 226 default: 227 return false; 228 } 229 } 230 231 @Override compareTo(ASTEntry o)232 public int compareTo(ASTEntry o) { 233 if (o == null) { 234 return 1; 235 } else if (o.childSelector == null) { 236 if (childSelector != null) { return 1; } 237 } else if (childSelector == null) { 238 return -1; 239 } 240 int c = treeKind.compareTo(o.treeKind); 241 if (c != 0) { return c; } 242 c = childSelector.compareTo(o.childSelector); 243 if (c != 0) { return c; } 244 return o.argument == null 245 ? argument == null ? 0 : 1 246 : argument == null ? -1 : argument.compareTo(o.argument); 247 } 248 249 @Override equals(Object o)250 public boolean equals(Object o) { 251 return o instanceof ASTEntry && compareTo((ASTEntry) o) == 0; 252 } 253 254 @Override hashCode()255 public int hashCode() { 256 int base = treeKind.hashCode() ^ childSelector.hashCode(); 257 int shift = argument == null ? 0 : 2 + argument; 258 return Integer.rotateRight(base, shift); 259 } 260 261 @Override toString()262 public String toString() { 263 StringBuilder b = new StringBuilder(); 264 switch (treeKind) { 265 case CLASS: 266 case ENUM: 267 case INTERFACE: 268 b.append("Class"); 269 break; 270 case AND: 271 case CONDITIONAL_AND: 272 case CONDITIONAL_OR: 273 case DIVIDE: 274 case EQUAL_TO: 275 case GREATER_THAN: 276 case GREATER_THAN_EQUAL: 277 case LEFT_SHIFT: 278 case LESS_THAN: 279 case LESS_THAN_EQUAL: 280 case MINUS: 281 case MULTIPLY: 282 case NOT_EQUAL_TO: 283 case OR: 284 case PLUS: 285 case REMAINDER: 286 case RIGHT_SHIFT: 287 case XOR: 288 b.append("Binary"); 289 break; 290 case LOGICAL_COMPLEMENT: 291 case POSTFIX_DECREMENT: 292 case POSTFIX_INCREMENT: 293 case PREFIX_DECREMENT: 294 case PREFIX_INCREMENT: 295 case UNARY_MINUS: 296 case UNARY_PLUS: 297 case UNSIGNED_RIGHT_SHIFT: 298 b.append("Unary"); 299 break; 300 case AND_ASSIGNMENT: 301 case DIVIDE_ASSIGNMENT: 302 case LEFT_SHIFT_ASSIGNMENT: 303 case MINUS_ASSIGNMENT: 304 case MULTIPLY_ASSIGNMENT: 305 case OR_ASSIGNMENT: 306 case PLUS_ASSIGNMENT: 307 case REMAINDER_ASSIGNMENT: 308 case RIGHT_SHIFT_ASSIGNMENT: 309 case UNSIGNED_RIGHT_SHIFT_ASSIGNMENT: 310 case XOR_ASSIGNMENT: 311 b.append("CompoundAssignment"); 312 break; 313 case EXTENDS_WILDCARD: 314 case SUPER_WILDCARD: 315 case UNBOUNDED_WILDCARD: 316 b.append("Wildcard"); 317 break; 318 case ANNOTATION: 319 case TYPE_ANNOTATION: 320 b.append("Annotation"); 321 break; 322 default: 323 String s = treeKind.toString(); 324 int n = s.length(); 325 boolean cap = true; // capitalize next character 326 for (int i = 0; i < n; i++) { 327 char c = s.charAt(i); 328 if (c == '_') { 329 cap = true; 330 } else { 331 b.append(cap ? Character.toUpperCase(c) 332 : Character.toLowerCase(c)); 333 cap = false; 334 } 335 } 336 } 337 b.append(".").append(childSelector); 338 if (argument != null) { b.append(" ").append(argument); } 339 return b.toString(); 340 } 341 } 342 343 private static Comparator<ASTPath> comparator = new Comparator<ASTPath>() { 344 @Override 345 public int compare(ASTPath p1, ASTPath p2) { 346 return p1 == null ? (p2 == null ? 0 : -1) : p1.compareTo(p2); 347 } 348 }; 349 ASTPath()350 ASTPath() {} 351 empty()352 public static ASTPath empty() { return EMPTY; } 353 getComparator()354 public static Comparator<ASTPath> getComparator() { 355 return comparator; 356 } 357 358 // TODO: replace w/ skip list? 359 @Override iterator()360 public Iterator<ASTEntry> iterator() { 361 PersistentStack<ASTEntry> s = this; 362 int n = size(); 363 ASTEntry[] a = new ASTEntry[n]; 364 while (--n >= 0) { 365 a[n] = s.peek(); 366 s = s.pop(); 367 } 368 return Arrays.asList(a).iterator(); 369 } 370 extendNewArray(int depth)371 public ASTPath extendNewArray(int depth) { 372 return extend(new ASTEntry(Tree.Kind.NEW_ARRAY, ASTPath.TYPE, depth)); 373 } 374 newArrayLevel(int depth)375 public ASTPath newArrayLevel(int depth) { 376 return add(new ASTEntry(Tree.Kind.NEW_ARRAY, ASTPath.TYPE, depth)); 377 } 378 add(ASTEntry entry)379 public ASTPath add(ASTEntry entry) { 380 ASTPath path = EMPTY; 381 for (ASTEntry e : this) { path = path.extend(e); } 382 return path.extend(entry); 383 } 384 extend(ASTEntry entry)385 public ASTPath extend(ASTEntry entry) { 386 return (ASTPath) push(entry); 387 } 388 getParentPath()389 public ASTPath getParentPath() { 390 return (ASTPath) pop(); 391 } 392 get(int index)393 public ASTEntry get(int index) { 394 PersistentStack<ASTEntry> s = this; 395 int n = size(); 396 if (index >= n) { 397 throw new NoSuchElementException(Integer.toString(index)); 398 } 399 if (index < 0) { 400 index += n; 401 if (index < 0) { 402 throw new IllegalArgumentException("negative index " + index); 403 } 404 } 405 while (--n > index) { s = s.pop(); } 406 return s.peek(); 407 } 408 409 @Override hashCode()410 public int hashCode() { 411 // hacky fix: remove {Method,Class}.body for comparison 412 PersistentStack<ASTEntry> s = canonical(this); 413 int hash = 0; 414 while (!s.isEmpty()) { 415 hash = Integer.rotateRight(hash ^ s.peek().hashCode(), 1); 416 s = s.pop(); 417 } 418 return hash; 419 } 420 421 @Override equals(Object o)422 public boolean equals(Object o) { 423 return o instanceof ASTPath && equals((ASTPath) o); 424 } 425 equals(ASTPath astPath)426 public boolean equals(ASTPath astPath) { 427 return compareTo(astPath) == 0; 428 } 429 430 @Override compareTo(ASTPath o)431 public int compareTo(ASTPath o) { 432 // hacky fix: remove {Method,Class}.body for comparison 433 PersistentStack<ASTEntry> s0 = canonical(this); 434 PersistentStack<ASTEntry> s1 = canonical(o); 435 Deque<ASTEntry> d0 = new LinkedList<ASTEntry>(); 436 Deque<ASTEntry> d1 = new LinkedList<ASTEntry>(); 437 int c = 0; 438 while (!s0.isEmpty()) { 439 d0.push(s0.peek()); 440 s0 = s0.pop(); 441 } 442 while (!s1.isEmpty()) { 443 d1.push(s1.peek()); 444 s1 = s1.pop(); 445 } 446 int n0 = d0.size(); 447 int n1 = d1.size(); 448 c = Integer.compare(n0, n1); 449 if (c == 0) { 450 Iterator<ASTEntry> i0 = d0.iterator(); 451 Iterator<ASTEntry> i1 = d1.iterator(); 452 while (i0.hasNext()) { 453 c = i0.next().compareTo(i1.next()); 454 if (c != 0) { return c; } 455 } 456 } 457 return c; 458 } 459 canonical(ASTPath astPath)460 private static ASTPath canonical(ASTPath astPath) { 461 // TODO 462 return astPath; 463 } 464 465 @Override toString()466 public String toString() { 467 if (isEmpty()) { return ""; } 468 Iterator<ASTEntry> iter = iterator(); 469 StringBuilder sb = new StringBuilder().append(iter.next()); 470 while (iter.hasNext()) { 471 sb = sb.append(", ").append(iter.next()); 472 } 473 return sb.toString(); 474 } 475 476 /** 477 * Create a new {@code ASTPath} from a formatted string description. 478 * 479 * @param s formatted string as in JAIF {@code insert-\{cast,annotation\}} 480 * @return the corresponding {@code ASTPath} 481 * @throws ParseException 482 */ parse(final String s)483 public static ASTPath parse(final String s) throws ParseException { 484 return new Parser(s).parseASTPath(); 485 } 486 487 /** 488 * Determine whether this {@code ASTPath} matches a given {@code TreePath}. 489 */ matches(TreePath treePath)490 public boolean matches(TreePath treePath) { 491 CompilationUnitTree cut = treePath.getCompilationUnit(); 492 Tree leaf = treePath.getLeaf(); 493 ASTPath astPath = ASTIndex.indexOf(cut).get(leaf).astPath; // FIXME 494 return this.equals(astPath); 495 } 496 497 static class Parser { 498 // adapted from annotations.io.IndexFileParser 499 // TODO: refactor IndexFileParser to use this class 500 501 StreamTokenizer st; 502 Parser(String s)503 Parser(String s) { 504 st = new StreamTokenizer(new StringReader(s)); 505 } 506 getTok()507 private void getTok() { 508 try { 509 st.nextToken(); 510 } catch (IOException e) { 511 throw new RuntimeException(e); 512 } 513 } 514 gotType(int t)515 private boolean gotType(int t) { 516 return st.ttype == t; 517 } 518 intVal()519 private int intVal() throws ParseException { 520 if (gotType(StreamTokenizer.TT_NUMBER)) { 521 int n = (int) st.nval; 522 if (n == st.nval) { 523 return n; 524 } 525 } 526 throw new ParseException("expected integer, got " + st); 527 } 528 strVal()529 private String strVal() throws ParseException { 530 if (gotType(StreamTokenizer.TT_WORD)) { 531 return st.sval; 532 } 533 throw new ParseException("expected string, got " + st); 534 } 535 536 /** 537 * Parses an AST path. 538 * @return the AST path 539 */ parseASTPath()540 ASTPath parseASTPath() throws ParseException { 541 ASTPath astPath = new ASTPath().extend(parseASTEntry()); 542 while (gotType(',')) { 543 getTok(); 544 astPath = astPath.extend(parseASTEntry()); 545 } 546 return astPath; 547 } 548 549 /** 550 * Parses and returns the next AST entry. 551 * @return a new AST entry 552 * @throws ParseException if the next entry type is invalid 553 */ parseASTEntry()554 ASTEntry parseASTEntry() throws ParseException { 555 String s = strVal(); 556 557 if (s.equals("AnnotatedType")) { 558 return newASTEntry(Tree.Kind.ANNOTATED_TYPE, 559 new String[] {ASTPath.ANNOTATION, ASTPath.UNDERLYING_TYPE}, 560 new String[] {ASTPath.ANNOTATION}); 561 } else if (s.equals("ArrayAccess")) { 562 return newASTEntry(Tree.Kind.ARRAY_ACCESS, 563 new String[] {ASTPath.EXPRESSION, ASTPath.INDEX}); 564 } else if (s.equals("ArrayType")) { 565 return newASTEntry(Tree.Kind.ARRAY_TYPE, 566 new String[] {ASTPath.TYPE}); 567 } else if (s.equals("Assert")) { 568 return newASTEntry(Tree.Kind.ASSERT, 569 new String[] {ASTPath.CONDITION, ASTPath.DETAIL}); 570 } else if (s.equals("Assignment")) { 571 return newASTEntry(Tree.Kind.ASSIGNMENT, 572 new String[] {ASTPath.VARIABLE, ASTPath.EXPRESSION}); 573 } else if (s.equals("Binary")) { 574 // Always use Tree.Kind.PLUS for Binary 575 return newASTEntry(Tree.Kind.PLUS, 576 new String[] {ASTPath.LEFT_OPERAND, ASTPath.RIGHT_OPERAND}); 577 } else if (s.equals("Block")) { 578 return newASTEntry(Tree.Kind.BLOCK, 579 new String[] {ASTPath.STATEMENT}, 580 new String[] {ASTPath.STATEMENT}); 581 } else if (s.equals("Case")) { 582 return newASTEntry(Tree.Kind.CASE, 583 new String[] {ASTPath.EXPRESSION, ASTPath.STATEMENT}, 584 new String[] {ASTPath.STATEMENT}); 585 } else if (s.equals("Catch")) { 586 return newASTEntry(Tree.Kind.CATCH, 587 new String[] {ASTPath.PARAMETER, ASTPath.BLOCK}); 588 } else if (s.equals("Class")) { 589 return newASTEntry(Tree.Kind.CLASS, 590 new String[] {ASTPath.BOUND, ASTPath.INITIALIZER, 591 ASTPath.TYPE_PARAMETER}, 592 new String[] {ASTPath.BOUND, ASTPath.INITIALIZER, 593 ASTPath.TYPE_PARAMETER}); 594 } else if (s.equals("CompoundAssignment")) { 595 // Always use Tree.Kind.PLUS_ASSIGNMENT for CompoundAssignment 596 return newASTEntry(Tree.Kind.PLUS_ASSIGNMENT, 597 new String[] {ASTPath.VARIABLE, ASTPath.EXPRESSION}); 598 } else if (s.equals("ConditionalExpression")) { 599 return newASTEntry(Tree.Kind.CONDITIONAL_EXPRESSION, 600 new String[] {ASTPath.CONDITION, 601 ASTPath.TRUE_EXPRESSION, 602 ASTPath.FALSE_EXPRESSION}); 603 } else if (s.equals("DoWhileLoop")) { 604 return newASTEntry(Tree.Kind.DO_WHILE_LOOP, 605 new String[] {ASTPath.CONDITION, ASTPath.STATEMENT}); 606 } else if (s.equals("EnhancedForLoop")) { 607 return newASTEntry(Tree.Kind.ENHANCED_FOR_LOOP, 608 new String[] {ASTPath.VARIABLE, 609 ASTPath.EXPRESSION, 610 ASTPath.STATEMENT}); 611 } else if (s.equals("ExpressionStatement")) { 612 return newASTEntry(Tree.Kind.EXPRESSION_STATEMENT, 613 new String[] {ASTPath.EXPRESSION}); 614 } else if (s.equals("ForLoop")) { 615 return newASTEntry(Tree.Kind.FOR_LOOP, 616 new String[] {ASTPath.INITIALIZER, ASTPath.CONDITION, 617 ASTPath.UPDATE, ASTPath.STATEMENT}, 618 new String[] {ASTPath.INITIALIZER, ASTPath.UPDATE}); 619 } else if (s.equals("If")) { 620 return newASTEntry(Tree.Kind.IF, 621 new String[] {ASTPath.CONDITION, 622 ASTPath.THEN_STATEMENT, ASTPath.ELSE_STATEMENT}); 623 } else if (s.equals("InstanceOf")) { 624 return newASTEntry(Tree.Kind.INSTANCE_OF, 625 new String[] {ASTPath.EXPRESSION, ASTPath.TYPE}); 626 } else if (s.equals("LabeledStatement")) { 627 return newASTEntry(Tree.Kind.LABELED_STATEMENT, 628 new String[] {ASTPath.STATEMENT}); 629 } else if (s.equals("LambdaExpression")) { 630 return newASTEntry(Tree.Kind.LAMBDA_EXPRESSION, 631 new String[] {ASTPath.PARAMETER, ASTPath.BODY}, 632 new String[] {ASTPath.PARAMETER}); 633 } else if (s.equals("MemberReference")) { 634 return newASTEntry(Tree.Kind.MEMBER_REFERENCE, 635 new String[] {ASTPath.QUALIFIER_EXPRESSION, ASTPath.TYPE_ARGUMENT}, 636 new String[] {ASTPath.TYPE_ARGUMENT}); 637 } else if (s.equals("MemberSelect")) { 638 return newASTEntry(Tree.Kind.MEMBER_SELECT, 639 new String[] {ASTPath.EXPRESSION}); 640 } else if (s.equals("Method")) { 641 return newASTEntry(Tree.Kind.METHOD, 642 new String[] {ASTPath.BODY, ASTPath.PARAMETER, 643 ASTPath.TYPE, ASTPath.TYPE_PARAMETER}, 644 new String[] {ASTPath.PARAMETER, ASTPath.TYPE_PARAMETER}); 645 } else if (s.equals("MethodInvocation")) { 646 return newASTEntry(Tree.Kind.METHOD_INVOCATION, 647 new String[] {ASTPath.TYPE_ARGUMENT, 648 ASTPath.METHOD_SELECT, ASTPath.ARGUMENT}, 649 new String[] {ASTPath.TYPE_ARGUMENT, ASTPath.ARGUMENT}); 650 } else if (s.equals("NewArray")) { 651 return newASTEntry(Tree.Kind.NEW_ARRAY, 652 new String[] {ASTPath.TYPE, ASTPath.DIMENSION, 653 ASTPath.INITIALIZER}, 654 new String[] {ASTPath.TYPE, ASTPath.DIMENSION, 655 ASTPath.INITIALIZER}); 656 } else if (s.equals("NewClass")) { 657 return newASTEntry(Tree.Kind.NEW_CLASS, 658 new String[] {ASTPath.ENCLOSING_EXPRESSION, 659 ASTPath.TYPE_ARGUMENT, ASTPath.IDENTIFIER, 660 ASTPath.ARGUMENT, ASTPath.CLASS_BODY}, 661 new String[] {ASTPath.TYPE_ARGUMENT, ASTPath.ARGUMENT}); 662 } else if (s.equals("ParameterizedType")) { 663 return newASTEntry(Tree.Kind.PARAMETERIZED_TYPE, 664 new String[] {ASTPath.TYPE, ASTPath.TYPE_ARGUMENT}, 665 new String[] {ASTPath.TYPE_ARGUMENT}); 666 } else if (s.equals("Parenthesized")) { 667 return newASTEntry(Tree.Kind.PARENTHESIZED, 668 new String[] {ASTPath.EXPRESSION}); 669 } else if (s.equals("Return")) { 670 return newASTEntry(Tree.Kind.RETURN, 671 new String[] {ASTPath.EXPRESSION}); 672 } else if (s.equals("Switch")) { 673 return newASTEntry(Tree.Kind.SWITCH, 674 new String[] {ASTPath.EXPRESSION, ASTPath.CASE}, 675 new String[] {ASTPath.CASE}); 676 } else if (s.equals("Synchronized")) { 677 return newASTEntry(Tree.Kind.SYNCHRONIZED, 678 new String[] {ASTPath.EXPRESSION, ASTPath.BLOCK}); 679 } else if (s.equals("Throw")) { 680 return newASTEntry(Tree.Kind.THROW, 681 new String[] {ASTPath.EXPRESSION}); 682 } else if (s.equals("Try")) { 683 return newASTEntry(Tree.Kind.TRY, 684 new String[] {ASTPath.BLOCK, ASTPath.CATCH, ASTPath.FINALLY_BLOCK}, 685 new String[] {ASTPath.CATCH}); 686 } else if (s.equals("TypeCast")) { 687 return newASTEntry(Tree.Kind.TYPE_CAST, 688 new String[] {ASTPath.TYPE, ASTPath.EXPRESSION}); 689 } else if (s.equals("Unary")) { 690 // Always use Tree.Kind.UNARY_PLUS for Unary 691 return newASTEntry(Tree.Kind.UNARY_PLUS, 692 new String[] {ASTPath.EXPRESSION}); 693 } else if (s.equals("UnionType")) { 694 return newASTEntry(Tree.Kind.UNION_TYPE, 695 new String[] {ASTPath.TYPE_ALTERNATIVE}, 696 new String[] {ASTPath.TYPE_ALTERNATIVE}); 697 } else if (s.equals("Variable")) { 698 return newASTEntry(Tree.Kind.VARIABLE, 699 new String[] {ASTPath.TYPE, ASTPath.INITIALIZER}); 700 } else if (s.equals("WhileLoop")) { 701 return newASTEntry(Tree.Kind.WHILE_LOOP, 702 new String[] {ASTPath.CONDITION, ASTPath.STATEMENT}); 703 } else if (s.equals("Wildcard")) { 704 // Always use Tree.Kind.UNBOUNDED_WILDCARD for Wildcard 705 return newASTEntry(Tree.Kind.UNBOUNDED_WILDCARD, 706 new String[] {ASTPath.BOUND}); 707 } 708 709 throw new ParseException("Invalid AST path type: " + s); 710 } 711 712 /** 713 * Parses and constructs a new AST entry, where none of the child selections require 714 * arguments. For example, the call: 715 * 716 * <pre> 717 * {@code newASTEntry(Tree.Kind.WHILE_LOOP, new String[] {"condition", "statement"});</pre> 718 * 719 * constructs a while loop AST entry, where the valid child selectors are "condition" or 720 * "statement". 721 * 722 * @param kind the kind of this AST entry 723 * @param legalChildSelectors a list of the legal child selectors for this AST entry 724 * @return a new {@link ASTEntry} 725 * @throws ParseException if an illegal argument is found 726 */ newASTEntry(Tree.Kind kind, String[] legalChildSelectors)727 private ASTEntry newASTEntry(Tree.Kind kind, String[] legalChildSelectors) 728 throws ParseException { 729 return newASTEntry(kind, legalChildSelectors, null); 730 } 731 732 /** 733 * Parses and constructs a new AST entry. For example, the call: 734 * 735 * <pre> 736 * {@code newASTEntry(Tree.Kind.CASE, new String[] {"expression", "statement"}, new String[] {"statement"}); 737 * </pre> 738 * 739 * constructs a case AST entry, where the valid child selectors are 740 * "expression" or "statement" and the "statement" child selector requires 741 * an argument. 742 * 743 * @param kind the kind of this AST entry 744 * @param legalChildSelectors a list of the legal child selectors for this AST entry 745 * @param argumentChildSelectors a list of the child selectors that also require an argument. 746 * Entries here should also be in the legalChildSelectors list. 747 * @return a new {@link ASTEntry} 748 * @throws ParseException if an illegal argument is found 749 */ newASTEntry(Tree.Kind kind, String[] legalChildSelectors, String[] argumentChildSelectors)750 private ASTEntry newASTEntry(Tree.Kind kind, String[] legalChildSelectors, 751 String[] argumentChildSelectors) throws ParseException { 752 if (gotType('.')) { 753 getTok(); 754 } else { 755 throw new ParseException("expected '.', got " + st); 756 } 757 758 String s = strVal(); 759 for (String arg : legalChildSelectors) { 760 if (s.equals(arg)) { 761 if (argumentChildSelectors != null 762 && ArraysMDE.indexOf(argumentChildSelectors, arg) >= 0) { 763 getTok(); 764 return new ASTEntry(kind, arg, intVal()); 765 } else { 766 return new ASTEntry(kind, arg); 767 } 768 } 769 } 770 771 throw new ParseException("Invalid argument for " + kind 772 + " (legal arguments - " + Arrays.toString(legalChildSelectors) 773 + "): " + s); 774 } 775 } 776 777 static class Matcher { 778 // adapted from IndexFileParser.parseASTPath et al. 779 // TODO: refactor switch statement into TreeVisitor? 780 public static final DebugWriter dbug = new DebugWriter(); 781 private ASTPath astPath; 782 Matcher(ASTPath astPath)783 Matcher(ASTPath astPath) { 784 this.astPath = astPath; 785 } 786 nonDecl(TreePath path)787 private boolean nonDecl(TreePath path) { 788 switch (path.getLeaf().getKind()) { 789 case CLASS: 790 case METHOD: 791 return false; 792 case VARIABLE: 793 TreePath parentPath = path.getParentPath(); 794 return parentPath != null 795 && parentPath.getLeaf().getKind() != Tree.Kind.CLASS; 796 default: 797 return true; 798 } 799 } 800 matches(TreePath path)801 public boolean matches(TreePath path) { 802 return matches(path, -1); 803 } 804 matches(TreePath path, int depth)805 public boolean matches(TreePath path, int depth) { 806 if (path == null) { 807 return false; 808 } 809 810 // actualPath stores the path through the source code AST to this 811 // location (specified by the "path" parameter to this method). It is 812 // computed by traversing from this location up the source code AST 813 // until it reaches a method node (this gets only the part of the path 814 // within a method) or class node (this gets only the part of the path 815 // within a field). 816 List<Tree> actualPath = new ArrayList<Tree>(); 817 while (path != null && nonDecl(path)) { 818 actualPath.add(0, path.getLeaf()); 819 path = path.getParentPath(); 820 } 821 822 if (dbug.isEnabled()) { 823 dbug.debug("AST [%s]%n", astPath); 824 for (Tree t : actualPath) { 825 dbug.debug(" %s: %s%n", t.getKind(), 826 t.toString().replace('\n', ' ')); 827 } 828 } 829 830 if (astPath.isEmpty() || actualPath.isEmpty() 831 || actualPath.size() != astPath.size() + 1) { 832 return false; 833 } 834 835 for (int i = 0; i < astPath.size() && i < actualPath.size(); i++) { 836 ASTPath.ASTEntry astNode = astPath.get(i); 837 Tree actualNode = actualPath.get(i); 838 839 // Based on the child selector and (optional) argument in "astNode", 840 // "next" will get set to the next source node below "actualNode". 841 // Then "next" will be compared with the node following "astNode" 842 // in "actualPath". If it's not a match, this is not the correct 843 // location. If it is a match, keep going. 844 Tree next = null; 845 dbug.debug("astNode: %s%n", astNode); 846 dbug.debug("actualNode: %s%n", actualNode.getKind()); 847 if (!kindsMatch(astNode.getTreeKind(), actualNode.getKind())) { 848 return false; 849 } 850 851 switch (actualNode.getKind()) { 852 case ANNOTATED_TYPE: { 853 AnnotatedTypeTree annotatedType = (AnnotatedTypeTree) actualNode; 854 if (astNode.childSelectorIs(ASTPath.ANNOTATION)) { 855 int arg = astNode.getArgument(); 856 List<? extends AnnotationTree> annos = annotatedType.getAnnotations(); 857 if (arg >= annos.size()) { 858 return false; 859 } 860 next = annos.get(arg); 861 } else { 862 next = annotatedType.getUnderlyingType(); 863 } 864 break; 865 } 866 case ARRAY_ACCESS: { 867 ArrayAccessTree arrayAccess = (ArrayAccessTree) actualNode; 868 if (astNode.childSelectorIs(ASTPath.EXPRESSION)) { 869 next = arrayAccess.getExpression(); 870 } else { 871 next = arrayAccess.getIndex(); 872 } 873 break; 874 } 875 case ARRAY_TYPE: { 876 ArrayTypeTree arrayType = (ArrayTypeTree) actualNode; 877 next = arrayType.getType(); 878 break; 879 } 880 case ASSERT: { 881 AssertTree azzert = (AssertTree) actualNode; 882 if (astNode.childSelectorIs(ASTPath.CONDITION)) { 883 next = azzert.getCondition(); 884 } else { 885 next = azzert.getDetail(); 886 } 887 break; 888 } 889 case ASSIGNMENT: { 890 AssignmentTree assignment = (AssignmentTree) actualNode; 891 if (astNode.childSelectorIs(ASTPath.VARIABLE)) { 892 next = assignment.getVariable(); 893 } else { 894 next = assignment.getExpression(); 895 } 896 break; 897 } 898 case BLOCK: { 899 BlockTree block = (BlockTree) actualNode; 900 int arg = astNode.getArgument(); 901 List<? extends StatementTree> statements = block.getStatements(); 902 if (arg >= block.getStatements().size()) { 903 return false; 904 } 905 next = statements.get(arg); 906 break; 907 } 908 case CASE: { 909 CaseTree caze = (CaseTree) actualNode; 910 if (astNode.childSelectorIs(ASTPath.EXPRESSION)) { 911 next = caze.getExpression(); 912 } else { 913 int arg = astNode.getArgument(); 914 List<? extends StatementTree> statements = caze.getStatements(); 915 if (arg >= statements.size()) { 916 return false; 917 } 918 next = statements.get(arg); 919 } 920 break; 921 } 922 case CATCH: { 923 CatchTree cach = (CatchTree) actualNode; 924 if (astNode.childSelectorIs(ASTPath.PARAMETER)) { 925 next = cach.getParameter(); 926 } else { 927 next = cach.getBlock(); 928 } 929 break; 930 } 931 case CLASS: { 932 ClassTree clazz = (ClassTree) actualNode; 933 int arg = astNode.getArgument(); 934 if (astNode.childSelectorIs(ASTPath.BOUND)) { 935 next = arg == -1 ? clazz.getExtendsClause() 936 : clazz.getImplementsClause().get(arg); 937 } else { 938 next = clazz.getTypeParameters().get(arg); 939 } 940 break; 941 } 942 case CONDITIONAL_EXPRESSION: { 943 ConditionalExpressionTree conditionalExpression = 944 (ConditionalExpressionTree) actualNode; 945 if (astNode.childSelectorIs(ASTPath.CONDITION)) { 946 next = conditionalExpression.getCondition(); 947 } else if (astNode.childSelectorIs(ASTPath.TRUE_EXPRESSION)) { 948 next = conditionalExpression.getTrueExpression(); 949 } else { 950 next = conditionalExpression.getFalseExpression(); 951 } 952 break; 953 } 954 case DO_WHILE_LOOP: { 955 DoWhileLoopTree doWhileLoop =(DoWhileLoopTree) actualNode; 956 if (astNode.childSelectorIs(ASTPath.CONDITION)) { 957 next = doWhileLoop.getCondition(); 958 } else { 959 next = doWhileLoop.getStatement(); 960 } 961 break; 962 } 963 case ENHANCED_FOR_LOOP: { 964 EnhancedForLoopTree enhancedForLoop = (EnhancedForLoopTree) actualNode; 965 if (astNode.childSelectorIs(ASTPath.VARIABLE)) { 966 next = enhancedForLoop.getVariable(); 967 } else if (astNode.childSelectorIs(ASTPath.EXPRESSION)) { 968 next = enhancedForLoop.getExpression(); 969 } else { 970 next = enhancedForLoop.getStatement(); 971 } 972 break; 973 } 974 case EXPRESSION_STATEMENT: { 975 ExpressionStatementTree expressionStatement = 976 (ExpressionStatementTree) actualNode; 977 next = expressionStatement.getExpression(); 978 break; 979 } 980 case FOR_LOOP: { 981 ForLoopTree forLoop = (ForLoopTree) actualNode; 982 if (astNode.childSelectorIs(ASTPath.INITIALIZER)) { 983 int arg = astNode.getArgument(); 984 List<? extends StatementTree> inits = forLoop.getInitializer(); 985 if (arg >= inits.size()) { 986 return false; 987 } 988 next = inits.get(arg); 989 } else if (astNode.childSelectorIs(ASTPath.CONDITION)) { 990 next = forLoop.getCondition(); 991 } else if (astNode.childSelectorIs(ASTPath.UPDATE)) { 992 int arg = astNode.getArgument(); 993 List<? extends ExpressionStatementTree> updates = forLoop.getUpdate(); 994 if (arg >= updates.size()) { 995 return false; 996 } 997 next = updates.get(arg); 998 } else { 999 next = forLoop.getStatement(); 1000 } 1001 break; 1002 } 1003 case IF: { 1004 IfTree iff = (IfTree) actualNode; 1005 if (astNode.childSelectorIs(ASTPath.CONDITION)) { 1006 next = iff.getCondition(); 1007 } else if (astNode.childSelectorIs(ASTPath.THEN_STATEMENT)) { 1008 next = iff.getThenStatement(); 1009 } else { 1010 next = iff.getElseStatement(); 1011 } 1012 break; 1013 } 1014 case INSTANCE_OF: { 1015 InstanceOfTree instanceOf = (InstanceOfTree) actualNode; 1016 if (astNode.childSelectorIs(ASTPath.EXPRESSION)) { 1017 next = instanceOf.getExpression(); 1018 } else { 1019 next = instanceOf.getType(); 1020 } 1021 break; 1022 } 1023 case LABELED_STATEMENT: { 1024 LabeledStatementTree labeledStatement = 1025 (LabeledStatementTree) actualNode; 1026 next = labeledStatement.getStatement(); 1027 break; 1028 } 1029 case LAMBDA_EXPRESSION: { 1030 LambdaExpressionTree lambdaExpression = 1031 (LambdaExpressionTree) actualNode; 1032 if (astNode.childSelectorIs(ASTPath.PARAMETER)) { 1033 int arg = astNode.getArgument(); 1034 List<? extends VariableTree> params = 1035 lambdaExpression.getParameters(); 1036 if (arg >= params.size()) { 1037 return false; 1038 } 1039 next = params.get(arg); 1040 } else { 1041 next = lambdaExpression.getBody(); 1042 } 1043 break; 1044 } 1045 case MEMBER_REFERENCE: { 1046 MemberReferenceTree memberReference = (MemberReferenceTree) actualNode; 1047 if (astNode.childSelectorIs(ASTPath.QUALIFIER_EXPRESSION)) { 1048 next = memberReference.getQualifierExpression(); 1049 } else { 1050 int arg = astNode.getArgument(); 1051 List<? extends ExpressionTree> typeArgs = 1052 memberReference.getTypeArguments(); 1053 if (arg >= typeArgs.size()) { 1054 return false; 1055 } 1056 next = typeArgs.get(arg); 1057 } 1058 break; 1059 } 1060 case MEMBER_SELECT: { 1061 MemberSelectTree memberSelect = (MemberSelectTree) actualNode; 1062 next = memberSelect.getExpression(); 1063 break; 1064 } 1065 case METHOD: { 1066 MethodTree method = (MethodTree) actualNode; 1067 int arg = astNode.getArgument(); 1068 if (astNode.childSelectorIs(ASTPath.TYPE)) { 1069 next = method.getReturnType(); 1070 } else if (astNode.childSelectorIs(ASTPath.PARAMETER)) { 1071 next = arg == -1 ? method.getReceiverParameter() 1072 : method.getParameters().get(arg); 1073 } else if (astNode.childSelectorIs(ASTPath.TYPE_PARAMETER)) { 1074 next = method.getTypeParameters().get(arg); 1075 } else if (astNode.childSelectorIs(ASTPath.BODY)) { 1076 next = method.getBody(); 1077 } else { // THROWS? 1078 return false; 1079 } 1080 break; 1081 } 1082 case METHOD_INVOCATION: { 1083 MethodInvocationTree methodInvocation = 1084 (MethodInvocationTree) actualNode; 1085 if (astNode.childSelectorIs(ASTPath.TYPE_ARGUMENT)) { 1086 int arg = astNode.getArgument(); 1087 List<? extends Tree> typeArgs = methodInvocation.getTypeArguments(); 1088 if (arg >= typeArgs.size()) { 1089 return false; 1090 } 1091 next = typeArgs.get(arg); 1092 } else if (astNode.childSelectorIs(ASTPath.METHOD_SELECT)) { 1093 next = methodInvocation.getMethodSelect(); 1094 } else { 1095 int arg = astNode.getArgument(); 1096 List<? extends ExpressionTree> args = methodInvocation.getArguments(); 1097 if (arg >= args.size()) { 1098 return false; 1099 } 1100 next = args.get(arg); 1101 } 1102 break; 1103 } 1104 case NEW_ARRAY: { 1105 NewArrayTree newArray = (NewArrayTree) actualNode; 1106 if (astNode.childSelectorIs(ASTPath.TYPE)) { 1107 int arg = astNode.getArgument(); 1108 if (arg < 0) { 1109 next = newArray.getType(); 1110 } else { 1111 return arg == depth; 1112 } 1113 } else if (astNode.childSelectorIs(ASTPath.DIMENSION)) { 1114 int arg = astNode.getArgument(); 1115 List<? extends ExpressionTree> dims = newArray.getDimensions(); 1116 if (arg >= dims.size()) { 1117 return false; 1118 } 1119 next = dims.get(arg); 1120 } else { 1121 int arg = astNode.getArgument(); 1122 List<? extends ExpressionTree> inits = newArray.getInitializers(); 1123 if (arg >= inits.size()) { 1124 return false; 1125 } 1126 next = inits.get(arg); 1127 } 1128 break; 1129 } 1130 case NEW_CLASS: { 1131 NewClassTree newClass = (NewClassTree) actualNode; 1132 if (astNode.childSelectorIs(ASTPath.ENCLOSING_EXPRESSION)) { 1133 next = newClass.getEnclosingExpression(); 1134 } else if (astNode.childSelectorIs(ASTPath.TYPE_ARGUMENT)) { 1135 int arg = astNode.getArgument(); 1136 List<? extends Tree> typeArgs = newClass.getTypeArguments(); 1137 if (arg >= typeArgs.size()) { 1138 return false; 1139 } 1140 next = typeArgs.get(arg); 1141 } else if (astNode.childSelectorIs(ASTPath.IDENTIFIER)) { 1142 next = newClass.getIdentifier(); 1143 } else if (astNode.childSelectorIs(ASTPath.ARGUMENT)) { 1144 int arg = astNode.getArgument(); 1145 List<? extends ExpressionTree> args = newClass.getArguments(); 1146 if (arg >= args.size()) { 1147 return false; 1148 } 1149 next = args.get(arg); 1150 } else { 1151 next = newClass.getClassBody(); 1152 } 1153 break; 1154 } 1155 case PARAMETERIZED_TYPE: { 1156 ParameterizedTypeTree parameterizedType = 1157 (ParameterizedTypeTree) actualNode; 1158 if (astNode.childSelectorIs(ASTPath.TYPE)) { 1159 next = parameterizedType.getType(); 1160 } else { 1161 int arg = astNode.getArgument(); 1162 List<? extends Tree> typeArgs = parameterizedType.getTypeArguments(); 1163 if (arg >= typeArgs.size()) { 1164 return false; 1165 } 1166 next = typeArgs.get(arg); 1167 } 1168 break; 1169 } 1170 case PARENTHESIZED: { 1171 ParenthesizedTree parenthesized = (ParenthesizedTree) actualNode; 1172 next = parenthesized.getExpression(); 1173 break; 1174 } 1175 case RETURN: { 1176 ReturnTree returnn = (ReturnTree) actualNode; 1177 next = returnn.getExpression(); 1178 break; 1179 } 1180 case SWITCH: { 1181 SwitchTree zwitch = (SwitchTree) actualNode; 1182 if (astNode.childSelectorIs(ASTPath.EXPRESSION)) { 1183 next = zwitch.getExpression(); 1184 } else { 1185 int arg = astNode.getArgument(); 1186 List<? extends CaseTree> cases = zwitch.getCases(); 1187 if (arg >= cases.size()) { 1188 return false; 1189 } 1190 next = cases.get(arg); 1191 } 1192 break; 1193 } 1194 case SYNCHRONIZED: { 1195 SynchronizedTree synchronizzed = (SynchronizedTree) actualNode; 1196 if (astNode.childSelectorIs(ASTPath.EXPRESSION)) { 1197 next = synchronizzed.getExpression(); 1198 } else { 1199 next = synchronizzed.getBlock(); 1200 } 1201 break; 1202 } 1203 case THROW: { 1204 ThrowTree throww = (ThrowTree) actualNode; 1205 next = throww.getExpression(); 1206 break; 1207 } 1208 case TRY: { 1209 TryTree tryy = (TryTree) actualNode; 1210 if (astNode.childSelectorIs(ASTPath.BLOCK)) { 1211 next = tryy.getBlock(); 1212 } else if (astNode.childSelectorIs(ASTPath.CATCH)) { 1213 int arg = astNode.getArgument(); 1214 List<? extends CatchTree> catches = tryy.getCatches(); 1215 if (arg >= catches.size()) { 1216 return false; 1217 } 1218 next = catches.get(arg); 1219 } else if (astNode.childSelectorIs(ASTPath.FINALLY_BLOCK)) { 1220 next = tryy.getFinallyBlock(); 1221 } else { 1222 int arg = astNode.getArgument(); 1223 List<? extends Tree> resources = tryy.getResources(); 1224 if (arg >= resources.size()) { 1225 return false; 1226 } 1227 next = resources.get(arg); 1228 } 1229 break; 1230 } 1231 case TYPE_CAST: { 1232 TypeCastTree typeCast = (TypeCastTree) actualNode; 1233 if (astNode.childSelectorIs(ASTPath.TYPE)) { 1234 next = typeCast.getType(); 1235 } else { 1236 next = typeCast.getExpression(); 1237 } 1238 break; 1239 } 1240 case UNION_TYPE: { 1241 UnionTypeTree unionType = (UnionTypeTree) actualNode; 1242 int arg = astNode.getArgument(); 1243 List<? extends Tree> typeAlts = unionType.getTypeAlternatives(); 1244 if (arg >= typeAlts.size()) { 1245 return false; 1246 } 1247 next = typeAlts.get(arg); 1248 break; 1249 } 1250 case VARIABLE: { 1251 VariableTree var = (VariableTree) actualNode; 1252 if (astNode.childSelectorIs(ASTPath.INITIALIZER)) { 1253 next = var.getInitializer(); 1254 } else { 1255 next = var.getType(); 1256 } 1257 break; 1258 } 1259 case WHILE_LOOP: { 1260 WhileLoopTree whileLoop = (WhileLoopTree) actualNode; 1261 if (astNode.childSelectorIs(ASTPath.CONDITION)) { 1262 next = whileLoop.getCondition(); 1263 } else { 1264 next = whileLoop.getStatement(); 1265 } 1266 break; 1267 } 1268 default: { 1269 if (isBinaryOperator(actualNode.getKind())) { 1270 BinaryTree binary = (BinaryTree) actualNode; 1271 if (astNode.childSelectorIs(ASTPath.LEFT_OPERAND)) { 1272 next = binary.getLeftOperand(); 1273 } else { 1274 next = binary.getRightOperand(); 1275 } 1276 } else if (isCompoundAssignment(actualNode.getKind())) { 1277 CompoundAssignmentTree compoundAssignment = 1278 (CompoundAssignmentTree) actualNode; 1279 if (astNode.childSelectorIs(ASTPath.VARIABLE)) { 1280 next = compoundAssignment.getVariable(); 1281 } else { 1282 next = compoundAssignment.getExpression(); 1283 } 1284 } else if (isUnaryOperator(actualNode.getKind())) { 1285 UnaryTree unary = (UnaryTree) actualNode; 1286 next = unary.getExpression(); 1287 } else if (isWildcard(actualNode.getKind())) { 1288 WildcardTree wildcard = (WildcardTree) actualNode; 1289 // The following check is necessary because Oracle has decided that 1290 // x instanceof Class<? extends Object> 1291 // will remain illegal even though it means the same thing as 1292 // x instanceof Class<?>. 1293 if (i > 0) { // TODO: refactor GenericArrayLoc to use same code? 1294 Tree ancestor = actualPath.get(i-1); 1295 if (ancestor.getKind() == Tree.Kind.INSTANCE_OF) { 1296 System.err.println("WARNING: wildcard bounds not allowed" 1297 + " in 'instanceof' expression; skipping insertion"); 1298 return false; 1299 } else if (i > 1 && ancestor.getKind() == 1300 Tree.Kind.PARAMETERIZED_TYPE) { 1301 ancestor = actualPath.get(i-2); 1302 if (ancestor.getKind() == Tree.Kind.ARRAY_TYPE) { 1303 System.err.println("WARNING: wildcard bounds not allowed" 1304 + " in generic array type; skipping insertion"); 1305 return false; 1306 } 1307 } 1308 } 1309 next = wildcard.getBound(); 1310 } else { 1311 throw new IllegalArgumentException("Illegal kind: " 1312 + actualNode.getKind()); 1313 } 1314 break; 1315 } 1316 } 1317 1318 dbug.debug("next: %s%n", next); 1319 if (next != actualPath.get(i + 1)) { 1320 dbug.debug("no next match%n"); 1321 return false; 1322 } 1323 } 1324 return true; 1325 } 1326 1327 /** 1328 * Determines if the given kinds match, false otherwise. Two kinds match if 1329 * they're exactly the same or if the two kinds are both compound 1330 * assignments, unary operators, binary operators or wildcards. 1331 * <p> 1332 * This is necessary because in the JAIF file these kinds are represented by 1333 * their general types (i.e. BinaryOperator, CompoundOperator, etc.) rather 1334 * than their kind (i.e. PLUS, MINUS, PLUS_ASSIGNMENT, XOR_ASSIGNMENT, 1335 * etc.). Internally, a single kind is used to represent each general type 1336 * (i.e. PLUS is used for BinaryOperator, PLUS_ASSIGNMENT is used for 1337 * CompoundAssignment, etc.). Yet, the actual source nodes have the correct 1338 * kind. So if an AST path entry has a PLUS kind, that really means it could 1339 * be any BinaryOperator, resulting in PLUS matching any other 1340 * BinaryOperator. 1341 * 1342 * @param kind1 1343 * the first kind to match 1344 * @param kind2 1345 * the second kind to match 1346 * @return {@code true} if the kinds match as described above, {@code false} 1347 * otherwise. 1348 */ kindsMatch(Tree.Kind kind1, Tree.Kind kind2)1349 private static boolean kindsMatch(Tree.Kind kind1, Tree.Kind kind2) { 1350 return kind1 == kind2 1351 || (isCompoundAssignment(kind1) && isCompoundAssignment(kind2)) 1352 || (isUnaryOperator(kind1) && isUnaryOperator(kind2)) 1353 || (isBinaryOperator(kind1) && isBinaryOperator(kind2)) 1354 || (isWildcard(kind1) && isWildcard(kind2)); 1355 } 1356 1357 /** 1358 * Determines if the given kind is a compound assignment. 1359 * 1360 * @param kind 1361 * the kind to test 1362 * @return true if the given kind is a compound assignment 1363 */ isCompoundAssignment(Tree.Kind kind)1364 private static boolean isCompoundAssignment(Tree.Kind kind) { 1365 return kind == Tree.Kind.PLUS_ASSIGNMENT 1366 || kind == Tree.Kind.MINUS_ASSIGNMENT 1367 || kind == Tree.Kind.MULTIPLY_ASSIGNMENT 1368 || kind == Tree.Kind.DIVIDE_ASSIGNMENT 1369 || kind == Tree.Kind.OR_ASSIGNMENT 1370 || kind == Tree.Kind.AND_ASSIGNMENT 1371 || kind == Tree.Kind.REMAINDER_ASSIGNMENT 1372 || kind == Tree.Kind.LEFT_SHIFT_ASSIGNMENT 1373 || kind == Tree.Kind.RIGHT_SHIFT 1374 || kind == Tree.Kind.UNSIGNED_RIGHT_SHIFT_ASSIGNMENT 1375 || kind == Tree.Kind.XOR_ASSIGNMENT; 1376 } 1377 1378 /** 1379 * Determines if the given kind is a unary operator. 1380 * 1381 * @param kind 1382 * the kind to test 1383 * @return true if the given kind is a unary operator 1384 */ isUnaryOperator(Tree.Kind kind)1385 private static boolean isUnaryOperator(Tree.Kind kind) { 1386 return kind == Tree.Kind.POSTFIX_INCREMENT 1387 || kind == Tree.Kind.POSTFIX_DECREMENT 1388 || kind == Tree.Kind.PREFIX_INCREMENT 1389 || kind == Tree.Kind.PREFIX_DECREMENT 1390 || kind == Tree.Kind.UNARY_PLUS 1391 || kind == Tree.Kind.UNARY_MINUS 1392 || kind == Tree.Kind.BITWISE_COMPLEMENT 1393 || kind == Tree.Kind.LOGICAL_COMPLEMENT; 1394 } 1395 1396 /** 1397 * Determines if the given kind is a binary operator. 1398 * 1399 * @param kind 1400 * the kind to test 1401 * @return true if the given kind is a binary operator 1402 */ isBinaryOperator(Tree.Kind kind)1403 private static boolean isBinaryOperator(Tree.Kind kind) { 1404 return kind == Tree.Kind.MULTIPLY 1405 || kind == Tree.Kind.DIVIDE 1406 || kind == Tree.Kind.REMAINDER 1407 || kind == Tree.Kind.PLUS 1408 || kind == Tree.Kind.MINUS 1409 || kind == Tree.Kind.LEFT_SHIFT 1410 || kind == Tree.Kind.RIGHT_SHIFT 1411 || kind == Tree.Kind.UNSIGNED_RIGHT_SHIFT 1412 || kind == Tree.Kind.LESS_THAN 1413 || kind == Tree.Kind.GREATER_THAN 1414 || kind == Tree.Kind.LESS_THAN_EQUAL 1415 || kind == Tree.Kind.GREATER_THAN_EQUAL 1416 || kind == Tree.Kind.EQUAL_TO 1417 || kind == Tree.Kind.NOT_EQUAL_TO 1418 || kind == Tree.Kind.AND 1419 || kind == Tree.Kind.XOR 1420 || kind == Tree.Kind.OR 1421 || kind == Tree.Kind.CONDITIONAL_AND 1422 || kind == Tree.Kind.CONDITIONAL_OR; 1423 } 1424 1425 /** 1426 * Determines if the given kind is a wildcard. 1427 * 1428 * @param kind 1429 * the kind to test 1430 * @return true if the given kind is a wildcard 1431 */ isWildcard(Tree.Kind kind)1432 private static boolean isWildcard(Tree.Kind kind) { 1433 return kind == Tree.Kind.UNBOUNDED_WILDCARD 1434 || kind == Tree.Kind.EXTENDS_WILDCARD 1435 || kind == Tree.Kind.SUPER_WILDCARD; 1436 } 1437 } 1438 isTypeSelector(String selector)1439 public static boolean isTypeSelector(String selector) { 1440 return Arrays.<String>binarySearch(typeSelectors, 1441 selector, Collator.getInstance()) >= 0; 1442 } 1443 isClassEquiv(Tree.Kind kind)1444 public static boolean isClassEquiv(Tree.Kind kind) { 1445 switch (kind) { 1446 case CLASS: 1447 case INTERFACE: 1448 case ENUM: 1449 case ANNOTATION_TYPE: 1450 return true; 1451 default: 1452 return false; 1453 } 1454 } 1455 1456 /** 1457 * Determines if the given kind is a compound assignment. 1458 * 1459 * @param kind 1460 * the kind to test 1461 * @return true if the given kind is a compound assignment 1462 */ isCompoundAssignment(Tree.Kind kind)1463 public static boolean isCompoundAssignment(Tree.Kind kind) { 1464 return kind == Tree.Kind.PLUS_ASSIGNMENT 1465 || kind == Tree.Kind.MINUS_ASSIGNMENT 1466 || kind == Tree.Kind.MULTIPLY_ASSIGNMENT 1467 || kind == Tree.Kind.DIVIDE_ASSIGNMENT 1468 || kind == Tree.Kind.OR_ASSIGNMENT 1469 || kind == Tree.Kind.AND_ASSIGNMENT 1470 || kind == Tree.Kind.REMAINDER_ASSIGNMENT 1471 || kind == Tree.Kind.LEFT_SHIFT_ASSIGNMENT 1472 || kind == Tree.Kind.RIGHT_SHIFT_ASSIGNMENT 1473 || kind == Tree.Kind.UNSIGNED_RIGHT_SHIFT_ASSIGNMENT 1474 || kind == Tree.Kind.XOR_ASSIGNMENT; 1475 } 1476 1477 /** 1478 * Determines if the given kind is a unary operator. 1479 * 1480 * @param kind 1481 * the kind to test 1482 * @return true if the given kind is a unary operator 1483 */ isUnaryOperator(Tree.Kind kind)1484 public static boolean isUnaryOperator(Tree.Kind kind) { 1485 return kind == Tree.Kind.POSTFIX_INCREMENT 1486 || kind == Tree.Kind.POSTFIX_DECREMENT 1487 || kind == Tree.Kind.PREFIX_INCREMENT 1488 || kind == Tree.Kind.PREFIX_DECREMENT 1489 || kind == Tree.Kind.UNARY_PLUS 1490 || kind == Tree.Kind.UNARY_MINUS 1491 || kind == Tree.Kind.BITWISE_COMPLEMENT 1492 || kind == Tree.Kind.LOGICAL_COMPLEMENT; 1493 } 1494 1495 /** 1496 * Determines if the given kind is a binary operator. 1497 * 1498 * @param kind 1499 * the kind to test 1500 * @return true if the given kind is a binary operator 1501 */ isBinaryOperator(Tree.Kind kind)1502 public static boolean isBinaryOperator(Tree.Kind kind) { 1503 return kind == Tree.Kind.MULTIPLY || kind == Tree.Kind.DIVIDE 1504 || kind == Tree.Kind.REMAINDER || kind == Tree.Kind.PLUS 1505 || kind == Tree.Kind.MINUS || kind == Tree.Kind.LEFT_SHIFT 1506 || kind == Tree.Kind.RIGHT_SHIFT 1507 || kind == Tree.Kind.UNSIGNED_RIGHT_SHIFT 1508 || kind == Tree.Kind.LESS_THAN 1509 || kind == Tree.Kind.GREATER_THAN 1510 || kind == Tree.Kind.LESS_THAN_EQUAL 1511 || kind == Tree.Kind.GREATER_THAN_EQUAL 1512 || kind == Tree.Kind.EQUAL_TO || kind == Tree.Kind.NOT_EQUAL_TO 1513 || kind == Tree.Kind.AND || kind == Tree.Kind.XOR 1514 || kind == Tree.Kind.OR || kind == Tree.Kind.CONDITIONAL_AND 1515 || kind == Tree.Kind.CONDITIONAL_OR; 1516 } 1517 isLiteral(Tree.Kind kind)1518 public static boolean isLiteral(Tree.Kind kind) { 1519 switch (kind) { 1520 case INT_LITERAL: 1521 case LONG_LITERAL: 1522 case FLOAT_LITERAL: 1523 case DOUBLE_LITERAL: 1524 case BOOLEAN_LITERAL: 1525 case CHAR_LITERAL: 1526 case STRING_LITERAL: 1527 case NULL_LITERAL: 1528 return true; 1529 default: 1530 return false; 1531 } 1532 } 1533 isExpression(Tree.Kind kind)1534 public static boolean isExpression(Tree.Kind kind) { 1535 switch (kind) { 1536 case ARRAY_ACCESS: 1537 case ASSIGNMENT: 1538 case CONDITIONAL_EXPRESSION: 1539 case EXPRESSION_STATEMENT: 1540 case MEMBER_SELECT: 1541 case MEMBER_REFERENCE: 1542 case IDENTIFIER: 1543 case INSTANCE_OF: 1544 case METHOD_INVOCATION: 1545 case NEW_ARRAY: 1546 case NEW_CLASS: 1547 case LAMBDA_EXPRESSION: 1548 case PARENTHESIZED: 1549 case TYPE_CAST: 1550 return true; 1551 default: 1552 return isUnaryOperator(kind) || isBinaryOperator(kind) 1553 || isCompoundAssignment(kind) || isLiteral(kind); 1554 } 1555 } 1556 isTypeKind(Tree.Kind kind)1557 public static boolean isTypeKind(Tree.Kind kind) { 1558 switch (kind) { 1559 case ANNOTATED_TYPE: 1560 case ARRAY_TYPE: 1561 case IDENTIFIER: 1562 case INTERSECTION_TYPE: 1563 // case MEMBER_SELECT: 1564 case PARAMETERIZED_TYPE: 1565 case PRIMITIVE_TYPE: 1566 case UNION_TYPE: 1567 return true; 1568 default: 1569 return false; 1570 } 1571 } 1572 1573 /** 1574 * Determines if the given kind is a wildcard. 1575 * 1576 * @param kind 1577 * the kind to test 1578 * @return true if the given kind is a wildcard 1579 */ isWildcard(Tree.Kind kind)1580 public static boolean isWildcard(Tree.Kind kind) { 1581 return kind == Tree.Kind.UNBOUNDED_WILDCARD 1582 || kind == Tree.Kind.EXTENDS_WILDCARD 1583 || kind == Tree.Kind.SUPER_WILDCARD; 1584 } 1585 1586 /** 1587 * Determines if the given kind is a declaration. 1588 * 1589 * @param kind 1590 * the kind to test 1591 * @return true if the given kind is a declaration 1592 */ isDeclaration(Tree.Kind kind)1593 public static boolean isDeclaration(Tree.Kind kind) { 1594 return kind == Tree.Kind.ANNOTATION 1595 || kind == Tree.Kind.CLASS 1596 || kind == Tree.Kind.ENUM 1597 || kind == Tree.Kind.INTERFACE 1598 || kind == Tree.Kind.METHOD 1599 || kind == Tree.Kind.VARIABLE; 1600 } 1601 1602 /** 1603 * Determines whether an {@code ASTPath} can identify nodes of the 1604 * given kind. 1605 * 1606 * @param kind 1607 * the kind to test 1608 * @return true if the given kind can be identified by an {@code ASTPath}. 1609 */ isHandled(Tree.Kind kind)1610 public static boolean isHandled(Tree.Kind kind) { 1611 switch (kind) { 1612 case BREAK: 1613 case COMPILATION_UNIT: 1614 case CONTINUE: 1615 case IMPORT: 1616 case MODIFIERS: 1617 return false; 1618 default: 1619 return !isDeclaration(kind); 1620 } 1621 } // TODO: need "isType"? 1622 } 1623 1624 /** 1625 * 1626 * @author dbro 1627 * 1628 * @param <E> type of stack elements 1629 */ 1630 class ConsStack<E> implements PersistentStack<E> { 1631 private int size; 1632 private E elem; 1633 private PersistentStack<E> rest; 1634 ConsStack()1635 public ConsStack() { 1636 size = 0; 1637 elem = null; 1638 rest = null; 1639 } 1640 extend(T el, S s0)1641 private static <T, S extends ConsStack<T>> S extend(T el, S s0) { 1642 try { 1643 @SuppressWarnings("unchecked") 1644 S s1 = (S) s0.getClass().newInstance(); 1645 ConsStack<T> cs = (ConsStack<T>) s1; 1646 cs.size = 1 + s0.size(); 1647 cs.elem = el; 1648 cs.rest = s0; 1649 return s1; 1650 } catch (InstantiationException e) { 1651 throw new RuntimeException(e); 1652 } catch (IllegalAccessException e) { 1653 throw new RuntimeException(e); 1654 } 1655 } 1656 1657 @Override isEmpty()1658 public boolean isEmpty() { return size == 0; } 1659 1660 @Override peek()1661 public E peek() { 1662 if (size > 0) { return elem; } 1663 throw new IllegalStateException("peek() on empty stack"); 1664 } 1665 1666 @Override pop()1667 public PersistentStack<E> pop() { 1668 if (size > 0) { return rest; } 1669 throw new IllegalStateException("pop() on empty stack"); 1670 } 1671 1672 @Override push(E elem)1673 public PersistentStack<E> push(E elem) { 1674 return extend(elem, this); 1675 } 1676 1677 @Override size()1678 public int size() { return size; } 1679 1680 @Override toString()1681 public String toString() { 1682 if (size > 0) { 1683 StringBuilder sb = new StringBuilder("]").insert(0, peek()); 1684 for (PersistentStack<E> stack = pop(); !stack.isEmpty(); 1685 stack = stack.pop()) { 1686 sb = sb.insert(0, ", ").insert(0, stack.peek()); 1687 } 1688 return sb.insert(0, "[").toString(); 1689 } 1690 return "[]"; 1691 } 1692 } 1693