Newer
Older
AlgebraicDataflowArchitectureModel / AlgebraicDataflowArchitectureModel / src / generators / CodeGeneratorFromControlFlowGraph.java
package generators;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import code.ast.Block;
import code.ast.CompilationUnit;
import code.ast.FieldDeclaration;
import code.ast.MethodDeclaration;
import code.ast.TypeDeclaration;
import code.ast.VariableDeclaration;
import models.Edge;
import models.Node;
import models.algebra.Constant;
import models.algebra.Expression;
import models.algebra.Field;
import models.algebra.InvalidMessage;
import models.algebra.ParameterizedIdentifierIsFutureWork;
import models.algebra.Symbol;
import models.algebra.Term;
import models.algebra.Type;
import models.algebra.UnificationFailed;
import models.algebra.ValueUndefined;
import models.algebra.Variable;
import models.controlFlowModel.ControlFlowGraph;
import models.controlFlowModel.EntryPointObjectNode;
import models.controlFlowModel.ObjectNode;
import models.controlFlowModel.StatefulObjectNode;
import models.dataConstraintModel.ChannelMember;
import models.dataConstraintModel.DataConstraintModel;
import models.dataConstraintModel.ResourcePath;
import models.dataFlowModel.DataFlowEdge;
import models.dataFlowModel.DataTransferChannel;
import models.dataFlowModel.DataTransferModel;
import models.dataFlowModel.IFlowGraph;
import models.dataFlowModel.PushPullAttribute;
import models.dataFlowModel.PushPullValue;
import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork;
import models.dataFlowModel.ResourceNode;
import models.dataFlowModel.StoreAttribute;
import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor;

public class CodeGeneratorFromControlFlowGraph extends CodeGenerator {

	@Override
	public void generateCodeFromFlowGraph(DataTransferModel model, IFlowGraph flowGraph, ArrayList<Set<Node>> components,
			TypeDeclaration mainComponent, MethodDeclaration mainConstructor, ArrayList<CompilationUnit> codes, ILanguageSpecific langSpec) {
		// Reconstruct data-flow information.
		Map<Edge, Map<PushPullValue, List<ResourceNode>>> dataFlowInform = new HashMap<>();
		ControlFlowGraph controlFlowGraph = (ControlFlowGraph) flowGraph;
		for (Node root: controlFlowGraph.getPushCallGraph().getRootNodes()) {
			Set<ResourceNode> treeResources = traverseCallTree(root, new HashSet<>());
			annotateDataFlowAttributes(root, dataFlowInform, treeResources, new ArrayList<>());
			removeRedundantAttributes(root, dataFlowInform);
		}
		for (Node root: controlFlowGraph.getPullCallGraph().getRootNodes()) {
			Set<ResourceNode> treeResources = traverseCallTree(root, new HashSet<>());
			annotateDataFlowAttributes(root, dataFlowInform, treeResources, new ArrayList<>());
			removeRedundantAttributes(root, dataFlowInform);
		}
		
		// For each component other than the main component.
		Map<Node, TypeDeclaration> componentMap = new HashMap<>();
		for (Set<Node> componentNodeSet: components) {
			// Declare this component.
			Node componentNode = componentNodeSet.iterator().next();
			String componentName = langSpec.toComponentName(((ObjectNode) componentNode).getName());
			TypeDeclaration component = langSpec.newTypeDeclaration(componentName);
			for (Node compNode: componentNodeSet) {
				componentMap.put(compNode, component);
			}
			
			// Declare the constructor and the fields to refer to the callee components.
			List<ResourcePath> depends = new ArrayList<>();
			MethodDeclaration constructor = declareConstructorAndFieldsToCalleeComponents(componentNodeSet, component, depends, langSpec);
			
			if (componentNode instanceof StatefulObjectNode) {
				// For this resource.
				ResourceNode resourceNode = ((StatefulObjectNode) componentNode).getResource();
				ResourcePath res = resourceNode.getResource();
				Type resStateType = res.getResourceStateType();
				
				// Declare the field in this resource to store the state.
				if (((StoreAttribute) resourceNode.getAttribute()).isStored()) {
					FieldDeclaration stateField = langSpec.newFieldDeclaration(resStateType, fieldOfResourceState, langSpec.getFieldInitializer(resStateType, res.getInitialValue()));
					component.addField(stateField);
				}
				
				// Declare the accessor method in the main component to call the getter method.
				declareAccessorInMainComponent(mainComponent, res, langSpec);
				
				// Declare the fields to refer to reference resources.
				declareFieldsToReferenceResources(model, resourceNode, component, constructor, depends, langSpec);				
			}
			
			// Update the main component for this component.
			updateMainComponent(mainComponent, mainConstructor, componentNode, constructor, langSpec);
			if (constructor.getParameters() == null) {
				component.removeMethod(constructor);
			}
						
			// Add compilation unit for this component.
			CompilationUnit cu = langSpec.newCompilationUnit(component);
			codes.add(cu);
		}
		
		// Declare and Fill the getter method to return the resource state.
		for (Node node: controlFlowGraph.getPushCallGraph().getNodes()) {
			TypeDeclaration component = componentMap.get(node);
			if (node instanceof StatefulObjectNode) {
				ResourceNode resourceNode = ((StatefulObjectNode) node).getResource();
				Type resStateType = resourceNode.getResource().getResourceStateType();				
				if (((StoreAttribute) resourceNode.getAttribute()).isStored()) {
					// Declare the getter method in this resource to obtain the state.
					MethodDeclaration getter = langSpec.newMethodDeclaration(getterOfResourceState, resStateType);
					component.addMethod(getter);
					fillGetterMethodToReturnStateField(getter, resourceNode.getResource().getResourceStateType(), langSpec);		// return this.value;
				}
			}
		}
		
		for (Node node: controlFlowGraph.getPullCallGraph().getNodes()) {
			String nodeName = ((ObjectNode) node).getName();
			if (componentMap.get(node) == null) {
				for (Node node2: componentMap.keySet()) {
					if (((ObjectNode) node2).getName().equals(nodeName)) {
						componentMap.put(node, componentMap.get(node2));		// Since nodes shared by PUSH and PULL call graphs are duplicated.
						break;
					}
				}
			}
		}
		
		// Declare other getter methods.
		for (Node root: controlFlowGraph.getPullCallGraph().getRootNodes()) {
			MethodDeclaration getter = declareAndFillGetterMethods(root, null, dataFlowInform, componentMap, langSpec);			
		}
		
		// Declare update and input methods.
		for (Node root: controlFlowGraph.getPushCallGraph().getRootNodes()) {
			MethodDeclaration input = declareAndFillUpdateAndInputMethods(root, null, null, dataFlowInform, componentMap, langSpec);
			mainComponent.addMethod(input);
		}
	}

	private Set<ResourceNode> traverseCallTree(Node node, Set<ResourceNode> visited) {
		if (node instanceof StatefulObjectNode) {
			ResourceNode resNode = ((StatefulObjectNode) node).getResource();
			visited.add(resNode);
		}
		// Traverse the call tree.
		for (Edge e: node.getOutEdges()) {
			visited = traverseCallTree(e.getDestination(), visited);
		}
		return visited;
	}

	private void annotateDataFlowAttributes(Node node, Map<Edge, Map<PushPullValue, List<ResourceNode>>> dataFlowInform, Set<ResourceNode> resourceNodes, List<Edge> path) {
		if (node instanceof StatefulObjectNode) {
			// Add data-flow attributes to the path to node.
			ResourceNode resNode = ((StatefulObjectNode) node).getResource();
			for (Edge outE: resNode.getOutEdges()) {
				// If resNode is the source of data-flow.
				ResourceNode dstOfDataFlowNode = (ResourceNode) outE.getDestination();
				if (resourceNodes.contains(dstOfDataFlowNode)) {
					// If the data transfer is closed within this call tree.
					for (Edge e: path) {
						// Add pull attributes to the path to resNode.
						Map<PushPullValue, List<ResourceNode>> edgeAttributes = dataFlowInform.get(e);
						if (edgeAttributes == null) {
							edgeAttributes = new HashMap<>();
							dataFlowInform.put(e, edgeAttributes);
						}
						List<ResourceNode> pullSrcs = edgeAttributes.get(PushPullValue.PULL);
						if (pullSrcs == null) {
							pullSrcs = new ArrayList<>();
							edgeAttributes.put(PushPullValue.PULL, pullSrcs);
						}
						pullSrcs.add(resNode);
					}
				}
			}
			for (Edge inE: resNode.getInEdges()) {
				// If resNode is a destination of data-flow.
				ResourceNode srcOfDataFlowNode = (ResourceNode) inE.getSource();
				if (resourceNodes.contains(srcOfDataFlowNode)) {
					// If the data transfer is closed done within this call tree.
					for (Edge e: path) {
						// Add push attributes to the path to resNode.
						Map<PushPullValue, List<ResourceNode>> edgeAttributes = dataFlowInform.get(e);
						if (edgeAttributes == null) {
							edgeAttributes = new HashMap<>();
							dataFlowInform.put(e, edgeAttributes);
						}
						List<ResourceNode> pushSrcs = edgeAttributes.get(PushPullValue.PUSH);
						if (pushSrcs == null) {
							pushSrcs = new ArrayList<>();
							edgeAttributes.put(PushPullValue.PUSH, pushSrcs);
						}
						pushSrcs.add(srcOfDataFlowNode);
					}
				}
			}
		}
		// Traverse the call tree.
		for (Edge e: node.getOutEdges()) {
			path.add(e);
			annotateDataFlowAttributes(e.getDestination(), dataFlowInform, resourceNodes, path);
			path.remove(e);
		}
	}
	
	private void removeRedundantAttributes(Node node, Map<Edge, Map<PushPullValue, List<ResourceNode>>> dataFlowInform) {
		// Traverse the call tree.
		for (Edge e: node.getOutEdges()) {
			// Remove attributes that are common to PUSH and PULL.
			if (dataFlowInform.get(e) == null) {
				dataFlowInform.put(e, new HashMap<>());
			}
			List<ResourceNode> pushFlows = dataFlowInform.get(e).get(PushPullValue.PUSH);
			List<ResourceNode> pullFlows = dataFlowInform.get(e).get(PushPullValue.PULL);
			if (pushFlows == null) {
				pushFlows = new ArrayList<>();
			}
			if (pullFlows == null) {
				pullFlows = new ArrayList<>();
			}
			List<ResourceNode> pushFlowsOrg = new ArrayList<>(pushFlows);
			for (ResourceNode r: pullFlows) {
				pushFlows.remove(r);
			}
			for (ResourceNode r: pushFlowsOrg) {
				pullFlows.remove(r);
			}
			pushFlows = new ArrayList<>(new HashSet<>(pushFlows));
			pullFlows = new ArrayList<>(new HashSet<>(pullFlows));
			dataFlowInform.get(e).put(PushPullValue.PUSH, pushFlows);
			dataFlowInform.get(e).put(PushPullValue.PULL, pullFlows);
			removeRedundantAttributes(e.getDestination(), dataFlowInform);
		}
	}
	
	private MethodDeclaration declareConstructorAndFieldsToCalleeComponents(Set<Node> componentNodeSet, TypeDeclaration component, 
			List<ResourcePath> depends, ILanguageSpecific langSpec) {
		// Declare a constructor in each component.
		MethodDeclaration constructor = component.createConstructor();
		Block block = new Block();
		constructor.setBody(block);
		
		// Declare fields in each component. (for control-flow graph)
		for (Node componentNode: componentNodeSet) {
			for (Edge e: componentNode.getOutEdges()) {
				ObjectNode dstNode = (ObjectNode) e.getDestination();
				addReference(component, constructor, dstNode, langSpec);
				if (dstNode instanceof StatefulObjectNode) {
					ResourcePath dstRes = ((StatefulObjectNode) dstNode).getResource().getResource();
					if (!depends.contains(dstRes)) depends.add(dstRes);
				}
			}
		}
		return constructor;
	}
	

	private MethodDeclaration declareAndFillGetterMethods(Node node, Edge inEdge, 
			Map<Edge, Map<PushPullValue, List<ResourceNode>>> dataFlowInform, Map<Node, TypeDeclaration> componentMap,
			ILanguageSpecific langSpec) {
		TypeDeclaration component = componentMap.get(node);
		List<ResourceNode> resourcesToReturn = null;
		if (inEdge != null) {
			resourcesToReturn = dataFlowInform.get(inEdge).get(PushPullValue.PULL);
		}
		if (node instanceof StatefulObjectNode) {
			ResourceNode resourceNode = ((StatefulObjectNode) node).getResource();
			Type resStateType = resourceNode.getResource().getResourceStateType();				
			MethodDeclaration getter = langSpec.newMethodDeclaration(getterOfResourceState, resStateType);
			MethodDeclaration getter2 = getMethod(component, getter.getName());
			if (getter2 == null) {
				component.addMethod(getter);
			} else {
				getter = getter2;
			}
			if (((StoreAttribute) resourceNode.getAttribute()).isStored()) {
				if (getter2 == null) {
					// Declare the getter method in this resource to obtain the state.
					fillGetterMethodToReturnStateField(getter, resourceNode.getResource().getResourceStateType(), langSpec);		// return this.value;
				}
			} else {
				// Invocations to other getter methods when at least one incoming data-flow edges is PULL-style.
				boolean isContainedPush = false;
				DataTransferChannel ch = null;
				HashMap<ResourcePath, IResourceStateAccessor> inputResourceToStateAccessor = new HashMap<>();
				for (Edge eIn: resourceNode.getInEdges()) {
					DataFlowEdge dataFlowInEdge = (DataFlowEdge) eIn;
					if (((PushPullAttribute) dataFlowInEdge.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) {
						// PUSH data transfer
						isContainedPush = true;
						inputResourceToStateAccessor.put(((ResourceNode) dataFlowInEdge.getSource()).getResource(), getPushAccessor());
					} else {
						// PULL data transfer
						for (Edge callEdge: node.getOutEdges()) {
							// For each call edge.
							ObjectNode calledNode = (ObjectNode) callEdge.getDestination();
							List<ResourceNode> returnedResources = dataFlowInform.get(callEdge).get(PushPullValue.PULL);
							if (returnedResources.contains((ResourceNode) dataFlowInEdge.getSource())) {
								if (returnedResources.size() == 1) {
									MethodDeclaration nextGetter = declareAndFillGetterMethods(calledNode, callEdge, dataFlowInform, componentMap, langSpec);
									inputResourceToStateAccessor.put(((ResourceNode) dataFlowInEdge.getSource()).getResource(), getPullAccessor(calledNode.getName(), nextGetter.getName()));
									break;
								} else {
									MethodDeclaration nextGetter = declareAndFillGetterMethods(calledNode, callEdge, dataFlowInform, componentMap, langSpec);
									int idx = returnedResources.indexOf((ResourceNode) dataFlowInEdge.getSource());
									int len = returnedResources.size();
									inputResourceToStateAccessor.put(((ResourceNode) dataFlowInEdge.getSource()).getResource(), 
											getPullAccessor(langSpec.getTupleGet(langSpec.getMethodInvocation(langSpec.getFieldAccessor(calledNode.getName()), nextGetter.getName()), idx, len)));
									break;
								}
							}
						}
						ch = dataFlowInEdge.getChannel();		// Always unique.
					}
				}
				// For reference channel members.
				for (ChannelMember c: ch.getReferenceChannelMembers()) {
					inputResourceToStateAccessor.put(c.getResource(), getPullAccessor());			// by pull data transfer
				}
				
				// Add a return statement.
				try {
					for (ChannelMember out: ch.getOutputChannelMembers()) {
						if (out.getResource().equals(resourceNode.getResource())) {
							String[] sideEffects = new String[] {""};
							// The following process is common to the cases of 1) and 2).
							// 1) All incoming edges are in PULL-style.
							// 2) At least one incoming edge is in PUSH-style.
							String curState = ch.deriveUpdateExpressionOf(out, getPullAccessor(), inputResourceToStateAccessor).toImplementation(sideEffects);
							getter.addStatement(sideEffects[0] + langSpec.getReturnStatement(curState) + langSpec.getStatementDelimiter());
							break;
						}
					}
				} catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork
						| InvalidMessage | UnificationFailed | ValueUndefined e) {
					e.printStackTrace();
				}
			}
			if (resourcesToReturn == null || resourcesToReturn.size() == 1) return getter;
		} else if (resourcesToReturn == null || resourcesToReturn.size() == 1) {
			// Declare a mediate getter method to return a single value.
			String getterMethodName = "get";
			ResourceNode returnedRes = null; 
			if (resourcesToReturn != null) {
				returnedRes = resourcesToReturn.get(0);
			} else {
				// Unexpected.
			}
			getterMethodName += langSpec.toComponentName(returnedRes.getResource().getResourceName()) + "Value";
			MethodDeclaration mediateGetter = getMethod(component, getterMethodName);
			if (mediateGetter != null) return mediateGetter;
			mediateGetter = langSpec.newMethodDeclaration(getterMethodName, returnedRes.getResource().getResourceStateType());
			component.addMethod(mediateGetter);
			
			// Add a return statement.
			if (node.getOutdegree() == 1) {
				Edge callEdge = node.getOutEdges().iterator().next();
				ObjectNode calledNode = (ObjectNode) callEdge.getDestination();
				MethodDeclaration nextGetter = declareAndFillGetterMethods(calledNode, callEdge, dataFlowInform, componentMap, langSpec);
				mediateGetter.addStatement(
						langSpec.getReturnStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(calledNode.getName()), nextGetter.getName()))
						+ langSpec.getStatementDelimiter());
			} else {
				// Unexpected.
			}
			return mediateGetter;
		}
		// Declare a mediate getter method to return multiple values.
		String getterMethodName = "get";
		for (ResourceNode rn: resourcesToReturn) {
			getterMethodName += langSpec.toComponentName(rn.getResource().getResourceName());
		}
		getterMethodName += "Values";
		MethodDeclaration mediateGetter = getMethod(component, getterMethodName);
		if (mediateGetter != null) return mediateGetter;
		Type returnType = createReturnType(resourcesToReturn, langSpec);
		mediateGetter = langSpec.newMethodDeclaration(getterMethodName, returnType);
		component.addMethod(mediateGetter);
		
		// Add a return statement.
		if (node.getOutdegree() == 1 && resourcesToReturn != null
				&& resourcesToReturn.equals(dataFlowInform.get(node.getOutEdges().iterator().next()).get(PushPullValue.PULL))) {
			// Directly returns the returned value.
			Edge outEdge = node.getOutEdges().iterator().next();
			ObjectNode dstNode = (ObjectNode) outEdge.getDestination();
			MethodDeclaration nextGetter = declareAndFillGetterMethods(dstNode, outEdge, dataFlowInform, componentMap, langSpec);
			String getterInvocation = langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstNode.getName()), nextGetter.getName());
			mediateGetter.addStatement(langSpec.getReturnStatement(getterInvocation) + langSpec.getStatementDelimiter());
		} else {
			List<String> params = new ArrayList<>();
			for (ResourceNode rn: resourcesToReturn) {
				ResourcePath rId = rn.getResource();
				if (rId.getResourceName().equals(((ObjectNode) node).getName())) {
					params.add(langSpec.getMethodInvocation(getterOfResourceState));
				} else {
					for (Edge outEdge: node.getOutEdges()) {
						ObjectNode dstNode = (ObjectNode) outEdge.getDestination();
						List<ResourceNode> returnedResources = dataFlowInform.get(outEdge).get(PushPullValue.PULL);
						if (returnedResources.contains(rn)) {
							if (returnedResources.size() == 1) {
								MethodDeclaration nextGetter = declareAndFillGetterMethods(dstNode, outEdge, dataFlowInform, componentMap, langSpec);
								params.add(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstNode.getName()), nextGetter.getName()));
							} else {
								MethodDeclaration nextGetter = declareAndFillGetterMethods(dstNode, outEdge, dataFlowInform, componentMap, langSpec);
								int idx = returnedResources.indexOf(rn);
								int len = returnedResources.size();
								params.add(langSpec.getTupleGet(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstNode.getName()), nextGetter.getName()), idx, len));
							}
							break;
						}
					}
				}
			}
			mediateGetter.addStatement(
					langSpec.getReturnStatement(langSpec.getConstructorInvocation(returnType.getImplementationTypeName(), params))
					+ langSpec.getStatementDelimiter());
		}
		return mediateGetter;
	}

	private MethodDeclaration declareAndFillUpdateAndInputMethods(Node node, Edge inEdge, Node prevResNode, 
			Map<Edge, Map<PushPullValue, List<ResourceNode>>> dataFlowInform, Map<Node, TypeDeclaration> componentMap, ILanguageSpecific langSpec) {
		TypeDeclaration component = componentMap.get(node);
		List<ResourceNode> resourcesToReturn = null; 
		List<ResourceNode> resourcesToReceive = null;
		if (dataFlowInform.get(inEdge) != null) {
			resourcesToReturn = dataFlowInform.get(inEdge).get(PushPullValue.PULL);
			resourcesToReceive = dataFlowInform.get(inEdge).get(PushPullValue.PUSH);
		}
		if (node instanceof StatefulObjectNode) {
			// Declare update or input method in the resource component.
			ResourceNode resourceNode = ((StatefulObjectNode) node).getResource();
			MethodDeclaration updateOrInput = null;
			if (!(prevResNode instanceof EntryPointObjectNode) 
					|| (resourcesToReceive != null && resourcesToReceive.size() > 0)) {
				updateOrInput = getUpdateMethod(inEdge, component, dataFlowInform, langSpec);
				if (updateOrInput != null) return updateOrInput;
				// Declare an update method.
				updateOrInput = declareUpdateMethod(node, inEdge, component, dataFlowInform, langSpec);
			} else {
				DataTransferChannel ch = ((EntryPointObjectNode) prevResNode).getIOChannel();
				updateOrInput = getInputMethod(resourceNode, ch, component);
				if (updateOrInput != null) return updateOrInput;
				// Declare an input method.
				updateOrInput = declareInputMethod(resourceNode, ch, langSpec);
			}
			component.addMethod(updateOrInput);
			
			Map<ResourceNode, String> resToVar = new HashMap<>();
			Map<String, List<ResourceNode>> varToRes = new HashMap<>();
			for (Edge outEdge: node.getOutEdges()) {
				Node dstNode = outEdge.getDestination();
				MethodDeclaration calleeMethod = declareAndFillUpdateAndInputMethods(dstNode, outEdge, node, dataFlowInform, componentMap, langSpec);
				// Add a statement to call the destination method.
				List<ResourceNode> returnedResources = dataFlowInform.get(outEdge).get(PushPullValue.PULL);
				String varName = addInvocationInResourceUpdate(node, updateOrInput, calleeMethod, ((ObjectNode) dstNode).getName(), returnedResources, langSpec);
				if (varName != null && returnedResources != null) {
					for (ResourceNode rn: returnedResources) {
						String resName = rn.getResource().getResourceName();
						resToVar.put(rn, resName);
						varToRes.put(resName, Arrays.asList(new ResourceNode[] {rn}));
					}
//					// Alternative implementation.
//					varToRes.put(varName, returnedResources);
//					for (ResourceNode rn: returnedResources) {
//						resToVar.put(rn, varName);
//					}
				}
			}
			
			if (resourcesToReturn != null && resourcesToReturn.size() > 0) {
				// Set the return type and add a return statement.
				Type returnType = createReturnType(resourcesToReturn, langSpec);		
				updateOrInput.setReturnType(returnType);
				String returnValue = createReturnValue(resourcesToReturn, node, returnType, resToVar, varToRes, langSpec);
				updateOrInput.addStatement(langSpec.getReturnStatement(returnValue) + langSpec.getStatementDelimiter());
			}
			return updateOrInput;
		} else if (node instanceof EntryPointObjectNode) {
			// Declare an input method.
			MethodDeclaration input = null;
			for (Edge outEdge: node.getOutEdges()) {
				Node dstNode = outEdge.getDestination();
				MethodDeclaration calleeMethod = declareAndFillUpdateAndInputMethods(dstNode, outEdge, node, dataFlowInform, componentMap, langSpec);
				if (input == null) {
					// Declare an input method.
					if (calleeMethod.getParameters() != null) {
						input  = langSpec.newMethodDeclaration(calleeMethod.getName(), false, null, new ArrayList<>(calleeMethod.getParameters()));
					} else {
						input  = langSpec.newMethodDeclaration(calleeMethod.getName(), null);
					}
				}
				// Add a statement to call the destination method.
				String varName = addInvocationInMediatorUpdate(input, calleeMethod, ((ObjectNode) dstNode).getName(), dataFlowInform.get(outEdge).get(PushPullValue.PULL), langSpec);
			}
			return input;
		} else {
			// Declare update or input method in the mediate component.
			List<MethodDeclaration> updateMethods = getUpdateMethods(component);
			if (updateMethods.size() > 0) return updateMethods.get(0);
			MethodDeclaration updateOrInput = null;
			if (!(prevResNode instanceof EntryPointObjectNode)
					|| (resourcesToReceive != null && resourcesToReceive.size() > 0)) {
				// Declare an update method.
				updateOrInput = declareUpdateMethod(node, inEdge, component, dataFlowInform, langSpec);
				component.addMethod(updateOrInput);
			}
			
			if (node.getOutdegree() == 1 && resourcesToReturn != null && resourcesToReturn.size() > 0
					&& resourcesToReturn.equals(dataFlowInform.get(node.getOutEdges().iterator().next()).get(PushPullValue.PULL))) {			
				// Directly returns the returned value.
				Edge outEdge = node.getOutEdges().iterator().next();
				ObjectNode dstNode = (ObjectNode) outEdge.getDestination();
				MethodDeclaration calleeMethod = declareAndFillUpdateAndInputMethods(dstNode, outEdge, prevResNode, dataFlowInform, componentMap, langSpec);
				if (updateOrInput == null && prevResNode instanceof EntryPointObjectNode) {
					// Declare an input method.
					if (calleeMethod.getParameters() != null) {
						updateOrInput  = langSpec.newMethodDeclaration(calleeMethod.getName(), false, null, new ArrayList<>(calleeMethod.getParameters()));
					} else {
						updateOrInput  = langSpec.newMethodDeclaration(calleeMethod.getName(), null);
					}
					component.addMethod(updateOrInput);
				}
				// Set the return type and add a return statement.
				updateOrInput.setReturnType(calleeMethod.getReturnType());
				String updateInvocation = langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstNode.getName()), calleeMethod.getName());
				updateOrInput.addStatement(langSpec.getReturnStatement(updateInvocation) + langSpec.getStatementDelimiter());
			} else {
				Map<ResourceNode, String> resToVar = new HashMap<>();
				Map<String, List<ResourceNode>> varToRes = new HashMap<>();
				for (Edge outEdge: node.getOutEdges()) {
					Node dstNode = outEdge.getDestination();
					MethodDeclaration calleeMethod = declareAndFillUpdateAndInputMethods(dstNode, outEdge, prevResNode, dataFlowInform, componentMap, langSpec);
					if (updateOrInput == null && prevResNode instanceof EntryPointObjectNode) {
						// Declare an input method.
						if (calleeMethod.getParameters() != null) {
							updateOrInput  = langSpec.newMethodDeclaration(calleeMethod.getName(), false, null, new ArrayList<>(calleeMethod.getParameters()));
						} else {
							updateOrInput  = langSpec.newMethodDeclaration(calleeMethod.getName(), null);
						}
						component.addMethod(updateOrInput);
					}
					// Add a statement to call the destination method.
					List<ResourceNode> returnedResources = dataFlowInform.get(outEdge).get(PushPullValue.PULL);
					String varName = addInvocationInMediatorUpdate(updateOrInput, calleeMethod, ((ObjectNode) dstNode).getName(), returnedResources, langSpec);
					if (varName != null && returnedResources != null) {
						for (ResourceNode rn: returnedResources) {
							String resName = rn.getResource().getResourceName();
							resToVar.put(rn, resName);
							varToRes.put(resName, Arrays.asList(new ResourceNode[] {rn}));
						}
//						// Alternative implementation.
//						varToRes.put(varName, returnedResources);
//						for (ResourceNode rn: returnedResources) {
//							resToVar.put(rn, varName);
//						}
					}
				}
				if (resourcesToReturn != null && resourcesToReturn.size() > 0) {
					// Set the return type and add a return statement.
					Type returnType = createReturnType(resourcesToReturn, langSpec);		
					updateOrInput.setReturnType(returnType);
					String returnValue = createReturnValue(resourcesToReturn, node, returnType, resToVar, varToRes, langSpec);
					updateOrInput.addStatement(langSpec.getReturnStatement(returnValue) + langSpec.getStatementDelimiter());
				}
			} 
			return updateOrInput;
		}
	}

	private MethodDeclaration declareUpdateMethod(Node node, Edge inEdge, TypeDeclaration component,
			Map<Edge, Map<PushPullValue, List<ResourceNode>>> dataFlowInform, ILanguageSpecific langSpec) {
		// Declare an update method in the component.
		ArrayList<VariableDeclaration> vars = new ArrayList<>();
		List<ResourceNode> passedResoueces = dataFlowInform.get(inEdge).get(PushPullValue.PUSH);
		Set<ResourcePath> passedIds = new HashSet<>();
		String methodName = updateMethodName;
		for (ResourceNode rn: passedResoueces) {
			ResourcePath rId = rn.getResource();
			passedIds.add(rId);
			methodName += langSpec.toComponentName(rId.getResourceName());
			vars.add(langSpec.newVariableDeclaration(rId.getResourceStateType(), rId.getResourceName()));				
		}
		MethodDeclaration update = langSpec.newMethodDeclaration(methodName, false, null, vars);
		
		if (node instanceof StatefulObjectNode) {
			// Add a statement to update the state field
			ResourceNode resourceNode = ((StatefulObjectNode) node).getResource();
			if (((StoreAttribute) resourceNode.getAttribute()).isStored()) {
				try {
					boolean stateUpdateAdded = false;
					for (Edge e: resourceNode.getInEdges()) {
						DataFlowEdge re = (DataFlowEdge) e;
						for (ChannelMember in: re.getChannel().getInputChannelMembers()) {
							if (passedIds.contains(in.getResource())) {
								for (ChannelMember out: re.getChannel().getOutputChannelMembers()) {
									if (out.getResource().equals(resourceNode.getResource())) {
										Expression updateExp = re.getChannel().deriveUpdateExpressionOf(out, getPushAccessor());
										String[] sideEffects = new String[] {""};
										String curState = updateExp.toImplementation(sideEffects);
										String updateStatement;
										if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) {
											updateStatement = sideEffects[0];
										} else {
											updateStatement = sideEffects[0] + langSpec.getFieldAccessor(fieldOfResourceState) + langSpec.getAssignment() + curState + langSpec.getStatementDelimiter();	// this.value = ...
										}
										update.addFirstStatement(updateStatement);
										stateUpdateAdded = true;
										break;
									}
								}
							}
							if (stateUpdateAdded) break;
						}
						if (stateUpdateAdded) break;
					}
				} catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork
						| InvalidMessage | UnificationFailed | ValueUndefined e1) {
					e1.printStackTrace();
				}
			}
			
			// Declare the field to cache the state of the source resource in the type of the destination resource.
			if (node.getIndegree() > 1) {
				// If incoming edges are multiple
				for (ResourcePath srcRes: passedIds) {
					String srcResName = srcRes.getResourceName();
					if (langSpec.declareField()) {
						// Declare the cache field. 
						FieldDeclaration cacheField = langSpec.newFieldDeclaration(
																srcRes.getResourceStateType(), 
																srcResName, 
																langSpec.getFieldInitializer(srcRes.getResourceStateType(), srcRes.getInitialValue()));
						component.addField(cacheField);
						
					}
					// Update the cache field.
					String cashStatement = langSpec.getFieldAccessor(srcResName) + langSpec.getAssignment() + srcResName + langSpec.getStatementDelimiter();
					update.addFirstStatement(cashStatement);
				}
			}
		}
		return update;
	}

	private MethodDeclaration declareInputMethod(ResourceNode resourceNode, DataTransferChannel ch, ILanguageSpecific langSpec) {
		MethodDeclaration input = null;
		for (ChannelMember out : ch.getOutputChannelMembers()) {
			if (out.getResource().equals(resourceNode.getResource())) {
				Expression message = out.getStateTransition().getMessageExpression();
				if (message instanceof Term) {
					// Declare an input method in this component.
					ArrayList<VariableDeclaration> params = new ArrayList<>();
					for (Variable var: message.getVariables().values()) {
						params.add(langSpec.newVariableDeclaration(var.getType(), var.getName()));
					}
					input = langSpec.newMethodDeclaration(((Term) message).getSymbol().getImplName(), false, null, params);
				} else if (message instanceof Variable) {
					// Declare an input method in this component.
					input = langSpec.newMethodDeclaration(((Variable) message).getName(), null);
				}
				
				if (input != null) {
					// Add a statement to update the state field to the input method.
					try {
						String[] sideEffects = new String[] {""};
						Expression updateExp;
						updateExp = ch.deriveUpdateExpressionOf(out, getPullAccessor());
						String newState = updateExp.toImplementation(sideEffects);
						String updateStatement;
						if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) {
							updateStatement = sideEffects[0];	
						} else {
							updateStatement = sideEffects[0] + langSpec.getFieldAccessor(fieldOfResourceState) + langSpec.getAssignment() + newState + langSpec.getStatementDelimiter();
						}
						input.addFirstStatement(updateStatement);
					} catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork
							| InvalidMessage | UnificationFailed | ValueUndefined e) {
						e.printStackTrace();
					}
				}
				break;
			}
		}
		return input;
	}

	private String createReturnValue(List<ResourceNode> resourcesToReturn, Node node, Type returnType, Map<ResourceNode, String> resToVar, Map<String, List<ResourceNode>> varToRes, ILanguageSpecific langSpec) {
		List<String> params = new ArrayList<>();
		for (ResourceNode rn: resourcesToReturn) {
			ResourcePath rId = rn.getResource();
			if (rId.getResourceName().equals(((ObjectNode) node).getName())) {
				params.add(langSpec.getFieldAccessor(fieldOfResourceState));
			} else {
				String varName = resToVar.get(rn);
				if (varToRes.get(varName).size() == 1) {
					params.add(varName);
				} else {
					params.add(langSpec.getTupleGet(varName, varToRes.get(varName).indexOf(rn), varToRes.get(varName).size()));
				}
			}
		}
		if (params.size() == 1) {
			return params.iterator().next();
		} else {
			return langSpec.getConstructorInvocation(returnType.getImplementationTypeName(), params);
		}
	}

	private Type createReturnType(List<ResourceNode> resourcesToReturn, ILanguageSpecific langSpec) {
		if (resourcesToReturn.size() == 1) {
			return resourcesToReturn.iterator().next().getResource().getResourceStateType();
		}
		List<Type> compTypes = new ArrayList<>();
		for (ResourceNode rn: resourcesToReturn) {
			ResourcePath rId = rn.getResource();
			compTypes.add(rId.getResourceStateType());
		}
		Type returnType = langSpec.newTupleType(compTypes);
		return returnType;
	}
	

	private String addInvocationInResourceUpdate(Node node, MethodDeclaration resourceUpdateMethod, MethodDeclaration calleeMethod, String dstNodeName, List<ResourceNode> returnResources, ILanguageSpecific langSpec) {
		List<String> params = new ArrayList<>();
		params.add(langSpec.getFieldAccessor(fieldOfResourceState));
		if (calleeMethod.getParameters() != null) {
			for (VariableDeclaration v: calleeMethod.getParameters()) {
				if (!((ObjectNode) node).getName().equals(v.getName())) {
					params.add(v.getName());
				}
			}
		}
//		for (ChannelMember rc: re.getChannelGenerator().getReferenceChannelMembers()) {
//			// to get the value of reference member.
//			IdentifierTemplate ref = rc.getIdentifierTemplate();
//			if (referredSet == null) {
//				referredSet = new HashSet<>();
//				referredResources.put(update, referredSet);
//			}
//			if (ref != resourceNode.getIdentifierTemplate()) {
//				String refVarName = ref.getResourceName();
//				if (!referredSet.contains(ref)) {
//					referredSet.add(ref);
//					Expression refGetter = langSpec.getPullAccessor().getCurrentStateAccessorFor(ref, ((ResourceNode) dOut.getSource()).getIdentifierTemplate());
//					String[] sideEffects = new String[] {""};
//					String refExp = refGetter.toImplementation(sideEffects);
//					String refTypeName = ref.getResourceStateType().getInterfaceTypeName();
//					resourceUpdateMethod.addFirstStatement(sideEffects[0] + langSpec.getVariableDeclaration(refTypeName, refVarName) + langSpec.getAssignment() + refExp + langSpec.getStatementDelimiter());
//				}
//				params.add(refVarName);
//			}
//		}
		if (calleeMethod.getReturnType() == null || langSpec.isVoidType(calleeMethod.getReturnType()) || returnResources == null) {
			resourceUpdateMethod.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstNodeName), 
																				calleeMethod.getName(), 
																				params) + langSpec.getStatementDelimiter());	// this.dst.updateSrc(value, refParams);
			return null;
		} else {
			String targetVarName = null;
			if (returnResources.size() == 1) {
				ResourceNode targetNode = returnResources.get(0);
				targetVarName = targetNode.getResource().getResourceName();
				resourceUpdateMethod.addStatement(
						langSpec.getVariableDeclaration(calleeMethod.getReturnType().getInterfaceTypeName(), targetVarName) 
						+ langSpec.getAssignment() 
						+ langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstNodeName), 
														calleeMethod.getName(), 
														params) + langSpec.getStatementDelimiter());	// ResType res = this.dst.updateSrc(value, refParams);
			} else {
				targetVarName = getMultipleResourcesVarName(returnResources, langSpec);
				VariableDeclaration targetVar = langSpec.newVariableDeclaration(calleeMethod.getReturnType(), targetVarName);
				List<VariableDeclaration> vars = new ArrayList<>();
				for (ResourceNode rn: returnResources) {
					ResourcePath rId = rn.getResource();
					vars.add(langSpec.newVariableDeclaration(rId.getResourceStateType(), rId.getResourceName()));
				}
				resourceUpdateMethod.addStatement(
						langSpec.getDecomposedTuple(
								langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstNodeName), calleeMethod.getName(), params), 
								targetVar, 		// ResType res = this.dst.updateSrc(value, refParams);
								vars));			// Type1 res1 = res.getKey();	Type2 res2 = res.getValue();
			}
			return targetVarName;
		}
	}
	
	private String addInvocationInMediatorUpdate(MethodDeclaration resourceUpdateMethod, MethodDeclaration calleeMethod, String dstNodeName, List<ResourceNode> returnResources, ILanguageSpecific langSpec) {
		List<String> params = new ArrayList<>();
		if (calleeMethod.getParameters() != null) {
			for (VariableDeclaration v: calleeMethod.getParameters()) {
				params.add(v.getName());
			}
		}
		if (calleeMethod.getReturnType() == null || langSpec.isVoidType(calleeMethod.getReturnType()) || returnResources == null ) {
			resourceUpdateMethod.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstNodeName), 
																				calleeMethod.getName(), 
																				params) + langSpec.getStatementDelimiter());	// this.dst.updateSrc(value, refParams);
			return null;
		} else {
			String targetVarName = null;
			if (returnResources.size() == 1) {
				ResourceNode targetNode = returnResources.get(0);
				targetVarName = targetNode.getResource().getResourceName();
				resourceUpdateMethod.addStatement(
						langSpec.getVariableDeclaration(calleeMethod.getReturnType().getInterfaceTypeName(), targetVarName) 
						+ langSpec.getAssignment() 
						+ langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstNodeName), 
														calleeMethod.getName(), 
														params) + langSpec.getStatementDelimiter());	// ResType res = this.dst.updateSrc(value, refParams);
			} else {
				targetVarName = getMultipleResourcesVarName(returnResources, langSpec);
				VariableDeclaration targetVar = langSpec.newVariableDeclaration(calleeMethod.getReturnType(), targetVarName);
				List<VariableDeclaration> vars = new ArrayList<>();
				for (ResourceNode rn: returnResources) {
					ResourcePath rId = rn.getResource();
					vars.add(langSpec.newVariableDeclaration(rId.getResourceStateType(), rId.getResourceName()));
				}
				resourceUpdateMethod.addStatement(
						langSpec.getDecomposedTuple(
								langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstNodeName), calleeMethod.getName(), params), 
								targetVar, 		// ResType res = this.dst.updateSrc(value, refParams);
								vars));			// Type1 res1 = res.getKey();	Type2 res2 = res.getValue();
			}
			return targetVarName;
		}
	}
	
	private String getMultipleResourcesVarName(List<ResourceNode> resources, ILanguageSpecific langSpec) {
		String varName = null;
		for (ResourceNode rn: resources) {
			if (varName == null) {
				varName = rn.getResource().getResourceName();
			} else {
				varName += langSpec.toComponentName(rn.getResource().getResourceName());
			}
		}
		return varName;
	}
	
	private List<MethodDeclaration> getUpdateMethods(TypeDeclaration component) {
		List<MethodDeclaration> updates = new ArrayList<>();
		for (MethodDeclaration m: component.getMethods()) {
			if (m.getName().startsWith(updateMethodName)) {
				updates.add(m);
			}
		}
		return updates;
	}

	protected IResourceStateAccessor getPullAccessor(final String receiverName, final String getterOfResourceState) {
		return new IResourceStateAccessor() {
			@Override
			public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) {
				if (target.equals(from)) {
					return new Field(fieldOfResourceState,
							target.getResourceStateType() != null ? target.getResourceStateType()
									: DataConstraintModel.typeInt);
				}
				// for reference channel member
				Term getter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD));
				getter.addChild(new Field(receiverName, target.getResourceStateType()));
				return getter;
			}

			@Override
			public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) {
				Term getter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD));
				getter.addChild(new Field(receiverName, target.getResourceStateType()));
				return getter;
			}
		};
	}

	protected IResourceStateAccessor getPullAccessor(final String resourceAccessor) {
		return new IResourceStateAccessor() {
			@Override
			public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) {
				if (target.equals(from)) {
					return new Field(fieldOfResourceState,
							target.getResourceStateType() != null ? target.getResourceStateType()
									: DataConstraintModel.typeInt);
				}
				// for reference channel member
				Term getter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD));
				getter.addChild(new Field(target.getResourceName(), target.getResourceStateType()));
				return getter;
			}

			@Override
			public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) {
				return new Constant(resourceAccessor);
			}
		};
	}
}