package annotator.find; import java.util.List; import annotations.el.LocalLocation; import annotator.scanner.LocalVariableScanner; import com.sun.source.tree.MethodTree; import com.sun.source.tree.Tree; import com.sun.source.tree.VariableTree; import com.sun.source.util.TreePath; import com.sun.tools.javac.util.Pair; /** * Criterion for being a specific local variable. */ public class LocalVariableCriterion implements Criterion { private final String fullMethodName; private final LocalLocation loc; public LocalVariableCriterion(String methodName, LocalLocation loc) { this.fullMethodName = methodName.substring(0, methodName.indexOf(")") + 1); this.loc = loc; } /** {@inheritDoc} */ @Override public boolean isSatisfiedBy(TreePath path, Tree leaf) { assert path == null || path.getLeaf() == leaf; return isSatisfiedBy(path); } /** {@inheritDoc} */ @Override public boolean isSatisfiedBy(TreePath path) { if (path == null) { return false; } TreePath parentPath = path.getParentPath(); if (parentPath != null) { Tree parent = parentPath.getLeaf(); if (parent != null) { if ((parent instanceof VariableTree) // Avoid matching formal parameters && (! (parentPath.getParentPath().getLeaf() instanceof MethodTree))) { VariableTree vtt = (VariableTree) parent; String varName = vtt.getName().toString(); if (loc.varName!=null && loc.varName.equals(varName)) { int varIndex = LocalVariableScanner.indexOfVarTree(path, vtt, varName); if (loc.varIndex==varIndex) { // the location specifies a variable name and index and it matches the current variable // -> hurray return true; } return false; } Pair<String, Pair<Integer, Integer>> key = Pair.of(fullMethodName, Pair.of(loc.index, loc.scopeStart)); String potentialVarName = LocalVariableScanner.getFromMethodNameIndexMap(key); if (potentialVarName != null) { if (varName.equals(potentialVarName)) { // now use methodNameCounter to ensure that if this is the // i'th variable of this name, its offset is the i'th offset // of all variables with this name List<Integer> allOffsetsWithThisName = LocalVariableScanner.getFromMethodNameCounter(fullMethodName, potentialVarName); // methodNameCounter.get(fullMethodName).get(potentialVarName); Integer thisVariablesOffset = allOffsetsWithThisName.indexOf(loc.scopeStart); // now you need to make sure that this is the // thisVariablesOffset'th variable tree in the entire source int i = LocalVariableScanner.indexOfVarTree(path, parent, potentialVarName); if (i == thisVariablesOffset) { return true; } } } } else { // If present leaf does not yet satisfy the local variable // criterion, note that it actually is the correct local variable // if any of its parents satisfy this local variable criterion // (and going all the way up past the top-level tree is taken // care of by the check for null above. // // For example, if you have the tree for "Integer" // for the local variable "List<Integer> foo;" // the parent of the current leaf will satisfy the local variable // criterion directly. The fact that you will never return true // for something that is not the correct local variable comes // from the fact that you can't contain one local variable // within another. For example, you can't have // List<Integer bar> foo; // Thus, no local variable tree can contain another local // variable tree. // Another general example: // List<Integer> foo = ...; // If the tree for ... contains one local variable, there is no fear // of a conflict with "List<Integer>", because "List<Integer> foo" // is a subtree of "List<Integer> foo = ...;", so the two // (possibly) conflicting local variable trees are both subtrees // of the same tree, and neither is an ancestor of the other. return this.isSatisfiedBy(parentPath); // To do: should stop this once it gets to method? or some other top level? } } } return false; } @Override public Kind getKind() { return Kind.LOCAL_VARIABLE; } @Override public String toString() { return "LocalVariableCriterion: in: " + fullMethodName + " loc: " + loc; } }