PageRenderTime 53ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 1ms

/plugins/org.jnario/src/org/jnario/compiler/JnarioCompiler.java

http://github.com/bmwcarit/Jnario
Java | 456 lines | 390 code | 55 blank | 11 comment | 61 complexity | 54b906def399b4f5b8cb7f0d9ccd8da7 MD5 | raw file
Possible License(s): Apache-2.0
  1. /*******************************************************************************
  2. * Copyright (c) 2012 BMW Car IT and others.
  3. * All rights reserved. This program and the accompanying materials
  4. * are made available under the terms of the Eclipse Public License v1.0
  5. * which accompanies this distribution, and is available at
  6. * http://www.eclipse.org/legal/epl-v10.html
  7. *******************************************************************************/
  8. package org.jnario.compiler;
  9. import static com.google.common.collect.Iterables.filter;
  10. import static com.google.common.collect.Sets.newHashSet;
  11. import static org.eclipse.xtext.nodemodel.util.NodeModelUtils.getNode;
  12. import static org.eclipse.xtext.util.Strings.convertToJavaString;
  13. import static org.jnario.jvmmodel.DoubleArrowSupport.isDoubleArrow;
  14. import java.util.HashSet;
  15. import java.util.Iterator;
  16. import java.util.Set;
  17. import org.eclipse.emf.ecore.EObject;
  18. import org.eclipse.xtend.core.compiler.XtendCompiler;
  19. import org.eclipse.xtext.common.types.JvmGenericType;
  20. import org.eclipse.xtext.common.types.JvmIdentifiableElement;
  21. import org.eclipse.xtext.common.types.JvmOperation;
  22. import org.eclipse.xtext.common.types.JvmType;
  23. import org.eclipse.xtext.common.types.JvmTypeReference;
  24. import org.eclipse.xtext.nodemodel.INode;
  25. import org.eclipse.xtext.serializer.ISerializer;
  26. import org.eclipse.xtext.xbase.XAbstractFeatureCall;
  27. import org.eclipse.xtext.xbase.XBinaryOperation;
  28. import org.eclipse.xtext.xbase.XClosure;
  29. import org.eclipse.xtext.xbase.XExpression;
  30. import org.eclipse.xtext.xbase.XFeatureCall;
  31. import org.eclipse.xtext.xbase.XNullLiteral;
  32. import org.eclipse.xtext.xbase.XSwitchExpression;
  33. import org.eclipse.xtext.xbase.XbaseFactory;
  34. import org.eclipse.xtext.xbase.compiler.output.ITreeAppendable;
  35. import org.jnario.Assertion;
  36. import org.jnario.MockLiteral;
  37. import org.jnario.Should;
  38. import org.jnario.ShouldThrow;
  39. import org.jnario.lib.Assert;
  40. import org.jnario.util.MockingSupport;
  41. import org.jnario.util.SourceAdapter;
  42. import com.google.common.base.Predicate;
  43. import com.google.common.collect.Iterables;
  44. import com.google.inject.Inject;
  45. /**
  46. * @author Sebastian Benz - Initial contribution and API
  47. */
  48. public class JnarioCompiler extends XtendCompiler {
  49. @Inject
  50. private JnarioExpressionHelper expressionHelper;
  51. @Inject ISerializer serializer;
  52. @Override
  53. public void internalToConvertedExpression(XExpression obj,
  54. ITreeAppendable appendable) {
  55. if (obj instanceof Assertion) {
  56. _toJavaExpression((Assertion) obj, appendable);
  57. } else if (obj instanceof Should) {
  58. _toJavaExpression((Should) obj, appendable);
  59. } else if (obj instanceof ShouldThrow) {
  60. _toJavaExpression((ShouldThrow) obj, appendable);
  61. } else if (obj instanceof MockLiteral) {
  62. _toJavaExpression((MockLiteral) obj, appendable);
  63. } else {
  64. super.internalToConvertedExpression(obj, appendable);
  65. }
  66. }
  67. @Override
  68. public void doInternalToJavaStatement(XExpression obj,
  69. ITreeAppendable appendable, boolean isReferenced) {
  70. if (obj instanceof Assertion) {
  71. _toJavaStatement((Assertion) obj, appendable, isReferenced);
  72. } else if (obj instanceof Should) {
  73. _toJavaStatement((Should) obj, appendable, isReferenced);
  74. } else if (obj instanceof ShouldThrow) {
  75. _toJavaStatement((ShouldThrow) obj, appendable, isReferenced);
  76. } else if (obj instanceof MockLiteral) {
  77. _toJavaStatement((MockLiteral) obj, appendable, isReferenced);
  78. } else
  79. super.doInternalToJavaStatement(obj, appendable, isReferenced);
  80. }
  81. public void _toJavaStatement(ShouldThrow should, ITreeAppendable b, boolean isReferenced) {
  82. if (should.getType() == null || should.getType().getType() == null) {
  83. return;
  84. }
  85. String expectedException = b.declareSyntheticVariable(should, "expectedException");
  86. b.newLine().append("boolean ").append(expectedException).append(" = false;").newLine();
  87. String message = b.declareSyntheticVariable(should, "message");
  88. b.append("String ").append(message).append(" = \"\";");
  89. b.newLine().append("try{").increaseIndentation();
  90. toJavaStatement(should.getExpression(), b, false);
  91. b.newLine().append(message).append(" = \"Expected \" + ")
  92. .append(should.getType().getType())
  93. .append(".class.getName() + \" for ")
  94. .append(javaStringNewLine())
  95. .append(" ")
  96. .append(serialize(should.getExpression()).replace("\n", "\n ")).append(javaStringNewLine())
  97. .append(" with:\"");
  98. appendValues(should.getExpression(), b, new HashSet<String>());
  99. b.append(";");
  100. b.decreaseIndentation().newLine().append("}catch(").increaseIndentation()
  101. .append(should.getType().getType()).append(" e){").newLine()
  102. .append(expectedException).append(" = true;")
  103. .decreaseIndentation().newLine().append("}");
  104. b.newLine()
  105. .append(assertType(should))
  106. .append(".assertTrue(").append(message).append(", ")
  107. .append(expectedException).append(");");
  108. }
  109. public void _toJavaStatement(Should should, ITreeAppendable b,
  110. boolean isReferenced) {
  111. _toShouldExpression(should, b, should.isNot());
  112. }
  113. private void _toShouldExpression(XBinaryOperation should,
  114. ITreeAppendable b, boolean isNot) {
  115. if(should.getRightOperand() instanceof XNullLiteral){
  116. _toShouldBeNullExpression(should, b, isNot);
  117. }else{
  118. toShouldBeExpression(should, b, isNot);
  119. }
  120. }
  121. private void toShouldBeExpression(XBinaryOperation should,
  122. ITreeAppendable b, boolean isNot) {
  123. super._toJavaStatement(should, b, true);
  124. b.newLine().append(assertType(should));
  125. if (isNot) {
  126. b.append(".assertFalse(");
  127. } else {
  128. b.append(".assertTrue(");
  129. }
  130. generateMessageFor(should, b);
  131. b.append(" + \"" + javaStringNewLine() + "\", ");
  132. super._toJavaExpression(should, b);
  133. b.append(");").newLine();
  134. }
  135. private void _toShouldBeNullExpression(XBinaryOperation should,
  136. ITreeAppendable b, boolean isNot) {
  137. super.toJavaStatement(should.getLeftOperand(), b, true);
  138. b.newLine().append(assertType(should));
  139. if (isNot) {
  140. b.append(".assertNotNull(");
  141. } else {
  142. b.append(".assertNull(");
  143. }
  144. generateNullMessageFor(should, b);
  145. b.append(" + \"" + javaStringNewLine() + "\", ");
  146. super.toJavaExpression(should.getLeftOperand(), b);
  147. b.append(");").newLine();
  148. }
  149. protected XFeatureCall createFeatureCall(
  150. JvmIdentifiableElement nullValueMatcher) {
  151. XFeatureCall featureCall = XbaseFactory.eINSTANCE.createXFeatureCall();
  152. featureCall.setFeature(nullValueMatcher);
  153. return featureCall;
  154. }
  155. protected JvmIdentifiableElement getNullValueMatcher(XBinaryOperation should) {
  156. return getMethod(should, org.jnario.lib.Should.class.getName(), "nullValue");
  157. }
  158. protected JvmIdentifiableElement getMethod(XBinaryOperation should, String type, String methodName, String...argumentTypes) {
  159. JvmGenericType coreMatchersType = (JvmGenericType) jvmType(type, should);
  160. if(coreMatchersType == null){
  161. return null;
  162. }
  163. Iterable<JvmOperation> operations = Iterables.filter(coreMatchersType.getMembers(), JvmOperation.class);
  164. for (JvmOperation jvmOperation : operations) {
  165. if(methodName.equals(jvmOperation.getSimpleName()) && hasArguments(jvmOperation, argumentTypes)){
  166. return jvmOperation;
  167. }
  168. }
  169. return null;
  170. }
  171. private boolean hasArguments(JvmOperation jvmOperation,
  172. String[] argumentTypes) {
  173. if(jvmOperation.getParameters().size() != argumentTypes.length){
  174. return false;
  175. }
  176. for (int i = 0; i < argumentTypes.length; i++) {
  177. String argumentType = argumentTypes[i];
  178. JvmTypeReference actual = getTypeComputationServices().getTypeReferences().getTypeForName(argumentType, jvmOperation);
  179. JvmTypeReference expected = jvmOperation.getParameters().get(i).getParameterType();
  180. // System.out.println(expected.getQualifiedName() + "=>" + actual.getQualifiedName());
  181. if(!expected.getQualifiedName().equals(actual.getQualifiedName())){
  182. return false;
  183. }
  184. }
  185. return true;
  186. }
  187. private String javaStringNewLine() {
  188. return convertToJavaString("\n");
  189. }
  190. public void _toJavaExpression(MockLiteral expr, ITreeAppendable b) {
  191. JvmType mockito = getTypeComputationServices().getTypeReferences().findDeclaredType(MockingSupport.CLASS_NAME, expr);
  192. b.append(mockito).append(".mock(");
  193. b.append(expr.getType()).append(".class");
  194. b.append(")");
  195. }
  196. public void _toJavaStatement(MockLiteral expr, ITreeAppendable b, boolean isReferenced) {
  197. generateComment(expr, b, isReferenced);
  198. }
  199. public void _toJavaExpression(Should should, ITreeAppendable b) {
  200. b.append("true");
  201. }
  202. public void _toJavaExpression(ShouldThrow should, ITreeAppendable b) {
  203. b.append("true");
  204. }
  205. public void _toJavaStatement(Assertion assertion, ITreeAppendable b,
  206. boolean isReferenced) {
  207. if (assertion.getExpression() == null) {
  208. return;
  209. }
  210. generateSingleAssertion(assertion.getExpression(), b);
  211. }
  212. public void _toJavaExpression(Assertion assertion, ITreeAppendable b) {
  213. b.append("true");
  214. }
  215. private void generateSingleAssertion(XExpression expr, ITreeAppendable b) {
  216. toJavaStatement(expr, b, true);
  217. b.newLine();
  218. b.append(assertType(expr));
  219. b.append(".assertTrue(");
  220. generateMessageFor(expr, b);
  221. b.append(" + \"" + javaStringNewLine() + "\", ");
  222. toJavaExpression(expr, b);
  223. b.append(");");
  224. b.newLine();
  225. }
  226. private JvmType assertType(XExpression expr) {
  227. return jvmType(Assert.class, expr);
  228. }
  229. private boolean isVoid(XExpression expr) {
  230. JvmTypeReference type = getType(expr);
  231. return getTypeComputationServices().getTypeReferences().is(type, Void.TYPE);
  232. }
  233. private JvmType jvmType(Class<?> type, EObject context) {
  234. return jvmType(type.getName(), context);
  235. }
  236. private JvmType jvmType(String type, EObject context) {
  237. JvmTypeReference jvmTypeReference = getTypeComputationServices().getTypeReferences().getTypeForName(type, context);
  238. if(jvmTypeReference == null){
  239. return null;
  240. }
  241. return jvmTypeReference.getType();
  242. }
  243. public void generateMessageFor(Should should, ITreeAppendable b) {
  244. b.append("\"\\nExpected ");
  245. b.append(serialize(should));
  246. b.append(" but\"");
  247. Set<String> valueExpressions = newHashSet();
  248. XExpression left = should.getLeftOperand();
  249. toLiteralValue(left, b, valueExpressions);
  250. appendValues(left, b, valueExpressions);
  251. XExpression right = should.getRightOperand();
  252. toLiteralValue(right, b, valueExpressions);
  253. appendValues(right, b, valueExpressions);
  254. if (valueExpressions.isEmpty()) {
  255. b.append(" + \" did not.\"");
  256. }
  257. }
  258. private void generateNullMessageFor(XBinaryOperation should, ITreeAppendable b) {
  259. b.append("\"\\nExpected ");
  260. b.append(serialize(should));
  261. b.append("\\n but is \"");
  262. toValue(should.getLeftOperand(), b);
  263. }
  264. private void generateMessageFor(XExpression expression, ITreeAppendable b) {
  265. b.append("\"\\nExpected ");
  266. b.append(serialize(expression));
  267. b.append(" but\"");
  268. Set<String> valueExpressions = newHashSet();
  269. appendValues(expression, b, valueExpressions);
  270. if (valueExpressions.isEmpty()) {
  271. b.append(" + \" did not.\"");
  272. }
  273. }
  274. private void appendValues(XExpression expression, ITreeAppendable b,
  275. Set<String> valueExpressions) {
  276. Iterator<XExpression> subExpressions = allSubExpressions(expression);
  277. if (subExpressions.hasNext()) {
  278. while (subExpressions.hasNext()) {
  279. XExpression subExpression = subExpressions.next();
  280. appendActualValues(subExpression, b, valueExpressions);
  281. }
  282. } else {
  283. toLiteralValue(expression, b, valueExpressions);
  284. }
  285. }
  286. protected String serialize(XExpression expression) {
  287. INode node = findNode(expression);
  288. if(node == null){
  289. return "";
  290. }
  291. String result = node.getText();
  292. result = result.trim();
  293. result = removeSurroundingParentheses(result);
  294. return convertToJavaString(result);
  295. }
  296. private INode findNode(XExpression expression) {
  297. INode node = getNode(expression);
  298. if(node != null) {
  299. return node;
  300. }
  301. EObject source = SourceAdapter.find(expression);
  302. while(node == null && isExpressions(source)){
  303. node = getNode(source);
  304. source = source.eContainer();
  305. }
  306. return node;
  307. }
  308. private boolean isExpressions(EObject source) {
  309. return source != null && source instanceof XExpression;
  310. }
  311. protected String removeSurroundingParentheses(String result) {
  312. if (result.startsWith("(") && result.endsWith(")")) {
  313. result = result.substring(1, result.length() - 1);
  314. }
  315. return result.trim();
  316. }
  317. protected void appendActualValues(XExpression expression,
  318. ITreeAppendable b, Set<String> valueExpressions) {
  319. toLiteralValue(expression, b, valueExpressions);
  320. Iterator<XExpression> subExpressions = allSubExpressions(expression);
  321. while (subExpressions.hasNext()) {
  322. XExpression subExpression = subExpressions.next();
  323. appendActualValues(subExpression, b, valueExpressions);
  324. }
  325. }
  326. protected Iterator<XExpression> allSubExpressions(XExpression expression) {
  327. Predicate<XExpression> noSwitchCases = new Predicate<XExpression>() {
  328. public boolean apply(XExpression e) {
  329. return !(e.eContainer() instanceof XSwitchExpression);
  330. }
  331. };
  332. Predicate<XExpression> noLiteralExpressions = new Predicate<XExpression>() {
  333. public boolean apply(XExpression expr) {
  334. return !expressionHelper.isLiteral(expr);
  335. }
  336. };
  337. Iterable<XExpression> subExpressions = filter(expression.eContents(), XExpression.class);
  338. subExpressions = filter(subExpressions, noLiteralExpressions);
  339. subExpressions = filter(subExpressions, noSwitchCases);
  340. return subExpressions.iterator();
  341. }
  342. protected void toLiteralValue(XExpression expression, ITreeAppendable b,
  343. Set<String> valueMappings) {
  344. if (expressionHelper.isLiteral(expression)) {
  345. return;
  346. }
  347. if (isVoid(expression)) {
  348. return;
  349. }
  350. if(isClosure(expression)){
  351. return;
  352. }
  353. toValue(expression, b, valueMappings);
  354. }
  355. private void toValue(XExpression expression, ITreeAppendable b,
  356. Set<String> valueMappings) {
  357. String expr = serialize(expression);
  358. if (expr.isEmpty() || valueMappings.contains(expr)) {
  359. return;
  360. }
  361. valueMappings.add(expr);
  362. b.append("\n + \"\\n ");
  363. b.append(expr);
  364. b.append(" is \"");
  365. toValue(expression, b);
  366. }
  367. private void toValue(XExpression expression, ITreeAppendable b) {
  368. b.append(" + new ");
  369. b.append("org.hamcrest.StringDescription");
  370. b.append("().appendValue(");
  371. toJavaExpression(expression, b);
  372. b.append(").toString()");
  373. }
  374. private boolean isClosure(XExpression expression) {
  375. JvmTypeReference type = getType(expression);
  376. return type.getQualifiedName().startsWith("org.eclipse.xtext.xbase.lib.Functions");
  377. }
  378. @Override
  379. protected boolean isVariableDeclarationRequired(XExpression expr,
  380. ITreeAppendable b) {
  381. if (expr instanceof Assertion) {
  382. return false;
  383. }
  384. return super.isVariableDeclarationRequired(expr, b);
  385. }
  386. @Override
  387. protected void _toJavaStatement(XAbstractFeatureCall expr,
  388. ITreeAppendable b, boolean isReferenced) {
  389. if(!isDoubleArrow(expr)){
  390. super._toJavaStatement(expr, b, isReferenced);
  391. return;
  392. }
  393. XBinaryOperation doubleArrow = (XBinaryOperation) expr;
  394. if(doubleArrow.getRightOperand() instanceof XClosure){
  395. super._toJavaStatement(expr, b, isReferenced);
  396. return;
  397. }else{
  398. _toShouldExpression((XBinaryOperation) expr, b, false);
  399. }
  400. }
  401. }