1package annotator.scanner;
2
3import java.util.ArrayList;
4import java.util.HashMap;
5import java.util.List;
6import java.util.Map;
7
8import com.sun.source.tree.LambdaExpressionTree;
9import com.sun.source.tree.Tree;
10import com.sun.source.util.TreePath;
11
12/**
13 * LambdaScanner stores information about the names and offsets of
14 * lambda expressions inside a method, and can also be used to scan the
15 * source tree and determine the index of a given instanceof check,
16 * where the i^th index corresponds to the i^th instanceof check, using
17 * 0-based indexing.
18 */
19public class LambdaScanner extends CommonScanner {
20
21  /**
22   * Computes the index of the given lambda expression tree amongst all
23   * lambda expression trees inside its method, using 0-based indexing.
24   *
25   * @param origpath the path ending in the given lambda expression tree
26   * @param tree the lambda expression tree to search for
27   * @return the index of the given lambda expression tree
28   */
29  public static int indexOfLambdaExpressionTree(TreePath origpath, Tree tree) {
30    TreePath path = findCountingContext(origpath);
31    if (path == null) {
32      return -1;
33    }
34
35    LambdaScanner ls = new LambdaScanner(tree);
36    ls.scan(path, null);
37    return ls.index;
38  }
39
40  private int index;
41  private boolean done;
42  private final Tree tree;
43
44  /**
45   * Creates an InstanceOfScanner that will scan the source tree for the
46   *  given node representing the lambda expression to find.
47   * @param tree the given lambda expression to search for
48   */
49  private LambdaScanner(Tree tree) {
50    this.index = -1;
51    this.done = false;
52    this.tree = tree;
53  }
54
55  @Override
56  public Void visitLambdaExpression(LambdaExpressionTree node, Void p) {
57    if (!done) {
58      index++;
59    }
60    if (tree == node) {
61      done = true;
62    }
63    return super.visitLambdaExpression(node, p);
64  }
65
66  // Map from name of a method to a list of bytecode offsets of all
67  // lambda expressions in that method.
68  private static Map<String, List<Integer>> methodNameToLambdaExpressionOffsets =
69      new HashMap<String, List<Integer>>();
70
71  /**
72   * Adds a lambda expression bytecode offset to the current list of
73   * offsets for methodName.  This method must be called with
74   * monotonically increasing offsets for any one method.
75   *
76   * @param methodName the name of the method
77   * @param offset the offset to add
78   */
79  public static void addLambdaExpressionToMethod(String methodName, Integer offset) {
80    List<Integer> offsetList =
81        methodNameToLambdaExpressionOffsets.get(methodName);
82    if (offsetList == null) {
83      offsetList = new ArrayList<Integer>();
84      methodNameToLambdaExpressionOffsets.put(methodName, offsetList);
85    }
86    offsetList.add(offset);
87  }
88
89  /**
90   * Returns the index of the given offset within the list of offsets
91   * for the given method, using 0-based indexing, or returns a negative
92   * number if the offset is not one of the offsets in the context.
93   *
94   * @param methodName the name of the method
95   * @param offset the offset of the lambda expression
96   * @return the index of the given offset, or a negative number
97   *  if the offset does not exist inside the context
98   */
99  public static Integer
100  getMethodLambdaExpressionIndex(String methodName, Integer offset) {
101    List<Integer> offsetList =
102        methodNameToLambdaExpressionOffsets.get(methodName);
103    if (offsetList == null) {
104      return -1;
105    }
106
107    return offsetList.indexOf(offset);
108  }
109}
110