1package annotator.find;
2
3import java.util.ArrayList;
4import java.util.List;
5
6import javax.lang.model.element.Modifier;
7import javax.lang.model.type.TypeKind;
8
9import annotations.io.ASTPath;
10import annotator.Main;
11
12import com.sun.source.tree.AnnotatedTypeTree;
13import com.sun.source.tree.AnnotationTree;
14import com.sun.source.tree.ArrayAccessTree;
15import com.sun.source.tree.ArrayTypeTree;
16import com.sun.source.tree.AssertTree;
17import com.sun.source.tree.AssignmentTree;
18import com.sun.source.tree.BinaryTree;
19import com.sun.source.tree.BlockTree;
20import com.sun.source.tree.CaseTree;
21import com.sun.source.tree.CatchTree;
22import com.sun.source.tree.ClassTree;
23import com.sun.source.tree.CompoundAssignmentTree;
24import com.sun.source.tree.ConditionalExpressionTree;
25import com.sun.source.tree.DoWhileLoopTree;
26import com.sun.source.tree.EnhancedForLoopTree;
27import com.sun.source.tree.ExpressionStatementTree;
28import com.sun.source.tree.ExpressionTree;
29import com.sun.source.tree.ForLoopTree;
30import com.sun.source.tree.IdentifierTree;
31import com.sun.source.tree.IfTree;
32import com.sun.source.tree.InstanceOfTree;
33import com.sun.source.tree.LabeledStatementTree;
34import com.sun.source.tree.LambdaExpressionTree;
35import com.sun.source.tree.MemberReferenceTree;
36import com.sun.source.tree.MemberSelectTree;
37import com.sun.source.tree.MethodInvocationTree;
38import com.sun.source.tree.MethodTree;
39import com.sun.source.tree.NewArrayTree;
40import com.sun.source.tree.NewClassTree;
41import com.sun.source.tree.ParameterizedTypeTree;
42import com.sun.source.tree.ParenthesizedTree;
43import com.sun.source.tree.ReturnTree;
44import com.sun.source.tree.StatementTree;
45import com.sun.source.tree.SwitchTree;
46import com.sun.source.tree.SynchronizedTree;
47import com.sun.source.tree.ThrowTree;
48import com.sun.source.tree.Tree;
49import com.sun.source.tree.TryTree;
50import com.sun.source.tree.TypeCastTree;
51import com.sun.source.tree.TypeParameterTree;
52import com.sun.source.tree.UnaryTree;
53import com.sun.source.tree.UnionTypeTree;
54import com.sun.source.tree.VariableTree;
55import com.sun.source.tree.WhileLoopTree;
56import com.sun.source.tree.WildcardTree;
57import com.sun.source.tree.Tree.Kind;
58import com.sun.source.util.SimpleTreeVisitor;
59import com.sun.source.util.TreePath;
60import com.sun.tools.javac.code.Flags;
61import com.sun.tools.javac.code.Type;
62import com.sun.tools.javac.tree.JCTree;
63
64/**
65 * A criterion to determine if a node matches a path through the AST.
66 */
67public class ASTPathCriterion implements Criterion {
68
69    public static boolean debug = Main.debug;
70
71    /**
72     * The path through the AST to match.
73     */
74    ASTPath astPath;
75
76    /**
77     * Constructs a new ASTPathCriterion to match the given AST path.
78     * <p>
79     * This assumes that the astPath is valid. Specifically, that all of its
80     * arguments have been previously validated.
81     *
82     * @param astPath
83     *            the AST path to match
84     */
85    public ASTPathCriterion(ASTPath astPath) {
86        this.astPath = astPath;
87    }
88
89    /** {@inheritDoc} */
90    @Override
91    public boolean isSatisfiedBy(TreePath path, Tree leaf) {
92        assert path == null || path.getLeaf() == leaf;
93        return isSatisfiedBy(path);
94    }
95
96    /** {@inheritDoc} */
97    @Override
98    public boolean isSatisfiedBy(TreePath path) {
99        if (path == null) {
100            return false;
101        }
102
103        // actualPath stores the path through the source code AST to this
104        // location (specified by the "path" parameter to this method). It is
105        // computed by traversing from this location up the source code AST
106        // until it reaches a method node (this gets only the part of the path
107        // within a method) or class node (this gets only the part of the path
108        // within a field).
109        List<Tree> actualPath = new ArrayList<Tree>();
110        Tree leaf = path.getLeaf();
111        Tree.Kind kind = leaf.getKind();
112        while (kind != Tree.Kind.METHOD && !ASTPath.isClassEquiv(kind)) {
113            actualPath.add(0, leaf);
114            path = path.getParentPath();
115            if (path == null) { break; }
116            leaf = path.getLeaf();
117            kind = leaf.getKind();
118        }
119
120        // If astPath starts with Method.* or Class.*, include the
121        // MethodTree or ClassTree on actualPath.
122        if (path != null && !astPath.isEmpty()) {
123            Tree.Kind entryKind = astPath.get(0).getTreeKind();
124            if (entryKind == Tree.Kind.METHOD
125                            && kind == Tree.Kind.METHOD
126                    || entryKind == Tree.Kind.CLASS
127                            && ASTPath.isClassEquiv(kind)) {
128                actualPath.add(0, leaf);
129            }
130        }
131
132        if (debug) {
133            System.out.println("ASTPathCriterion.isSatisfiedBy");
134            System.out.println("  " + astPath);
135            for (Tree t : actualPath) {
136                System.out.println("  " + t.getKind() + ": "
137                        + t.toString().replace('\n', ' '));
138            }
139        }
140
141        int astPathLen = astPath.size();
142        int actualPathLen = actualPath.size();
143        if (astPathLen == 0 || actualPathLen == 0) { return false; }
144        // if (actualPathLen != astPathLen + (isOnNewArrayType ? 0 : 1)) {
145        //    return false;
146        // }
147
148        Tree next = null;
149        int i = 0;
150        while (true) {
151            ASTPath.ASTEntry astNode = astPath.get(i);
152            Tree actualNode = actualPath.get(i);
153            if (!kindsMatch(astNode.getTreeKind(), actualNode.getKind())) {
154                return isBoundableWildcard(actualPath, i);
155            }
156
157            if (debug) {
158                System.out.println("astNode: " + astNode);
159                System.out.println("actualNode: " + actualNode.getKind());
160            }
161
162            // Based on the child selector and (optional) argument in "astNode",
163            // "next" will get set to the next source node below "actualNode".
164            // Then "next" will be compared with the node following "astNode"
165            // in "actualPath". If it's not a match, this is not the correct
166            // location. If it is a match, keep going.
167            next = getNext(actualNode, astPath, i);
168            if (next == null) {
169                return checkNull(actualPath, i);
170            }
171            if (!(next instanceof JCTree)) {
172                // converted from array type, not in source AST...
173                if (actualPathLen == i+1) {
174                // need to extend actualPath with "artificial" node
175                  actualPath.add(next);
176                  ++actualPathLen;
177                }
178            }
179            if (debug) {
180                System.out.println("next: " + next);
181            }
182
183            // if (++i >= astPathLen || i >= actualPathLen) { break; }
184            if (++i >= astPathLen) { break; }
185            if (i >= actualPathLen) {
186              return checkNull(actualPath, i-1);
187            }
188            if (!matchNext(next, actualPath.get(i))) {
189                if (debug) {
190                    System.out.println("no next match");
191                }
192              return false;
193            }
194        }
195
196        if (i < actualPathLen && matchNext(next, actualPath.get(i))
197                || i <= actualPathLen
198                        && next.getKind() == Tree.Kind.NEW_ARRAY) {
199            return true;
200        }
201
202        if (debug) {
203            System.out.println("no next match");
204        }
205        return false;
206    }
207
208    private boolean matchNext(Tree next, Tree node) {
209        boolean b1 = next instanceof JCTree;
210        boolean b2 = node instanceof JCTree;
211        if (b1 && !b2) {
212          next = Insertions.TypeTree.fromJCTree((JCTree) next);
213        } else if (b2 && !b1) {
214          node = Insertions.TypeTree.fromJCTree((JCTree) node);
215        }
216
217        try {
218            return next.accept(new SimpleTreeVisitor<Boolean, Tree>() {
219                @Override
220                public Boolean
221                defaultAction(Tree t1, Tree t2) {
222                    return t1 == t2;
223                }
224
225                @Override
226                public Boolean
227                visitIdentifier(IdentifierTree v, Tree t) {
228                    return v == t;
229                    // IdentifierTree i2 = (IdentifierTree) t;
230                    // return i1.getName().toString()
231                    //        .equals(i2.getName().toString());
232                }
233
234                @Override
235                public Boolean
236                visitAnnotatedType(AnnotatedTypeTree a1, Tree t) {
237                    AnnotatedTypeTree a2 = (AnnotatedTypeTree) t;
238                    return matchNext(a1.getUnderlyingType(),
239                            a2.getUnderlyingType());
240                }
241
242                // @Override
243                // public Boolean
244                // visitArrayType(ArrayTypeTree b1, Tree t) {
245                //    ArrayTypeTree b2 = (ArrayTypeTree) t;
246                //    return matchNext(b1.getType(), b2.getType());
247                // }
248
249                @Override
250                public Boolean
251                visitMemberSelect(MemberSelectTree c1, Tree t) {
252                    MemberSelectTree c2 = (MemberSelectTree) t;
253                    return c1.getIdentifier().toString()
254                                .equals(c2.getIdentifier().toString())
255                            && matchNext(c1.getExpression(),
256                                    c2.getExpression());
257                }
258
259                @Override
260                public Boolean
261                visitWildcard(WildcardTree d1, Tree t) {
262                    return d1 == (WildcardTree) t;
263                    // WildcardTree d2 = (WildcardTree) t;
264                    // Tree bound2 = d2.getBound();
265                    // Tree bound1 = d1.getBound();
266                    // return bound1 == bound2 || matchNext(bound1, bound2);
267                }
268
269                @Override
270                public Boolean
271                visitParameterizedType(ParameterizedTypeTree e1, Tree t) {
272                    ParameterizedTypeTree e2 = (ParameterizedTypeTree) t;
273                    List<? extends Tree> l2 = e2.getTypeArguments();
274                    List<? extends Tree> l1 = e1.getTypeArguments();
275                    if (l1.size() == l2.size()) {
276                        int i = 0;
277                        for (Tree t1 : l1) {
278                            Tree t2 = l2.get(i++);
279                            if (!matchNext(t1, t2)) { return false; }
280                        }
281                        return matchNext(e1.getType(), e2.getType());
282                    }
283                    return false;
284                }
285            }, node);
286        } catch (RuntimeException ex) {
287            return false;
288        }
289    }
290
291    private Tree getNext(Tree actualNode, ASTPath astPath, int ix) {
292        try {
293            ASTPath.ASTEntry astNode = astPath.get(ix);
294            switch (actualNode.getKind()) {
295            case ANNOTATED_TYPE: {
296                AnnotatedTypeTree annotatedType =
297                    (AnnotatedTypeTree) actualNode;
298                if (astNode.childSelectorIs(ASTPath.ANNOTATION)) {
299                    int arg = astNode.getArgument();
300                    List<? extends AnnotationTree> annos =
301                        annotatedType.getAnnotations();
302                    if (arg >= annos.size()) {
303                        return null;
304                    }
305                    return annos.get(arg);
306                } else {
307                    return annotatedType.getUnderlyingType();
308                }
309            }
310            case ARRAY_ACCESS: {
311                ArrayAccessTree arrayAccess = (ArrayAccessTree) actualNode;
312                if (astNode.childSelectorIs(ASTPath.EXPRESSION)) {
313                    return arrayAccess.getExpression();
314                } else {
315                    return arrayAccess.getIndex();
316                }
317            }
318            case ARRAY_TYPE: {
319                ArrayTypeTree arrayType = (ArrayTypeTree) actualNode;
320                return arrayType.getType();
321            }
322            case ASSERT: {
323                AssertTree azzert = (AssertTree) actualNode;
324                if (astNode.childSelectorIs(ASTPath.CONDITION)) {
325                    return azzert.getCondition();
326                } else {
327                    return azzert.getDetail();
328                }
329            }
330            case ASSIGNMENT: {
331                AssignmentTree assignment = (AssignmentTree) actualNode;
332                if (astNode.childSelectorIs(ASTPath.VARIABLE)) {
333                    return assignment.getVariable();
334                } else {
335                    return assignment.getExpression();
336                }
337            }
338            case BLOCK: {
339                BlockTree block = (BlockTree) actualNode;
340                int arg = astNode.getArgument();
341                List<? extends StatementTree> statements = block.getStatements();
342                if (arg >= block.getStatements().size()) {
343                    return null;
344                }
345                return statements.get(arg);
346            }
347            case CASE: {
348                CaseTree caze = (CaseTree) actualNode;
349                if (astNode.childSelectorIs(ASTPath.EXPRESSION)) {
350                    return caze.getExpression();
351                } else {
352                    int arg = astNode.getArgument();
353                    List<? extends StatementTree> statements = caze.getStatements();
354                    if (arg >= statements.size()) {
355                        return null;
356                    }
357                    return statements.get(arg);
358                }
359            }
360            case CATCH: {
361                CatchTree cach = (CatchTree) actualNode;
362                if (astNode.childSelectorIs(ASTPath.PARAMETER)) {
363                    return cach.getParameter();
364                } else {
365                    return cach.getBlock();
366                }
367            }
368            case ANNOTATION:
369            case CLASS:
370            case ENUM:
371            case INTERFACE: {
372                ClassTree clazz = (ClassTree) actualNode;
373                int arg = astNode.getArgument();
374                if (astNode.childSelectorIs(ASTPath.TYPE_PARAMETER)) {
375                    return clazz.getTypeParameters().get(arg);
376                } else if (astNode.childSelectorIs(ASTPath.INITIALIZER)) {
377                    int i = 0;
378                    for (Tree member : clazz.getMembers()) {
379                        if (member.getKind() == Tree.Kind.BLOCK && arg == i++) {
380                            return member;
381                        }
382                    }
383                    return null;
384                } else if (astNode.childSelectorIs(ASTPath.BOUND)) {
385                  return arg < 0 ? clazz.getExtendsClause()
386                          : clazz.getImplementsClause().get(arg);
387                } else {
388                    return null;
389                }
390            }
391            case CONDITIONAL_EXPRESSION: {
392                ConditionalExpressionTree conditionalExpression = (ConditionalExpressionTree) actualNode;
393                if (astNode.childSelectorIs(ASTPath.CONDITION)) {
394                    return conditionalExpression.getCondition();
395                } else if (astNode.childSelectorIs(ASTPath.TRUE_EXPRESSION)) {
396                    return conditionalExpression.getTrueExpression();
397                } else {
398                    return conditionalExpression.getFalseExpression();
399                }
400            }
401            case DO_WHILE_LOOP: {
402                DoWhileLoopTree doWhileLoop = (DoWhileLoopTree) actualNode;
403                if (astNode.childSelectorIs(ASTPath.CONDITION)) {
404                    return doWhileLoop.getCondition();
405                } else {
406                    return doWhileLoop.getStatement();
407                }
408            }
409            case ENHANCED_FOR_LOOP: {
410                EnhancedForLoopTree enhancedForLoop = (EnhancedForLoopTree) actualNode;
411                if (astNode.childSelectorIs(ASTPath.VARIABLE)) {
412                    return enhancedForLoop.getVariable();
413                } else if (astNode.childSelectorIs(ASTPath.EXPRESSION)) {
414                    return enhancedForLoop.getExpression();
415                } else {
416                    return enhancedForLoop.getStatement();
417                }
418            }
419            case EXPRESSION_STATEMENT: {
420                ExpressionStatementTree expressionStatement = (ExpressionStatementTree) actualNode;
421                return expressionStatement.getExpression();
422            }
423            case FOR_LOOP: {
424                ForLoopTree forLoop = (ForLoopTree) actualNode;
425                if (astNode.childSelectorIs(ASTPath.INITIALIZER)) {
426                    int arg = astNode.getArgument();
427                    List<? extends StatementTree> inits = forLoop.getInitializer();
428                    if (arg >= inits.size()) {
429                        return null;
430                    }
431                    return inits.get(arg);
432                } else if (astNode.childSelectorIs(ASTPath.CONDITION)) {
433                    return forLoop.getCondition();
434                } else if (astNode.childSelectorIs(ASTPath.UPDATE)) {
435                    int arg = astNode.getArgument();
436                    List<? extends ExpressionStatementTree> updates = forLoop.getUpdate();
437                    if (arg >= updates.size()) {
438                        return null;
439                    }
440                    return updates.get(arg);
441                } else {
442                    return forLoop.getStatement();
443                }
444            }
445            case IF: {
446                IfTree iff = (IfTree) actualNode;
447                if (astNode.childSelectorIs(ASTPath.CONDITION)) {
448                    return iff.getCondition();
449                } else if (astNode.childSelectorIs(ASTPath.THEN_STATEMENT)) {
450                    return iff.getThenStatement();
451                } else {
452                    return iff.getElseStatement();
453                }
454            }
455            case INSTANCE_OF: {
456                InstanceOfTree instanceOf = (InstanceOfTree) actualNode;
457                if (astNode.childSelectorIs(ASTPath.EXPRESSION)) {
458                    return instanceOf.getExpression();
459                } else {
460                    return instanceOf.getType();
461                }
462            }
463            case LABELED_STATEMENT: {
464                LabeledStatementTree labeledStatement = (LabeledStatementTree) actualNode;
465                return labeledStatement.getStatement();
466            }
467            case LAMBDA_EXPRESSION: {
468                LambdaExpressionTree lambdaExpression = (LambdaExpressionTree) actualNode;
469                if (astNode.childSelectorIs(ASTPath.PARAMETER)) {
470                    int arg = astNode.getArgument();
471                    List<? extends VariableTree> params = lambdaExpression.getParameters();
472                    if (arg >= params.size()) {
473                        return null;
474                    }
475                    return params.get(arg);
476                } else {
477                    return lambdaExpression.getBody();
478                }
479            }
480            case MEMBER_REFERENCE: {
481                MemberReferenceTree memberReference = (MemberReferenceTree) actualNode;
482                if (astNode.childSelectorIs(ASTPath.QUALIFIER_EXPRESSION)) {
483                    return memberReference.getQualifierExpression();
484                } else {
485                    int arg = astNode.getArgument();
486                    List<? extends ExpressionTree> typeArgs = memberReference.getTypeArguments();
487                    if (arg >= typeArgs.size()) {
488                        return null;
489                    }
490                    return typeArgs.get(arg);
491                }
492            }
493            case MEMBER_SELECT: {
494                MemberSelectTree memberSelect = (MemberSelectTree) actualNode;
495                return memberSelect.getExpression();
496            }
497            case METHOD: {
498                MethodTree method = (MethodTree) actualNode;
499                if (astNode.childSelectorIs(ASTPath.TYPE)) {
500                    return method.getReturnType();
501                } else if (astNode.childSelectorIs(ASTPath.PARAMETER)) {
502                    int arg = astNode.getArgument();
503                    List<? extends VariableTree> params =
504                            method.getParameters();
505                    return arg < 0 ? method.getReceiverParameter()
506                            : arg < params.size() ? params.get(arg) : null;
507                } else if (astNode.childSelectorIs(ASTPath.TYPE_PARAMETER)) {
508                    int arg = astNode.getArgument();
509                    return method.getTypeParameters().get(arg);
510                } else {    // BODY
511                    return method.getBody();
512                }
513            }
514            case METHOD_INVOCATION: {
515                MethodInvocationTree methodInvocation = (MethodInvocationTree) actualNode;
516                if (astNode.childSelectorIs(ASTPath.TYPE_ARGUMENT)) {
517                    int arg = astNode.getArgument();
518                    List<? extends Tree> typeArgs = methodInvocation.getTypeArguments();
519                    if (arg >= typeArgs.size()) {
520                        return null;
521                    }
522                    return typeArgs.get(arg);
523                } else if (astNode.childSelectorIs(ASTPath.METHOD_SELECT)) {
524                    return methodInvocation.getMethodSelect();
525                } else {
526                    int arg = astNode.getArgument();
527                    List<? extends ExpressionTree> args = methodInvocation.getArguments();
528                    if (arg >= args.size()) {
529                        return null;
530                    }
531                    return args.get(arg);
532                }
533            }
534            case NEW_ARRAY: {
535                NewArrayTree newArray = (NewArrayTree) actualNode;
536                if (astNode.childSelectorIs(ASTPath.TYPE)) {
537                    Type type = ((JCTree.JCNewArray) newArray).type;
538                    Tree typeTree = Insertions.TypeTree.fromType(type);
539                    int arg = astNode.getArgument();
540                    if (arg == 0 && astPath.size() == ix+1) {
541                        return newArray;
542                        // if (astPath.size() != ix+1) { return null; }
543                        // return typeTree;
544                        // return ((ArrayTypeTree) typeTree).getType();
545                        // return newArray;
546                    }
547                    typeTree = ((NewArrayTree) typeTree).getType();
548                    while (--arg > 0) {
549                        if (typeTree.getKind() != Tree.Kind.ARRAY_TYPE) {
550                            return null;
551                        }
552                        typeTree = ((ArrayTypeTree) typeTree).getType();
553                    }
554                    return typeTree;
555                } else if (astNode.childSelectorIs(ASTPath.DIMENSION)) {
556                    int arg = astNode.getArgument();
557                    List<? extends ExpressionTree> dims = newArray.getDimensions();
558                    return arg < dims.size() ? dims.get(arg) : null;
559                } else {
560                    int arg = astNode.getArgument();
561                    List<? extends ExpressionTree> inits =
562                          newArray.getInitializers();
563                    return arg < inits.size() ? inits.get(arg) : null;
564                }
565            }
566            case NEW_CLASS: {
567                NewClassTree newClass = (NewClassTree) actualNode;
568                if (astNode.childSelectorIs(ASTPath.ENCLOSING_EXPRESSION)) {
569                    return newClass.getEnclosingExpression();
570                } else if (astNode.childSelectorIs(ASTPath.TYPE_ARGUMENT)) {
571                    int arg = astNode.getArgument();
572                    List<? extends Tree> typeArgs = newClass.getTypeArguments();
573                    if (arg >= typeArgs.size()) {
574                        return null;
575                    }
576                    return typeArgs.get(arg);
577                } else if (astNode.childSelectorIs(ASTPath.IDENTIFIER)) {
578                    return newClass.getIdentifier();
579                } else if (astNode.childSelectorIs(ASTPath.ARGUMENT)) {
580                    int arg = astNode.getArgument();
581                    List<? extends ExpressionTree> args = newClass.getArguments();
582                    if (arg >= args.size()) {
583                        return null;
584                    }
585                    return args.get(arg);
586                } else {
587                    return newClass.getClassBody(); // For anonymous classes
588                }
589            }
590            case PARAMETERIZED_TYPE: {
591                ParameterizedTypeTree parameterizedType = (ParameterizedTypeTree) actualNode;
592                if (astNode.childSelectorIs(ASTPath.TYPE)) {
593                    return parameterizedType.getType();
594                } else {
595                    int arg = astNode.getArgument();
596                    List<? extends Tree> typeArgs = parameterizedType.getTypeArguments();
597                    if (arg >= typeArgs.size()) {
598                        return null;
599                    }
600                    return typeArgs.get(arg);
601                }
602            }
603            case PARENTHESIZED: {
604                ParenthesizedTree parenthesized = (ParenthesizedTree) actualNode;
605                return parenthesized.getExpression();
606            }
607            case RETURN: {
608                ReturnTree returnn = (ReturnTree) actualNode;
609                return returnn.getExpression();
610            }
611            case SWITCH: {
612                SwitchTree zwitch = (SwitchTree) actualNode;
613                if (astNode.childSelectorIs(ASTPath.EXPRESSION)) {
614                    return zwitch.getExpression();
615                } else {
616                    int arg = astNode.getArgument();
617                    List<? extends CaseTree> cases = zwitch.getCases();
618                    if (arg >= cases.size()) {
619                        return null;
620                    }
621                    return cases.get(arg);
622                }
623            }
624            case SYNCHRONIZED: {
625                SynchronizedTree synchronizzed = (SynchronizedTree) actualNode;
626                if (astNode.childSelectorIs(ASTPath.EXPRESSION)) {
627                    return synchronizzed.getExpression();
628                } else {
629                    return synchronizzed.getBlock();
630                }
631            }
632            case THROW: {
633                ThrowTree throww = (ThrowTree) actualNode;
634                return throww.getExpression();
635            }
636            case TRY: {
637                TryTree tryy = (TryTree) actualNode;
638                if (astNode.childSelectorIs(ASTPath.BLOCK)) {
639                    return tryy.getBlock();
640                } else if (astNode.childSelectorIs(ASTPath.CATCH)) {
641                    int arg = astNode.getArgument();
642                    List<? extends CatchTree> catches = tryy.getCatches();
643                    if (arg >= catches.size()) {
644                        return null;
645                    }
646                    return catches.get(arg);
647                } else if (astNode.childSelectorIs(ASTPath.FINALLY_BLOCK)) {
648                    return tryy.getFinallyBlock();
649                } else {
650                    int arg = astNode.getArgument();
651                    List<? extends Tree> resources = tryy.getResources();
652                    if (arg >= resources.size()) {
653                        return null;
654                    }
655                    return resources.get(arg);
656                }
657            }
658            case TYPE_CAST: {
659                TypeCastTree typeCast = (TypeCastTree) actualNode;
660                if (astNode.childSelectorIs(ASTPath.TYPE)) {
661                    return typeCast.getType();
662                } else {
663                    return typeCast.getExpression();
664                }
665            }
666            case TYPE_PARAMETER: {
667                TypeParameterTree typeParam = (TypeParameterTree) actualNode;
668                List<? extends Tree> bounds = typeParam.getBounds();
669                int arg = astNode.getArgument();
670                return bounds.get(arg);
671            }
672            case UNION_TYPE: {
673                UnionTypeTree unionType = (UnionTypeTree) actualNode;
674                int arg = astNode.getArgument();
675                List<? extends Tree> typeAlts = unionType.getTypeAlternatives();
676                if (arg >= typeAlts.size()) {
677                    return null;
678                }
679                return typeAlts.get(arg);
680            }
681            case VARIABLE: {
682                // A VariableTree can have modifiers, but we only look at
683                // the initializer and type because modifiers can't be
684                // annotated. Any annotations on the LHS must be on the type.
685                VariableTree var = (VariableTree) actualNode;
686                if (astNode.childSelectorIs(ASTPath.INITIALIZER)) {
687                    return var.getInitializer();
688                } else if (astNode.childSelectorIs(ASTPath.TYPE)) {
689                    return var.getType();
690                } else {
691                    return null;
692                }
693            }
694            case WHILE_LOOP: {
695                WhileLoopTree whileLoop = (WhileLoopTree) actualNode;
696                if (astNode.childSelectorIs(ASTPath.CONDITION)) {
697                    return whileLoop.getCondition();
698                } else {
699                    return whileLoop.getStatement();
700                }
701            }
702            default: {
703                if (ASTPath.isBinaryOperator(actualNode.getKind())) {
704                    BinaryTree binary = (BinaryTree) actualNode;
705                    if (astNode.childSelectorIs(ASTPath.LEFT_OPERAND)) {
706                        return binary.getLeftOperand();
707                    } else {
708                        return binary.getRightOperand();
709                    }
710                } else if (ASTPath.isCompoundAssignment(actualNode.getKind())) {
711                    CompoundAssignmentTree compoundAssignment =
712                            (CompoundAssignmentTree) actualNode;
713                    if (astNode.childSelectorIs(ASTPath.VARIABLE)) {
714                        return compoundAssignment.getVariable();
715                    } else {
716                        return compoundAssignment.getExpression();
717                    }
718                } else if (ASTPath.isUnaryOperator(actualNode.getKind())) {
719                    UnaryTree unary = (UnaryTree) actualNode;
720                    return unary.getExpression();
721                } else if (isWildcard(actualNode.getKind())) {
722                    WildcardTree wildcard = (WildcardTree) actualNode;
723                    return wildcard.getBound();
724                } else {
725                    throw new IllegalArgumentException("Illegal kind: "
726                            + actualNode.getKind());
727                }
728              }
729            }
730        } catch (RuntimeException ex) { return null; }
731    }
732
733    private boolean checkNull(List<Tree> path, int ix) {
734        Tree node = path.get(path.size()-1);
735        int last = astPath.size() - 1;
736        ASTPath.ASTEntry entry = astPath.get(ix);
737        Tree.Kind kind = entry.getTreeKind();
738
739        switch (kind) {
740        // case ANNOTATION:
741        // case INTERFACE:
742        case CLASS:  // "extends" clause?
743            return ASTPath.isClassEquiv(kind)
744                    && ix == last && entry.getArgument() == -1
745                    && entry.childSelectorIs(ASTPath.BOUND);
746        case TYPE_PARAMETER:
747            return node.getKind() == Tree.Kind.TYPE_PARAMETER
748                    && ix == last && entry.getArgument() == 0
749                    && entry.childSelectorIs(ASTPath.BOUND);
750        case METHOD:  // nullary constructor? receiver?
751            if (node.getKind() != Tree.Kind.METHOD) { return false; }
752            MethodTree method = (MethodTree) node;
753            List<? extends VariableTree> params = method.getParameters();
754            if ("<init>".equals(method.getName().toString())) {
755                if (ix == last) { return true; }
756                ASTPath.ASTEntry next = astPath.get(++ix);
757                String selector = next.getChildSelector();
758                Tree typeTree =
759                    ASTPath.TYPE_PARAMETER.equals(selector)
760                        ? method.getTypeParameters().get(next.getArgument())
761                  : ASTPath.PARAMETER.equals(selector)
762                        ? params.get(next.getArgument()).getType()
763                  : null;
764                return typeTree != null && checkTypePath(ix, typeTree);
765            } else if (entry.childSelectorIs(ASTPath.PARAMETER)
766                    && entry.getArgument() == -1) {
767                if (ix == last) { return true; }
768                VariableTree rcvrParam = method.getReceiverParameter();
769                if (rcvrParam == null) {  // TODO
770                  // ClassTree clazz = methodReceiverType(path);
771                  // return checkReceiverType(ix,
772                  //    ((JCTree.JCClassDecl) clazz).type);
773                } else {
774                  return checkTypePath(ix+1, rcvrParam.getType());
775                }
776            }
777            return false;
778        case NEW_ARRAY:
779            if (node.getKind() != Tree.Kind.NEW_ARRAY) { return false; }
780            NewArrayTree newArray = (NewArrayTree) node;
781            int arg = entry.getArgument();
782            if (entry.childSelectorIs(ASTPath.TYPE)) {
783                if (ix == last) { return true; }
784                // Tree t = newArray.getType();
785                // int depth = 1;
786                // while (t.getKind() == Tree.Kind.ARRAY_TYPE) {
787                //    t = ((ArrayTypeTree) t).getType();
788                //    ++depth;
789                // }
790                return arg == arrayDepth(newArray);
791            } else {
792                List<? extends ExpressionTree> typeTrees =
793                        entry.childSelectorIs(ASTPath.DIMENSION)
794                                ? newArray.getDimensions()
795                      : entry.childSelectorIs(ASTPath.INITIALIZER)
796                                ? newArray.getInitializers()
797                      : null;
798                return typeTrees != null && arg < typeTrees.size()
799                      && checkTypePath(ix+1, typeTrees.get(arg));
800            }
801        case UNBOUNDED_WILDCARD:
802            return isBoundableWildcard(path, path.size()-1);
803        default:  // TODO: casts?
804            return false;
805        }
806    }
807
808    private static int arrayDepth(Tree tree) {
809        if (tree.getKind() == Tree.Kind.NEW_ARRAY) {
810            NewArrayTree newArray = (NewArrayTree) tree;
811            Tree type = newArray.getType();
812            if (type != null) {
813                return type.accept(new SimpleTreeVisitor<Integer, Integer>() {
814                    @Override
815                    public Integer visitArrayType(ArrayTypeTree t, Integer i) {
816                        return t.getType().accept(this, i+1);
817                    }
818                    @Override
819                    public Integer defaultAction(Tree t, Integer i) {
820                        return i;
821                    }
822                }, 1);
823            }
824            int depth = newArray.getDimensions().size();
825            for (ExpressionTree elem : newArray.getInitializers()) {
826                Tree.Kind kind = elem.getKind();
827                if (kind == Tree.Kind.NEW_ARRAY
828                        || kind == Tree.Kind.ARRAY_TYPE) {
829                    depth = Math.max(depth, arrayDepth(elem)+1);
830                }
831            }
832            return depth;
833        } else if (tree.getKind() == Tree.Kind.ANNOTATED_TYPE) {
834            return arrayDepth(((AnnotatedTypeTree) tree).getUnderlyingType());
835        } else if (tree.getKind() == Tree.Kind.ARRAY_TYPE) {
836            return 1 + arrayDepth(((ArrayTypeTree) tree).getType());
837        } else {
838            return 0;
839        }
840    }
841
842    private boolean checkReceiverType(int i, Type t) {
843        if (t == null) { return false; }
844        while (++i < astPath.size()) {
845            ASTPath.ASTEntry entry = astPath.get(i);
846            switch (entry.getTreeKind()) {
847            case ANNOTATED_TYPE:
848              break;
849            case ARRAY_TYPE:
850              if (t.getKind() != TypeKind.ARRAY) { return false; }
851              t = ((Type.ArrayType) t).getComponentType();
852              break;
853            case MEMBER_SELECT:
854              // TODO
855              break;
856            case PARAMETERIZED_TYPE:
857              if (entry.childSelectorIs(ASTPath.TYPE_PARAMETER)) {
858                if (!t.isParameterized()) { return false; }
859                List<Type> args = t.getTypeArguments();
860                int a = entry.getArgument();
861                if (a >= args.size()) { return false; }
862                t = args.get(a);
863              } // else TYPE -- stay?
864              break;
865            case TYPE_PARAMETER:
866              if (t.getKind() != TypeKind.WILDCARD) { return false; }
867              t = t.getLowerBound();
868              break;
869            case EXTENDS_WILDCARD:
870              if (t.getKind() != TypeKind.WILDCARD) { return false; }
871              t = ((Type.WildcardType) t).getExtendsBound();
872              break;
873            case SUPER_WILDCARD:
874              if (t.getKind() != TypeKind.WILDCARD) { return false; }
875              t = ((Type.WildcardType) t).getSuperBound();
876              break;
877            case UNBOUNDED_WILDCARD:
878              if (t.getKind() != TypeKind.WILDCARD) { return false; }
879              t = t.getLowerBound();
880              break;
881            default:
882              return false;
883            }
884            if (t == null) { return false; }
885        }
886        return true;
887    }
888
889    private static ClassTree methodReceiverType(TreePath path) {
890      Tree t = path.getLeaf();
891      if (t.getKind() != Tree.Kind.METHOD) { return null; }
892      JCTree.JCMethodDecl method = (JCTree.JCMethodDecl) t;
893      if ((method.mods.flags & Flags.STATIC) != 0) { return null; }
894
895      // Find the name of the class with type parameters to create the
896      // receiver. Walk up the tree and pick up class names to add to
897      // the receiver type. Since we're starting from the innermost
898      // class, the classes we get to at earlier iterations of the loop
899      // are inside of the classes we get to at later iterations.
900      TreePath parent = path.getParentPath();
901      Tree leaf = parent.getLeaf();
902      Tree.Kind kind = leaf.getKind();
903
904      // For an inner class constructor, the receiver comes from the
905      // superclass, so skip past the first type definition.
906      boolean skip = method.getReturnType() == null;
907
908      while (kind != Tree.Kind.COMPILATION_UNIT
909          && kind != Tree.Kind.NEW_CLASS) {
910        if (kind == Tree.Kind.CLASS
911            || kind == Tree.Kind.INTERFACE
912            || kind == Tree.Kind.ENUM
913            || kind == Tree.Kind.ANNOTATION_TYPE) {
914          JCTree.JCClassDecl clazz = (JCTree.JCClassDecl) leaf;
915          boolean isStatic = kind == Tree.Kind.INTERFACE
916              || kind == Tree.Kind.ENUM
917              || clazz.getModifiers().getFlags().contains(Modifier.STATIC);
918          skip &= !isStatic;
919          if (!skip || isStatic) { return clazz; }
920          skip = false;
921        }
922
923        parent = path.getParentPath();
924        leaf = parent.getLeaf();
925        kind = leaf.getKind();
926      }
927
928      throw new IllegalArgumentException("no receiver for non-inner constructor");
929    }
930
931    private boolean checkTypePath(int i, Tree typeTree) {
932        try {
933loop:       while (typeTree != null && i < astPath.size()) {
934                ASTPath.ASTEntry entry = astPath.get(i);
935                Tree.Kind kind = entry.getTreeKind();
936                switch (kind) {
937                case ANNOTATED_TYPE:
938                    typeTree = ((AnnotatedTypeTree) typeTree)
939                        .getUnderlyingType();
940                    continue;
941                case ARRAY_TYPE:
942                    typeTree = ((ArrayTypeTree) typeTree).getType();
943                    break;
944                case MEMBER_SELECT:
945                    typeTree = ((MemberSelectTree) typeTree).getExpression();
946                    break;
947                case PARAMETERIZED_TYPE:
948                    if (entry.childSelectorIs(ASTPath.TYPE_ARGUMENT)) {
949                        int arg = entry.getArgument();
950                        typeTree = ((ParameterizedTypeTree) typeTree)
951                                .getTypeArguments().get(arg);
952                    } else {  // TYPE
953                        typeTree = ((ParameterizedTypeTree) typeTree).getType();
954                    }
955                    break;
956                default:
957                    if (isWildcard(kind)) {
958                        return ++i == astPath.size();  // ???
959                    }
960                    break loop;
961                }
962                ++i;
963            }
964        } catch (RuntimeException ex) {}
965        return false;
966    }
967
968    /**
969     * Determines if the given kinds match, false otherwise. Two kinds match if
970     * they're exactly the same or if the two kinds are both compound
971     * assignments, unary operators, binary operators or wildcards.
972     * <p>
973     * This is necessary because in the JAIF file these kinds are represented by
974     * their general types (i.e. BinaryOperator, CompoundOperator, etc.) rather
975     * than their kind (i.e. PLUS, MINUS, PLUS_ASSIGNMENT, XOR_ASSIGNMENT,
976     * etc.). Internally, a single kind is used to represent each general type
977     * (i.e. PLUS is used for BinaryOperator, PLUS_ASSIGNMENT is used for
978     * CompoundAssignment, etc.). Yet, the actual source nodes have the correct
979     * kind. So if an AST path entry has a PLUS kind, that really means it could
980     * be any BinaryOperator, resulting in PLUS matching any other
981     * BinaryOperator.
982     *
983     * @param kind1
984     *            the first kind to match
985     * @param kind2
986     *            the second kind to match
987     * @return {@code true} if the kinds match as described above, {@code false}
988     *         otherwise.
989     */
990    private boolean kindsMatch(Tree.Kind kind1, Tree.Kind kind2) {
991        return kind1 == kind2 ? true
992              : ASTPath.isClassEquiv(kind1)
993                      ? ASTPath.isClassEquiv(kind2)
994              : ASTPath.isCompoundAssignment(kind1)
995                      ? ASTPath.isCompoundAssignment(kind2)
996              : ASTPath.isUnaryOperator(kind1)
997                      ? ASTPath.isUnaryOperator(kind2)
998              : ASTPath.isBinaryOperator(kind1)
999                      ? ASTPath.isBinaryOperator(kind2)
1000              : ASTPath.isWildcard(kind1)
1001                      ? ASTPath.isWildcard(kind2)
1002              : false;
1003    }
1004
1005    /**
1006     * Determines if the given kind is a binary operator.
1007     *
1008     * @param kind
1009     *            the kind to test
1010     * @return true if the given kind is a binary operator
1011     */
1012    public boolean isBinaryOperator(Tree.Kind kind) {
1013        return kind == Tree.Kind.MULTIPLY || kind == Tree.Kind.DIVIDE
1014                || kind == Tree.Kind.REMAINDER || kind == Tree.Kind.PLUS
1015                || kind == Tree.Kind.MINUS || kind == Tree.Kind.LEFT_SHIFT
1016                || kind == Tree.Kind.RIGHT_SHIFT
1017                || kind == Tree.Kind.UNSIGNED_RIGHT_SHIFT
1018                || kind == Tree.Kind.LESS_THAN
1019                || kind == Tree.Kind.GREATER_THAN
1020                || kind == Tree.Kind.LESS_THAN_EQUAL
1021                || kind == Tree.Kind.GREATER_THAN_EQUAL
1022                || kind == Tree.Kind.EQUAL_TO || kind == Tree.Kind.NOT_EQUAL_TO
1023                || kind == Tree.Kind.AND || kind == Tree.Kind.XOR
1024                || kind == Tree.Kind.OR || kind == Tree.Kind.CONDITIONAL_AND
1025                || kind == Tree.Kind.CONDITIONAL_OR;
1026    }
1027
1028    public boolean isExpression(Tree.Kind kind) {
1029        switch (kind) {
1030        case ARRAY_ACCESS:
1031        case ASSIGNMENT:
1032        case CONDITIONAL_EXPRESSION:
1033        case EXPRESSION_STATEMENT:
1034        case MEMBER_SELECT:
1035        case MEMBER_REFERENCE:
1036        case IDENTIFIER:
1037        case INSTANCE_OF:
1038        case METHOD_INVOCATION:
1039        case NEW_ARRAY:
1040        case NEW_CLASS:
1041        case LAMBDA_EXPRESSION:
1042        case PARENTHESIZED:
1043        case TYPE_CAST:
1044        case POSTFIX_INCREMENT:
1045        case POSTFIX_DECREMENT:
1046        case PREFIX_INCREMENT:
1047        case PREFIX_DECREMENT:
1048        case UNARY_PLUS:
1049        case UNARY_MINUS:
1050        case BITWISE_COMPLEMENT:
1051        case LOGICAL_COMPLEMENT:
1052        case MULTIPLY:
1053        case DIVIDE:
1054        case REMAINDER:
1055        case PLUS:
1056        case MINUS:
1057        case LEFT_SHIFT:
1058        case RIGHT_SHIFT:
1059        case UNSIGNED_RIGHT_SHIFT:
1060        case LESS_THAN:
1061        case GREATER_THAN:
1062        case LESS_THAN_EQUAL:
1063        case GREATER_THAN_EQUAL:
1064        case EQUAL_TO:
1065        case NOT_EQUAL_TO:
1066        case AND:
1067        case XOR:
1068        case OR:
1069        case CONDITIONAL_AND:
1070        case CONDITIONAL_OR:
1071        case MULTIPLY_ASSIGNMENT:
1072        case DIVIDE_ASSIGNMENT:
1073        case REMAINDER_ASSIGNMENT:
1074        case PLUS_ASSIGNMENT:
1075        case MINUS_ASSIGNMENT:
1076        case LEFT_SHIFT_ASSIGNMENT:
1077        case RIGHT_SHIFT_ASSIGNMENT:
1078        case UNSIGNED_RIGHT_SHIFT_ASSIGNMENT:
1079        case AND_ASSIGNMENT:
1080        case XOR_ASSIGNMENT:
1081        case OR_ASSIGNMENT:
1082        case INT_LITERAL:
1083        case LONG_LITERAL:
1084        case FLOAT_LITERAL:
1085        case DOUBLE_LITERAL:
1086        case BOOLEAN_LITERAL:
1087        case CHAR_LITERAL:
1088        case STRING_LITERAL:
1089        case NULL_LITERAL:
1090            return true;
1091        default:
1092            return false;
1093        }
1094    }
1095
1096    /**
1097     * Determines if the given kind is a wildcard.
1098     *
1099     * @param kind
1100     *            the kind to test
1101     * @return true if the given kind is a wildcard
1102     */
1103    private boolean isWildcard(Tree.Kind kind) {
1104        return kind == Tree.Kind.UNBOUNDED_WILDCARD
1105                || kind == Tree.Kind.EXTENDS_WILDCARD
1106                || kind == Tree.Kind.SUPER_WILDCARD;
1107    }
1108
1109    // The following check is necessary because Oracle has decided that
1110    //   x instanceof Class<? extends Object>
1111    // will remain illegal even though it means the same thing as
1112    //   x instanceof Class<?>.
1113    private boolean isBoundableWildcard(List<Tree> actualPath, int i) {
1114        if (i <= 0) { return false; }
1115        Tree actualNode = actualPath.get(i);
1116        if (actualNode.getKind() == Tree.Kind.UNBOUNDED_WILDCARD) {
1117          // isWildcard(actualNode.getKind())
1118          // TODO: refactor GenericArrayLoc to use same code?
1119          Tree ancestor = actualPath.get(i-1);
1120          if (ancestor.getKind() == Tree.Kind.INSTANCE_OF) {
1121            TreeFinder.warn.debug("WARNING: wildcard bounds not allowed "
1122                + "in 'instanceof' expression; skipping insertion%n");
1123            return false;
1124          } else if (i > 1 && ancestor.getKind() ==
1125              Tree.Kind.PARAMETERIZED_TYPE) {
1126            ancestor = actualPath.get(i-2);
1127            if (ancestor.getKind() == Tree.Kind.ARRAY_TYPE) {
1128              TreeFinder.warn.debug("WARNING: wildcard bounds not allowed "
1129                  + "in 'instanceof' expression; skipping insertion%n");
1130              return false;
1131            }
1132          }
1133          return true;
1134        }
1135        return false;
1136    }
1137
1138    /** {@inheritDoc} */
1139    @Override
1140    public Kind getKind() {
1141        return Kind.AST_PATH;
1142    }
1143
1144    /** {@inheritDoc} */
1145    @Override
1146    public String toString() {
1147        return "ASTPathCriterion: " + astPath;
1148    }
1149}
1150