PageRenderTime 44ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/groovy-eclipse/org.codehaus.groovy.eclipse.core/src/org/codehaus/groovy/eclipse/core/types/TypeEvaluator.java

http://groovy-eclipse.googlecode.com/
Java | 473 lines | 358 code | 72 blank | 43 comment | 23 complexity | d53adc97bc96224cb51419585c0c3ac0 MD5 | raw file
Possible License(s): Apache-2.0
  1. package org.codehaus.groovy.eclipse.core.types;
  2. import java.io.ByteArrayInputStream;
  3. import java.util.ArrayList;
  4. import org.codehaus.groovy.antlr.GroovySourceAST;
  5. import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
  6. import org.codehaus.groovy.ast.ClassNode;
  7. import org.codehaus.groovy.ast.MethodNode;
  8. import org.codehaus.groovy.ast.ModuleNode;
  9. import org.codehaus.groovy.ast.expr.ArrayExpression;
  10. import org.codehaus.groovy.ast.expr.AttributeExpression;
  11. import org.codehaus.groovy.ast.expr.BinaryExpression;
  12. import org.codehaus.groovy.ast.expr.BitwiseNegationExpression;
  13. import org.codehaus.groovy.ast.expr.BooleanExpression;
  14. import org.codehaus.groovy.ast.expr.CastExpression;
  15. import org.codehaus.groovy.ast.expr.ClassExpression;
  16. import org.codehaus.groovy.ast.expr.ConstantExpression;
  17. import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
  18. import org.codehaus.groovy.ast.expr.Expression;
  19. import org.codehaus.groovy.ast.expr.FieldExpression;
  20. import org.codehaus.groovy.ast.expr.GStringExpression;
  21. import org.codehaus.groovy.ast.expr.ListExpression;
  22. import org.codehaus.groovy.ast.expr.MapExpression;
  23. import org.codehaus.groovy.ast.expr.MethodCallExpression;
  24. import org.codehaus.groovy.ast.expr.NotExpression;
  25. import org.codehaus.groovy.ast.expr.PostfixExpression;
  26. import org.codehaus.groovy.ast.expr.PrefixExpression;
  27. import org.codehaus.groovy.ast.expr.PropertyExpression;
  28. import org.codehaus.groovy.ast.expr.RangeExpression;
  29. import org.codehaus.groovy.ast.expr.RegexExpression;
  30. import org.codehaus.groovy.ast.expr.SpreadExpression;
  31. import org.codehaus.groovy.ast.expr.SpreadMapExpression;
  32. import org.codehaus.groovy.ast.expr.TernaryExpression;
  33. import org.codehaus.groovy.ast.expr.VariableExpression;
  34. import org.codehaus.groovy.ast.stmt.BlockStatement;
  35. import org.codehaus.groovy.ast.stmt.ExpressionStatement;
  36. import org.codehaus.groovy.ast.stmt.ReturnStatement;
  37. import org.codehaus.groovy.ast.stmt.Statement;
  38. import org.codehaus.groovy.control.SourceUnit;
  39. import org.codehaus.groovy.eclipse.core.GroovyCore;
  40. import org.codehaus.groovy.eclipse.core.compiler.GroovyCompiler;
  41. import org.codehaus.groovy.eclipse.core.compiler.GroovyCompilerConfigurationBuilder;
  42. import org.codehaus.groovy.eclipse.core.compiler.IGroovyCompilationReporter;
  43. import org.codehaus.groovy.eclipse.core.compiler.IGroovyCompiler;
  44. import org.codehaus.groovy.eclipse.core.compiler.IGroovyCompilerConfiguration;
  45. import org.codehaus.groovy.eclipse.core.util.UnsupportedVisitException;
  46. import org.codehaus.groovy.syntax.Types;
  47. /**
  48. * The type evaluator attempts to evaluate an expression and return the type of the result.
  49. *
  50. * @author empovazan
  51. */
  52. public class TypeEvaluator {
  53. public static class EvalResult {
  54. String signature;
  55. boolean isClass;
  56. EvalResult(String signature, boolean isClass) {
  57. this.signature = signature;
  58. this.isClass = isClass;
  59. }
  60. public String getSignature() {
  61. return signature;
  62. }
  63. public boolean isClass() {
  64. return isClass;
  65. }
  66. }
  67. /**
  68. * Internal reporter used to get the AST.
  69. */
  70. private static class Reporter implements IGroovyCompilationReporter {
  71. ModuleNode moduleNode;
  72. public void beginReporting() {
  73. moduleNode = null;
  74. }
  75. public void beginReporting(String fileName) {
  76. }
  77. public void compilationError(String fileName, int line, int startCol, int endCol, String message,
  78. String stackTrace) {
  79. }
  80. public void endReporting() {
  81. }
  82. public void endReporting(String fileName) {
  83. }
  84. public void generatedAST(String fileName, ModuleNode moduleNode) {
  85. this.moduleNode = moduleNode;
  86. }
  87. public void generatedCST(String fileName, GroovySourceAST cst) {
  88. }
  89. public void generatedClasses(String fileName, String[] classNames, String[] classFilePaths) {
  90. }
  91. }
  92. private final Reporter reporter = new Reporter();
  93. private final Visitor visitor = new Visitor();
  94. private final ITypeEvaluationContext context;
  95. /**
  96. * Creates a type evaluator for the given evaluation context.
  97. * @param context
  98. */
  99. public TypeEvaluator(ITypeEvaluationContext context) {
  100. this.context = context;
  101. }
  102. /**
  103. * Evaluate an expression for its type.
  104. *
  105. * @param expression
  106. * @return The evaluation result, or null if a type could not be evaluated.
  107. */
  108. public EvalResult evaluate(String expression) {
  109. ModuleNode moduleNode = compileExpression(expression);
  110. if (moduleNode != null) {
  111. Statement stmt = extractFirstStatement(moduleNode);
  112. stmt.visit(visitor);
  113. return visitor.getResult();
  114. }
  115. return null;
  116. }
  117. /**
  118. * Given an expression in
  119. * @param expression
  120. * @return
  121. */
  122. public EvalResult evaluate(Expression expression) {
  123. try {
  124. expression.visit(visitor);
  125. } catch (UnsupportedVisitException e) {
  126. GroovyCore.logException("Unsupported type evaluation: " + e.getMessage(), e);
  127. }
  128. return visitor.getResult();
  129. }
  130. /**
  131. * Compile the simple expression without any type resolution.
  132. *
  133. * @param expression
  134. * @return
  135. */
  136. private ModuleNode compileExpression(String expression) {
  137. expression = decorateExpression(expression);
  138. IGroovyCompiler compiler = new GroovyCompiler();
  139. ClassLoader loader = context.getClassLoader();
  140. if (loader == null) {
  141. loader = Thread.currentThread().getContextClassLoader();
  142. }
  143. IGroovyCompilerConfiguration config = new GroovyCompilerConfigurationBuilder()
  144. .classLoader(loader)
  145. .buildAST()
  146. .resolveAST()
  147. .done();
  148. compiler.compile("CompletionExpression", new ByteArrayInputStream(expression.getBytes()), config, reporter);
  149. return reporter.moduleNode;
  150. }
  151. private String decorateExpression(String expression) {
  152. String[] imports = context.getImports();
  153. if (imports.length != 0) {
  154. StringBuffer sb = new StringBuffer();
  155. for (int i = 0; i < imports.length; i++) {
  156. sb.append("import ").append(imports[i]).append("\n");
  157. }
  158. sb.append(expression);
  159. return sb.toString();
  160. }
  161. return expression;
  162. }
  163. /**
  164. * Extract the first statement of the code block. This is the statement to visit to evaluate the type.
  165. *
  166. * @param moduleNode
  167. * @return
  168. */
  169. private Statement extractFirstStatement(ModuleNode moduleNode) {
  170. ClassNode classNode = (ClassNode) moduleNode.getClasses().get(0);
  171. MethodNode methodNode = (MethodNode) classNode.getMethods("run").get(0);
  172. return (Statement) ((BlockStatement) methodNode.getCode()).getStatements().get(0);
  173. }
  174. class Visitor extends ClassCodeVisitorSupport {
  175. class StackItem {
  176. String signature;
  177. Object value;
  178. boolean isClass;
  179. public StackItem(String signature) {
  180. this(signature, false);
  181. }
  182. public StackItem(String signature, Object value) {
  183. this(signature, value, false);
  184. }
  185. public StackItem(String signature, boolean isClass) {
  186. this(signature, "", isClass);
  187. }
  188. public StackItem(String signature, Object value, boolean isClass) {
  189. this.signature = signature;
  190. this.value = value;
  191. this.isClass = isClass;
  192. }
  193. @Override
  194. public String toString() {
  195. return signature + ":" + value + ":" + isClass;
  196. }
  197. }
  198. class Stack extends ArrayList {
  199. private static final long serialVersionUID = -1280137281407764358L;
  200. @SuppressWarnings("unchecked")
  201. public void push(StackItem item) {
  202. add(item);
  203. }
  204. public void push(String signature) {
  205. push(signature, null, false);
  206. }
  207. public void push(String signature, Object value) {
  208. push(signature, value, false);
  209. }
  210. public void push(String signature, Object value, boolean isClass) {
  211. push(new StackItem(signature, value, isClass));
  212. }
  213. public StackItem peek() {
  214. return (StackItem) get(size() - 1);
  215. }
  216. public StackItem pop() {
  217. return (StackItem) remove(size() - 1);
  218. }
  219. }
  220. Stack stack = new Stack();
  221. EvalResult getResult() {
  222. if (stack.size() > 0) {
  223. StackItem item = stack.pop();
  224. return new EvalResult(item.signature, item.isClass);
  225. }
  226. return null;
  227. }
  228. @Override
  229. protected SourceUnit getSourceUnit() {
  230. return null;
  231. }
  232. @Override
  233. public void visitArrayExpression(ArrayExpression expression) {
  234. throw new UnsupportedVisitException("visitArrayException");
  235. }
  236. @Override
  237. public void visitAttributeExpression(AttributeExpression expression) {
  238. throw new UnsupportedVisitException("visitAttributeExpression");
  239. }
  240. @Override
  241. public void visitBitwiseNegationExpression(BitwiseNegationExpression expression) {
  242. super.visitBitwiseNegationExpression(expression);
  243. StackItem objectType = stack.pop();
  244. if (objectType.signature.equals("java.lang.String")) {
  245. stack.push(new StackItem("java.util.regex.Pattern"));
  246. } else if (objectType.signature.equals("java.lang.Integer")) {
  247. stack.push(new StackItem("java.lang.Integer"));
  248. } else if (objectType.signature.equals("java.lang.Long")) {
  249. stack.push(new StackItem("java.lang.Long"));
  250. } else {
  251. throw new UnsupportedVisitException("visitBitwiseNegationExpression");
  252. }
  253. }
  254. @Override
  255. public void visitBinaryExpression(BinaryExpression expression) {
  256. // FUTURE: emp - if a list access, find the types of elements place in the list, and choose the base type.
  257. super.visitBinaryExpression(expression);
  258. String signature = TypeUtil.OBJECT_TYPE;
  259. StackItem right = stack.pop();
  260. StackItem left = stack.pop();
  261. if (expression.getOperation().getType() == Types.LEFT_SQUARE_BRACKET) {
  262. signature = left.signature;
  263. if (signature.charAt(0) == '[') {
  264. signature = signature.charAt(1) == 'L' ? signature.substring(2, signature.length() - 1) : signature
  265. .substring(1);
  266. }
  267. }
  268. stack.push(signature);
  269. }
  270. @Override
  271. public void visitBooleanExpression(BooleanExpression expression) {
  272. throw new UnsupportedVisitException("visitBooleanExpression");
  273. }
  274. @Override
  275. public void visitCastExpression(CastExpression expression) {
  276. super.visitCastExpression(expression);
  277. stack.push(new StackItem(expression.getType().getName(), true));
  278. }
  279. @Override
  280. public void visitClassExpression(ClassExpression expression) {
  281. super.visitClassExpression(expression);
  282. ClassNode type = expression.getType();
  283. stack.push(new StackItem(type.getName(), true));
  284. }
  285. @Override
  286. public void visitConstantExpression(ConstantExpression expression) {
  287. // Type and value here
  288. stack.push(new StackItem(expression.getType().getName(), expression
  289. .getValue()));
  290. }
  291. @Override
  292. public void visitConstructorCallExpression(ConstructorCallExpression call) {
  293. int stackSize = stack.size();
  294. super.visitConstructorCallExpression(call);
  295. int argCount = stack.size() - stackSize; // Ignore args.
  296. for (int i = 0; i < argCount; ++i) {
  297. stack.pop();
  298. }
  299. // C_tor is typed, nothing to do.
  300. stack.push(call.getType().getName());
  301. }
  302. @Override
  303. public void visitExpressionStatement(ExpressionStatement statement) {
  304. super.visitExpressionStatement(statement);
  305. }
  306. @Override
  307. public void visitFieldExpression(FieldExpression expression) {
  308. throw new UnsupportedVisitException("visitFieldExpression");
  309. }
  310. @Override
  311. public void visitGStringExpression(GStringExpression expression) {
  312. super.visitGStringExpression(expression);
  313. stack.push("groovy.lang.GString");
  314. }
  315. @Override
  316. public void visitListExpression(ListExpression expression) {
  317. super.visitListExpression(expression);
  318. stack.push("java.util.List");
  319. }
  320. @Override
  321. public void visitMapExpression(MapExpression expression) {
  322. super.visitMapExpression(expression);
  323. stack.push("java.util.Map");
  324. }
  325. @Override
  326. public void visitMethodCallExpression(MethodCallExpression call) {
  327. int stackSize = stack.size();
  328. super.visitMethodCallExpression(call);
  329. // Get the argument types.
  330. int argCount = stack.size() - stackSize - 2;
  331. String[] argTypes = new String[argCount];
  332. for (int i = 0; i < argCount; ++i) {
  333. StackItem arg = stack.pop();
  334. argTypes[i] = arg.signature;
  335. }
  336. // Method name, remove it
  337. stack.pop();
  338. // Return type.
  339. StackItem objectType = stack.pop();
  340. // The method parameters for this call should be inferred at this point.
  341. Method method = context.lookupMethod(objectType.signature, call.getMethod().getText(), argTypes, false,
  342. objectType.isClass);
  343. stack.push(new StackItem(method.getReturnType()));
  344. }
  345. @Override
  346. public void visitNotExpression(NotExpression expression) {
  347. throw new UnsupportedVisitException("visitNotExpression");
  348. }
  349. @Override
  350. public void visitPostfixExpression(PostfixExpression expression) {
  351. throw new UnsupportedVisitException("visitPostfixExpression");
  352. }
  353. @Override
  354. public void visitPrefixExpression(PrefixExpression expression) {
  355. throw new UnsupportedVisitException("visitPrefixExpression");
  356. }
  357. @Override
  358. public void visitPropertyExpression(PropertyExpression expression) {
  359. super.visitPropertyExpression(expression);
  360. StackItem propertyItem = stack.pop();
  361. StackItem objectItem = stack.pop();
  362. Type property = context.lookupProperty(objectItem.signature, (String) propertyItem.value,
  363. false, propertyItem.isClass);
  364. //fields are detected as properties
  365. if (property==null) {
  366. property = context.lookupField(objectItem.signature, (String) propertyItem.value,
  367. false, propertyItem.isClass);
  368. }
  369. stack.push(new StackItem(property.getSignature()));
  370. }
  371. @Override
  372. public void visitRangeExpression(RangeExpression expression) {
  373. throw new UnsupportedVisitException("visitRangeExpression");
  374. }
  375. @Override
  376. public void visitRegexExpression(RegexExpression expression) {
  377. throw new UnsupportedVisitException("visitRegexExpression");
  378. }
  379. @Override
  380. public void visitReturnStatement(ReturnStatement statement) {
  381. throw new UnsupportedVisitException("visitReturnStatement");
  382. }
  383. @Override
  384. public void visitSpreadExpression(SpreadExpression expression) {
  385. throw new UnsupportedVisitException("visitSpreadExpression");
  386. }
  387. @Override
  388. public void visitSpreadMapExpression(SpreadMapExpression expression) {
  389. throw new UnsupportedVisitException("visitSpreadMapExpression");
  390. }
  391. @Override
  392. public void visitTernaryExpression(TernaryExpression expression) {
  393. super.visitTernaryExpression(expression);
  394. }
  395. @Override
  396. public void visitVariableExpression(VariableExpression expression) {
  397. Type type = context.lookupSymbol(expression.getName());
  398. stack.push(new StackItem(type.getSignature(), expression.getName(), type.getType() == Type.CLASS));
  399. }
  400. }
  401. }