• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package annotations.io;
2 
3 import java.util.ArrayDeque;
4 import java.util.Deque;
5 
6 import com.sun.source.tree.ClassTree;
7 import com.sun.source.tree.CompilationUnitTree;
8 import com.sun.source.tree.MethodTree;
9 import com.sun.source.tree.Tree;
10 import com.sun.source.tree.Tree.Kind;
11 import com.sun.source.tree.VariableTree;
12 import com.sun.source.util.TreePath;
13 
14 /**
15  * Structure bundling an {@link ASTPath} with information about its
16  *  starting point. Necessary because the {@link ASTPath} structure
17  *  does not include the declaration from which it originates.
18  *
19  * @author dbro
20  */
21 public class ASTRecord implements Comparable<ASTRecord> {
22   /**
23    * The AST to which this {@code ASTRecord} pertains.
24    */
25   public final CompilationUnitTree ast;
26 
27   /**
28    * Name of the enclosing class declaration.
29    */
30   public final String className;
31 
32   /**
33    * Name of the enclosing method declaration, or null if there is none.
34    */
35   public final String methodName;
36 
37   /**
38    * Name of the enclosing variable declaration, or null if there is none.
39    */
40   public final String varName;
41 
42   /**
43    * Path through AST, from specified declaration to descendant node.
44    */
45   public final ASTPath astPath;
46 
ASTRecord(CompilationUnitTree ast, String className, String methodName, String varName, ASTPath astPath)47   public ASTRecord(CompilationUnitTree ast, String className,
48       String methodName, String varName, ASTPath astPath) {
49     this.ast = ast;
50     this.className = className;
51     this.methodName = methodName;
52     this.varName = varName;
53     // FIXME: ensure path is canonical
54     if (varName != null) {
55       // TODO?
56     } else if (methodName != null) {
57       int n = astPath.size();
58       if (n > 0 && astPath.get(0).getTreeKind() != Tree.Kind.METHOD
59           && astPath.get(0).getTreeKind() != Tree.Kind.VARIABLE) {
60         ASTPath bodyPath = ASTPath.empty().add(
61             new ASTPath.ASTEntry(Tree.Kind.METHOD, ASTPath.BODY));
62         for (int i = 0; i < n; i++) { bodyPath = bodyPath.add(astPath.get(i)); }
63         astPath = bodyPath;
64       }
65     }
66     this.astPath = astPath;
67   }
68 
newArrayLevel(int depth)69   public ASTRecord newArrayLevel(int depth) {
70     return new ASTRecord(ast, className, methodName, varName,
71         astPath.extendNewArray(depth));
72   }
73 
replacePath(ASTPath newPath)74   public ASTRecord replacePath(ASTPath newPath) {
75     return new ASTRecord(ast, className, methodName, varName, newPath);
76   }
77 
78   @Override
equals(Object o)79   public boolean equals(Object o) {
80     return o instanceof ASTRecord && equals((ASTRecord) o);
81   }
82 
equals(ASTRecord astRecord)83   public boolean equals(ASTRecord astRecord) {
84     return compareTo(astRecord) == 0;
85   }
86 
87   @Override
compareTo(ASTRecord rec)88   public int compareTo(ASTRecord rec) {
89     int d = ast == null
90         ? rec.ast == null ? 0 : -1
91         : rec.ast == null ? 1 : Integer
92             .compare(ast.hashCode(), rec.ast.hashCode());
93     if (d == 0) {
94       d = className == null
95           ? rec.className == null ? 0 : -1
96           : rec.className == null ? 1 : className.compareTo(rec.className);
97       if (d == 0) {
98         d = methodName == null
99             ? rec.methodName == null ? 0 : -1
100             : rec.methodName == null ? 1 : methodName.compareTo(rec.methodName);
101         if (d == 0) {
102           d = varName == null
103               ? rec.varName == null ? 0 : -1
104               : rec.varName == null ? 1 : varName.compareTo(rec.varName);
105           if (d == 0) {
106             d = astPath == null
107                 ? rec.astPath == null ? 0 : -1
108                 : rec.astPath == null ? 1 : astPath.compareTo(rec.astPath);
109           }
110         }
111       }
112     }
113     return d;
114   }
115 
116   @Override
hashCode()117   public int hashCode() {
118     return ast.hashCode()
119         ^ (className == null ? 0
120             : Integer.rotateRight(className.hashCode(), 3))
121         ^ (methodName == null ? 0
122             : Integer.rotateRight(methodName.hashCode(), 6))
123         ^ (varName == null ? 0
124             : Integer.rotateRight(varName.hashCode(), 9))
125         ^ (astPath == null ? 0
126             : Integer.rotateRight(astPath.hashCode(), 12));
127   }
128 
129   /**
130    * Indicates whether this record identifies the given {@link TreePath}.
131    */
matches(TreePath treePath)132   public boolean matches(TreePath treePath) {
133     String clazz = null;
134     String meth = null;
135     String var = null;
136     boolean matchVars = false;  // members only!
137     Deque<Tree> stack = new ArrayDeque<Tree>();
138     for (Tree tree : treePath) { stack.push(tree); }
139     while (!stack.isEmpty()) {
140       Tree tree = stack.pop();
141       switch (tree.getKind()) {
142       case CLASS:
143       case INTERFACE:
144       case ENUM:
145       case ANNOTATION_TYPE:
146         clazz = ((ClassTree) tree).getSimpleName().toString();
147         meth = null;
148         var = null;
149         matchVars = true;
150         break;
151       case METHOD:
152         assert meth == null;
153         meth = ((MethodTree) tree).getName().toString();
154         matchVars = false;
155         break;
156       case VARIABLE:
157         if (matchVars) {
158           assert var == null;
159           var = ((VariableTree) tree).getName().toString();
160           matchVars = false;
161         }
162         break;
163       default:
164         matchVars = false;
165         continue;
166       }
167     }
168     return className.equals(clazz)
169         && (methodName == null ? meth == null : methodName.equals(meth))
170         && (varName == null ? var == null : varName.equals(var))
171         && astPath.matches(treePath);
172   }
173 
174   @Override
toString()175   public String toString() {
176     return new StringBuilder()
177         .append(className == null ? "" : className).append(":")
178         .append(methodName == null ? "" : methodName).append(":")
179         .append(varName == null ? "" : varName).append(":")
180         .append(astPath).toString();
181   }
182 
extend(ASTPath.ASTEntry entry)183   public ASTRecord extend(ASTPath.ASTEntry entry) {
184     return new ASTRecord(ast, className, methodName, varName,
185         astPath.extend(entry));
186   }
187 
extend(Kind kind, String sel)188   public ASTRecord extend(Kind kind, String sel) {
189     return extend(new ASTPath.ASTEntry(kind, sel));
190   }
191 
extend(Kind kind, String sel, int arg)192   public ASTRecord extend(Kind kind, String sel, int arg) {
193     return extend(new ASTPath.ASTEntry(kind, sel, arg));
194   }
195 }
196