1 package annotator.find; 2 3 import java.util.List; 4 5 import annotations.el.LocalLocation; 6 import annotator.scanner.LocalVariableScanner; 7 8 import com.sun.source.tree.MethodTree; 9 import com.sun.source.tree.Tree; 10 import com.sun.source.tree.VariableTree; 11 import com.sun.source.util.TreePath; 12 import com.sun.tools.javac.util.Pair; 13 14 /** 15 * Criterion for being a specific local variable. 16 */ 17 public class LocalVariableCriterion implements Criterion { 18 19 private final String fullMethodName; 20 private final LocalLocation loc; 21 LocalVariableCriterion(String methodName, LocalLocation loc)22 public LocalVariableCriterion(String methodName, LocalLocation loc) { 23 this.fullMethodName = methodName.substring(0, methodName.indexOf(")") + 1); 24 this.loc = loc; 25 } 26 27 /** {@inheritDoc} */ 28 @Override isSatisfiedBy(TreePath path, Tree leaf)29 public boolean isSatisfiedBy(TreePath path, Tree leaf) { 30 assert path == null || path.getLeaf() == leaf; 31 return isSatisfiedBy(path); 32 } 33 34 /** {@inheritDoc} */ 35 @Override isSatisfiedBy(TreePath path)36 public boolean isSatisfiedBy(TreePath path) { 37 if (path == null) { 38 return false; 39 } 40 41 TreePath parentPath = path.getParentPath(); 42 if (parentPath != null) { 43 Tree parent = parentPath.getLeaf(); 44 if (parent != null) { 45 if ((parent instanceof VariableTree) 46 // Avoid matching formal parameters 47 && (! (parentPath.getParentPath().getLeaf() instanceof MethodTree))) { 48 VariableTree vtt = (VariableTree) parent; 49 String varName = vtt.getName().toString(); 50 51 if (loc.varName!=null && loc.varName.equals(varName)) { 52 int varIndex = LocalVariableScanner.indexOfVarTree(path, vtt, varName); 53 54 if (loc.varIndex==varIndex) { 55 // the location specifies a variable name and index and it matches the current variable 56 // -> hurray 57 return true; 58 } 59 return false; 60 } 61 62 Pair<String, Pair<Integer, Integer>> key = 63 Pair.of(fullMethodName, Pair.of(loc.index, loc.scopeStart)); 64 String potentialVarName = 65 LocalVariableScanner.getFromMethodNameIndexMap(key); 66 if (potentialVarName != null) { 67 if (varName.equals(potentialVarName)) { 68 // now use methodNameCounter to ensure that if this is the 69 // i'th variable of this name, its offset is the i'th offset 70 // of all variables with this name 71 List<Integer> allOffsetsWithThisName = 72 LocalVariableScanner.getFromMethodNameCounter(fullMethodName, potentialVarName); 73 // methodNameCounter.get(fullMethodName).get(potentialVarName); 74 Integer thisVariablesOffset = 75 allOffsetsWithThisName.indexOf(loc.scopeStart); 76 77 // now you need to make sure that this is the 78 // thisVariablesOffset'th variable tree in the entire source 79 int i = LocalVariableScanner.indexOfVarTree(path, parent, potentialVarName); 80 81 if (i == thisVariablesOffset) { 82 return true; 83 } 84 } 85 } 86 } else { 87 // If present leaf does not yet satisfy the local variable 88 // criterion, note that it actually is the correct local variable 89 // if any of its parents satisfy this local variable criterion 90 // (and going all the way up past the top-level tree is taken 91 // care of by the check for null above. 92 // 93 // For example, if you have the tree for "Integer" 94 // for the local variable "List<Integer> foo;" 95 // the parent of the current leaf will satisfy the local variable 96 // criterion directly. The fact that you will never return true 97 // for something that is not the correct local variable comes 98 // from the fact that you can't contain one local variable 99 // within another. For example, you can't have 100 // List<Integer bar> foo; 101 // Thus, no local variable tree can contain another local 102 // variable tree. 103 // Another general example: 104 // List<Integer> foo = ...; 105 // If the tree for ... contains one local variable, there is no fear 106 // of a conflict with "List<Integer>", because "List<Integer> foo" 107 // is a subtree of "List<Integer> foo = ...;", so the two 108 // (possibly) conflicting local variable trees are both subtrees 109 // of the same tree, and neither is an ancestor of the other. 110 return this.isSatisfiedBy(parentPath); 111 // To do: should stop this once it gets to method? or some other top level? 112 } 113 } 114 } 115 return false; 116 } 117 118 119 @Override getKind()120 public Kind getKind() { 121 return Kind.LOCAL_VARIABLE; 122 } 123 124 @Override toString()125 public String toString() { 126 return "LocalVariableCriterion: in: " + fullMethodName + " loc: " + loc; 127 } 128 } 129