/*
 * This file has been developed at the University of Munich, Chair for Programming & Software Engineering.
 * 
 * This file is licensed under the Eclipse Public License (EPL) 1.0
 * 
 */
package eu.mdd4soa.trans.smm2java.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.gmt.modisco.java.AbstractTypeDeclaration;
import org.eclipse.gmt.modisco.java.Block;
import org.eclipse.gmt.modisco.java.CastExpression;
import org.eclipse.gmt.modisco.java.CatchClause;
import org.eclipse.gmt.modisco.java.ClassInstanceCreation;
import org.eclipse.gmt.modisco.java.CompilationUnit;
import org.eclipse.gmt.modisco.java.ConstructorDeclaration;
import org.eclipse.gmt.modisco.java.DoStatement;
import org.eclipse.gmt.modisco.java.EnumConstantDeclaration;
import org.eclipse.gmt.modisco.java.EnumDeclaration;
import org.eclipse.gmt.modisco.java.Expression;
import org.eclipse.gmt.modisco.java.ExpressionStatement;
import org.eclipse.gmt.modisco.java.FieldDeclaration;
import org.eclipse.gmt.modisco.java.IfStatement;
import org.eclipse.gmt.modisco.java.InfixExpression;
import org.eclipse.gmt.modisco.java.InfixExpressionKind;
import org.eclipse.gmt.modisco.java.InstanceofExpression;
import org.eclipse.gmt.modisco.java.MethodDeclaration;
import org.eclipse.gmt.modisco.java.MethodInvocation;
import org.eclipse.gmt.modisco.java.Package;
import org.eclipse.gmt.modisco.java.ParameterizedType;
import org.eclipse.gmt.modisco.java.ParenthesizedExpression;
import org.eclipse.gmt.modisco.java.PrefixExpression;
import org.eclipse.gmt.modisco.java.PrefixExpressionKind;
import org.eclipse.gmt.modisco.java.ReturnStatement;
import org.eclipse.gmt.modisco.java.SingleVariableDeclaration;
import org.eclipse.gmt.modisco.java.Statement;
import org.eclipse.gmt.modisco.java.StringLiteral;
import org.eclipse.gmt.modisco.java.SwitchStatement;
import org.eclipse.gmt.modisco.java.SynchronizedStatement;
import org.eclipse.gmt.modisco.java.ThrowStatement;
import org.eclipse.gmt.modisco.java.TryStatement;
import org.eclipse.gmt.modisco.java.Type;
import org.eclipse.gmt.modisco.java.TypeDeclaration;
import org.eclipse.gmt.modisco.java.WhileStatement;
import org.eclipse.gmt.modisco.java.emf.JavaFactory;

import eu.mdd4soa.smm.behaviour.Compensate;
import eu.mdd4soa.smm.behaviour.CompensateAll;
import eu.mdd4soa.smm.behaviour.CompensationHandler;
import eu.mdd4soa.smm.behaviour.CompositeElement;
import eu.mdd4soa.smm.behaviour.DataHandling;
import eu.mdd4soa.smm.behaviour.Decision;
import eu.mdd4soa.smm.behaviour.EventHandler;
import eu.mdd4soa.smm.behaviour.ExceptionHandler;
import eu.mdd4soa.smm.behaviour.Handler;
import eu.mdd4soa.smm.behaviour.Loop;
import eu.mdd4soa.smm.behaviour.Parallel;
import eu.mdd4soa.smm.behaviour.Path;
import eu.mdd4soa.smm.behaviour.Receive;
import eu.mdd4soa.smm.behaviour.Reply;
import eu.mdd4soa.smm.behaviour.Send;
import eu.mdd4soa.smm.behaviour.SendAndReceive;
import eu.mdd4soa.smm.behaviour.ServiceActivity;
import eu.mdd4soa.smm.behaviour.ServiceElement;
import eu.mdd4soa.smm.behaviour.ServiceInteraction;
import eu.mdd4soa.smm.behaviour.Throw;
import eu.mdd4soa.smm.data.Assignment;
import eu.mdd4soa.smm.data.Declaration;
import eu.mdd4soa.smm.data.InteractionParameter;
import eu.mdd4soa.smm.data.LeftHandSideExpression;
import eu.mdd4soa.smm.data.Literal;
import eu.mdd4soa.smm.data.Operation;
import eu.mdd4soa.smm.data.PropertyReference;
import eu.mdd4soa.smm.data.ReceiveParameter;
import eu.mdd4soa.smm.data.SendParameter;
import eu.mdd4soa.smm.data.Variable;
import eu.mdd4soa.smm.data.VariableReference;
import eu.mdd4soa.smm.exception.TransformationException;
import eu.mdd4soa.smm.statik.InterfaceOperation;
import eu.mdd4soa.smm.statik.Service;
import eu.mdd4soa.trans.smm2java.JMHelper;
import eu.mdd4soa.trans.smm2java.impl.PartnerTracker.Partner;

public class BehaviourConverter {

	private static final String EVENT_MSG_SEND= "SEND";

	private static final String EVENT_MSG_RECEIVED= "RECEIVED";

	public static void convert(ServiceActivity beh, Package rootPackage, TypeTracker typeTracker, PartnerTracker partners)
			throws TransformationException {
		BehaviourConverter c= new BehaviourConverter(beh, typeTracker, rootPackage, partners);
		c.execute();
	}

	static class DecisionBlock {

		static enum State {
			MIDDLE, END
		}

		private State fState;

		private Block fOriginalBlock;

		private Block fElseBlock;

		public DecisionBlock(State st) {
			fState= st;
		}

		public void setState(State state) {
			fState= state;
		}

		public State getState() {
			return fState;
		}

		public void setOriginalBlock(Block body) {
			fOriginalBlock= body;
		}

		public void setElseBlock(Block elseBlock) {
			fElseBlock= elseBlock;
		}

		public Block getElseBlock() {
			return fElseBlock;
		}

		public Block getOriginalBlock() {
			return fOriginalBlock;
		}

	}

	class LoopEndBlock {

		private Block fOriginalBlock;

		public LoopEndBlock(Block body) {
			fOriginalBlock= body;
		}

		public Block getOriginalBlock() {
			return fOriginalBlock;
		}


	}

	class ScopeTracker {

		private List<ServiceElement> fServiceScopes;

		private EnumDeclaration fServiceScopeEnum;

		private Map<ServiceElement, EnumConstantDeclaration> fServiceScopeToEnum;

		private Map<ServiceElement, MethodDeclaration> fServiceScopeToRunMethod;

		public ScopeTracker(List<ServiceElement> allScopes) {
			fServiceScopes= allScopes;
			fServiceScopeToEnum= new HashMap<ServiceElement, EnumConstantDeclaration>();
			fServiceScopeToRunMethod= new HashMap<ServiceElement, MethodDeclaration>();
		}

		public List<ServiceElement> getAllScopes() {
			return fServiceScopes;
		}

		public void setEnumConstant(ServiceElement scope, EnumConstantDeclaration enumDefinition) {
			fServiceScopeToEnum.put(scope, enumDefinition);
		}

		public Expression getEnumAccess(ServiceElement majorElement) {
			EnumConstantDeclaration enumDef= fServiceScopeToEnum.get(majorElement);
			return JMHelper.createEnumAccess(fServiceScopeEnum, enumDef);
		}

		public void setEnumDefinition(EnumDeclaration scopeEnum) {
			fServiceScopeEnum= scopeEnum;
		}

		public Type getEnumDeclaration() {
			return fServiceScopeEnum;
		}

		public void setRunMethod(ServiceElement majorElement, MethodDeclaration method) {
			fServiceScopeToRunMethod.put(majorElement, method);
		}


		public MethodDeclaration getRunMethod(ServiceElement ce) {
			return fServiceScopeToRunMethod.get(ce);
		}

	}

	class BarrierTracker {

		private EnumDeclaration fBarrierEnum;

		private List<ServiceInteraction> fBarrierActions;

		private Map<ServiceInteraction, EnumConstantDeclaration> fServiceBarrierToEnum;

		private Map<ServiceInteraction, MethodDeclaration> fServiceBarrierToRunMethod;

		public BarrierTracker(List<ServiceInteraction> interactions) {
			fBarrierActions= interactions;
			fServiceBarrierToEnum= new HashMap<ServiceInteraction, EnumConstantDeclaration>();
			fServiceBarrierToRunMethod= new HashMap<ServiceInteraction, MethodDeclaration>();
		}

		public void setEnumDefinition(EnumDeclaration barrierEnum) {
			fBarrierEnum= barrierEnum;
		}

		public List<ServiceInteraction> getBarrierActions() {
			return fBarrierActions;
		}

		public void setEnumConstant(ServiceInteraction si, EnumConstantDeclaration enumConst) {
			fServiceBarrierToEnum.put(si, enumConst);
		}

		public Expression getEnumAccess(ServiceInteraction barrierAction) {
			EnumConstantDeclaration enumDef= fServiceBarrierToEnum.get(barrierAction);
			return JMHelper.createEnumAccess(fBarrierEnum, enumDef);
		}

		public Type getEnumDefinition() {
			return fBarrierEnum;
		}

		public void setRunMethod(ServiceInteraction barrierAction, MethodDeclaration method) {
			fServiceBarrierToRunMethod.put(barrierAction, method);
		}

		public EnumConstantDeclaration getEnumConstant(ServiceInteraction theReceive) {
			return fServiceBarrierToEnum.get(theReceive);
		}

		public MethodDeclaration getRunMethod(ServiceInteraction theReceive) {
			return fServiceBarrierToRunMethod.get(theReceive);
		}

	}

	private static final JavaFactory jf= JavaFactory.eINSTANCE;


	// General stuff:

	private ServiceActivity fBehaviour;

	private TypeTracker fTypeTracker;

	private Package fRootPackage;

	private PartnerTracker fPartners;


	// The behaviour class, unit, and constructor

	private TypeDeclaration fBClass;

	private CompilationUnit fBUnit;

	private ConstructorDeclaration fConstructor;


	// Trackers

	private ScopeTracker fScopeTracker;

	private BarrierTracker fBarrierTracker;

	private Map<Service, FieldDeclaration> fLockFields;


	// Some fields in the generated code

	private FieldDeclaration fFieldRunnables;

	private FieldDeclaration fFieldBarriers;

	private FieldDeclaration fFieldBarrierRunnables;


	// Convenience fields

	private AbstractTypeDeclaration fHashMap;

	private MethodDeclaration fHashMapPutMethod;

	private MethodDeclaration fHashMapGetMethod;

	private MethodDeclaration fExceptionPrintMethod;

	// Sim helpers

	private AbstractTypeDeclaration fSimHelper;

	private MethodDeclaration fSimHelperLogMethod;

	private MethodDeclaration fSimHelperWaitMethod;


	public BehaviourConverter(ServiceActivity beh, TypeTracker typeTracker, Package rootPackage, PartnerTracker partners) {
		fBehaviour= beh;
		fTypeTracker= typeTracker;
		fRootPackage= rootPackage;
		fPartners= partners;
	}

	/**
	 * The main method. The algorithm, in general, first creates the structure
	 * (i.e. all methods, fields, anonymous runnable instantiations, etc), and
	 * afterwards fills the methods with life.
	 * 
	 * The first part works as follows:
	 * <ol>
	 * <li>add provided to implements clause and create the necessary overriding
	 * methods (but leave them empty)</li>
	 * <li>add a field for each required partner and initialise in constructor.</li>
	 * <li>create an enum for "scopes"</li>
	 * <li>create an enum for barriers (one barrier for each receive and each
	 * reply)</li>
	 * <li>create a method _setupBarriers(), in which one barrier and one
	 * barrier runnable is created for each barrier (the latter with anonymous
	 * classes and a run() method which is left empty)</li>
	 * <li>create a method _setupRunnables(), in which a runnable for each scope
	 * is created (as an anonymous class with a run() and requestEnd() method
	 * which are left empty)</li>
	 * <li>create a method _setupVariables() in which the scope parent/child
	 * relationship is defined</li>
	 * <li>for each scope, create the behaviour (see next algorithm)</li>
	 * </ol>
	 * 
	 * The second part now fills the already existing methods with statements.
	 * Affected are the run() and requestEnd() methods of all scope runnables,
	 * the run() methods of all barrier runnables, and the implemented methods
	 * handling incoming calls (see {@link #createBehaviour(CompositeElement)}).
	 * 
	 * @throws TransformationException
	 * 
	 */
	private void execute() throws TransformationException {

		// ------- Setup --------

		fScopeTracker= new ScopeTracker(getRelevantElements(fBehaviour));

		fBarrierTracker= new BarrierTracker(getAllBarrierRequiringInteractions(fBehaviour));

		fLockFields= new HashMap<Service, FieldDeclaration>();

		fHashMap= (AbstractTypeDeclaration) fTypeTracker.getType("java.util.HashMap");
		fHashMapPutMethod= JMHelper.findMethod((AbstractTypeDeclaration) fHashMap, "put");
		fHashMapGetMethod= JMHelper.findMethod((AbstractTypeDeclaration) fHashMap, "get");

		Type exceptionType= fTypeTracker.getType("java.lang.Throwable");
		fExceptionPrintMethod= JMHelper.findMethod((AbstractTypeDeclaration) exceptionType, "printStackTrace");

		fBClass= fTypeTracker.createClass(fRootPackage.getName() + "." + JMHelper.toFirstUpper(fBehaviour.getName()));
		fTypeTracker.registerMainClass(fBehaviour, fBClass);
		fBUnit= fBClass.getOriginalCompilationUnit();

		fConstructor= jf.createConstructorDeclaration();
		fConstructor.setName(fBClass.getName());
		fConstructor.setModifier(JMHelper.createModifier("public"));
		fConstructor.setBody(jf.createBlock());

		fBClass.getBodyDeclarations().add(fConstructor);
		fBClass.setModifier(JMHelper.createModifier("public"));

		JMHelper.ensureImport(fBUnit, fHashMap);
		JMHelper.ensureImport(fBUnit, fTypeTracker.getType("util.ServiceException"));

		// Simulation help.
		fSimHelper= (AbstractTypeDeclaration) fTypeTracker.getType("util.SimHelper");
		JMHelper.ensureImport(fBUnit, fSimHelper);
		fSimHelperLogMethod= JMHelper.findMethod((AbstractTypeDeclaration) fSimHelper, "log");
		fSimHelperWaitMethod= JMHelper.findMethod((AbstractTypeDeclaration) fSimHelper, "waitRandom");

		// ------- Implementation --------

		// Implemented + required interfaces
		createProvidedPartners();
		createFieldsForRequiredPartners();

		// Enums
		createServiceActivityEnum();
		createBarrierEnum();

		// Partner locks
		createPartnerLockFields();

		// Declarations
		createBarriers();
		createRunnables();

		// Finally, create the method bodies.
		for (ServiceElement ce : fScopeTracker.getAllScopes())
			createBehaviour(ce);

		MethodDeclaration startMethod= JMHelper.createMethod("public", "start");
		startMethod.getBody().getStatements().add(createStartScope(null, fBehaviour));
		fBClass.getBodyDeclarations().add(startMethod);


		// ------- Done--------
	}

	// *********************** Enums ************************

	private void createServiceActivityEnum() {
		EnumDeclaration scopeEnum= JMHelper.createEnumDeclaration("ServiceActivity");
		fBClass.getBodyDeclarations().add(scopeEnum);
		fScopeTracker.setEnumDefinition(scopeEnum);

		for (ServiceElement sa : fScopeTracker.getAllScopes())
			fScopeTracker.setEnumConstant(sa, JMHelper.createEnumConstant(scopeEnum, sa.getName()));
	}

	private void createBarrierEnum() {
		EnumDeclaration barrierEnum= JMHelper.createEnumDeclaration("Barriers");
		fBClass.getBodyDeclarations().add(barrierEnum);
		fBarrierTracker.setEnumDefinition(barrierEnum);

		for (ServiceInteraction si : fBarrierTracker.getBarrierActions()) {

			// TODO probably need to ensure that we have only one operation call
			// for this operation... otherwise, use indices
			String name= si.getOperation().getName();
			if (si instanceof Reply)
				name+= "_reply";

			fBarrierTracker.setEnumConstant(si, JMHelper.createEnumConstant(barrierEnum, name));
		}
	}

	// *********************** Declarations ************************

	private void createBarriers() {
		// BARRIERS

		Type transitionRunnable= fTypeTracker.getType("util.BarrierRunnable");
		Type cyclicBarrier= fTypeTracker.getType("java.util.concurrent.CyclicBarrier");

		JMHelper.ensureImport(fBUnit, fHashMap);
		JMHelper.ensureImport(fBUnit, transitionRunnable);
		JMHelper.ensureImport(fBUnit, cyclicBarrier);

		ParameterizedType parameterizedHashMap1= fTypeTracker.createParameterizedType(fHashMap, fBarrierTracker.getEnumDefinition(),
				transitionRunnable);
		ParameterizedType parameterizedHashMap2= fTypeTracker.createParameterizedType(fHashMap, fBarrierTracker.getEnumDefinition(), cyclicBarrier);

		fFieldBarrierRunnables= JMHelper.createField("private", "barrierRunnables", parameterizedHashMap1);
		fFieldBarriers= JMHelper.createField("private", "barriers", parameterizedHashMap2);

		JMHelper.addField(fBClass, fFieldBarriers);
		JMHelper.addField(fBClass, fFieldBarrierRunnables);

		MethodDeclaration barrierSetup= JMHelper.createMethod("protected", fTypeTracker.getType("void"), "_setupBarriers");
		fConstructor.getBody().getStatements().add(JMHelper.createMethodInvocationStatement(barrierSetup));
		fBClass.getBodyDeclarations().add(barrierSetup);

		barrierSetup.getBody().getStatements().add(JMHelper.createFieldInitializationStatement(fFieldBarrierRunnables, parameterizedHashMap1));
		barrierSetup.getBody().getStatements().add(JMHelper.createFieldInitializationStatement(fFieldBarriers, parameterizedHashMap2));

		for (ServiceInteraction si : fBarrierTracker.getBarrierActions()) {

			// (1) create barrier runnable
			MethodDeclaration runmethod= JMHelper.createMethod("public", fTypeTracker.getType("void"), "run");
			ClassInstanceCreation cic= JMHelper.createAnonymousClassCreation(transitionRunnable, runmethod);
			ExpressionStatement st2= JMHelper.createMethodInvocationStatement(JMHelper.createFieldAccess(fFieldBarrierRunnables), fHashMapPutMethod,
					fBarrierTracker.getEnumAccess(si), cic);
			barrierSetup.getBody().getStatements().add(st2);

			fBarrierTracker.setRunMethod(si, runmethod);

			// (2) create barrier
			MethodInvocation getBarrierExpression= JMHelper.createMethodInvocation(JMHelper.createFieldAccess(fFieldBarrierRunnables),
					fHashMapGetMethod, fBarrierTracker.getEnumAccess(si));

			ClassInstanceCreation cic2= JMHelper.createConstructorInvocation(cyclicBarrier, JMHelper.createNumberLiteral(2), getBarrierExpression);
			ExpressionStatement st3= JMHelper.createMethodInvocationStatement(JMHelper.createFieldAccess(fFieldBarriers), fHashMapPutMethod,
					fBarrierTracker.getEnumAccess(si), cic2);

			barrierSetup.getBody().getStatements().add(st3);
		}
	}

	private void createRunnables() {
		Type serviceRunnable= fTypeTracker.getType("util.ServiceRunnable");
		ParameterizedType runnableParameterizedMap= fTypeTracker.createParameterizedType(fHashMap, fScopeTracker.getEnumDeclaration(),
				serviceRunnable);
		fFieldRunnables= JMHelper.createField("private", "runnables", runnableParameterizedMap);
		JMHelper.addField(fBClass, fFieldRunnables);

		JMHelper.ensureImport(fBUnit, serviceRunnable);

		MethodDeclaration runnableSetup= JMHelper.createMethod("protected", fTypeTracker.getType("void"), "_setupRunnables");
		fConstructor.getBody().getStatements().add(JMHelper.createMethodInvocationStatement(runnableSetup));
		fBClass.getBodyDeclarations().add(runnableSetup);

		// Initialize the field
		runnableSetup.getBody().getStatements().add(JMHelper.createFieldInitializationStatement(fFieldRunnables, runnableParameterizedMap));

		// Add the individual runnables
		// TODO a parallel is also a "major element"!
		for (ServiceElement majorElement : fScopeTracker.getAllScopes()) {

			MethodDeclaration runMethod= JMHelper.createMethod("public", fTypeTracker.getType("void"), "run");

			fScopeTracker.setRunMethod(majorElement, runMethod);

			ClassInstanceCreation transitionRunnableCreation= JMHelper.createAnonymousClassCreation(serviceRunnable, runMethod);

			ExpressionStatement putIntoRunnablesExpression= JMHelper.createMethodInvocationStatement(JMHelper.createFieldAccess(fFieldRunnables),
					fHashMapPutMethod, fScopeTracker.getEnumAccess(majorElement), transitionRunnableCreation);

			runnableSetup.getBody().getStatements().add(putIntoRunnablesExpression);
		}
	}


	// *********************** Behaviours ************************


	private void createBehaviour(ServiceElement ce) throws TransformationException {

		Block runMethodBody= fScopeTracker.getRunMethod(ce).getBody();

		TryStatement tryStatement= jf.createTryStatement();
		Block body= jf.createBlock();
		tryStatement.setBody(body);

		runMethodBody.getStatements().add(tryStatement);

		// Start event handlers and interrupting activities if necessary.

		if (ce instanceof ServiceActivity) {
			ServiceActivity sa= (ServiceActivity) ce;

			// Start event handlers?
			if (hasEventHandlers(sa)) {
				body.getStatements().addAll(createStartEventHandlers(sa));
			}
			// Start interrupting receives?
			if (!sa.getInterruptingReceives().isEmpty()) {
				for (Receive ircv : sa.getInterruptingReceives()) {
					body.getStatements().add(createStartScope(ircv.getInterrupting(), ircv));
				}
			}
		}

		if (ce instanceof EventHandler) {
			WhileStatement whileStatement= jf.createWhileStatement();
			Block whileBody= jf.createBlock();
			whileStatement.setBody(whileBody);
			whileStatement.setExpression(createIsThreadNotInterruptedExpression());

			body.getStatements().add(whileStatement);
			body= whileBody;

			body.getStatements().add(createCanEndStatement(true));

		}

		// for event handler
		boolean firstReceiveHandled= false;

		// catch interrupted exception and do nothing. this is normal as we
		// abort with this type of exception

		CatchClause c1= jf.createCatchClause();
		SingleVariableDeclaration localVar= JMHelper.createLocalVariable(fTypeTracker.getType("java.lang.Exception"), "e");
		c1.setException(localVar);
		c1.setBody(jf.createBlock());

		InstanceofExpression instanceofInterruptedException= JavaFactory.eINSTANCE.createInstanceofExpression();
		instanceofInterruptedException.setLeftOperand(JMHelper.createLocalVariableAccess(localVar));
		instanceofInterruptedException.setRightOperand(JMHelper.createTypeAccessTo(fTypeTracker.getType("java.lang.InterruptedException")));

		ParenthesizedExpression parenthes= JavaFactory.eINSTANCE.createParenthesizedExpression();
		parenthes.setExpression(instanceofInterruptedException);

		PrefixExpression notInterrupted= JavaFactory.eINSTANCE.createPrefixExpression();
		notInterrupted.setOperator(PrefixExpressionKind.NOT);
		notInterrupted.setOperand(parenthes);

		IfStatement ifNotInterrupted= JavaFactory.eINSTANCE.createIfStatement();
		ifNotInterrupted.setExpression(notInterrupted);
		ifNotInterrupted.setThenStatement(JMHelper.createMethodInvocationStatement(JMHelper.createLocalVariableAccess(localVar),
				fExceptionPrintMethod));
		c1.getBody().getStatements().add(ifNotInterrupted);

		tryStatement.getCatchClauses().add(c1);

		List<Object> work= new ArrayList<Object>();
		if (ce instanceof CompositeElement)
			work.addAll( ((CompositeElement) ce).getChildren());
		else
			work.add(ce);

		while (!work.isEmpty()) {

			Object step= work.get(0);
			work.remove(0);

			if (step instanceof Send || step instanceof SendAndReceive) {

				if (!directlyFollowsInterruptingReceive((Send) step)) {

					// In case this is a locked partner (which has receives,
					// too) enclose in synchronized.
					Block bodyToUse= body;
					if (fLockFields.containsKey( ((Send) step).getPartner())) {
						SynchronizedStatement sync= JMHelper.createSynchronizedBlock(fLockFields.get( ((Send) step).getPartner()));
						body.getStatements().add(sync);
						bodyToUse= sync.getBody();
					}
					handleSend(ce, (Send) step, bodyToUse);
				}
			}

			if (step instanceof Receive && (! (step instanceof SendAndReceive))) {
				handleReceive(ce, (Receive) step, body);

				if (!firstReceiveHandled && ( (ce instanceof EventHandler))) {
					firstReceiveHandled= true;
					body.getStatements().add(createCanEndStatement(false));
				}

			}

			if (step instanceof Reply) {
				handleReply(ce, (Reply) step, body);
			}


			if (step instanceof DataHandling) {
				handleData(ce, (DataHandling) step, body);
			}

			if (step instanceof Compensate) {
				ServiceActivity target= ((Compensate) step).getTarget();
				CompensationHandler h= getCompensationHandler(target);
				if (h == null)
					throw new TransformationException("Did not find compensation handler on target of compensate action " + step);
				body.getStatements().add(createStartScope(ce, h));
				body.getStatements().add(createWaitForScope(h));
			}

			if (step instanceof CompensateAll) {
				// TODO
			}

			if (step instanceof Throw) {

				/**
				 * TODO this mechanisms should be documented. The idea is to
				 * store the exception and then to throw an interrupted
				 * exception. This is handled on the outside or propagated
				 * further.
				 */

				Throw t= (Throw) step;

				Type theException= fTypeTracker.getJavaTypeForSMMType(t.getExceptionType());
				if (theException == null)
					throw new TransformationException("Could not find exception type " + t.getExceptionType());

				JMHelper.ensureImport(fBUnit, theException);

				// create "throw" with "setException"
				ClassInstanceCreation theExInvoc= JMHelper.createConstructorInvocation(theException);
				MethodDeclaration setExpressionMethod= JMHelper.findMethod((AbstractTypeDeclaration) fTypeTracker.getType("util.ServiceRunnable"),
						"setException");
				body.getStatements().add(JMHelper.createMethodInvocationStatement(null, setExpressionMethod, theExInvoc));

				// Interrupt
				body.getStatements().add(createThrowInterrupedExceptionStatement());

			}

			if (step instanceof Decision) {

				Decision d= (Decision) step;

				EList<ServiceElement> children= d.getChildren();
				if (children.size() != 2)
					throw new TransformationException("Expecting decision to have two possible options.");

				IfStatement decisionStatement= jf.createIfStatement();
				body.getStatements().add(decisionStatement);

				Path left= (Path) children.get(0);
				Path right= (Path) children.get(1);

				// TODO this assumes both conditions are there and valid.
				Expression leftCondition= convertBooleanExpressionToJava(ce, left.getEnterCondition());
				Expression rightCondition= convertBooleanExpressionToJava(ce, right.getEnterCondition());

				Block thenBlock= jf.createBlock();
				Block elseBlock= jf.createBlock();

				decisionStatement.setExpression(leftCondition);
				decisionStatement.setThenStatement(thenBlock);

				IfStatement decisionStatement2= jf.createIfStatement();
				decisionStatement.setElseStatement(decisionStatement2);
				decisionStatement2.setExpression(rightCondition);
				decisionStatement2.setThenStatement(elseBlock);

				DecisionBlock block= new DecisionBlock(DecisionBlock.State.MIDDLE);
				block.setOriginalBlock(body);
				block.setElseBlock(elseBlock);

				body= thenBlock;

				work.add(0, block);
				work.addAll(0, right.getChildren());
				work.add(0, block);
				work.addAll(0, left.getChildren());
			}

			if (step instanceof DecisionBlock) {
				DecisionBlock b= (DecisionBlock) step;
				if (b.getState().equals(DecisionBlock.State.MIDDLE)) {
					body= b.getElseBlock();
					b.setState(DecisionBlock.State.END);

				} else {
					body= b.getOriginalBlock();
				}

			}

			if (step instanceof Loop) {
				Path onlyPath= (Path) ((Loop) step).getChildren().get(0);
				LoopEndBlock leb= new LoopEndBlock(body);


				Expression expr= null;
				eu.mdd4soa.smm.data.Expression leaveCondition= ((Loop) step).getLeaveCondition();
				if (leaveCondition != null) {
					Expression positive= convertBooleanExpressionToJava(ce, leaveCondition);
					PrefixExpression prefix= jf.createPrefixExpression();
					prefix.setOperand(positive);
					prefix.setOperator(PrefixExpressionKind.NOT);
					expr= prefix;
				} else
					expr= createIsThreadNotInterruptedExpression();

				DoStatement doStatement= jf.createDoStatement();
				doStatement.setExpression(expr);
				Block doBlock= jf.createBlock();
				doStatement.setBody(doBlock);
				body.getStatements().add(doStatement);

				body= doBlock;

				work.add(0, leb);
				work.addAll(0, onlyPath.getChildren());
			}

			if (step instanceof LoopEndBlock) {
				body= ((LoopEndBlock) step).getOriginalBlock();
			}

			if (step instanceof ServiceActivity) {

				ServiceActivity act= (ServiceActivity) step;

				// Kick off scope...
				body.getStatements().add(createStartScope(ce, act));
				body.getStatements().add(createWaitForScope(act));

				// If this scope has an exception handler, we can at least
				// theoretically handle it...
				List<Statement> incaseofexception= new ArrayList<Statement>();
				if (getFirstExceptionHandler(act) != null) {
					// TODO normally we would need to check exception TYPES
					// here. Future work.
					Handler eH= getFirstExceptionHandler(act);

					// Just start the handler and let it do its work.
					incaseofexception.add(createStartScope(ce, eH));
					incaseofexception.add(createWaitForScope(eH));

				} else {
					// Propagate.
					MethodDeclaration setExceptionMethod= JMHelper.findMethod((AbstractTypeDeclaration) fTypeTracker.getType("util.ServiceRunnable"),
							"setException");

					incaseofexception.add(JMHelper.createMethodInvocationStatement(null, setExceptionMethod,
							((ExpressionStatement) getExceptionGetterForSA(act)).getExpression()));
					incaseofexception.add(createThrowInterrupedExceptionStatement());
				}

				InfixExpression checkForNotNull= jf.createInfixExpression();
				checkForNotNull.setOperator(InfixExpressionKind.NOT_EQUALS);
				checkForNotNull.setLeftOperand( ((ExpressionStatement) getExceptionGetterForSA(act)).getExpression());
				checkForNotNull.setRightOperand(JMHelper.createNullLiteral());

				IfStatement st= jf.createIfStatement();
				st.setExpression(checkForNotNull);
				Block block= jf.createBlock();
				block.getStatements().addAll(incaseofexception);
				st.setThenStatement(block);

				body.getStatements().add(st);
			}

			if (step instanceof Parallel) {
				// TODO Parallel? Same as activity
			}


		}

		// "Normally" stop event handlers when done (TODO ev. handlers in ev.
		// handlers are not allowed).
		if (ce instanceof ServiceActivity) {
			ServiceActivity sa= (ServiceActivity) ce;

			// Stop event handlers?
			if (hasEventHandlers(sa)) {
				body.getStatements().addAll(createWaitForEventHandlers(sa));
			}
			// Stop interrupting receives?
			if (!sa.getInterruptingReceives().isEmpty()) {
				for (Receive ircv : sa.getInterruptingReceives()) {
					body.getStatements().add(createWaitForScope(ircv));
				}
			}
		}

		// In case this is an interrupting receive, we need to interrupt the
		// parent.
		if (ce instanceof Receive) {
			MethodDeclaration interruptParentMethod= JMHelper.findMethod((AbstractTypeDeclaration) fTypeTracker.getType("util.ServiceRunnable"),
					"interruptParent");
			body.getStatements().add(JMHelper.createMethodInvocationStatement(null, interruptParentMethod));
		}

		// Stop event handlers and interrupting activities if we have an
		// interruption.
		if (ce instanceof ServiceActivity) {
			ServiceActivity sa= (ServiceActivity) ce;

			EList<Statement> statementList= c1.getBody().getStatements();

			// TODO if we have interruptions, then we need to pull out the next
			// action
			// after the interrupting edge (if send or receive) such that it is
			// still possible,
			// while handling it, to do the event handlers. Otherwise, an
			// external partner might
			// send something to the event receive which cannot be receive
			// anymore.

			// ATM, we assume just one interrupting receive.
			// find the following element
			if (sa.getInterruptingReceives().size() == 1) {

				ServiceElement nextElement= getElementFollowingInterruption(sa);

				if (nextElement != null) {

					// TODO Just handle send/send&receive for the moment.
					if (nextElement instanceof Send) {

						// We need enclose this in a synchronized block as in
						// mixed states we might get a problem with the client
						// sending and receiving something at the same time.
						SynchronizedStatement sync= JMHelper.createSynchronizedBlock(fLockFields.get( ((Send) nextElement).getPartner()));
						statementList.add(sync);
						statementList= sync.getBody().getStatements();

						TryStatement trySt= JavaFactory.eINSTANCE.createTryStatement();

						CatchClause cc= JavaFactory.eINSTANCE.createCatchClause();
						SingleVariableDeclaration exVar= JMHelper.createLocalVariable(fTypeTracker.getType("java.lang.Exception"), "inE");
						cc.setException(exVar);
						cc.setBody(JavaFactory.eINSTANCE.createBlock());

						cc.getBody().getStatements()
								.add(JMHelper.createMethodInvocationStatement(JMHelper.createLocalVariableAccess(exVar), fExceptionPrintMethod));

						trySt.getCatchClauses().add(cc);
						trySt.setBody(JavaFactory.eINSTANCE.createBlock());
						handleSend(ce, (Send) nextElement, trySt.getBody());

						statementList.add(trySt);

					}
				}
			}

			// Stop event handlers? (note: do this inside the synchronized block
			// if there were interrupting edges) such
			// that the event handler is surely stopped when we are moving away
			// from the scope.
			if (hasEventHandlers(sa)) {
				statementList.addAll(createWaitForEventHandlers(sa));
			}

			// Stop interrupting receives?
			if (!sa.getInterruptingReceives().isEmpty()) {
				for (Receive ircv : sa.getInterruptingReceives()) {
					c1.getBody().getStatements().add(createForceEndScope(ircv));
				}
			}
		}

		// End is okay
		runMethodBody.getStatements().add(createCanEndStatement(true));


	}

	private ServiceElement getElementFollowingInterruption(ServiceElement sa) {

		ServiceElement nextElement= null;
		EList<ServiceElement> children= sa.getParent().getChildren();
		for (int i= 0; i < children.size(); i++)
			if (sa.equals(children.get(i)) && i < children.size() - 1)
				nextElement= children.get(i + 1);
		return nextElement;
	}

	private boolean directlyFollowsInterruptingReceive(ServiceElement step) {

		ServiceElement prevElement= null;
		EList<ServiceElement> children= step.getParent().getChildren();
		for (int i= 0; i < children.size(); i++)
			if (step.equals(children.get(i)) && i > 1) {
				prevElement= children.get(i - 1);
				break;
			}

		return prevElement instanceof ServiceActivity && ((ServiceActivity) prevElement).getInterruptingReceives().size() != 0;
	}

	private Expression createIsThreadNotInterruptedExpression() {

		PrefixExpression ex= jf.createPrefixExpression();
		ex.setOperator(PrefixExpressionKind.NOT);

		AbstractTypeDeclaration jlt= (AbstractTypeDeclaration) fTypeTracker.getType("java.lang.Thread");
		MethodDeclaration interrupted= JMHelper.findMethod(jlt, "interrupted");

		MethodInvocation invoc= JMHelper.createMethodInvocation(interrupted);
		invoc.setExpression(JMHelper.createTypeAccessTo(jlt));

		ex.setOperand(invoc);
		return ex;
	}

	private Statement createCanEndStatement(boolean b) {

		MethodDeclaration canEnd= JMHelper.findMethod((AbstractTypeDeclaration) fTypeTracker.getType("util.ServiceRunnable"), "setCanEnd");
		return JMHelper.createMethodInvocationStatement(null, canEnd, JMHelper.createBooleanLiteral(b));
	}

	private CompensationHandler getCompensationHandler(ServiceActivity target) {
		EList<Handler> handlers= target.getHandlers();
		for (Handler handler : handlers) {
			if (handler instanceof CompensationHandler)
				return (CompensationHandler) handler;
		}
		return null;
	}

	private Handler getFirstExceptionHandler(ServiceActivity act) {
		EList<Handler> handlers= act.getHandlers();
		for (Handler handler : handlers) {
			if (handler instanceof ExceptionHandler)
				return handler;
		}
		return null;
	}

	private ThrowStatement createThrowInterrupedExceptionStatement() {
		return JMHelper.createThrowStatement(JMHelper.createConstructorInvocation(fTypeTracker.getType("java.lang.InterruptedException")));
	}

	private Statement getExceptionGetterForSA(ServiceActivity act) {
		Statement getExceptionMethod= getServiceRunnableMethodInvocation(act, "getException");
		return getExceptionMethod;
	}

	// Wait for activity

	private Statement createWaitForScope(ServiceElement ircv) {
		return getServiceRunnableMethodInvocation(ircv, "waitForEnd");
	}

	private List<Statement> createWaitForEventHandlers(ServiceActivity act) {
		List<Statement> waitForHandlers= new ArrayList<Statement>();
		for (Handler handler : act.getHandlers()) {
			if (handler instanceof EventHandler)
				waitForHandlers.add(getServiceRunnableMethodInvocation(handler, "waitForEnd"));
		}
		return waitForHandlers;
	}

	// Force end:

	private Statement createForceEndScope(ServiceElement ircv) {
		return getServiceRunnableMethodInvocation(ircv, "forceEnd");
	}

	// Starting scopes:

	private Statement createStartScope(ServiceElement parent, ServiceElement toStart) {

		if (parent != null) {
			return getServiceRunnableMethodInvocation(toStart, "start", JMHelper.createThisExpression());
		} else
			return getServiceRunnableMethodInvocation(toStart, "start", JMHelper.createNullLiteral());

	}

	private List<Statement> createStartEventHandlers(ServiceActivity act) {
		List<Statement> startHandlers= new ArrayList<Statement>();
		for (Handler handler : act.getHandlers()) {
			if (handler instanceof EventHandler)
				startHandlers.add(createStartScope(act, handler));
		}
		return startHandlers;
	}


	private Statement getServiceRunnableMethodInvocation(ServiceElement act, String methodName, Expression... parameters) {
		MethodDeclaration startMethod= JMHelper.findMethod((AbstractTypeDeclaration) fTypeTracker.getType("util.ServiceRunnable"), methodName);
		MethodInvocation getter= JMHelper.createMethodInvocation(JMHelper.createFieldAccess(fFieldRunnables), fHashMapGetMethod,
				fScopeTracker.getEnumAccess(act));

		return JMHelper.createMethodInvocationStatement(getter, startMethod, parameters);
	}

	private void handleData(ServiceElement ce, DataHandling dataHandling, Block block) throws TransformationException {

		EList<eu.mdd4soa.smm.data.Statement> statements= dataHandling.getStatements();
		for (eu.mdd4soa.smm.data.Statement statement : statements) {
			Statement stt= convertStatementToJava(ce, statement, false);
			block.getStatements().add(stt);
		}

	}

	/**
	 * Handles a reply.
	 * 
	 * In {@link #handleReceive(CompositeElement, Receive, SwitchStatement)},
	 * the statements inside the invoked method have already been handled. Here,
	 * we need to "free" the reply barrier, and before that handle any return
	 * parameters.
	 * 
	 * @param currentScope
	 * @param currentElement
	 * @param block
	 * @throws TransformationException
	 */
	private void handleReply(ServiceElement currentScope, Reply reply, Block block) throws TransformationException {

		EList<InteractionParameter> sndParameters= reply.getSndParameters();
		if (sndParameters.size() > 1)
			throw new RuntimeException("Cannot handle reply with more than one send parameter in SMM2Java.");

		List<Statement> statementsInRunnable= new ArrayList<Statement>();
		if (sndParameters.size() == 1) {

			SendParameter sndParm= (SendParameter) sndParameters.get(0);
			Expression sndExpression= convertRightHandSideToJava(currentScope, sndParm.getSource(), true);

			MethodDeclaration setArgumentsMethod= JMHelper.findMethod((AbstractTypeDeclaration) fTypeTracker.getType("util.BarrierRunnable"),
					"setArguments");

			statementsInRunnable.add(JMHelper.createMethodInvocationStatement(null, setArgumentsMethod, sndExpression));
		}

		// Add to runnable.
		MethodDeclaration runMethod= fBarrierTracker.getRunMethod(reply);
		runMethod.getBody().getStatements().add(logInfo(EVENT_MSG_SEND, reply.getPartner().getName(), "return_" + reply.getOperation().getName()));
		runMethod.getBody().getStatements().addAll(statementsInRunnable);
		runMethod.getBody().getStatements()
				.add(JMHelper.createMethodInvocationStatement(JMHelper.createTypeAccessTo(fSimHelper), fSimHelperWaitMethod));

		// Barrier in the switch
		block.getStatements().add(createBarrierAwait(reply));

	}

	private void handleSend(ServiceElement currentScope, ServiceElement currentElement, Block parent) throws TransformationException {
		// A send operation.
		Send theSend= (Send) currentElement;

		// something like partner_XXX.DO()
		Partner partner= fPartners.getPartner(PartnerTracker.Type.REQUIRED, theSend.getPartner());

		FieldDeclaration partnerField= partner.getPartnerField();
		MethodDeclaration methodToCall= JMHelper.findMethod(partner.getInterface(), theSend.getOperation().getName());

		List<Expression> parameters= new ArrayList<Expression>();
		EList<InteractionParameter> sndParameters= theSend.getSndParameters();
		for (InteractionParameter interactionParameter : sndParameters) {
			SendParameter param= (SendParameter) interactionParameter;
			parameters.add(convertRightHandSideToJava(currentScope, param.getSource(), false));
		}
		boolean isVoid= true;

		Expression theCall= JMHelper.createMethodInvocation(JMHelper.createFieldAccess(partnerField), methodToCall,
				parameters.toArray(new Expression[0]));

		parent.getStatements().add(logInfo(EVENT_MSG_SEND, partner.getPartnerName(), theSend.getOperation().getName()));

		if (currentElement instanceof SendAndReceive) {
			EList<InteractionParameter> rcvParameters= theSend.getRcvParameters();
			if (rcvParameters.size() > 1)
				throw new TransformationException("A send and receive may have only one rcv parameter");

			if (rcvParameters.size() == 1) {
				isVoid= false;
				ReceiveParameter rcv= (ReceiveParameter) rcvParameters.get(0);

				// Cast the contents
				Type targetType= fTypeTracker.resolveTypeWithMultiplicityAndImports(fBUnit, rcv.getOperationParameter());
				CastExpression c= jf.createCastExpression();
				c.setType(JMHelper.createTypeAccessTo(targetType));
				c.setExpression(theCall);

				parent.getStatements().add(handleAssignmentToLeftHandSide(currentScope, c, rcv.getTarget(), false));
			}
		}

		if (isVoid) {
			ExpressionStatement st= jf.createExpressionStatement();
			st.setExpression(theCall);
			parent.getStatements().add(st);
		}


		if (currentElement instanceof SendAndReceive) {
			parent.getStatements().add(logInfo(EVENT_MSG_RECEIVED, partner.getPartnerName(), "return_" + theSend.getOperation().getName()));
			parent.getStatements().add(JMHelper.createMethodInvocationStatement(JMHelper.createTypeAccessTo(fSimHelper), fSimHelperWaitMethod));
		}

	}

	private void handleReceive(ServiceElement currentscope, Receive theReceive, Block block) throws TransformationException {

		InterfaceOperation receivedOperation= theReceive.getOperation();
		Reply reply= theReceive.getReply();

		MethodDeclaration getArgumentsMethod= JMHelper.findMethod((AbstractTypeDeclaration) fTypeTracker.getType("util.BarrierRunnable"),
				"getArguments");

		// (1) in the switch: barrier.await();
		block.getStatements().add(createBarrierAwait(theReceive));

		// (2) in the method invoked from external partner
		List<Statement> statementsInInvokedMethod= new ArrayList<Statement>();
		Partner partner= fPartners.getPartner(PartnerTracker.Type.PROVIDED, theReceive.getPartner());
		MethodDeclaration invokedMethod= partner.getMethodDeclaration(receivedOperation);

		// (2.a) parameter settings
		EList<SingleVariableDeclaration> parameters= invokedMethod.getParameters();
		List<Expression> argumentAccess= new ArrayList<Expression>();
		for (SingleVariableDeclaration singleVariableDeclaration : parameters)
			argumentAccess.add(JMHelper.createLocalVariableAccess(singleVariableDeclaration));

		MethodInvocation getRunnableInvocation= JMHelper.createMethodInvocation(JMHelper.createFieldAccess(fFieldBarrierRunnables),
				fHashMapGetMethod, fBarrierTracker.getEnumAccess(theReceive));
		MethodDeclaration setArgumentsMethod= JMHelper.findMethod((AbstractTypeDeclaration) fTypeTracker.getType("util.BarrierRunnable"),
				"setArguments");
		statementsInInvokedMethod.add(JMHelper.createMethodInvocationStatement(getRunnableInvocation, setArgumentsMethod,
				argumentAccess.toArray(new Expression[0])));

		// (2.b) waiting for barrier
		statementsInInvokedMethod.add(createBarrierAwait(theReceive));

		// (2.c.optional) reply?
		if (reply != null) {
			// This receive has a reply, which means that we need to wait until
			// we give the control back to the caller until the reply is
			// reached.

			// 2.c.1 wait for reply barrier
			statementsInInvokedMethod.add(createBarrierAwait(reply));

			// 2.c.2 grab return value and return (if any)
			if (!reply.getSndParameters().isEmpty()) {

				// get return value from runnable - may only be one.
				// List<Document> docs= (List<Document>)
				// barrier_runnable_getStatus_reply.getArguments().get(0);
				// return docs;

				// barrierRunnables.get(ServiceActivity.THE_REPLY)
				MethodInvocation getBarrierRunnable= JMHelper.createMethodInvocation(JMHelper.createFieldAccess(fFieldBarrierRunnables),
						fHashMapGetMethod, fBarrierTracker.getEnumAccess(reply));

				// barrierRunnables.get(ServiceActivity.THE_REPLY).getArguments()
				MethodInvocation getArguments= JMHelper.createMethodInvocation(getBarrierRunnable, getArgumentsMethod);

				// barrierRunnables.get(ServiceActivity.THE_REPLY).getArguments().get(0)
				MethodInvocation varContents= JMHelper.createMethodInvocation(getArguments, fHashMapGetMethod, JMHelper.createNumberLiteral(0));

				// (ReturnType)
				// barrierRunnables.get(ServiceActivity.THE_REPLY).getArguments().get(0)
				CastExpression theCast= jf.createCastExpression();
				theCast.setExpression(varContents);
				theCast.setType(JMHelper.createTypeAccessTo(invokedMethod.getReturnType().getType()));

				// return (ReturnType)
				// barrierRunnables.get(ServiceActivity.THE_REPLY).getArguments().get(0)
				ReturnStatement st= jf.createReturnStatement();
				st.setExpression(theCast);

				statementsInInvokedMethod.add(st);
			}
		}

		// enclose in synchronized-block
		SynchronizedStatement sync= JMHelper.createSynchronizedBlock(fLockFields.get(theReceive.getPartner()));
		invokedMethod.getBody().getStatements().add(sync);

		// (2.d) enclose in try/catch
		ThrowStatement statementInCatch= JMHelper.createThrowStatement(JMHelper.createConstructorInvocation(fTypeTracker
				.getType("util.ServiceException")));
		TryStatement trySt= JMHelper.createTryCatchStatement(fTypeTracker.getType("java.lang.Exception"), statementInCatch,
				statementsInInvokedMethod.toArray(new Statement[0]));
		sync.getBody().getStatements().add(trySt);

		// (3) in the barrier runnable
		MethodDeclaration barrierRunMethod= fBarrierTracker.getRunMethod(theReceive);
		barrierRunMethod.getBody().getStatements()
				.add(logInfo(EVENT_MSG_RECEIVED, theReceive.getPartner().getName(), theReceive.getOperation().getName()));


		// (3.a) assign parameters
		EList<InteractionParameter> rcvParameters= theReceive.getRcvParameters();
		int pIndex= 0;
		for (InteractionParameter interactionParameter : rcvParameters) {

			ReceiveParameter rcv= (ReceiveParameter) interactionParameter;

			MethodInvocation getArgumentsInvocation= JMHelper.createMethodInvocation(getArgumentsMethod);
			MethodInvocation varContents= JMHelper.createMethodInvocation(getArgumentsInvocation, fHashMapGetMethod,
					JMHelper.createNumberLiteral(pIndex));

			// Cast the contents
			Type targetType= fTypeTracker.resolveTypeWithMultiplicityAndImports(fBUnit, rcv.getOperationParameter());
			CastExpression c= jf.createCastExpression();
			c.setType(JMHelper.createTypeAccessTo(targetType));
			c.setExpression(varContents);

			barrierRunMethod.getBody().getStatements().add(handleAssignmentToLeftHandSide(currentscope, c, rcv.getTarget(), true));
			pIndex++;
		}

		barrierRunMethod.getBody().getStatements()
				.add(JMHelper.createMethodInvocationStatement(JMHelper.createTypeAccessTo(fSimHelper), fSimHelperWaitMethod));


	}

	private void createPartnerLockFields() {

		for (Service sv : fBehaviour.getParticipant().getPartners()) {

			FieldDeclaration fieldDec= JMHelper.createField("private", "lock_" + sv.getName(), fTypeTracker.getType("java.lang.Object"));
			JMHelper.addField(fBClass, fieldDec);

			ClassInstanceCreation newObject= JMHelper.createConstructorInvocation(fTypeTracker.getType("java.lang.Object"));
			fieldDec.getFragments().get(0).setInitializer(newObject);
			fLockFields.put(sv, fieldDec);
		}

	}

	/**
	 * Provided interfaces are implemented by the behaviour class. They are
	 * added as "implements" and all methods are created.
	 */
	private void createProvidedPartners() {

		for (Partner thePartner : fPartners.getPartners(PartnerTracker.Type.PROVIDED)) {
			fBClass.getSuperInterfaces().add(JMHelper.createTypeAccessTo(thePartner.getInterface()));
			JMHelper.ensureImport(fBUnit, thePartner.getInterface());

			List<InterfaceOperation> operations= thePartner.getOperations();
			for (InterfaceOperation interfaceOperation : operations) {

				MethodDeclaration declaration= fTypeTracker.createMethodForInterfaceOperation(fBUnit, interfaceOperation);
				declaration.setBody(jf.createBlock());
				fBClass.getBodyDeclarations().add(declaration);

				thePartner.setOperation(interfaceOperation, declaration);
			}
		}
	}

	/**
	 * Required interfaces each get a field "partner_XXX", a constructor
	 * argument, and an assignment in the constructor.
	 */
	private void createFieldsForRequiredPartners() {

		for (Partner thePartner : fPartners.getPartners(PartnerTracker.Type.REQUIRED)) {
			FieldDeclaration fieldDec= JMHelper.createField("private", "partner_" + thePartner.getPartnerName(), thePartner.getInterface());
			JMHelper.addField(fBClass, fieldDec);
			thePartner.setPartnerField(fieldDec);

			JMHelper.ensureImport(fBUnit, thePartner.getInterface());

			// Parameter for constructor.
			SingleVariableDeclaration dd= JMHelper.createLocalVariable(thePartner.getInterface(), thePartner.getPartnerName());
			fConstructor.getParameters().add(dd);

			ExpressionStatement fieldAssignment= JMHelper.createAssignmentToField(fieldDec, dd);
			fConstructor.getBody().getStatements().add(fieldAssignment);
		}
	}

	private ExpressionStatement createBarrierAwait(ServiceInteraction barrierAction) {

		MethodInvocation methodForGettingBarrier= JMHelper.createMethodInvocation(JMHelper.createFieldAccess(fFieldBarriers), fHashMapGetMethod,
				fBarrierTracker.getEnumAccess(barrierAction));

		MethodDeclaration barrierAwaitMethod= JMHelper.findMethod(
				(AbstractTypeDeclaration) fTypeTracker.getType("java.util.concurrent.CyclicBarrier"), "await");

		return JMHelper.createMethodInvocationStatement(methodForGettingBarrier, barrierAwaitMethod);
	}

	/**
	 * Converts a statement to Java.
	 * 
	 * @throws TransformationException
	 * 
	 */
	private Statement convertStatementToJava(ServiceElement currentScope, eu.mdd4soa.smm.data.Statement statement, boolean qualify)
			throws TransformationException {

		if (statement instanceof Assignment) {
			Assignment ass= (Assignment) statement;
			Expression rightHandSide= convertRightHandSideToJava(currentScope, ass.getSource(), qualify);
			LeftHandSideExpression target= ass.getTarget();
			ExpressionStatement expressionStatement= handleAssignmentToLeftHandSide(currentScope, rightHandSide, target, false);
			if (expressionStatement != null)
				return expressionStatement;
			else
				throw new TransformationException("Assignment statement " + statement + " has an unknown left-hand side: " + ass.getTarget());
		}

		if (statement instanceof Declaration) {
			Declaration dec= (Declaration) statement;

			Type theType= fTypeTracker.resolveTypeWithMultiplicityAndImports(fBUnit, dec.getDeclaredVar().getVariable());

			ClassInstanceCreation newExpression= JMHelper.createConstructorInvocation(theType);

			Type varType= fTypeTracker.getType("util.ServiceRunnable");
			MethodDeclaration methodAddVariable= JMHelper.findMethod((AbstractTypeDeclaration) varType, "addVariable");

			StringLiteral varNameReference= JMHelper.createStringLiteral(dec.getDeclaredVar().getVariable().getName());
			return JMHelper.createMethodInvocationStatement(null, methodAddVariable, varNameReference, newExpression);

		}
		return null;
	}

	/**
	 * TODO write about this, too -- generating the correct getters and setters
	 * and casts is not trivial.
	 * 
	 * @param currentScope
	 * @param toAssign
	 * @param targetToConvert
	 * @return
	 * @throws TransformationException
	 */
	private ExpressionStatement handleAssignmentToLeftHandSide(ServiceElement currentScope, Expression toAssign,
			LeftHandSideExpression targetToConvert, boolean qualify) throws TransformationException {

		// qualify?
		Expression on= null;
		if (qualify)
			on= JMHelper.createMethodInvocation(JMHelper.createFieldAccess(fFieldRunnables), fHashMapGetMethod,
					fScopeTracker.getEnumAccess(currentScope));


		Type varType= fTypeTracker.getType("util.ServiceRunnable");
		MethodDeclaration methodAddVariable= JMHelper.findMethod((AbstractTypeDeclaration) varType, "addVariable");
		MethodDeclaration methodGetVariable= JMHelper.findMethod((AbstractTypeDeclaration) varType, "getVariable");
		if (targetToConvert instanceof VariableReference) {
			// Assign the complete variable.
			Variable variable= ((VariableReference) targetToConvert).getVariable();
			StringLiteral varNameReference= JMHelper.createStringLiteral(variable.getName());



			return JMHelper.createMethodInvocationStatement(on, methodAddVariable, varNameReference, toAssign);
		}
		if (targetToConvert instanceof PropertyReference) {
			// Assign only part of the variable; therefore, retrieve it.
			PropertyReference propRef= (PropertyReference) targetToConvert;
			if (! (propRef.getParent() instanceof VariableReference))
				throw new TransformationException("Was expecting a variable reference as parent of property " + targetToConvert);

			Variable variable= ((VariableReference) propRef.getParent()).getVariable();
			Type theType= fTypeTracker.resolveTypeWithMultiplicityAndImports(fBUnit, variable);

			StringLiteral varNameReference= JMHelper.createStringLiteral(variable.getName());
			MethodInvocation getterForVariable= JMHelper.createMethodInvocation(on, methodGetVariable, varNameReference);

			CastExpression theCast= jf.createCastExpression();
			theCast.setExpression(getterForVariable);
			theCast.setType(JMHelper.createTypeAccessTo(theType));

			ParenthesizedExpression parenthesis= jf.createParenthesizedExpression();
			parenthesis.setExpression(theCast);

			// Now, use setter for the field to be set
			String identifier= ((PropertyReference) targetToConvert).getProperty().getIdentifier();


			MethodDeclaration propertySetterMethod= JMHelper.findMethod((AbstractTypeDeclaration) theType, "set" + JMHelper.toFirstUpper(identifier));

			return JMHelper.createMethodInvocationStatement(parenthesis, propertySetterMethod, toAssign);
		}
		return null;
	}

	private Expression convertBooleanExpressionToJava(ServiceElement ce, eu.mdd4soa.smm.data.Expression expression) throws TransformationException {

		if (expression instanceof Operation) {
			Operation op= (Operation) expression;

			InfixExpression ife= jf.createInfixExpression();
			switch (op.getOperationType()) {
				case EQUALS:
					ife.setOperator(InfixExpressionKind.EQUALS);
					break;
				case LESS:
					ife.setOperator(InfixExpressionKind.LESS);
					break;
				case LESS_EQUAL:
					ife.setOperator(InfixExpressionKind.LESS_EQUALS);
					break;
				case GREATER:
					ife.setOperator(InfixExpressionKind.GREATER);
					break;
				case GREATER_EQUAL:
					ife.setOperator(InfixExpressionKind.GREATER_EQUALS);
					break;
				case NOT_EQUALS:
					ife.setOperator(InfixExpressionKind.NOT_EQUALS);
					break;
				default:
					throw new TransformationException("Unknown operation type: " + op.getOperationType());
			}

			EList<eu.mdd4soa.smm.data.Expression> operands= op.getOperands();
			if (operands.size() != 2)
				throw new TransformationException("More than two operands in expression " + expression);

			Expression op1= convertRightHandSideToJava(ce, operands.get(0), false);
			Expression op2= convertRightHandSideToJava(ce, operands.get(1), false);
			ife.setLeftOperand(op1);
			ife.setRightOperand(op2);
			return ife;
		}

		throw new TransformationException("Unknown expression type: " + expression);
	}

	/**
	 * Converts an expression to Java.
	 * 
	 * @param source
	 * @param b
	 * @return
	 * @throws TransformationException
	 */
	private Expression convertRightHandSideToJava(ServiceElement currentScope, eu.mdd4soa.smm.data.Expression source, boolean qualify)
			throws TransformationException {

		if (source instanceof VariableReference) {
			VariableReference varRef= (VariableReference) source;
			Variable variable= varRef.getVariable();
			return getVariableAccess(currentScope, variable, qualify);
		}

		if (source instanceof PropertyReference) {
			PropertyReference ref= (PropertyReference) source;

			Expression convertedParent= convertRightHandSideToJava(currentScope, ref.getParent(), qualify);
			ParenthesizedExpression pe= jf.createParenthesizedExpression();
			pe.setExpression(convertedParent);

			AbstractTypeDeclaration parentType= null;
			if (ref.getParent() instanceof PropertyReference)
				parentType= (AbstractTypeDeclaration) fTypeTracker.resolveTypeWithMultiplicityAndImports(fBUnit,
						((PropertyReference) ref.getParent()).getProperty());
			else if (ref.getParent() instanceof VariableReference)
				parentType= (AbstractTypeDeclaration) fTypeTracker.resolveTypeWithMultiplicityAndImports(fBUnit,
						((VariableReference) ref.getParent()).getVariable());
			else
				throw new TransformationException("Unknown parent of property reference : " + ref.getParent());

			MethodDeclaration getter= JMHelper.findMethod(parentType, "get" + JMHelper.toFirstUpper(ref.getProperty().getIdentifier()));

			return JMHelper.createMethodInvocation(pe, getter);
		}
		if (source instanceof Literal) {
			Object value= ((Literal) source).getValue();
			if (value instanceof String)
				return JMHelper.createStringLiteral((String) value);
			if (value instanceof Boolean)
				return JMHelper.createBooleanLiteral((Boolean) value);
			if (value instanceof Integer)
				return JMHelper.createNumberLiteral((Integer) value);

			throw new TransformationException("Unknown literal type: " + value);
		}

		throw new TransformationException("Unknown expression type: " + source);
	}

	private Expression getVariableAccess(ServiceElement currentScope, Variable variable, boolean qualify) {

		Expression on= null;
		if (qualify)
			on= JMHelper.createMethodInvocation(JMHelper.createFieldAccess(fFieldRunnables), fHashMapGetMethod,
					fScopeTracker.getEnumAccess(currentScope));


		Type type= fTypeTracker.getType("util.ServiceRunnable");
		MethodDeclaration methodGetVariable= JMHelper.findMethod((AbstractTypeDeclaration) type, "getVariable");

		StringLiteral varNameReference= JMHelper.createStringLiteral(variable.getName());
		MethodInvocation mi= JMHelper.createMethodInvocation(on, methodGetVariable, varNameReference);

		Type javaType= fTypeTracker.resolveTypeWithMultiplicityAndImports(fBUnit, variable);

		CastExpression expr= jf.createCastExpression();
		expr.setType(JMHelper.createTypeAccessTo(javaType));
		expr.setExpression(mi);

		return expr;
	}

	private List<ServiceInteraction> getAllBarrierRequiringInteractions(ServiceActivity activity) {

		List<ServiceInteraction> allrecvs= new ArrayList<ServiceInteraction>();

		TreeIterator<Object> allContents= EcoreUtil.getAllContents(activity, true);
		while (allContents.hasNext()) {
			Object next= allContents.next();
			if (next instanceof Receive || next instanceof Reply) {
				allrecvs.add((ServiceInteraction) next);
			}
		}

		return allrecvs;
	}

	private CompositeElement getParent(ServiceElement sa) {
		if (sa instanceof ServiceActivity)
			return ((ServiceActivity) sa).getParent();

		if (sa instanceof Handler)
			return ((Handler) sa).getServiceActivity();

		if (sa instanceof Receive)
			return ((Receive) sa).getInterrupting();

		return null;
	}

	private List<ServiceElement> getRelevantElements(ServiceActivity fBehaviour) {

		List<ServiceElement> allSas= new ArrayList<ServiceElement>();
		allSas.add(fBehaviour);

		TreeIterator<Object> allContents= EcoreUtil.getAllContents(fBehaviour, false);
		while (allContents.hasNext()) {
			Object next= allContents.next();
			if (next instanceof ServiceActivity) {
				ServiceActivity sa= (ServiceActivity) next;
				allSas.add(sa);
				allSas.addAll(sa.getInterruptingReceives());

			}
			if (next instanceof Handler)
				allSas.add((Handler) next);
		}

		return allSas;
	}


	private boolean hasEventHandlers(ServiceActivity sa) {
		EList<Handler> handlers= sa.getHandlers();
		for (Handler handler : handlers) {
			if (handler instanceof EventHandler)
				return true;
		}
		return false;
	}

	private Statement logInfo(String event, String partner, String message) {

		String complete= event + ":" + partner + ":" + message;
		return JMHelper.createMethodInvocationStatement(JMHelper.createTypeAccessTo(fSimHelper), fSimHelperLogMethod,
				JMHelper.createStringLiteral(complete));
	}
}
