Home | History | Annotate | Download | only in scanner
      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