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