All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jnario.compiler.JnarioCompiler Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * Copyright (c) 2012 BMW Car IT and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *******************************************************************************/
package org.jnario.compiler;

import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Sets.newHashSet;
import static org.eclipse.xtext.nodemodel.util.NodeModelUtils.getNode;
import static org.eclipse.xtext.util.Strings.convertToJavaString;
import static org.jnario.jvmmodel.DoubleArrowSupport.isDoubleArrow;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtend.core.compiler.XtendCompiler;
import org.eclipse.xtext.common.types.JvmGenericType;
import org.eclipse.xtext.common.types.JvmIdentifiableElement;
import org.eclipse.xtext.common.types.JvmOperation;
import org.eclipse.xtext.common.types.JvmType;
import org.eclipse.xtext.common.types.JvmTypeReference;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.serializer.ISerializer;
import org.eclipse.xtext.xbase.XAbstractFeatureCall;
import org.eclipse.xtext.xbase.XBinaryOperation;
import org.eclipse.xtext.xbase.XClosure;
import org.eclipse.xtext.xbase.XExpression;
import org.eclipse.xtext.xbase.XFeatureCall;
import org.eclipse.xtext.xbase.XNullLiteral;
import org.eclipse.xtext.xbase.XSwitchExpression;
import org.eclipse.xtext.xbase.XbaseFactory;
import org.eclipse.xtext.xbase.compiler.output.ITreeAppendable;
import org.jnario.Assertion;
import org.jnario.MockLiteral;
import org.jnario.Should;
import org.jnario.ShouldThrow;
import org.jnario.lib.Assert;
import org.jnario.util.MockingSupport;
import org.jnario.util.SourceAdapter;

import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.inject.Inject;

/**
 * @author Sebastian Benz - Initial contribution and API
 */
public class JnarioCompiler extends XtendCompiler {

	@Inject
	private JnarioExpressionHelper expressionHelper;
	
	@Inject ISerializer serializer;

	@Override
	public void internalToConvertedExpression(XExpression obj,
			ITreeAppendable appendable) {
		if (obj instanceof Assertion) {
			_toJavaExpression((Assertion) obj, appendable);
		} else if (obj instanceof Should) {
			_toJavaExpression((Should) obj, appendable);
		} else if (obj instanceof ShouldThrow) {
			_toJavaExpression((ShouldThrow) obj, appendable);
		} else if (obj instanceof MockLiteral) {
			_toJavaExpression((MockLiteral) obj, appendable);
		} else {
			super.internalToConvertedExpression(obj, appendable);
		}
	}

	@Override
	public void doInternalToJavaStatement(XExpression obj,
			ITreeAppendable appendable, boolean isReferenced) {
		if (obj instanceof Assertion) {
			_toJavaStatement((Assertion) obj, appendable, isReferenced);
		} else if (obj instanceof Should) {
			_toJavaStatement((Should) obj, appendable, isReferenced);
		} else if (obj instanceof ShouldThrow) {
			_toJavaStatement((ShouldThrow) obj, appendable, isReferenced);
		} else if (obj instanceof MockLiteral) {
			_toJavaStatement((MockLiteral) obj, appendable, isReferenced);
		} else
			super.doInternalToJavaStatement(obj, appendable, isReferenced);
	}

	public void _toJavaStatement(ShouldThrow should, ITreeAppendable b,	boolean isReferenced) {
		if (should.getType() == null || should.getType().getType() == null) {
			return;
		}
		String expectedException = b.declareSyntheticVariable(should, "expectedException");
		b.newLine().append("boolean ").append(expectedException).append(" = false;").newLine();
		String message = b.declareSyntheticVariable(should, "message");
		b.append("String ").append(message).append(" = \"\";");
		b.newLine().append("try{").increaseIndentation();
		toJavaStatement(should.getExpression(), b, false);
		b.newLine().append(message).append(" = \"Expected \" + ")
		.append(should.getType().getType())
		.append(".class.getName() + \" for ")
		.append(javaStringNewLine())
		.append("     ")
		.append(serialize(should.getExpression()).replace("\n", "\n    ")).append(javaStringNewLine())
		.append(" with:\"");
		appendValues(should.getExpression(), b, new HashSet());
		b.append(";");
		b.decreaseIndentation().newLine().append("}catch(").increaseIndentation()
				.append(should.getType().getType()).append(" e){").newLine()
				.append(expectedException).append(" = true;")
				.decreaseIndentation().newLine().append("}");
		b.newLine()
		.append(assertType(should))
		.append(".assertTrue(").append(message).append(", ")
		.append(expectedException).append(");");
	}
	
	
	
	public void _toJavaStatement(Should should, ITreeAppendable b,
			boolean isReferenced) {
		_toShouldExpression(should, b, should.isNot());
	}

	private void _toShouldExpression(XBinaryOperation should,
			ITreeAppendable b, boolean isNot) {
		if(should.getRightOperand() instanceof XNullLiteral){
			_toShouldBeNullExpression(should, b, isNot);
		}else{
			toShouldBeExpression(should, b, isNot);
		}
	}

	private void toShouldBeExpression(XBinaryOperation should,
			ITreeAppendable b, boolean isNot) {
		super._toJavaStatement(should, b, true);
		b.newLine().append(assertType(should));
		if (isNot) {
			b.append(".assertFalse(");
		} else {
			b.append(".assertTrue(");
		}
		generateMessageFor(should, b);
		b.append(" + \"" + javaStringNewLine() + "\", ");
		super._toJavaExpression(should, b);
		b.append(");").newLine();
	}

	private void _toShouldBeNullExpression(XBinaryOperation should,
			ITreeAppendable b, boolean isNot) {
		super.toJavaStatement(should.getLeftOperand(), b, true);
		b.newLine().append(assertType(should));
		if (isNot) {
			b.append(".assertNotNull(");
		} else {
			b.append(".assertNull(");
		}
		generateNullMessageFor(should, b);
		b.append(" + \"" + javaStringNewLine() + "\", ");
		super.toJavaExpression(should.getLeftOperand(), b);
		b.append(");").newLine();
	}


	protected XFeatureCall createFeatureCall(
			JvmIdentifiableElement nullValueMatcher) {
		XFeatureCall featureCall = XbaseFactory.eINSTANCE.createXFeatureCall();
		featureCall.setFeature(nullValueMatcher);
		return featureCall;
	}

	protected JvmIdentifiableElement getNullValueMatcher(XBinaryOperation should) {
		return getMethod(should, org.jnario.lib.Should.class.getName(), "nullValue");
	}

	protected JvmIdentifiableElement getMethod(XBinaryOperation should, String type, String methodName, String...argumentTypes) {
		JvmGenericType coreMatchersType = (JvmGenericType) jvmType(type, should);
		if(coreMatchersType == null){
			return null;
		}
		Iterable operations = Iterables.filter(coreMatchersType.getMembers(), JvmOperation.class);
		for (JvmOperation jvmOperation : operations) {
			if(methodName.equals(jvmOperation.getSimpleName()) && hasArguments(jvmOperation, argumentTypes)){
				return jvmOperation;
			}
		}
		return null;
	}

	private boolean hasArguments(JvmOperation jvmOperation,
			String[] argumentTypes) {
		if(jvmOperation.getParameters().size() != argumentTypes.length){
			return false;
		}
		for (int i = 0; i < argumentTypes.length; i++) {
			String argumentType = argumentTypes[i];
			JvmTypeReference actual = getTypeComputationServices().getTypeReferences().getTypeForName(argumentType, jvmOperation);
			JvmTypeReference expected = jvmOperation.getParameters().get(i).getParameterType();
//			System.out.println(expected.getQualifiedName() + "=>" + actual.getQualifiedName());
			if(!expected.getQualifiedName().equals(actual.getQualifiedName())){
				return false;
			}
		}
		return true;
	}

	private String javaStringNewLine() {
		return convertToJavaString("\n");
	}

	public void _toJavaExpression(MockLiteral expr, ITreeAppendable b) {
		JvmType mockito = getTypeComputationServices().getTypeReferences().findDeclaredType(MockingSupport.CLASS_NAME, expr);
		b.append(mockito).append(".mock(");
		b.append(expr.getType()).append(".class");
		b.append(")");
	}
	
	public void _toJavaStatement(MockLiteral expr, ITreeAppendable b, boolean isReferenced) {
		generateComment(expr, b, isReferenced);
	}
	
	public void _toJavaExpression(Should should, ITreeAppendable b) {
		b.append("true");
	}

	public void _toJavaExpression(ShouldThrow should, ITreeAppendable b) {
		b.append("true");
	}

	public void _toJavaStatement(Assertion assertion, ITreeAppendable b,
			boolean isReferenced) {
		if (assertion.getExpression() == null) {
			return;
		}
		generateSingleAssertion(assertion.getExpression(), b);
	}

	public void _toJavaExpression(Assertion assertion, ITreeAppendable b) {
		b.append("true");
	}

	private void generateSingleAssertion(XExpression expr, ITreeAppendable b) {
		toJavaStatement(expr, b, true);
		b.newLine();
		b.append(assertType(expr));
		b.append(".assertTrue(");
		generateMessageFor(expr, b);
		b.append(" + \"" + javaStringNewLine() + "\", ");
		toJavaExpression(expr, b);
		b.append(");");
		b.newLine();
	}

	private JvmType assertType(XExpression expr) {
		return jvmType(Assert.class, expr);
	}

	private boolean isVoid(XExpression expr) {
		JvmTypeReference type = getType(expr);
		return getTypeComputationServices().getTypeReferences().is(type, Void.TYPE);
	}
	
	private JvmType jvmType(Class type, EObject context) {
		return jvmType(type.getName(), context);
	}

	private JvmType jvmType(String type, EObject context) {
		JvmTypeReference jvmTypeReference = getTypeComputationServices().getTypeReferences().getTypeForName(type, context);
		if(jvmTypeReference == null){
			return null;
		}
		return jvmTypeReference.getType();
	}

	public void generateMessageFor(Should should, ITreeAppendable b) {
		b.append("\"\\nExpected ");
		b.append(serialize(should));
		b.append(" but\"");
		Set valueExpressions = newHashSet();

		XExpression left = should.getLeftOperand();
		toLiteralValue(left, b, valueExpressions);
		appendValues(left, b, valueExpressions);

		XExpression right = should.getRightOperand();
		toLiteralValue(right, b, valueExpressions);
		appendValues(right, b, valueExpressions);

		if (valueExpressions.isEmpty()) {
			b.append(" + \" did not.\"");
		}
	}

	private void generateNullMessageFor(XBinaryOperation should, ITreeAppendable b) {
		b.append("\"\\nExpected ");
		b.append(serialize(should));
		b.append("\\n     but is \"");
		toValue(should.getLeftOperand(), b);
	}

	private void generateMessageFor(XExpression expression, ITreeAppendable b) {
		b.append("\"\\nExpected ");
		b.append(serialize(expression));
		b.append(" but\"");
		Set valueExpressions = newHashSet();
		appendValues(expression, b, valueExpressions);
		if (valueExpressions.isEmpty()) {
			b.append(" + \" did not.\"");
		}
	}

	private void appendValues(XExpression expression, ITreeAppendable b,
			Set valueExpressions) {
		Iterator subExpressions = allSubExpressions(expression);
		if (subExpressions.hasNext()) {
			while (subExpressions.hasNext()) {
				XExpression subExpression = subExpressions.next();
				appendActualValues(subExpression, b, valueExpressions);
			}
		} else {
			toLiteralValue(expression, b, valueExpressions);
		}
	}

	protected String serialize(XExpression expression) {
		INode node = findNode(expression);
		if(node == null){
			return "";
		}
		String result = node.getText();
		result = result.trim();
		result = removeSurroundingParentheses(result);
		return convertToJavaString(result);
	}

	private INode findNode(XExpression expression) {
		INode node = getNode(expression);
		if(node != null) {
			return node;
		}
		EObject source = SourceAdapter.find(expression);
		while(node == null && isExpressions(source)){
			node = getNode(source);
			source = source.eContainer();
		}
		return node;
	}

	private boolean isExpressions(EObject source) {
		return source != null && source instanceof XExpression;
	}

	protected String removeSurroundingParentheses(String result) {
		if (result.startsWith("(") && result.endsWith(")")) {
			result = result.substring(1, result.length() - 1);
		}
		return result.trim();
	}

	protected void appendActualValues(XExpression expression,
			ITreeAppendable b, Set valueExpressions) {
		toLiteralValue(expression, b, valueExpressions);
		Iterator subExpressions = allSubExpressions(expression);
		while (subExpressions.hasNext()) {
			XExpression subExpression = subExpressions.next();
			appendActualValues(subExpression, b, valueExpressions);
		}
	}

	protected Iterator allSubExpressions(XExpression expression) {
		Predicate noSwitchCases = new Predicate() {
			public boolean apply(XExpression e) {
				return !(e.eContainer() instanceof XSwitchExpression);
			}
		};
		Predicate noLiteralExpressions = new Predicate() {
			public boolean apply(XExpression expr) {
				return !expressionHelper.isLiteral(expr);
			}
		};
		Iterable subExpressions = filter(expression.eContents(), XExpression.class);
		
		subExpressions = filter(subExpressions, noLiteralExpressions);
		subExpressions = filter(subExpressions, noSwitchCases);
		return subExpressions.iterator();
	}

	protected void toLiteralValue(XExpression expression, ITreeAppendable b,
			Set valueMappings) {
		if (expressionHelper.isLiteral(expression)) {
			return;
		}
		if (isVoid(expression)) {
			return;
		}
		if(isClosure(expression)){
			return;
		}
		toValue(expression, b, valueMappings);
	}

	private void toValue(XExpression expression, ITreeAppendable b,
			Set valueMappings) {
		String expr = serialize(expression);
		if (expr.isEmpty() || valueMappings.contains(expr)) {
			return;
		}
		valueMappings.add(expr);
		b.append("\n + \"\\n     ");
		b.append(expr);
		b.append(" is \"");
		toValue(expression, b);
	}

	private void toValue(XExpression expression, ITreeAppendable b) {
		b.append(" + new ");
		b.append("org.hamcrest.StringDescription");
		b.append("().appendValue(");
		toJavaExpression(expression, b);
		b.append(").toString()");
	}
	
	private boolean isClosure(XExpression expression) {
		JvmTypeReference type = getType(expression);
		return type.getQualifiedName().startsWith("org.eclipse.xtext.xbase.lib.Functions");
	}

	@Override
	protected boolean isVariableDeclarationRequired(XExpression expr,
			ITreeAppendable b) {
		if (expr instanceof Assertion) {
			return false;
		}
		return super.isVariableDeclarationRequired(expr, b);
	}


	@Override
	protected void _toJavaStatement(XAbstractFeatureCall expr,
			ITreeAppendable b, boolean isReferenced) {
		if(!isDoubleArrow(expr)){
			super._toJavaStatement(expr, b, isReferenced);
			return;
		}
		XBinaryOperation doubleArrow = (XBinaryOperation) expr;
		if(doubleArrow.getRightOperand() instanceof XClosure){
			super._toJavaStatement(expr, b, isReferenced);
			return;
		}else{
			_toShouldExpression((XBinaryOperation) expr, b, false);
		}
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy