• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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