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

import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import algorithms.TypeInference;

import java.util.Set;
import java.util.Stack;

import code.ast.Annotation;
import code.ast.Block;
import code.ast.CodeUtil;
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.Parameter;
import models.algebra.ParameterizedIdentifierIsFutureWork;
import models.algebra.Position;
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.dataConstraintModel.Channel;
import models.dataConstraintModel.ChannelMember;
import models.dataConstraintModel.DataConstraintModel;
import models.dataConstraintModel.JsonAccessor;
import models.dataConstraintModel.JsonTerm;
import models.dataConstraintModel.ResourceHierarchy;
import models.dataConstraintModel.ResourcePath;
import models.dataConstraintModel.Selector;
import models.dataFlowModel.DataFlowEdge;
import models.dataFlowModel.DataFlowGraph;
import models.dataFlowModel.DataTransferChannel;
import models.dataFlowModel.DataTransferModel;
import models.dataFlowModel.PushPullAttribute;
import models.dataFlowModel.PushPullValue;
import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork;
import models.dataFlowModel.ResourceNode;
import models.dataFlowModel.ChannelNode;
import models.dataFlowModel.StoreAttribute;
import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor;

public class CodeGeneratorFromDataFlowGraph extends CodeGenerator {

	public void generateCodeFromFlowGraph(DataTransferModel model, DataFlowGraph flowGraph, Collection<ResourceNode> components, ArrayList<CompilationUnit> codes, 
			Map<ResourceHierarchy, Set<ResourceHierarchy>> dependedRootComponentGraph, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) {
		Map<ResourceHierarchy, TypeDeclaration> resourceComponents = new HashMap<>();
		Map<ResourceHierarchy, MethodDeclaration> resourceConstructors = new HashMap<>();
		Map<ResourceHierarchy, Map<String, VariableDeclaration>> constructorParams = new HashMap<>();
		List<Map.Entry<ResourceHierarchy, String>> constructorStatements = new ArrayList<>();
		Map<MethodDeclaration, Map.Entry<Expression, Map.Entry<ResourceHierarchy, ResourceHierarchy>>> updateStatements = new HashMap<>();
		Map<ResourceHierarchy, Set<ResourceHierarchy>> descendantGetters = new HashMap<>();
		
		TypeDeclaration mainComponent = null;
		MethodDeclaration mainConstructor = null;
		if (platformSpec.hasMain()) {
			// Add the main component.
			if (getMainTypeName() == null) {
				setMainTypeName(langSpec.getMainComponentName());
			}
			mainComponent = langSpec.newTypeDeclaration(getMainTypeName());
			mainConstructor = mainComponent.createConstructor();
			CompilationUnit mainCU = langSpec.newCompilationUnit(mainComponent);
			codes.add(mainCU);
		}
		
		// For each components (1st pass).
		for (Node componentNode: components) {
			ResourceNode resourceNode = (ResourceNode) componentNode;
			TypeDeclaration component = null;
			if (generatesComponent(resourceNode.getResourceHierarchy())) {
				// A component will be generated for this resource.
				String resourceName = getComponentName(resourceNode.getResourceHierarchy(), langSpec);
				Type resStateType = getImplStateType(resourceNode.getResourceHierarchy(), langSpec);
				component = resourceComponents.get(resourceNode.getResourceHierarchy());
				List<ResourceHierarchy> depends = new ArrayList<>();
				if (component == null) {
					// Add compilation unit for this component.
					component = langSpec.newTypeDeclaration(resourceName);
					if (!platformSpec.isMonolithic() && resourceNode.getResourceHierarchy().getParent() == null) {
						// For each root node, add component annotations.
						((RestApiSpecific) platformSpec).addComponentAnnotations(component, resourceNode.getResourceName());
					}
					resourceComponents.put(resourceNode.getResourceHierarchy(), component);
					CompilationUnit cu = langSpec.newCompilationUnit(component);
					if (!platformSpec.isMonolithic()  && resourceNode.getResourceHierarchy().getParent() == null) {
						// For each root node, add platform specific imports.
						((RestApiSpecific) platformSpec).addPlatformSpecificImports(cu);
					}
					codes.add(cu);
					
					if (platformSpec.isMonolithic()) {
						// For monolithic applications (components are tightly coupled and must be built together). 
						
						// Declare the constructor.
						MethodDeclaration constructor = declareConstructor(resourceNode, component, dependedRootComponentGraph, depends, langSpec);
						
						if (platformSpec.hasMain() && resourceNode.getResourceHierarchy().getParent() == null) {
							// For each root resource
							// Update the main component for this component.
							updateMainComponent(mainComponent, mainConstructor, componentNode, constructor, depends, langSpec);
						}
						
						// Declare the fields to refer to reference resources.
						declareFieldsToReferenceResources(model, resourceNode, component, constructor, depends, langSpec);				
						
						if (constructor.getParameters() == null || constructor.getParameters().size() == 0) {
							component.removeMethod(constructor);
						} else {
							resourceConstructors.put(resourceNode.getResourceHierarchy(), constructor);						
						}
					}
					
					// Declare the field to store the state in this resource.
					if (((StoreAttribute) resourceNode.getAttribute()).isStored()) {
						declareStateField(resourceNode, resourceName, component, resStateType, constructorParams, langSpec);
					}
					
					// Declare the getter method in this resource to obtain the state.
					MethodDeclaration stateGetter = declareStateGetterMethod(resourceNode, component, resStateType, platformSpec, langSpec);
					
					// Declare the accessor method in the main component to call the getter method.
					if (platformSpec.hasMain()) {
						declareAccessorInMainComponent(mainComponent, resourceNode, stateGetter, platformSpec, langSpec);
					}
				}
				if (component != null) {
					// (#1) Declare the getter methods in this resource to obtain the descendant resources. (complementary to #2)
					declareDescendantGetterMethods(resourceNode, component, descendantGetters, langSpec);
				}
			}
		}
		
		// For each components (2nd pass).
		Map<Channel, ChannelMember> priorMemberForInputChannel = new HashMap<>();
		for (Node componentNode: components) {
			// Declare this resource.
			ResourceNode resourceNode = (ResourceNode) componentNode;
			Type resStateType = getImplStateType(resourceNode.getResourceHierarchy(), langSpec);
			TypeDeclaration component = null;
			TypeDeclaration parentComponent = null;
			TypeDeclaration rootComponent = null;
			if (generatesComponent(resourceNode.getResourceHierarchy())) {
				component = resourceComponents.get(resourceNode.getResourceHierarchy());				
			}
			if (resourceNode.getResourceHierarchy().getParent() != null) {
				parentComponent = resourceComponents.get(resourceNode.getResourceHierarchy().getParent());
			}
			rootComponent = resourceComponents.get(resourceNode.getResourceHierarchy().getRoot());
			
			// Declare cache fields and update methods in this resource, and an update accessor method in the type of root resource.
			Map.Entry<List<String>, Map<MethodDeclaration, Map.Entry<Expression, Map.Entry<ResourceHierarchy, ResourceHierarchy>>>> initStatementsAndUpdateUpdates
								= declareCacheFieldsAndUpdateMethods(resourceNode, component, parentComponent, rootComponent, platformSpec, langSpec);
			if (component == null) {
				// Constructor statements were not added to any component because no component had been generated.
				for (String statement: initStatementsAndUpdateUpdates.getKey()) {
					constructorStatements.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy().getParent(), statement));
				}
			}
			for (Map.Entry<MethodDeclaration, Map.Entry<Expression, Map.Entry<ResourceHierarchy, ResourceHierarchy>>> entry: initStatementsAndUpdateUpdates.getValue().entrySet()) {
				updateStatements.put(entry.getKey(), entry.getValue());
			}
			
			// Declare the fields to refer to other resources for push/pull transfer in the parent/this component, and the state field in the parent component.
			boolean bDeclareClientField = declareFieldsToReferToOtherResourcesAndStateFieldInParentComponent(resourceNode, component, parentComponent, constructorParams, platformSpec, langSpec);
			
			// (#2) Declare the getter method to obtain the resource state in an ancestor resource. (complementary to #1)
			if (component == null) {
				MethodDeclaration stateGetter = declareStateGetterMethodInAncestor(resourceNode, resourceComponents, resStateType, platformSpec, langSpec);
				
				if (stateGetter != null && platformSpec.hasMain()) {
					// Declare the accessor method in the main component to call the getter method.
					declareAccessorInMainComponent(mainComponent, resourceNode, stateGetter, platformSpec, langSpec);
				}
			}
			
			if (!platformSpec.isMonolithic()) {
				// Declare the getter accessor in the root resource.
				declareGetterAccessorInTheRootResource(resourceNode, rootComponent, platformSpec, langSpec);
			}
			
			// Declare input methods in this component and the main component.
			Map.Entry<List<String>, Map<MethodDeclaration, Map.Entry<Expression, Map.Entry<ResourceHierarchy, ResourceHierarchy>>>> initStatementsAndInputUpdates
								= declareInputMethodsInThisAndMainComponents(resourceNode, component, parentComponent, mainComponent, rootComponent, model, priorMemberForInputChannel, platformSpec, langSpec);
			if (component == null) {
				// Constructor statements were not added to any component because no component had been generated.
				for (String statement: initStatementsAndInputUpdates.getKey()) {
					constructorStatements.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy().getParent(), statement));
				}
			}
			for (Map.Entry<MethodDeclaration, Map.Entry<Expression, Map.Entry<ResourceHierarchy, ResourceHierarchy>>> entry: initStatementsAndInputUpdates.getValue().entrySet()) {
				updateStatements.put(entry.getKey(), entry.getValue());
			}
		}
		
		// Add constructor parameters to the ancestor components.
		for (ResourceNode root: flowGraph.getRootResourceNodes()) {
			addConstructorParameters(root.getResourceHierarchy(), resourceComponents, resourceConstructors, constructorParams, langSpec);
		}
		
		// Add constructor statements.
		for (Map.Entry<ResourceHierarchy, String> entry: constructorStatements) {
			resourceConstructors.get(entry.getKey()).addStatement(entry.getValue());
		}
		
		// Add update statements.
		for (MethodDeclaration method: updateStatements.keySet()) {
			Expression updateExp = updateStatements.get(method).getKey();
			ResourceHierarchy resource = updateStatements.get(method).getValue().getKey();
			ResourceHierarchy descendantRes = updateStatements.get(method).getValue().getValue();
			TypeDeclaration descendantComponent = resourceComponents.get(descendantRes);
			addUpdateStatementWithConstructorInvocationToMethod(method, updateExp, resource, descendantRes, descendantComponent, langSpec);
		}
	}

	private static List<VariableDeclaration> addConstructorParameters(ResourceHierarchy resource, Map<ResourceHierarchy, TypeDeclaration> resourceComponents,
			Map<ResourceHierarchy, MethodDeclaration> resourceConstructors, Map<ResourceHierarchy, Map<String, VariableDeclaration>> constructorParams, ILanguageSpecific langSpec) {
		List<VariableDeclaration> params = new ArrayList<>();
		for (ResourceHierarchy child: resource.getChildren()) {
			params.addAll(addConstructorParameters(child, resourceComponents, resourceConstructors, constructorParams, langSpec));
		}
		if (constructorParams.get(resource) != null) {
			for (VariableDeclaration param: constructorParams.get(resource).values()) {
				params.add(param);
			}
		}
		if (params.size() > 0) {
			MethodDeclaration constructor = resourceConstructors.get(resource);
			if (constructor == null) {
				if (resourceComponents.get(resource) != null) {
					String resourceName = getComponentName(resource, langSpec);
					constructor = langSpec.newMethodDeclaration(resourceName, true, null, null);
					Block body = new Block();
					constructor.setBody(body);
					resourceComponents.get(resource).addMethod(constructor);
					resourceConstructors.put(resource, constructor);
				}
			}
			if (constructor != null) {
				for (VariableDeclaration param: params) {
					boolean existsParam = false;
					if (constructor.getParameters() != null) {
						for (VariableDeclaration constParam: constructor.getParameters()) {
							if (constParam.getName().equals(param.getName())) {
								existsParam = true;
								break;
							}
						}
					}
					if (!existsParam) {
						constructor.addParameter(param);
						constructor.getBody().addStatement(langSpec.getFieldAccessor(langSpec.toVariableName(param.getName())) + langSpec.getAssignment() + langSpec.toVariableName(param.getName()) + langSpec.getStatementDelimiter());
					}
				}
			}
		}
		if (resource.getNumParameters() > 0) params.clear();
		return params;
	}
	
	private void addUpdateStatementWithConstructorInvocationToMethod(MethodDeclaration method, Expression exp, ResourceHierarchy resource, ResourceHierarchy descendantRes, TypeDeclaration descendantComponent, ILanguageSpecific langSpec) {
		Type replacedJsonType = descendantRes.getResourceStateType();
		String replacingClassName = getComponentName(descendantRes, langSpec);
		Type descendantType = new Type(replacingClassName, replacingClassName);
		Map<Position, Term> subTerms = ((Term) exp).getSubTerms(Term.class);
		Iterator<Entry<Position, Term>> termEntItr = subTerms.entrySet().iterator();
		while (termEntItr.hasNext()) {
			Entry<Position, Term> termEnt = termEntItr.next();
			Term jsonTerm = termEnt.getValue();
			if (jsonTerm.getType() != null) {
				if (jsonTerm.getType().equals(replacedJsonType)) {
					if (jsonTerm instanceof JsonTerm || jsonTerm.getSymbol().equals(DataConstraintModel.addMember)) {
						MethodDeclaration childConstructor = getConstructor(descendantComponent);
						List<String> params = new ArrayList<>();
						if (childConstructor != null) {
							for (VariableDeclaration var: childConstructor.getParameters()) {
								JsonAccessor jsonMember = new JsonAccessor(DataConstraintModel.dot);
								jsonMember.addChild(jsonTerm);
								jsonMember.addChild(new Constant(var.getName(), DataConstraintModel.typeString));
								Expression param = jsonMember.reduce();
								if (param != null) {
									if (param instanceof Term) {
										if (((Term) param).getType() == null) {
											((Term) param).setType(var.getType());
										}
									} else if (param instanceof Variable) {
										if (((Variable) param).getType() == null) {
											((Variable) param).setType(var.getType());
										}
									}
									params.add(param.toImplementation(null));
								} else {
									params.add(var.getName());
								}
							}
						}
						((Term) exp).replaceSubTerm(termEnt.getKey(), new Constant(langSpec.getConstructorInvocation(replacingClassName, params)));
						subTerms = ((Term) exp).getSubTerms(Term.class);
						termEntItr = subTerms.entrySet().iterator();
					} else {
						jsonTerm.setType(descendantType);
					}
				} else {
					Type oldType = jsonTerm.getType();
					Type newType = new Type(oldType.getTypeName(), 
											oldType.getImplementationTypeName().replace(replacedJsonType.getInterfaceTypeName(), replacingClassName), 
											oldType.getInterfaceTypeName().replace(replacedJsonType.getInterfaceTypeName(), replacingClassName));
					for (Type parent: oldType.getParentTypes()) {
						newType.addParentType(parent);
					}
					jsonTerm.setType(newType);
				}
			}
		}
		// Replace the type of the state field.
		Type fieldType = getImplStateType(resource, langSpec);
		if (exp instanceof Term) {
			((Term) exp).setType(fieldType);
			for (Map.Entry<Position, Variable> varEnt: ((Term) exp).getVariables().entrySet()) {
				if (varEnt.getValue().getName().equals(fieldOfResourceState)) {
					varEnt.getValue().setType(fieldType);
				}
			}
		} else if (exp instanceof Variable) {
			((Variable) exp).setType(fieldType);
		}
		String[] sideEffects = new String[] {""};
		String newState = exp.toImplementation(sideEffects);
		String updateStatement;
		if (exp instanceof Term && ((Term) exp).getSymbol().isImplWithSideEffect()) {
			updateStatement = sideEffects[0];	
			if (updateStatement.endsWith("\n")) {
				updateStatement = updateStatement.substring(0, updateStatement.length() - 1);
			}
		} else {
			updateStatement = sideEffects[0] + langSpec.getFieldAccessor(fieldOfResourceState) + langSpec.getAssignment() + newState + langSpec.getStatementDelimiter();
		}
		method.addFirstStatement(updateStatement);
	}

	private MethodDeclaration declareConstructor(ResourceNode resourceNode, TypeDeclaration component, Map<ResourceHierarchy, Set<ResourceHierarchy>> dependedRootComponentGraph, 
			List<ResourceHierarchy> depends, ILanguageSpecific langSpec) {
		// Declare a constructor in each component.
		String resourceName = getComponentName(resourceNode.getResourceHierarchy(), langSpec);
		MethodDeclaration constructor = langSpec.newMethodDeclaration(resourceName, true, null, null);
		Block block = new Block();
		constructor.setBody(block);
		component.addMethod(constructor);
		
		for (Edge resToCh: resourceNode.getOutEdges()) {
			DataTransferChannel ch = ((ChannelNode) resToCh.getDestination()).getChannel();
			// Check if the input resource is outside of the channel scope.
			boolean outsideInputResource = false;
			for (ChannelMember cm: ch.getInputChannelMembers()) {
				if (resourceNode.getOutSideResources().contains(cm.getResource()) && cm.isOutside()) {
					outsideInputResource = true;	// Regarded as pull transfer.
					break;
				}
			}
			for (Edge chToRes: resToCh.getDestination().getOutEdges()) {
				if (chToRes.getDestination() instanceof ResourceNode) {
					ResourceHierarchy dstRes = ((ResourceNode) chToRes.getDestination()).getResourceHierarchy();
					// Check if the output resource is outside of the channel scope.
					boolean outsideOutputResource = false;
					for (ChannelMember cm: ch.getOutputChannelMembers()) {
						if (((ResourceNode) chToRes.getDestination()).getInSideResources().contains(cm.getResource()) && cm.isOutside()) {
							outsideOutputResource = true;	// Regarded as push transfer.
							break;
						}
					}
					if ((((PushPullAttribute) ((DataFlowEdge) resToCh).getAttribute()).getSelectedOption() == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) {
						// for PUSH transfer
//						ResourceHierarchy dstRes = addReference(component, constructor, ((ResourceNode) chToRes.getDestination()).getOutSideResource().getResourceHierarchy(), langSpec);
//						if (outsideOutputResource) {
//							if (dstRes != null && dstRes.getParent() != null) {
//								// Reference to root resource.
//								addReference(component, constructor, dstRes.getRoot(), langSpec);
//							}
//						}
						if (!generatesComponent(dstRes)) {
							dstRes = dstRes.getParent();
						}
						if (!depends.contains(dstRes)) depends.add(dstRes);
					}
				}
			}
		}
		for (Edge chToRes: resourceNode.getInEdges()) {
			for (Edge resToCh: chToRes.getSource().getInEdges()) {
				ResourceHierarchy srcRes = ((ResourceNode) resToCh.getSource()).getResourceHierarchy();
				DataTransferChannel ch = ((ChannelNode) resToCh.getDestination()).getChannel();
				// Check if the input resource is outside of the channel scope.
				boolean outsideInputResource = false;
				for (ChannelMember cm: ch.getInputChannelMembers()) {
					if (((ResourceNode) resToCh.getSource()).getOutSideResources().contains(cm.getResource()) && cm.isOutside()) {
						outsideInputResource = true;	// Regarded as pull transfer.
						break;
					}
				}
				// Check if the output resource is outside of the channel scope.
				boolean outsideOutputResource = false;
				for (ChannelMember cm: ch.getOutputChannelMembers()) {
					if (resourceNode.getInSideResources().contains(cm.getResource()) && cm.isOutside()) {
						outsideOutputResource = true;	// Regarded as push transfer.
						break;
					}
				}
				if ((((PushPullAttribute) ((DataFlowEdge) resToCh).getAttribute()).getSelectedOption() != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) {
					// for PULL transfer
//					srcRes = addReference(component, constructor, ((ResourceNode) resToCh.getSource()).getOutSideResource().getResourceHierarchy(), langSpec);
//					if (outsideInputResource) {
//						if (srcRes != null & srcRes.getParent() != null) {
//							// Reference to root resource.
//							addReference(component, constructor, srcRes.getRoot(), langSpec);
//						}
//					}
					if (!generatesComponent(srcRes)) {
						srcRes = srcRes.getParent();
					}
					if (!depends.contains(srcRes)) depends.add(srcRes);
				}
			}
		}
		// Declare a field to refer to outside resources.
		if (resourceNode.getParent() == null) {
			for (ResourceHierarchy dependedRes: dependedRootComponentGraph.keySet()) {
				for (ResourceHierarchy dependingRes: dependedRootComponentGraph.get(dependedRes)) {
					if (resourceNode.getResourceHierarchy().equals(dependingRes)) {
						// Declare a field to refer to outside resources.
						depends.add(dependedRes);
						addReference(component, constructor, dependedRes, langSpec);
					}
				}
			}
		}
		return constructor;
	}
	
	private void declareStateField(ResourceNode resourceNode, String resourceName, TypeDeclaration component, Type resStateType, Map<ResourceHierarchy, Map<String, VariableDeclaration>> constructorParams, ILanguageSpecific langSpec) {
		Set<ResourceHierarchy> children = resourceNode.getResourceHierarchy().getChildren();
		if (children == null || children.size() == 0) {
			// leaf resource.
			FieldDeclaration stateField = langSpec.newFieldDeclaration(resStateType, fieldOfResourceState, langSpec.getFieldInitializer(resStateType, resourceNode.getResourceHierarchy().getInitialValue()));
			component.addField(stateField);
			Map<String, VariableDeclaration> nameToParam = constructorParams.get(resourceNode.getResourceHierarchy());
			if (nameToParam == null) {
				nameToParam = new HashMap<>();
				constructorParams.put(resourceNode.getResourceHierarchy(), nameToParam);
			}
			String varName = langSpec.toVariableName(resourceName);
			if (nameToParam.get(varName) == null) {
				nameToParam.put(varName, langSpec.newVariableDeclaration(resStateType, varName));
			}
		} else {
			ResourceHierarchy child = children.iterator().next();
			if (children.size() == 1 && child.getNumParameters() > 0) {
				// map or list.
				FieldDeclaration stateField = langSpec.newFieldDeclaration(resStateType, fieldOfResourceState, langSpec.getFieldInitializer(resStateType, resourceNode.getResourceHierarchy().getInitialValue()));
				component.addField(stateField);
			} else {
				// class
				for (ResourceHierarchy c: children) {
					String childTypeName = getComponentName(c, langSpec);
					Type childType = null;
					if (generatesComponent(c)) {
						// The child has a component.
						childType = new Type(childTypeName, childTypeName);
						String fieldName = langSpec.toVariableName(childTypeName);
						FieldDeclaration stateField = langSpec.newFieldDeclaration(childType, fieldName, langSpec.getConstructorInvocation(childTypeName, new ArrayList<>()));
						component.addField(stateField);
					}
				}
			}
		}
	}

	private boolean declareFieldsToReferToOtherResourcesAndStateFieldInParentComponent(ResourceNode resourceNode, TypeDeclaration component, TypeDeclaration parentComponent, 
			Map<ResourceHierarchy, Map<String, VariableDeclaration>> constructorParams, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) {
		// Declare reference fields for push data transfer.
		boolean noPullTransfer = true;
		boolean bDeclareClientField = false;
		for (Edge resToCh : resourceNode.getOutEdges()) {
			DataFlowEdge re = (DataFlowEdge) resToCh;
			ChannelNode directDstChNode = (ChannelNode) re.getDestination();
			DataTransferChannel directDstCh = directDstChNode.getChannel();
			// Check if the source resource is outside of the channel scope.
			boolean outsideInputResource = false;
			for (ChannelMember cm: directDstCh.getInputChannelMembers()) {
				if (cm.getResource().equals(resourceNode.getOutSideResource(directDstCh)) && cm.isOutside()) {
					outsideInputResource = true;	// Regarded as pull transfer.
					break;
				}
			}
			// Should take into account the channel hierarchy.
			Set<ChannelNode> ancestorDstChannels = directDstChNode.getAncestors();
			Set<ChannelNode> descendantDstChannels = directDstChNode.getDescendants();
			Set<Edge> outEdges = new HashSet<>();
			outEdges.addAll(directDstChNode.getOutEdges());
			for (ChannelNode ancestorDst: ancestorDstChannels) {
				outEdges.addAll(ancestorDst.getOutEdges());
			}
			for (ChannelNode descendantDst: descendantDstChannels) {
				outEdges.addAll(descendantDst.getOutEdges());
			}
			for (Edge chToRes: outEdges) {
				if (chToRes.getDestination() instanceof ResourceNode) {
					ResourceHierarchy dstRes = ((ResourceNode) chToRes.getDestination()).getResourceHierarchy();
					ChannelNode chNode = (ChannelNode) chToRes.getSource();
					DataTransferChannel ch = chNode.getChannel();
					// Check if the destination resource is outside of the channel scope.
					boolean outsideOutputResource = false;
					ChannelMember out = null;
					for (ChannelMember cm: ch.getOutputChannelMembers()) {
						if (((ResourceNode) chToRes.getDestination()).getInSideResources().contains(cm.getResource())) {
							out = cm;
							if (cm.isOutside()) {
								outsideOutputResource = true;	// Regarded as push transfer.
								break;
							}
						}
					}
					ResourcePath dstResPath = out.getResource();
					// Also take into account the channel hierarchy to determine push/pull transfer.
					if (descendantDstChannels.contains(chNode)) {
						outsideOutputResource = true;	// Regarded as (broadcasting) push transfer.
					}
					if (ancestorDstChannels.contains(chNode)) {
						outsideInputResource = true;	// Regarded as (collecting) pull transfer.
					}
					if ((((PushPullAttribute) re.getAttribute()).getSelectedOption() == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) {
						// Declare a field in the parent component to refer to the destination resource of push transfer.
						if (!generatesComponent(dstRes)) {
							dstRes = dstRes.getParent();
						}
						String dstResName = getComponentName(dstRes, langSpec);
						FieldDeclaration refFieldForPush = langSpec.newFieldDeclaration(new Type(dstResName, dstResName), langSpec.toVariableName(dstResName));
						VariableDeclaration refVarForPush = langSpec.newVariableDeclaration(new Type(dstResName, dstResName), langSpec.toVariableName(dstResName));
						if (!platformSpec.isMonolithic() 
								&& (outsideOutputResource || (resourceNode.getOutSideResource(ch).getCommonPrefix(dstResPath) == null && platformSpec.isDifferentTreesAsDifferentServices()))) {
							// Inter-service (for REST API)
							if (!bDeclareClientField && parentComponent != null) {
								// Declare a client field to connect to the destination resource of push transfer.
								((RestApiSpecific) platformSpec).addClientFieldDeclaration(parentComponent);
								bDeclareClientField = true;
							}
						} else {
							// Monolithic or Inner-service (for REST API)
							if (component != null) {
								// A component is created for this resource.
								if (resourceNode.getResourceHierarchy() != dstRes) {
									component.addField(refFieldForPush);
									if (!outsideOutputResource) {
										Map<String, VariableDeclaration> nameToParam = constructorParams.get(resourceNode.getResourceHierarchy());
										if (nameToParam == null) {
											nameToParam = new HashMap<>();
											constructorParams.put(resourceNode.getResourceHierarchy(), nameToParam);
										}
										if (nameToParam.get(dstResName) == null) {
											nameToParam.put(dstResName, refVarForPush);
										}
									}
								}
							} else {
								// No component is created for this resource.
								if (resourceNode.getParent().getResourceHierarchy() != dstRes && parentComponent != null) {
									parentComponent.addField(refFieldForPush);
									if (!outsideOutputResource) {
										Map<String, VariableDeclaration> nameToParam = constructorParams.get(resourceNode.getParent().getResourceHierarchy());
										if (nameToParam == null) {
											nameToParam = new HashMap<>();
											constructorParams.put(resourceNode.getParent().getResourceHierarchy(), nameToParam);
										}
										if (nameToParam.get(dstResName) == null) {
											nameToParam.put(dstResName, refVarForPush);
										}
									}
								}
							}
						}
						if (outsideOutputResource) {
							// When the reference to the destination resource can vary.
							if (dstRes.getParent() != null) {
								// Reference to its root resource.
								String dstRootResName = getComponentName(dstRes.getRoot(), langSpec);
								Type dstRootResType = new Type(dstRootResName, dstRootResName);
								dstRootResName = langSpec.toVariableName(dstRootResName);
								FieldDeclaration refRootFieldForPush = langSpec.newFieldDeclaration(dstRootResType, dstRootResName);
								VariableDeclaration refRootVarForPush = langSpec.newVariableDeclaration(dstRootResType, dstRootResName);
								if (component != null) {
									// A component is created for this resource.
									boolean existsField = false;
									for (FieldDeclaration field: component.getFields()) {
										if (dstRootResName.equals(field.getName())) {
											existsField = true;
											break;
										}
									}
									if (!existsField) {
										component.addField(refRootFieldForPush);
										Map<String, VariableDeclaration> nameToParam = constructorParams.get(resourceNode.getResourceHierarchy());
										if (nameToParam == null) {
											nameToParam = new HashMap<>();
											constructorParams.put(resourceNode.getResourceHierarchy(), nameToParam);
										}
										if (nameToParam.get(dstRootResName) == null) {
											nameToParam.put(dstRootResName, refRootVarForPush);
										}
									}
								} else {
									// No component is created for this resource.
									boolean existsField = false;
									for (FieldDeclaration field: parentComponent.getFields()) {
										if (dstRootResName.equals(field.getName())) {
											existsField = true;
											break;
										}
									}
									if (!existsField) {
										parentComponent.addField(refRootFieldForPush);
										Map<String, VariableDeclaration> nameToParam = constructorParams.get(resourceNode.getParent().getResourceHierarchy());
										if (nameToParam == null) {
											nameToParam = new HashMap<>();
											constructorParams.put(resourceNode.getParent().getResourceHierarchy(), nameToParam);
										}
										if (nameToParam.get(dstRootResName) == null) {
											nameToParam.put(dstRootResName, refRootVarForPush);
										}
									}
								}
							}
						}
					}
				}
			}
		}
		// Declare reference fields for pull data transfer.
		for (Edge chToRes : resourceNode.getInEdges()) {
			ChannelNode directSrcChNode = (ChannelNode) chToRes.getSource();
			DataTransferChannel directSrcCh = directSrcChNode.getChannel();
			// Should take into account the channel hierarchy.
			Set<ChannelNode> ancestorSrcChannels = directSrcChNode.getAncestors();
			Set<ChannelNode> descendantSrcChannels = directSrcChNode.getDescendants();
			Set<Edge> inEdges = new HashSet<>();
			inEdges.addAll(directSrcChNode.getInEdges());
			for (ChannelNode ancestorSrc: ancestorSrcChannels) {
				inEdges.addAll(ancestorSrc.getInEdges());
			}
			for (ChannelNode descendantSrc: descendantSrcChannels) {
				inEdges.addAll(descendantSrc.getInEdges());
			}
			for (Edge resToCh: inEdges) {
				DataFlowEdge re = (DataFlowEdge) resToCh;
				ChannelNode chNode = (ChannelNode) re.getDestination();
				DataTransferChannel ch = chNode.getChannel();
				ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(ch);
				// Check if the source resource is outside of the channel scope.
				boolean outsideInputResource = false;
				for (ChannelMember cm: ch.getInputChannelMembers()) {
					if (cm.getResource().equals(srcRes) && cm.isOutside()) {
						outsideInputResource = true;	// Regarded as pull transfer.
						break;
					}
				}
				// Check if the output resource is outside of the channel scope.
				boolean outsideOutputResource = false;
				for (ChannelMember cm: ch.getOutputChannelMembers()) {
					if (resourceNode.getInSideResources().contains(cm.getResource()) && cm.isOutside()) {
						outsideOutputResource = true;	// Regarded as push transfer.
						break;
					}
				}
				// Also take into account the channel hierarchy to determine push/pull transfer.
				if (descendantSrcChannels.contains(chNode)) {
					outsideInputResource = true;	// Regarded as (collecting) pull transfer.
				}
				if (ancestorSrcChannels.contains(chNode)) {
					outsideOutputResource = true;	// Regarded as (broadcasting) push transfer.
				}
				if ((((PushPullAttribute) re.getAttribute()).getSelectedOption() != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) {
					noPullTransfer = false;
					// Declare a field in the parent/this component to refer to the source resource of pull transfer.
					if (!generatesComponent(srcRes.getResourceHierarchy())) {
						srcRes = srcRes.getParent();
					}
					String srcResName = getComponentName(srcRes.getResourceHierarchy(), langSpec);
					FieldDeclaration refFieldForPull = langSpec.newFieldDeclaration(new Type(srcResName, srcResName), langSpec.toVariableName(srcResName));
					VariableDeclaration refVarForPull = langSpec.newVariableDeclaration(new Type(srcResName, srcResName), langSpec.toVariableName(srcResName));
					if (!platformSpec.isMonolithic() 
							&& (outsideInputResource || (resourceNode.getInSideResource(ch).getCommonPrefix(srcRes) == null && platformSpec.isDifferentTreesAsDifferentServices()))) {
						// Inter-service (for REST API)
						if (!bDeclareClientField && parentComponent != null) {
							// Declare a client field to connect to the destination resource of push transfer.
							((RestApiSpecific) platformSpec).addClientFieldDeclaration(parentComponent);
							bDeclareClientField = true;
						}
					} else {
						// Monolithic or Inner-service (for REST API)
						// Declare a field to directly refer to the source resource of pull transfer.
						if (component != null) {
							// A component is created for this resource.
							if (resourceNode.getResourceHierarchy() != srcRes.getResourceHierarchy()) {
								component.addField(refFieldForPull);
								if (!outsideInputResource) {
									Map<String, VariableDeclaration> nameToParam = constructorParams.get(resourceNode.getResourceHierarchy());
									if (nameToParam == null) {
										nameToParam = new HashMap<>();
										constructorParams.put(resourceNode.getResourceHierarchy(), nameToParam);
									}
									if (nameToParam.get(srcResName) == null) {
										nameToParam.put(srcResName, refVarForPull);
									}
								}
							}
						} else {
							// No component is created for this resource.
							if (resourceNode.getParent().getResourceHierarchy() != srcRes.getResourceHierarchy() && parentComponent != null) {
								parentComponent.addField(refFieldForPull);
								if (!outsideInputResource) {
									Map<String, VariableDeclaration> nameToParam = constructorParams.get(resourceNode.getParent().getResourceHierarchy());
									if (nameToParam == null) {
										nameToParam = new HashMap<>();
										constructorParams.put(resourceNode.getParent().getResourceHierarchy(), nameToParam);
									}
									if (nameToParam.get(srcResName) == null) {
										nameToParam.put(srcResName, refVarForPull);
									}
								}
							}
						}
					}
					if (outsideInputResource) {
						// When the reference to the source resource can vary.
						if (srcRes.getParent() != null) {
							// Reference to its root resource.
							String srcRootResName = getComponentName(srcRes.getRoot().getResourceHierarchy(), langSpec);
							Type srcRootResType = new Type(srcRootResName, srcRootResName);
							srcRootResName = langSpec.toVariableName(srcRootResName);
							FieldDeclaration refRootFieldForPull = langSpec.newFieldDeclaration(srcRootResType, srcRootResName);
							VariableDeclaration refRootVarForPull = langSpec.newVariableDeclaration(srcRootResType, srcRootResName);
							if (component != null) {
								// A component is created for this resource.
								boolean existsField = false;
								for (FieldDeclaration field: component.getFields()) {
									if (srcRootResName.equals(field.getName())) {
										existsField = true;
										break;
									}
								}
								if (!existsField) {
									component.addField(refRootFieldForPull);
									Map<String, VariableDeclaration> nameToParam = constructorParams.get(resourceNode.getResourceHierarchy());
									if (nameToParam == null) {
										nameToParam = new HashMap<>();
										constructorParams.put(resourceNode.getResourceHierarchy(), nameToParam);
									}
									if (nameToParam.get(srcRootResName) == null) {
										nameToParam.put(srcRootResName, refRootVarForPull);
									}
								}
							} else {
								// No component is created for this resource.
								boolean existsField = false;
								for (FieldDeclaration field: parentComponent.getFields()) {
									if (srcRootResName.equals(field.getName())) {
										existsField = true;
										break;
									}
								}
								if (!existsField) {
									parentComponent.addField(refRootFieldForPull);									
									Map<String, VariableDeclaration> nameToParam = constructorParams.get(resourceNode.getParent().getResourceHierarchy());
									if (nameToParam == null) {
										nameToParam = new HashMap<>();
										constructorParams.put(resourceNode.getParent().getResourceHierarchy(), nameToParam);
									}
									if (nameToParam.get(srcRootResName) == null) {
										nameToParam.put(srcRootResName, refRootVarForPull);
									}
								}
							}
						}
					}
				}
			}
		}
		// Declare the state field in the parent component.
		if (component == null) {
			ResourceHierarchy res = resourceNode.getResourceHierarchy();
			if (((StoreAttribute) resourceNode.getAttribute()).isStored() && noPullTransfer && res.getNumParameters() == 0) {
				String resName = langSpec.toVariableName(getComponentName(res, langSpec));
				boolean existsField = false;
				for (FieldDeclaration field: parentComponent.getFields()) {
					if (resName.equals(field.getName())) {
						existsField = true;
						break;
					}
				}
				if (!existsField) {
					FieldDeclaration stateField = langSpec.newFieldDeclaration(res.getResourceStateType(), resName);
					parentComponent.addField(stateField);
					Map<String, VariableDeclaration> nameToParam = constructorParams.get(resourceNode.getParent().getResourceHierarchy());
					if (nameToParam == null) {
						nameToParam = new HashMap<>();
						constructorParams.put(resourceNode.getParent().getResourceHierarchy(), nameToParam);
					}
					if (nameToParam.get(resName) == null) {
						nameToParam.put(resName, langSpec.newVariableDeclaration(res.getResourceStateType(), resName));
					}
				}
			}
		}
		return bDeclareClientField;
	}
	
	
	private MethodDeclaration declareStateGetterMethod(ResourceNode resourceNode, TypeDeclaration component, Type resStateType, 
			IPlatformSpecific platformSpec, ILanguageSpecific langSpec) {
		// Declare the getter method of the resource state.
		MethodDeclaration stateGetter = langSpec.newMethodDeclaration(getterOfResourceState, resStateType);
		if (!platformSpec.isMonolithic() && resourceNode.getResourceHierarchy().getParent() == null) {
			// Since this getter is also an accessor.
			((RestApiSpecific) platformSpec).addGetAnnotations(stateGetter);
		}
		component.addMethod(stateGetter);
		
		boolean hasDescendantIn = hasDescendantInput(resourceNode);
		if (((StoreAttribute) resourceNode.getAttribute()).isStored() && !hasDescendantIn) {
			fillStateGetterMethod(stateGetter, resourceNode.getResourceHierarchy(), resStateType, platformSpec, langSpec);
		} else {	
			// invocations to other getter methods when at least one incoming data-flow edges is PULL-style.
			addOtherGetterInvocationsToStateGatter(stateGetter, resourceNode, platformSpec, langSpec);
		}
		
		return stateGetter;
	}
	
	private MethodDeclaration declareStateGetterMethodInAncestor(ResourceNode resourceNode, Map<ResourceHierarchy, TypeDeclaration> resourceComponents, Type resStateType, 
			IPlatformSpecific platformSpec, ILanguageSpecific langSpec) {
		// Search an ancestor in which the getter method is declared.
		ResourceNode ancestorNode = resourceNode;
		ResourceNode childNode = null;
		Stack<ResourceNode> ancestors = new Stack<>();
		do {
			ancestors.push(ancestorNode);
			childNode = ancestorNode;
			ancestorNode = ancestorNode.getParent();
		} while (!generatesComponent(ancestorNode.getResourceHierarchy()));
		TypeDeclaration ancestorComponent = resourceComponents.get(ancestorNode.getResourceHierarchy());
		List<VariableDeclaration> getterParams = new ArrayList<>();
		int v = 1;
		while (ancestors.size() > 0) {
			ResourceNode curAncestor = ancestors.pop();
			Expression param = curAncestor.getPrimaryResourcePath().getLastParam();
			if (param instanceof Variable) {
				Variable var = (Variable) param;
				getterParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName()));
			} else if (param instanceof Term) {
				Term var = (Term) param;
				getterParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v));
			}
			v++;
		}
		// Check duplication.
		String getterName = getterPrefix + getComponentName(resourceNode.getResourceHierarchy(), langSpec);
		for (MethodDeclaration method: ancestorComponent.getMethods()) {
			if (method.getName().equals(getterName) 
					&& (method.getParameters() == null ? 0 : method.getParameters().size()) == getterParams.size()) return null;
		}
		
		// Declare the getter method of the resource state.
		MethodDeclaration stateGetter = null;
		if (getterParams.size() == 0) {
			stateGetter = langSpec.newMethodDeclaration(getterName, resStateType);
		} else {
			stateGetter = langSpec.newMethodDeclaration(getterName, false, resStateType, getterParams);
		}
		if (ancestorComponent != null) {
			ancestorComponent.addMethod(stateGetter);
		}
		
		boolean hasDescendantIn = hasDescendantInput(resourceNode);
		if (((StoreAttribute) resourceNode.getAttribute()).isStored() && !hasDescendantIn) {
			fillDescendantGetterMethod(stateGetter, resourceNode.getResourceHierarchy(), childNode.getResourceHierarchy(), ancestorNode.getResourceHierarchy(), ancestorComponent, langSpec);
		} else {	
			addOtherGetterInvocationsToStateGatter(stateGetter, resourceNode, platformSpec, langSpec);
		}
		
		return stateGetter;
	}

	private boolean hasDescendantInput(ResourceNode resourceNode) {
		boolean hasDescendantIn = false;
		outer: for (Edge chToRes: resourceNode.getInEdges()) {
			ChannelNode chNode = (ChannelNode) chToRes.getSource();
			Set<ChannelNode> descendantChannels = chNode.getDescendants();
			for (ChannelNode descendantCh: descendantChannels) {
				if (descendantCh.getIndegree() > 0) {
					hasDescendantIn = true;
					break outer;
				}
			}
		}
		return hasDescendantIn;
	}

	private void addOtherGetterInvocationsToStateGatter(MethodDeclaration stateGetter, ResourceNode resourceNode,
			IPlatformSpecific platformSpec, ILanguageSpecific langSpec) {
		try {
			// Data transfer on the same channel hierarchy.
			boolean isContainedPush = false;
			DataTransferChannel ch = null;
			DataTransferChannel ch2 = null;
			HashMap<ChannelMember, IResourceStateAccessor> inputResourceToStateAccessor = new HashMap<>();
			for (Edge chToRes: resourceNode.getInEdges()) {
				ch2 = ((ChannelNode) chToRes.getSource()).getChannel();
				for (Edge resToCh: chToRes.getSource().getInEdges()) {
					DataFlowEdge dIn = (DataFlowEdge) resToCh;
					ChannelMember in = null;
					for (ChannelMember cm: ch2.getInputChannelMembers()) {
						if (((ResourceNode) dIn.getSource()).getOutSideResources().contains(cm.getResource())) {
							in = cm;
							break;
						}
					}
					if (((PushPullAttribute) dIn.getAttribute()).getSelectedOption() == PushPullValue.PUSH) {
						// PUSH transfer
						isContainedPush = true;
						inputResourceToStateAccessor.put(in, getPushAccessor(platformSpec));
					} else {
						// PULL transfer
						inputResourceToStateAccessor.put(in, getPullAccessor(platformSpec));
						ch = ((ChannelNode) resToCh.getDestination()).getChannel();		// pull containing input side channel is at most one.
						if (!platformSpec.isMonolithic() 
								&& !in.isOutside() 
								&& in.getResource().getCommonPrefix(resourceNode.getInSideResource(ch2)) == null 
								&& platformSpec.isDifferentTreesAsDifferentServices()) {
							// for REST API
							ResourcePath srcResPath = in.getResource();
							String srcResourceName = langSpec.toVariableName(getComponentName(srcResPath.getResourceHierarchy(), langSpec));
							Type srcResourceType = srcResPath.getResourceStateType();
							List<String> pathParams = new ArrayList<>();
							for (Expression pathExp: srcResPath.getPathParams()) {
								String[] sideEffects = new String[] {""};
								pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \"");
							}
							generatePullDataTransfer(stateGetter, srcResourceName, 
													 srcResPath.getResourceHierarchy().toResourcePath(pathParams), srcResourceType, 
													 true, platformSpec, langSpec);
						}
					}
				}
			}
			if (ch == null) {
				ch = ch2;
			}
			ChannelMember out = null;
			for (ChannelMember cm: ch.getOutputChannelMembers()) {
				if (resourceNode.getInSideResources().contains(cm.getResource())) {
					out = cm;
					break;
				}
			}
			
			// for reference channel members.
			ResourcePath dstResPath = resourceNode.getInSideResource(ch);
			for (ChannelMember rc: ch.getReferenceChannelMembers()) {
				inputResourceToStateAccessor.put(rc, getPullAccessor(platformSpec));			// by pull data transfer
				ResourcePath refResPath = rc.getResource();
				if (!platformSpec.isMonolithic() 
						&& (rc.isOutside() 
								|| (refResPath.getCommonPrefix(dstResPath) == null && platformSpec.isDifferentTreesAsDifferentServices()))) {
					// for REST API
					String refResourceName = langSpec.toVariableName(getComponentName(refResPath.getResourceHierarchy(), langSpec));
					Type refResourceType = refResPath.getResourceStateType();
					List<String> pathParams = new ArrayList<>();
					for (Expression pathExp: refResPath.getPathParams()) {
						String[] sideEffects = new String[] {""};
						pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \"");
					}
					generatePullDataTransfer(stateGetter, refResourceName, 
											 refResPath.getResourceHierarchy().toResourcePath(pathParams), refResourceType,
											 true, platformSpec, langSpec);
				}
			}
			
			// Construct the base message.
			Map.Entry<Map<ChannelMember, Entry<ResourcePath, Set<ChannelMember>>>, Term> resourcePathsAndMessage;
			if (!isContainedPush) {
				// All incoming edges are in PULL-style.
				resourcePathsAndMessage = ch.fillOutsideResourcePaths(out, getPullAccessor(platformSpec), null);
			} else {
				// At least one incoming edge is in PUSH-style.
				resourcePathsAndMessage = ch.fillOutsideResourcePaths(out, getPullAccessor(platformSpec), inputResourceToStateAccessor);
			}
			Map<ChannelMember, Entry<ResourcePath, Set<ChannelMember>>> resourcePaths = resourcePathsAndMessage.getKey();
			Term messageTerm = resourcePathsAndMessage.getValue();
			
			// Data transfer from path depending resource.
			for (Entry<ChannelMember, Entry<ResourcePath, Set<ChannelMember>>> pathEnt: resourcePaths.entrySet()) {
				ChannelMember cm = pathEnt.getKey();
				ResourcePath srcResPath = pathEnt.getValue().getKey();
				// get outside srcResPath resource state by pull data transfer.
				if (!platformSpec.isMonolithic() 
						&& (cm.isOutside() 
								|| (srcResPath.getCommonPrefix(dstResPath)) == null && platformSpec.isDifferentTreesAsDifferentServices())) {
					// for REST API
					// Data transfer from an outside input resource is regarded as PULL transfer.
					List<String> pathParams = new ArrayList<>();
					for (Expression pathExp: srcResPath.getPathParams()) {
						String[] sideEffects = new String[] {""};
						pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \"");
					}
					// generate a pull data transfer from a depending in/ref resource.
					Type srcResourceType = srcResPath.getResourceStateType();
					String srcResName2 = langSpec.toVariableName(getComponentName(srcResPath.getResourceHierarchy(), langSpec));
					String srcPath2 = srcResPath.toString().replaceAll("\\{", "\"+").replaceAll("\\}", "+\"");
					generatePullDataTransfer(stateGetter, srcResName2, srcPath2, srcResourceType, false, platformSpec, langSpec);
				}
			}
			
			// Data transfer from the descendant channel hierarchies.
			Stack<Iterator<Channel>> channelItrStack = new Stack<>();
			DataTransferChannel curChannel = ch;
			if (curChannel.getChildren() != null && curChannel.getChildren().size() > 0) {
				// retrieve descendant channels recursively.
				Iterator<Channel> chItr = curChannel.getChildren().iterator();
				do {
					if (!chItr.hasNext()) {
						chItr = channelItrStack.pop();
					} else {
						curChannel = (DataTransferChannel) chItr.next();												
						// generate pull data transfers.
						Set<ChannelMember> chMems = new HashSet<>(curChannel.getInputChannelMembers());
						chMems.addAll(curChannel.getReferenceChannelMembers());
						for (ChannelMember cm2: chMems) {
							if (resourcePaths == null || !resourcePaths.keySet().contains(cm2)) {
								// not a depending channel member.
								ResourcePath src2 = cm2.getResource();
								Type srcResType2 = src2.getResourceStateType();
								String srcResName2 = langSpec.toVariableName(getComponentName(src2.getResourceHierarchy(), langSpec));
								if (platformSpec.isMonolithic()) {
									String srcGetter = getPullAccessor(platformSpec).getDirectStateAccessorFor(src2, resourceNode.getInSideResource(curChannel)).toImplementation(new String[] {});
									stateGetter.addStatement(langSpec.getVariableDeclaration(srcResType2.getInterfaceTypeName(), srcResName2) 
											+ langSpec.getAssignment() + srcGetter + langSpec.getStatementDelimiter());
								} else {
									String srcPath2 = src2.toString().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\"");
									generatePullDataTransfer(stateGetter, srcResName2, srcPath2, srcResType2, false, platformSpec, langSpec);
								}
							} else {
								// a depending channel member.
								ResourcePath src2 = resourcePaths.get(cm2).getKey();
								// get outside src2 resource state by pull data transfer.
								if (cm2.isOutside() || src2.getCommonPrefix(resourceNode.getInSideResource(curChannel)) == null) {
									// generate a pull data transfer from a depending in/ref resource.
									Type srcResType2 = src2.getResourceStateType();
									String srcResName2 = langSpec.toVariableName(getComponentName(src2.getResourceHierarchy(), langSpec));
									if (platformSpec.isMonolithic()) {
										String dependingGetter = getPullAccessor(platformSpec).getDirectStateAccessorFor(src2, resourceNode.getInSideResource(curChannel)).toImplementation(new String[] {});
										stateGetter.addStatement(langSpec.getVariableDeclaration(srcResType2.getInterfaceTypeName(), srcResName2)
												+ langSpec.getAssignment() + dependingGetter + langSpec.getStatementDelimiter());
									} else {
										String srcPath2 = src2.toString().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\"");
										generatePullDataTransfer(stateGetter, srcResName2, srcPath2, srcResType2, false, platformSpec, langSpec);
									}
								}
							}
						}
						// collect the message constraints by a descendant channel.
						List<Variable> varsForSideEffects = new ArrayList<>();
						int v = 0;
						resourcePathsAndMessage = curChannel.fillOutsideResourcePaths(out, getPullAccessor(platformSpec), null);
						if (resourcePathsAndMessage != null) {
							resourcePaths = resourcePathsAndMessage.getKey();
							Term messageTermSub = resourcePathsAndMessage.getValue();
							for (Entry<Position, Field> fieldEnt: ((Term) messageTermSub).getSubTerms(Field.class).entrySet()) {
								Position pos = fieldEnt.getKey();
								Field field = fieldEnt.getValue();
								Variable var = new Variable(field.getSymbol().getName(), field.getType());
								((Term) messageTermSub).replaceSubTerm(pos, var);
							}
							for (Map.Entry<Position, Term> subTermEnt: messageTermSub.getSubTerms(Term.class).entrySet()) {
								Term subTerm = subTermEnt.getValue();
								if (!(subTerm instanceof Constant) && subTerm.getSymbol().isImplWithSideEffect()) {
									Variable var = new Variable("v" + v, subTerm.getType());
									varsForSideEffects.add(var);
									v++;
									// Add a side effect statement within the loop
									Position pos = new Position();
									pos.addHeadOrder(0);
									subTerm.replaceSubTerm(pos, var);
									String[] sideEffects = new String[] {""};
									String curState = messageTermSub.toImplementation(sideEffects);
									stateGetter.addStatement(sideEffects[0].replaceAll("\n", ""));
									// Cancel the side effects in the return value.
									pos = subTermEnt.getKey();
									messageTermSub.replaceSubTerm(pos, var);
								}
							}
							if (messageTerm == null) {
								messageTerm = messageTermSub;
							} else {
								messageTerm = (Term) messageTerm.unify(messageTermSub);
							}
							if (messageTerm == null) {
								throw new UnificationFailed();
							}
						}
						// enclosed by a for loop (for data collecting pull transfer) 
						Expression selExp = curChannel.getSelectors().get(0).getExpression();
						Type selType = null;
						String forVarName = null;
						if (selExp instanceof Variable) {
							selType = ((Variable) selExp).getType();
							forVarName = ((Variable) selExp).getName();
							ChannelMember insideChMem = null;
							for (ChannelMember cm2 :curChannel.getInputChannelMembers()) {
								if (!cm2.isOutside()) {
									insideChMem = cm2;
									break;
								}
							}
							if (insideChMem == null) {
								for (ChannelMember cm2 :curChannel.getReferenceChannelMembers()) {
									if (!cm2.isOutside()) {
										insideChMem = cm2;
										break;
									}
								}
							}
							ResourcePath insideResPath = insideChMem.getResource();
							while (insideResPath.getParent() != null && (insideResPath.getLastParam() == null || !insideResPath.getLastParam().equals(selExp))) {
								insideResPath = insideResPath.getParent();
							}
							insideResPath = insideResPath.getParent();
							if (insideResPath != null) {
								String parent = null;
								if (platformSpec.isMonolithic() 
										|| insideResPath.getCommonPrefix(dstResPath) != null 
										|| !platformSpec.isDifferentTreesAsDifferentServices()) {
									if (!platformSpec.isMonolithic() && generatesComponent(insideResPath.getResourceHierarchy())) {
										Expression parentGetter = getPullAccessor(platformSpec).getDirectStateAccessorFor(insideResPath, dstResPath);
										Term valueGetter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD));
										valueGetter.addChild(parentGetter);
										parent = valueGetter.toImplementation(new String[] {});
									} else {
										parent = getPullAccessor(platformSpec).getDirectStateAccessorFor(insideResPath, dstResPath).toImplementation(new String[] {});
									}
								} else {
									// for REST API
									parent = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(insideResPath.getResourceHierarchy()));
								}
								if (selType.equals(DataConstraintModel.typeInt)) {
									// make a for loop (for a list) for data collecting.
									stateGetter.addFirstStatement(langSpec.getForStatementForList(forVarName, parent));
								} else if (selType.equals(DataConstraintModel.typeString)) {
									// make a for loop (for a map) for data collecting.
									stateGetter.addFirstStatement(langSpec.getForStatementForMap(forVarName, DataConstraintModel.typeString.getInterfaceTypeName(), parent));
								}
								if (!platformSpec.isMonolithic() 
										&& insideResPath.getCommonPrefix(dstResPath) == null 
										&& platformSpec.isDifferentTreesAsDifferentServices()) {
									// for REST API
									Type parentResType = insideResPath.getResourceStateType();
									String parentResName = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(insideResPath.getResourceHierarchy()));
									String parentResPath = insideResPath.toString().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\"");
									generatePullDataTransfer(stateGetter, parentResName, parentResPath, parentResType, true, platformSpec, langSpec);
								}
							}
						}
						// initialize the variables to hold side effects within the loop
						for (Variable var: varsForSideEffects) {
							stateGetter.addFirstStatement(langSpec.getVariableDeclaration(var.getType().getInterfaceTypeName(), var.getName())
									+ langSpec.getAssignment() + langSpec.getConstructorInvocation(var.getType().getImplementationTypeName(), null) + langSpec.getStatementDelimiter());
						}
						// end of the loop
						stateGetter.addStatement("}");
						if (curChannel.getChildren() != null && curChannel.getChildren().size() > 0) {
							channelItrStack.push(chItr);
							chItr = curChannel.getChildren().iterator();
						}
					}
				} while (!channelItrStack.isEmpty());
			}
			
			// generate a return statement.
			String[] sideEffects = new String[] {""};
			String curState = ch.deriveUpdateExpressionOf(out, messageTerm, getPullAccessor(platformSpec)).toImplementation(sideEffects);
			stateGetter.addStatement(sideEffects[0] + langSpec.getReturnStatement(curState) + langSpec.getStatementDelimiter());
		} catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork
				| InvalidMessage | UnificationFailed | ValueUndefined e) {
			e.printStackTrace();
		}
	}
	
	private void declareDescendantGetterMethods(ResourceNode resourceNode, TypeDeclaration component, Map<ResourceHierarchy, Set<ResourceHierarchy>> descendantGetters, ILanguageSpecific langSpec) {
		// Declare the getter methods in this resource to obtain descendant resources.
		Set<ResourceHierarchy> descendants = descendantGetters.get(resourceNode.getResourceHierarchy());
		if (descendants == null) {
			descendants = new HashSet<>();
			descendantGetters.put(resourceNode.getResourceHierarchy(), descendants);
		}
		for (ResourceNode child: resourceNode.getChildren()) {
			// A descendant of the child may generate a component.
			List<VariableDeclaration> params = new ArrayList<>();
			int v = 1;
			ResourceNode descendant = child;
			Set<ResourceNode> childNodes;
			do {
				Expression param = descendant.getPrimaryResourcePath().getLastParam();
				if (param != null) {
					if (param instanceof Variable) {
						Variable var = (Variable) param;
						params.add(langSpec.newVariableDeclaration(var.getType(), var.getName()));
					} else if (param instanceof Term) {
						Term var = (Term) param;
						params.add(langSpec.newVariableDeclaration(var.getType(), "v" + v));
					}
					v++;
				}
				if (generatesComponent(descendant.getResourceHierarchy())) {
					// If the descendant generates a component.
					if (!descendants.contains(descendant.getResourceHierarchy())) {
						descendants.add(descendant.getResourceHierarchy());
						String descendantCompName = getComponentName(descendant.getResourceHierarchy(), langSpec);
						Type descendantType = new Type(descendantCompName, descendantCompName);
						MethodDeclaration descendantGetter = null;
						if (params.size() == 0) {
							descendantGetter = langSpec.newMethodDeclaration(getterPrefix + descendantCompName, descendantType);
						} else {
							descendantGetter = langSpec.newMethodDeclaration(getterPrefix + descendantCompName, false, descendantType, params);
						}
						
						fillDescendantGetterMethod(descendantGetter, descendant.getResourceHierarchy(), child.getResourceHierarchy(), resourceNode.getResourceHierarchy(), component, langSpec);
						component.addMethod(descendantGetter);
					}
					break;
				}
				childNodes = descendant.getChildren();
			} while (childNodes != null && childNodes.size() == 1 && (descendant = childNodes.iterator().next()) != null);
		}
	}
	
	private Map.Entry<List<String>, Map<MethodDeclaration, Map.Entry<Expression, Map.Entry<ResourceHierarchy, ResourceHierarchy>>>> declareCacheFieldsAndUpdateMethods(ResourceNode resourceNode, 
			TypeDeclaration component, TypeDeclaration parentComponent, TypeDeclaration rootComponent, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) {
		// Declare cash fields and update methods in the component.
		String resComponentName = getComponentName(resourceNode.getResourceHierarchy(), langSpec);
		List<String> constructorStatements = new ArrayList<>();
		Map<MethodDeclaration, Map.Entry<Expression, Map.Entry<ResourceHierarchy, ResourceHierarchy>>> updateStatements = new HashMap<>();
		for (Edge chToRes: resourceNode.getInEdges()) {
			ChannelNode directSrcChannel = (ChannelNode) chToRes.getSource();
			DataTransferChannel ch = directSrcChannel.getChannel();
			// Should take into account the channel hierarchy.
			Set<ChannelNode> ancestorSrcChannels = directSrcChannel.getAncestors();
			Set<ChannelNode> descendantSrcChannels = directSrcChannel.getDescendants();
			Set<Edge> inEdges = new HashSet<>();
			inEdges.addAll(directSrcChannel.getInEdges());
			for (ChannelNode ancestorSrc: ancestorSrcChannels) {
				inEdges.addAll(ancestorSrc.getInEdges());
			}
			for (ChannelNode descendantSrc: descendantSrcChannels) {
				inEdges.addAll(descendantSrc.getInEdges());
			}
			for (Edge resToCh: inEdges) {
				// For each data transfer from srcResPath:ResourcePath to resourceNode:ResourceNode.
				DataFlowEdge re = (DataFlowEdge) resToCh;
				ChannelNode indirectSrcChNode = (ChannelNode) re.getDestination();
				DataTransferChannel indirectSrcCh = indirectSrcChNode.getChannel();
				ResourcePath srcResPath = ((ResourceNode) re.getSource()).getOutSideResource(indirectSrcCh);
				String srcResComponentName = getComponentName(srcResPath.getResourceHierarchy(), langSpec);
				String srcResName = langSpec.toVariableName(srcResComponentName);
				// Check if the input resource is outside of the channel scope.
				boolean outsideInputResource = false;
				for (ChannelMember cm: indirectSrcCh.getInputChannelMembers()) {
					if (cm.getResource().equals(srcResPath) && cm.isOutside()) {
						outsideInputResource = true;	// Regarded as pull transfer.
						break;
					}
				}
				// Check if the output resource is outside of the channel scope.
				boolean outsideOutputResource = false;
				ChannelMember out = null;
				ResourcePath dstResPath = null;
				for (ChannelMember cm: ch.getOutputChannelMembers()) {
					if (resourceNode.getInSideResources().contains(cm.getResource())) {
						out = cm;
						dstResPath = cm.getResource();
						if (cm.isOutside()) {
							outsideOutputResource = true;	// Regarded as push transfer.
							break;
						}
					}
				}
				// Also take into account the channel hierarchy to determine push/pull transfer.
				if (ancestorSrcChannels.contains(indirectSrcChNode)) {
					outsideOutputResource = true;	// Regarded as (broadcasting) push transfer.
				}
				if (descendantSrcChannels.contains(indirectSrcChNode)) {
					outsideInputResource = true;	// Regarded as (collecting) pull transfer.
				}
				if ((((PushPullAttribute) re.getAttribute()).getSelectedOption() == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) {
					// For push data transfer
					
					boolean hasRestAPI = false;
					boolean isRestAPI = false;
					if (!platformSpec.isMonolithic() 
							&& (outsideOutputResource || (resourceNode.getInSideResource(ch).getCommonPrefix(srcResPath) == null && platformSpec.isDifferentTreesAsDifferentServices()))) {
						// Inter-service
						hasRestAPI = true;
						if (resourceNode.getParent() == null) {
							// A root resource
							isRestAPI = true;
						}
					}
					// Declare an update method in the type of the destination resource.
					ArrayList<VariableDeclaration> parameters = new ArrayList<>();
					getUpdateResourcePathAndPathParams(dstResPath, parameters, isRestAPI, platformSpec, langSpec);		// Path parameters to identify the self resource.
					for (Selector selector: ch.getAllSelectors()) {
						if (selector.getExpression() instanceof Variable) {
							Variable selVar = (Variable) selector.getExpression();
							VariableDeclaration chParam = langSpec.newVariableDeclaration(selVar.getType(), selVar.getName());
							if (isRestAPI) ((RestApiSpecific) platformSpec).addFormParamAnnotation(chParam, selVar.getName());
							parameters.add(chParam);	// A channel parameter to specify the context of the collaboration.
						}
					}
					VariableDeclaration param = langSpec.newVariableDeclaration(srcResPath.getResourceStateType(), srcResName);
					if (isRestAPI) ((RestApiSpecific) platformSpec).addFormParamAnnotation(param, srcResName);
					parameters.add(param);			// The state of the source resource to carry the data-flow.			
					// For the refs.
					for (ResourcePath ref: ch.getReferenceResources()) {
						if (!resourceNode.getInSideResources().contains(ref)) {
							String refName = langSpec.toVariableName(getComponentName(ref.getResourceHierarchy(), langSpec));
							param = langSpec.newVariableDeclaration(ref.getResourceStateType(), refName);
							if (isRestAPI) ((RestApiSpecific) platformSpec).addFormParamAnnotation(param, refName);
							parameters.add(param);
						}
					}
					MethodDeclaration update = null;
					if (component != null) {
						for (MethodDeclaration method: component.getMethods()) {
							if (method.getName().equals(updateMethodPrefix + from + srcResComponentName)) {
								update = method;
								break;
							}
						}
						if (update == null) {
							update = langSpec.newMethodDeclaration(updateMethodPrefix + from + srcResComponentName, false, null, parameters);
						}
					} else if (parentComponent != null) {
						for (MethodDeclaration method: parentComponent.getMethods()) {
							if (method.getName().equals(updateMethodPrefix + resComponentName + from + srcResComponentName)) {
								update = method;
								break;
							}
						}
						if (update == null) {
							update = langSpec.newMethodDeclaration(updateMethodPrefix + resComponentName + from + srcResComponentName, false, null, parameters);
						}
					}
					// Calculate in-degree (PUSH transfer) of the destination resource.
					int inDegree = 0;
					for (Edge resToCh2: inEdges) {
						DataFlowEdge df =(DataFlowEdge) resToCh2;
						if (((PushPullAttribute) df.getAttribute()).getSelectedOption() == PushPullValue.PUSH) {
							inDegree++;
						}
					}
					if (isRestAPI) {
						// Determine whether the update method is put or post or delete.
						if (isPut(out)) {
							((RestApiSpecific) platformSpec).addPutAnnotations(update);
						} else {
							if (!isDelete(out)) {
								((RestApiSpecific) platformSpec).addPostAnnotations(update);
							} else {
								((RestApiSpecific) platformSpec).addDeleteAnnotations(update);
							}
						}
						if (inDegree > 1) {
							// If incoming edges are multiple, then a child resource for each source resource is defined in the destination resource so that its state can be updated separately.
							if (isRestAPI) ((RestApiSpecific) platformSpec).addPathAnnotation(update, "/" + srcResName);
						}
					}
					if (update != null) {
						if (component != null) {
							// A component is created for this resource.
							component.addMethod(update);
						} else if (parentComponent != null) {
							// No component is created for this resource.
							parentComponent.addMethod(update);
						}
					}
					// For a post/put REST API.
					if (hasRestAPI) {
						if (!isRestAPI) {
							// If not a root resource.
							// Declare an update accessor method in the type of root resource. 
							declareUpdateAccessorInTheRootResource(resourceNode, update.getName(), ch, out, srcResPath, dstResPath,
									rootComponent, inDegree, platformSpec, langSpec);
						}
						// to convert a json param to a tuple, pair or map object.
						for (VariableDeclaration jsonParam: update.getParameters()) {
							Type paramType = jsonParam.getType();
							String paramName = jsonParam.getName();
							String paramTypeName = paramType.getInterfaceTypeName();
							String strTypeName = DataConstraintModel.typeString.getInterfaceTypeName();
							String paramConverter = "";
							if (DataConstraintModel.typeList.isAncestorOf(paramType) && paramType != DataConstraintModel.typeList) {
								Type compType = TypeInference.getListComponentType(paramType);
								if (DataConstraintModel.typeTuple.isAncestorOf(compType)) {
									jsonParam.setType(DataConstraintModel.typeListStr);
									jsonParam.setName(paramName + "_json");
									paramConverter += langSpec.getVariableDeclaration(paramTypeName, paramName) + langSpec.getAssignment() + langSpec.getConstructorInvocation(paramType.getImplementationTypeName(), null) + langSpec.getStatementDelimiter() + "\n";
									paramConverter += langSpec.getForStatementForCollection("str", strTypeName, jsonParam.getName()) + "\n";
									String mapTypeName = convertFromEntryToMapType(compType, langSpec);
									paramConverter += "\t" + ((RestApiSpecific) platformSpec).getConversionFromJsonString("str", "i", mapTypeName) + langSpec.getStatementDelimiter() + "\n";
									paramConverter += "\t" + langSpec.getMethodInvocation(paramName, DataConstraintModel.append.getImplName(), List.of(getCodeForConversionFromMapToTuple(compType, "i", langSpec))) + langSpec.getStatementDelimiter() + "\n";
									paramConverter += langSpec.getEndForStatement("str");
									((RestApiSpecific) platformSpec).addJsonException(update);
								} else if (DataConstraintModel.typePair.isAncestorOf(compType)) {
									jsonParam.setType(DataConstraintModel.typeListStr);
									jsonParam.setName(paramName + "_json");
									paramConverter += langSpec.getVariableDeclaration(paramTypeName, paramName) + langSpec.getAssignment() + langSpec.getConstructorInvocation(paramType.getImplementationTypeName(), null) + langSpec.getStatementDelimiter() + "\n";
									paramConverter += langSpec.getForStatementForCollection("str", strTypeName, jsonParam.getName()) + "\n";
									String mapTypeName = convertFromEntryToMapType(compType, langSpec);
									paramConverter += "\t" + ((RestApiSpecific) platformSpec).getConversionFromJsonString("str", "i", mapTypeName) + langSpec.getStatementDelimiter() + "\n";
									paramConverter += "\t" + langSpec.getMethodInvocation(paramName, DataConstraintModel.append.getImplName(), List.of(getCodeForConversionFromMapToPair(compType, "i", langSpec))) + langSpec.getStatementDelimiter() + "\n";
									paramConverter += langSpec.getEndForStatement("str");
									((RestApiSpecific) platformSpec).addJsonException(update);
								} else if (DataConstraintModel.typeMap.isAncestorOf(compType)) {
									jsonParam.setType(DataConstraintModel.typeListStr);
									// To do.
								}
							} else if (DataConstraintModel.typeTuple.isAncestorOf(paramType)) {
								jsonParam.setType(DataConstraintModel.typeString);
								jsonParam.setName(paramName + "_json");
								paramConverter += langSpec.getVariableDeclaration(paramTypeName, paramName) + langSpec.getStatementDelimiter() + "\n";
								paramConverter += langSpec.getOpeningScoreDelimiter() + "\n";
								String mapTypeName = convertFromEntryToMapType(paramType, langSpec);
								paramConverter += "\t" + ((RestApiSpecific) platformSpec).getConversionFromJsonString(paramName + "_json", "i", mapTypeName) + langSpec.getStatementDelimiter() + "\n";
								paramConverter += "\t" + paramName + langSpec.getAssignment() + getCodeForConversionFromMapToTuple(paramType, "i", langSpec) + langSpec.getStatementDelimiter() + "\n";
								paramConverter += langSpec.getClosingScoreDelimiter();
								((RestApiSpecific) platformSpec).addJsonException(update);
							} else if (DataConstraintModel.typePair.isAncestorOf(paramType)) {
								jsonParam.setType(DataConstraintModel.typeString);
								jsonParam.setName(paramName + "_json");
								paramConverter += langSpec.getVariableDeclaration(paramTypeName, paramName) + langSpec.getStatementDelimiter() + "\n";
								paramConverter += langSpec.getOpeningScoreDelimiter() + "\n";
								String mapTypeName = convertFromEntryToMapType(paramType, langSpec);
								paramConverter += "\t" + ((RestApiSpecific) platformSpec).getConversionFromJsonString(paramName + "_json", "i", mapTypeName) + langSpec.getStatementDelimiter() + "\n";
								paramConverter += "\t" + paramName + langSpec.getAssignment() + getCodeForConversionFromMapToPair(paramType, "i", langSpec) + langSpec.getStatementDelimiter() + "\n";
								paramConverter += langSpec.getClosingScoreDelimiter();
								((RestApiSpecific) platformSpec).addJsonException(update);
							} else if (DataConstraintModel.typeMap.isAncestorOf(paramType)) {
								jsonParam.setType(DataConstraintModel.typeString);
								jsonParam.setName(paramName + "_json");
								paramConverter += langSpec.getVariableDeclaration(paramTypeName, paramName) + langSpec.getAssignment() + langSpec.getConstructorInvocation(paramType.getImplementationTypeName(), null) + langSpec.getStatementDelimiter() + "\n";
								paramConverter += langSpec.getOpeningScoreDelimiter() + "\n";
								String mapTypeName = convertFromEntryToMapType(paramType, langSpec);
								paramConverter += "\t" + ((RestApiSpecific) platformSpec).getConversionFromJsonString(paramName + "_json", "i", mapTypeName) + langSpec.getStatementDelimiter() + "\n";
								paramConverter += "\t" + getCodeForConversionFromMapToMap(paramType, "i", paramName, langSpec) + "\n";
								paramConverter += langSpec.getClosingScoreDelimiter();
								((RestApiSpecific) platformSpec).addJsonException(update);
							}
							if (paramConverter.length() > 0 && !update.getBody().getStatements().contains(paramConverter)) {
								update.addFirstStatement(paramConverter);
							}
						}
					}
					
					// Add a statement to update the state field
					if (((StoreAttribute) resourceNode.getAttribute()).isStored()) {
						try {
							if (resourceNode.getInSideResources().contains(out.getResource())) {
								Expression updateExp = null;
								if (ch.getReferenceChannelMembers().size() == 0) {
									updateExp = ch.deriveUpdateExpressionOf(out, getPushAccessor(platformSpec)).getKey();
								} else {
									// if there exists one or more reference channel member.
									HashMap<ChannelMember, IResourceStateAccessor> inputResourceToStateAccessor = new HashMap<>();
									for (ChannelMember in: ch.getInputChannelMembers()) {
										inputResourceToStateAccessor.put(in, getPushAccessor(platformSpec));
									}
									for (ChannelMember c: ch.getReferenceChannelMembers()) {
										inputResourceToStateAccessor.put(c, getRefAccessor(platformSpec));
									}
									updateExp = ch.deriveUpdateExpressionOf(out, getPushAccessor(platformSpec), inputResourceToStateAccessor).getKey();
								}
								// Replace Json constructor with a constructor of the child resource.
								ResourceHierarchy outRes = out.getResource().getResourceHierarchy();
								if (outRes.getChildren().size() == 1 && outRes.getChildren().iterator().next().getNumParameters() > 0) {
									ResourceHierarchy descendantRes = outRes;
									Set<ResourceHierarchy> children = descendantRes.getChildren();
									do {
										descendantRes = children.iterator().next();
										if (generatesComponent(descendantRes)) {
											updateStatements.put(update, new AbstractMap.SimpleEntry<>(updateExp, new AbstractMap.SimpleEntry<>(outRes, descendantRes)));
											updateExp = null;
											break;
										}
										children = descendantRes.getChildren();
									} while (children != null && children.size() == 1);
								}
								// Replace the type of the state field.
								Type fieldType = getImplStateType(outRes, langSpec);
								if (updateExp instanceof Term) {
									((Term) updateExp).setType(fieldType);
									for (Map.Entry<Position, Variable> varEnt: ((Term) updateExp).getVariables().entrySet()) {
										if (varEnt.getValue().getName().equals(fieldOfResourceState)) {
											varEnt.getValue().setType(fieldType);
										}
									}
								} else if (updateExp instanceof Variable) {
									((Variable) updateExp).setType(fieldType);
								}
								// Add statements to the update method.
								String[] sideEffects = new String[] {""};
								String newState = updateExp.toImplementation(sideEffects);
								int numOfOutResourcesWithTheSameHierarchy = 0;
								for (ResourcePath outResPath: ch.getOutputResources()) {
									if (outResPath.getResourceHierarchy().equals(outRes)) {
										numOfOutResourcesWithTheSameHierarchy++;
									}
								}
								String updateStatement = "";
								if (generatesComponent(outRes)) {
									if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) {
										updateStatement = sideEffects[0];
										if (updateStatement.endsWith("\n")) {
											updateStatement = updateStatement.substring(0, updateStatement.length() - 1);
										}
									} else {
										updateStatement = sideEffects[0] + langSpec.getFieldAccessor(fieldOfResourceState) + langSpec.getAssignment() + newState + langSpec.getStatementDelimiter();	// this.value = ...
									}
								} else {
									if (sideEffects[0] != null) {
										updateStatement = sideEffects[0];
										String resourceName = langSpec.toVariableName(getComponentName(outRes, langSpec));
										updateStatement = updateStatement.replace(langSpec.getFieldAccessor(fieldOfResourceState), langSpec.getFieldAccessor(resourceName));
										if (updateStatement.endsWith("\n")) {
											updateStatement = updateStatement.substring(0, updateStatement.length() - 1);
										}
									}
									if (DataConstraintModel.typeList.isAncestorOf(resourceNode.getParent().getResourceStateType())) {
										Term selector = new Term(DataConstraintModel.set);
										selector.addChild(new Constant(langSpec.getFieldAccessor(fieldOfResourceState)));
										selector.addChild(new Variable(update.getParameters().get(update.getParameters().size() - 2).getName()));
										selector.addChild(new Constant(newState));
										String[] sideEffects2 = new String[] {""};
										String newList = selector.toImplementation(sideEffects2);
										updateStatement += sideEffects2[0];
									} else if (DataConstraintModel.typeMap.isAncestorOf(resourceNode.getParent().getResourceStateType())) {
										Term selector = new Term(DataConstraintModel.insert);
										selector.addChild(new Constant(langSpec.getFieldAccessor(fieldOfResourceState)));
										selector.addChild(new Variable(update.getParameters().get(update.getParameters().size() - 2).getName()));
										selector.addChild(new Constant(newState));
										String[] sideEffects2 = new String[] {""};
										String newMap = selector.toImplementation(sideEffects2);
										updateStatement += sideEffects2[0];
									} else if (!(updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect())) {
										String resourceName = langSpec.toVariableName(getComponentName(outRes, langSpec));
										updateStatement += langSpec.getFieldAccessor(resourceName) + langSpec.getAssignment() + newState + langSpec.getStatementDelimiter();
									}
								}
								// add an update statement of the state of dst side resource.
								if (numOfOutResourcesWithTheSameHierarchy == 1) {
									update.addFirstStatement(updateStatement);
								} else {
									Term conditions = null;
									int i = 1;
									Map<ChannelMember, Entry<ResourcePath, Set<ChannelMember>>> resourcePaths = ch.fillOutsideResourcePaths(out, getPushAccessor(platformSpec));
									for (Expression pathParam: out.getResource().getPathParams()) {
										if (pathParam instanceof Variable) {
											String selfParamName = ((Variable) pathParam).getName();
											Expression arg = null;
											for (Selector selector: ch.getAllSelectors()) {
												if (selector.getExpression() instanceof Variable) {
													Variable selVar = (Variable) selector.getExpression();
													if (selVar.getName().equals(selfParamName)) {
														arg = selVar;
														break;
													}
												}
											}
											if (arg == null) {
												ResourcePath filledPath = resourcePaths.get(out).getKey();
												arg = filledPath.getPathParams().get(i - 1);
											}
											Term condition = new Term(DataConstraintModel.eq, new Expression[] {
													new Parameter("self" + (i > 1 ? i : ""), DataConstraintModel.typeString), 
													arg});
											if (conditions == null) {
												conditions = condition;
											} else {
												conditions = new Term(DataConstraintModel.and, new Expression[] {
														conditions, 
														condition});
											}
										}
										i++;
									}
									update.addFirstStatement(langSpec.getIfStatement(conditions, updateStatement));
								}
								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 (inDegree > 1
							|| (ch.getInputChannelMembers().size() == 1 && ch.getInputChannelMembers().iterator().next().getStateTransition().isRightPartial())) {
						// If incoming edges are multiple, or the current state of an input member is needed.
						if (langSpec.declareField()) {
							// Declare the cache field. 
							String cacheFieldName = langSpec.toVariableName(getComponentName(srcResPath.getResourceHierarchy(), langSpec));
							FieldDeclaration cacheField = langSpec.newFieldDeclaration(
																	srcResPath.getResourceStateType(), 
																	cacheFieldName, 
																	langSpec.getFieldInitializer(srcResPath.getResourceStateType(), srcResPath.getResourceHierarchy().getInitialValue()));
							if (component != null) {
								component.addField(cacheField);
							} else if (parentComponent != null){
								parentComponent.addField(cacheField);
							}
							
						}
						// Update the cache field.
						String cacheStatement = langSpec.getFieldAccessor(langSpec.toVariableName(srcResName)) + langSpec.getAssignment() + langSpec.toVariableName(srcResName) + langSpec.getStatementDelimiter();
						if (update.getBody() == null || !update.getBody().getStatements().contains(cacheStatement)) {
							update.addStatement(cacheStatement);
						}
					}
					
					Set<ChannelMember> outsideInputMembers = new HashSet<>();
					for (ChannelMember cm: ch.getInputChannelMembers()) {
						if (cm.isOutside()) {
							outsideInputMembers.add(cm);
						}
					}
					if (outsideInputMembers.size() > 0) {
						Map<ChannelMember, Entry<ResourcePath, Set<ChannelMember>>> resourcePaths = null;
						for (ChannelMember out1: ch.getOutputChannelMembers()) {
							if (resourceNode.getInSideResources().contains(out1.getResource())) {
								try {
									resourcePaths = ch.fillOutsideResourcePaths(out1, getPullAccessor(platformSpec));
								} catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork
										| InvalidMessage | UnificationFailed | ValueUndefined e) {
									e.printStackTrace();
								}
								break;
							}
						}
						if (resourcePaths != null && resourcePaths.size() > 0) {
							for (ChannelMember outsideMember: outsideInputMembers) {
								for (ChannelMember dependingMember: resourcePaths.get(outsideMember).getValue()) {
									if (dependingMember.getResource().equals(srcResPath)) {
										// An outside input resource path depends on srcRes.
										ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey();
										String outsideResName = langSpec.toVariableName(getComponentName(outsidePath.getResourceHierarchy(), langSpec));
										Expression outsideExp = getPullAccessor(platformSpec).getDirectStateAccessorFor(outsidePath, null);
										if (generatesComponent(outsidePath.getResourceHierarchy())) {
											outsideExp = ((Term) outsideExp).getChild(0);
										}
										Expression nextExp = dependingMember.getStateTransition().getNextStateExpression();
										if (nextExp != null && outsideExp instanceof Term) {
											if (nextExp instanceof Variable) {
												outsideExp = ((Term) outsideExp).substitute((Variable) nextExp, new Field(langSpec.toVariableName(getComponentName(dependingMember.getResource().getResourceHierarchy(), langSpec)))); 
											} else {
												// ToDo.
											}
										}
										String[] sideEffects = new String[] {""};
										String outsideAccessor = outsideExp.toImplementation(sideEffects);
										String updateReference = langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter();
										update.addStatement(updateReference);			// Update the reference field.
										// Update constructor.
										if (component != null) {
											MethodDeclaration constructor = getConstructor(component);
											constructor.addStatement(updateReference);		// Initialize the reference field.
										} else if (parentComponent != null){
											constructorStatements.add(updateReference);
										}
									}
								}
							}
						}
					}
					
					// Add an invocation to another update method (for a chain of update method invocations).
					boolean hasUpdateMethodinvoked = false;
					for (Edge resToCh2: resourceNode.getOutEdges()) {
						DataFlowEdge dOut = (DataFlowEdge) resToCh2;
						ChannelNode directDstChNode = (ChannelNode) resToCh2.getDestination();
						DataTransferChannel directDstCh = directDstChNode.getChannel();
						// Check if the input resource is outside of the channel scope.
						boolean outsideInputResource2 = false;
						ChannelMember in = null;
						Set<ChannelMember> outsideInputMembers2 = new HashSet<>();
						for (ChannelMember cm: directDstCh.getInputChannelMembers()) {
							if (cm.getResource().equals(resourceNode.getOutSideResource(directDstCh))) {
								if (cm.isOutside()) {
									outsideInputResource2 = true;	// Regarded as pull transfer.
								}
								in = cm;
							}
							if (cm.isOutside()) {
								outsideInputMembers2.add(cm);
							}
						}
						// Should take into account the channel hierarchy.
						Set<ChannelNode> ancestorDstChannels = directDstChNode.getAncestors();
						Set<ChannelNode> descendantDstChannels = directDstChNode.getDescendants();
						Set<Edge> outEdges = new HashSet<>();
						outEdges.addAll(directDstChNode.getOutEdges());
						for (ChannelNode ancestorDst: ancestorDstChannels) {
							outEdges.addAll(ancestorDst.getOutEdges());
						}
						for (ChannelNode descendantDst: descendantDstChannels) {
							outEdges.addAll(descendantDst.getOutEdges());
						}
						for (Edge chToRes2: outEdges) {
							// For each data transfer to dstNode:ResourceNode.
							ResourceNode dstNode = ((ResourceNode) chToRes2.getDestination());
							ChannelNode chNode2 = (ChannelNode) chToRes2.getSource();
							DataTransferChannel ch2 = chNode2.getChannel();
							// Check if the output resource is outside of the channel scope.
							boolean outsideOutputResource2 = false;
							ChannelMember out1 = null;
							for (ChannelMember cm: ch2.getOutputChannelMembers()) {
								if (dstNode.getInSideResources().contains(cm.getResource())) {
									out1 = cm;
									if (cm.isOutside()) {
										outsideOutputResource2 = true;
										break;
									}
								}
							}
							// Also take into account the channel hierarchy to determine push/pull transfer.
							if (descendantDstChannels.contains(chNode2)) {
								outsideOutputResource2 = true;	// Regarded as (broadcasting) push transfer.
							}
							if (ancestorDstChannels.contains(chNode2)) {
								outsideInputResource2 = true;	// Regarded as (collecting) pull transfer.
							}
							if ((((PushPullAttribute) dOut.getAttribute()).getSelectedOption() == PushPullValue.PUSH && !outsideInputResource2) || outsideOutputResource2) {
								// PUSH transfer
								boolean addForStatement = false;
								String forVarName = null;
								if (descendantDstChannels.contains(chNode2)) {
									// For hierarchical channels (broadcasting push transfer).
									if (ch2.getSelectors() != null && ch2.getSelectors().size() > 0) {
										Expression selExp = ch2.getSelectors().get(0).getExpression();
										Type selType = null;
										if (selExp instanceof Variable) {
											selType = ((Variable) selExp).getType();
											forVarName = ((Variable) selExp).getName();
											ChannelMember insideChMem = null;
											for (ChannelMember cm :ch2.getInputChannelMembers()) {
												if (!cm.isOutside()) {
													insideChMem = cm;
													break;
												}
											}
											if (insideChMem == null) {
												for (ChannelMember cm :ch2.getReferenceChannelMembers()) {
													if (!cm.isOutside()) {
														insideChMem = cm;
														break;
													}
												}
											}
											if (insideChMem == null) {
												for (ChannelMember cm :ch2.getOutputChannelMembers()) {
													if (!cm.isOutside()) {
														insideChMem = cm;
														break;
													}
												}
											}
											ResourcePath insideResPath = insideChMem.getResource();
											while (insideResPath.getParent() != null && (insideResPath.getLastParam() == null || !insideResPath.getLastParam().equals(selExp))) {
												insideResPath = insideResPath.getParent();
											}
											insideResPath = insideResPath.getParent();
											if (insideResPath != null) {
												String parent = null;
												if (platformSpec.isMonolithic() 
														|| insideResPath.getCommonPrefix(resourceNode.getOutSideResource(directDstCh)) != null 
														|| !platformSpec.isDifferentTreesAsDifferentServices()) {
													if (!platformSpec.isMonolithic() && generatesComponent(insideResPath.getResourceHierarchy())) {
														Expression getter = getPullAccessor(platformSpec).getDirectStateAccessorFor(insideResPath, resourceNode.getOutSideResource(directDstCh));
														Term valueGetter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD));
														valueGetter.addChild(getter);
														parent = valueGetter.toImplementation(new String[] {});
													} else {
														parent = getPullAccessor(platformSpec).getDirectStateAccessorFor(insideResPath, resourceNode.getOutSideResource(directDstCh)).toImplementation(new String[] {});
													}
												} else {
													// for REST API
													parent = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(insideResPath.getResourceHierarchy()));
												}
												if (selType.equals(DataConstraintModel.typeInt)) {
													// make a for loop (for a list) for broadcasting.
													update.addStatement(langSpec.getForStatementForList(forVarName, parent));
													addForStatement = true;
												} else if (selType.equals(DataConstraintModel.typeString)) {
													// make a for loop (for a map) for broadcasting.
													update.addStatement(langSpec.getForStatementForMap(forVarName, DataConstraintModel.typeString.getInterfaceTypeName(), parent));
													addForStatement = true;
												}
												if (!platformSpec.isMonolithic() 
														&& insideResPath.getCommonPrefix(resourceNode.getOutSideResource(directDstCh)) == null 
														&& platformSpec.isDifferentTreesAsDifferentServices()) {
													// for REST API
													Type parentResType = insideResPath.getResourceStateType();
													String parentResName = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(insideResPath.getResourceHierarchy()));
													String parentResPath = insideResPath.toString().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\"");
													generatePullDataTransfer(update, parentResName, parentResPath, parentResType, true, platformSpec, langSpec);
												}
											}
										} else if (selExp instanceof Term) {
											// not supported.
										}
									}
								}
								// Get the value of reference member to call the update method.
								List<Map.Entry<Type, Map.Entry<String, String>>> refParams = new ArrayList<>();
								Map<MethodDeclaration, Set<ResourcePath>> referredResources = new HashMap<>(); 
								Set<ResourcePath> referredSet = referredResources.get(update);
								for (ChannelMember rc: ch2.getReferenceChannelMembers()) {
									ResourcePath ref = rc.getResource();
									if (referredSet == null) {
										referredSet = new HashSet<>();
										referredResources.put(update, referredSet);
									}
									if (!resourceNode.getInSideResources().contains(ref)) {
										String refVarName = langSpec.toVariableName(getComponentName(ref.getResourceHierarchy(), langSpec));
										Type refResourceType = ref.getResourceStateType();
										if (!referredSet.contains(ref)) {
											referredSet.add(ref);
											String[] sideEffects = new String[] {""};
											if (!platformSpec.isMonolithic() && rc.isOutside()) {
												List<String> pathParams = new ArrayList<>();
												for (Expression pathExp: ref.getPathParams()) {
													pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \"");
												}
												generatePullDataTransfer(update, refVarName, ref.getResourceHierarchy().toResourcePath(pathParams), refResourceType, false, platformSpec, langSpec);
											} else {
												ResourcePath srcRes = in.getResource();
												if (!generatesComponent(srcRes.getResourceHierarchy())) {
													srcRes = srcRes.getParent();
												}
												Expression refGetter = getPullAccessor(platformSpec).getDirectStateAccessorFor(ref, srcRes);
												String refExp = refGetter.toImplementation(sideEffects);
												String refTypeName = ref.getResourceStateType().getInterfaceTypeName();
												update.addStatement(sideEffects[0] + langSpec.getVariableDeclaration(refTypeName, refVarName) + langSpec.getAssignment() + refExp + langSpec.getStatementDelimiter());
											}
										}
										refParams.add(new AbstractMap.SimpleEntry<>(ref.getResourceStateType(),
													  new AbstractMap.SimpleEntry<>(refVarName,
																					refVarName)));
									}
								}
								// Update fields to refer to outside resources.
								if (platformSpec.isMonolithic()) {
									try {
										Map<ChannelMember, Entry<ResourcePath, Set<ChannelMember>>> resourcePaths = ch2.fillOutsideResourcePaths(out1, getPullAccessor(platformSpec));
										if (resourcePaths != null && resourcePaths.size() > 0) {
											for (ChannelMember outsideMember: resourcePaths.keySet()) {
												ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey();
												if (!generatesComponent(outsidePath.getResourceHierarchy())) {
													outsidePath = outsidePath.getParent();
												}
												String outsideResName = langSpec.toVariableName(getComponentName(outsidePath.getResourceHierarchy(), langSpec));
												Expression outsideExp = getPullAccessor(platformSpec).getDirectStateAccessorFor(outsidePath, null);
												if (generatesComponent(outsidePath.getResourceHierarchy())) {
													outsideExp = ((Term) outsideExp).getChild(0);
												}
												if (outsideExp instanceof Field) {
													outsideExp = new Variable(((Field) outsideExp).getSymbol().getName(), ((Field) outsideExp).getType());
												} else if (outsideExp instanceof Term) {
													for (Entry<Position, Field> fieldEnt: ((Term) outsideExp).getSubTerms(Field.class).entrySet()) {
														Position pos = fieldEnt.getKey();
														Field field = fieldEnt.getValue();
														Variable var = new Variable(field.getSymbol().getName(), field.getType());
														((Term) outsideExp).replaceSubTerm(pos, var);
													}
												}
												String[] sideEffects = new String[] {""};
												String outsideAccessor = outsideExp.toImplementation(sideEffects);
												update.addStatement(langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter());		// change the reference field.
											}
										}
									} catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork
											| InvalidMessage | UnificationFailed | ValueUndefined e) {
										e.printStackTrace();
									}
								}
								// Values of path parameters to call the update method.
								List<Map.Entry<Type, Map.Entry<String, String>>> pathParams = new ArrayList<>();								
								for (Expression pathParam: out1.getResource().getPathParams()) {
									if (pathParam instanceof Variable) {
										Variable pathVar = (Variable) pathParam;
										pathParams.add(new AbstractMap.SimpleEntry<>(pathVar.getType(),
													   new AbstractMap.SimpleEntry<>(pathVar.getName(),
															   						 pathVar.getName())));
									}
								}
								// Values of channel parameters to call the update method.
								List<Map.Entry<Type, Map.Entry<String, String>>> params = new ArrayList<>();								
								for (Selector selector: ch2.getAllSelectors()) {
									if (selector.getExpression() instanceof Variable) {
										Variable selVar = (Variable) selector.getExpression();
										params.add(new AbstractMap.SimpleEntry<>(selVar.getType(), 
												   new AbstractMap.SimpleEntry<>(selVar.getName(), 
														   						 selVar.getName())));
									}
								}
								// Value of the source side (input side) resource to call the update method.
								ResourceHierarchy srcRes2 = resourceNode.getResourceHierarchy();
								if (generatesComponent(srcRes2)) {
									params.add(new AbstractMap.SimpleEntry<>(srcRes2.getResourceStateType(), 
											   new AbstractMap.SimpleEntry<>(langSpec.toVariableName(srcRes2.getResourceName()), 
																			 langSpec.getFieldAccessor(fieldOfResourceState))));
								} else {
									params.add(new AbstractMap.SimpleEntry<>(srcRes2.getResourceStateType(),
											   new AbstractMap.SimpleEntry<>(langSpec.toVariableName(srcRes2.getResourceName()),
													   						 langSpec.getFieldAccessor(langSpec.toVariableName(srcRes2.getResourceName())))));
									srcRes2 = srcRes2.getParent();									
								}
								params.addAll(refParams);
								// Call the update method.
								String updateMethodName = null;
								ResourceHierarchy dstRes = dstNode.getResourceHierarchy();
								if (!generatesComponent(dstRes)) {
									updateMethodName = updateMethodPrefix + getComponentName(dstRes, langSpec) + from + resComponentName;
									dstRes = dstRes.getParent();
								} else {
									updateMethodName = updateMethodPrefix + from + resComponentName;									
								}
								String dstCompName = langSpec.toVariableName(getComponentName(dstRes, langSpec));
								if (outsideOutputResource2 
										|| (!platformSpec.isMonolithic() && in.getResource().getCommonPrefix(out1.getResource()) == null && platformSpec.isDifferentTreesAsDifferentServices())) {
									// Inter-servces
									if (!platformSpec.isMonolithic()) {
										// REST API
										RestApiSpecific restApiSpec = (RestApiSpecific) platformSpec;
										String httpMethod = null;
										if (out1.getStateTransition().isRightUnary()) {
											httpMethod = "put";									
										} else {
											httpMethod = "post";
										}
										String[] sideEffects = new String[] {""};
										List<String> pathParamsUrl = new ArrayList<>();
										for (Expression pathExp: out1.getResource().getPathParams()) {
											pathParamsUrl.add("\" + " + pathExp.toImplementation(sideEffects) + " + \"");
										}
										String resName = langSpec.toVariableName(resComponentName);
										if (inDegree <= 1) {
											resName = null;
										}
										Map<ChannelMember, Entry<ResourcePath, Set<ChannelMember>>> filledPaths = null;
										try {
											filledPaths = ch2.fillOutsideResourcePaths(out1, getPushAccessor(platformSpec));
										} catch (ParameterizedIdentifierIsFutureWork
												| ResolvingMultipleDefinitionIsFutureWork | InvalidMessage
												| UnificationFailed | ValueUndefined e) {
											e.printStackTrace();
										}
										String dstPath = null;
										if (filledPaths != null && filledPaths.get(out1) != null) {
											ResourcePath filledDstPath = filledPaths.get(out1).getKey();
											dstPath = filledDstPath.toString().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\"");
										} else {
											dstPath = dstRes.toResourcePath(pathParamsUrl);
										}
										// Call the update method.
										if (!hasUpdateMethodinvoked) {
											// The first call to an update method in this method
											update.addStatement(restApiSpec.getHttpMethodParamsConstructionStatement(srcRes2.getResourceName(), params, true));
											update.addStatement(langSpec.getVariableDeclaration(DataConstraintModel.typeString.getInterfaceTypeName(), "result")
																		+ langSpec.getAssignment() + restApiSpec.getHttpMethodCallStatement(restApiSpec.getBaseURL(), dstPath, resName, httpMethod));
											hasUpdateMethodinvoked = true;
										} else {
											// After the second time of call to update methods in this method
											update.addStatement(restApiSpec.getHttpMethodParamsConstructionStatement(srcRes2.getResourceName(), params, false));
											update.addStatement("result" + langSpec.getAssignment() + restApiSpec.getHttpMethodCallStatement(restApiSpec.getBaseURL(), dstPath, resName, httpMethod));
										}
									} else {
										// Use the reference field to refer to outside destination resource.									
										List<String> args = new ArrayList<>();
										for (Map.Entry<Type, Map.Entry<String, String>> paramEnt: pathParams) {
											args.add(paramEnt.getValue().getValue());
										}
										for (Map.Entry<Type, Map.Entry<String, String>> paramEnt: params) {
											args.add(paramEnt.getValue().getValue());
										}
										update.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, args) 
												+ langSpec.getStatementDelimiter());	// this.dst.updateDstFromSrc(value, refParams);
									}
								} else {
									// Intra-service
									// The destination resource is not outside.
									List<String> args = new ArrayList<>();
									for (Map.Entry<Type, Map.Entry<String, String>> paramEnt: pathParams) {
										args.add(paramEnt.getValue().getValue());
									}
									for (Map.Entry<Type, Map.Entry<String, String>> paramEnt: params) {
										args.add(paramEnt.getValue().getValue());
									}
									if (srcRes2 != dstRes) {
										update.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, args) 
												+ langSpec.getStatementDelimiter());	// this.dst.updateDstFromSrc(value, refParams);
									} else {
										update.addStatement(langSpec.getMethodInvocation(updateMethodName, args)
												+ langSpec.getStatementDelimiter());	// this.updateDstFromSrc(value, refParams);
									}
								}
								if (addForStatement) {
									// Close the for loop
									update.addStatement(langSpec.getEndForStatement(forVarName));
								}
							}
						}
						if (outsideInputMembers2.size() > 0) {
							if (!generatesComponent(resourceNode.getResourceHierarchy())) {
								// srcRes2 does not have a component.
								ResourcePath srcRes2 = resourceNode.getOutSideResource(directDstCh);
								for (Edge chToRes2: outEdges) {
									ChannelNode chNode2 = (ChannelNode) chToRes2.getSource();
									DataTransferChannel ch2 = chNode2.getChannel();
									for (ChannelMember out2: ch2.getOutputChannelMembers()) {
										if (!generatesComponent(out2.getResource().getResourceHierarchy())) {
											// Also dstRes2 does not have a component.
											ResourcePath dstRes2 = out2.getResource();
											if (srcRes2.getParent().equals(dstRes2.getParent())) {
												Map<ChannelMember, Entry<ResourcePath, Set<ChannelMember>>> resourcePaths = null;
												try {
													resourcePaths = ch2.fillOutsideResourcePaths(out2, getPullAccessor(platformSpec));
													if (resourcePaths != null && resourcePaths.size() > 0) {
														for (ChannelMember outsideMember: outsideInputMembers2) {
															for (ChannelMember dependedMember: resourcePaths.get(outsideMember).getValue()) {
																if (dependedMember.getResource().equals(srcRes2)) {
																	// An outside input resource path depends on srcRes.
																	ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey();
																	if (!generatesComponent(outsidePath.getResourceHierarchy())) {
																		outsidePath = outsidePath.getParent();
																	}
																	String outsideResName = langSpec.toVariableName(getComponentName(outsidePath.getResourceHierarchy(), langSpec));
																	Expression outsideExp = getPullAccessor(platformSpec).getDirectStateAccessorFor(outsidePath, null);
																	if (generatesComponent(outsidePath.getResourceHierarchy())) {
																		outsideExp = ((Term) outsideExp).getChild(0);
																	}
																	String[] sideEffects = new String[] {""};
																	String outsideAccessor = outsideExp.toImplementation(sideEffects);
																	String updateReference = langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter();
																	update.addStatement(updateReference);			// Update the reference field.
																	// Update constructor.
																	if (component != null) {
																		MethodDeclaration constructor = getConstructor(component);
																		constructor.addStatement(updateReference);		// Initialize the reference field.
																	} else if (parentComponent != null) {
																		constructorStatements.add(updateReference);
																	}
																}
															}
														}
													}
												} catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork
														| InvalidMessage | UnificationFailed | ValueUndefined e) {
													e.printStackTrace();
												}
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}
		return new AbstractMap.SimpleEntry<>(constructorStatements, updateStatements);
	}
	
	private Map.Entry<List<String>, Map<MethodDeclaration, Map.Entry<Expression, Map.Entry<ResourceHierarchy, ResourceHierarchy>>>> declareInputMethodsInThisAndMainComponents(ResourceNode resourceNode, TypeDeclaration component,
			TypeDeclaration parentComponent, TypeDeclaration mainComponent, TypeDeclaration rootComponent, DataTransferModel model, Map<Channel, ChannelMember> priorMemberForInputChannel, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) {
		// Declare input methods.
		String resName = resourceNode.getResourceName();
		String resComponentName = langSpec.toComponentName(resName);
		List<String> constructorStatements = new ArrayList<>();
		Map<MethodDeclaration, Map.Entry<Expression, Map.Entry<ResourceHierarchy, ResourceHierarchy>>> inputStatements = new HashMap<>();
		for (Channel ch: model.getInputChannels()) {
			for (ChannelMember cm : ((DataTransferChannel) ch).getOutputChannelMembers()) {
				if (!cm.isOutside()) {
					if (priorMemberForInputChannel.get(ch) == null) {
						priorMemberForInputChannel.put(ch, cm);		// The receiver of the input event when multiple output resources are defined for the channel.
					}
				}
			}
			for (ChannelMember out: ((DataTransferChannel) ch).getOutputChannelMembers()) {
				if (resourceNode.getInSideResources().contains(out.getResource())) {
					Expression message = out.getStateTransition().getMessageExpression();
					MethodDeclaration input = null;
					MethodDeclaration inputAccessor = null;
					MethodDeclaration rootInputAccessor = null;
					if (message instanceof Term) {
						// Declare an input method in this component.
						ArrayList<VariableDeclaration> resInputParams = new ArrayList<>();
						ArrayList<VariableDeclaration> mainInputParams = new ArrayList<>();
						ArrayList<VariableDeclaration> rootInputParams = new ArrayList<>();
						String resourcePath = null;
						if (!platformSpec.isMonolithic()) {
							resourcePath = getInputMethodResourcePathAndPathParams(out.getResource(), rootInputParams, platformSpec, langSpec);	// Path parameters for the input REST API.
							if (resourcePath.indexOf('/') > 0) {
								resourcePath = resourcePath.substring(resourcePath.indexOf('/'));
							} else {
								resourcePath = "";
							}
						}
						// The path parameters are not to be passed to the input method of each resource (resInputParams) 
						// because they are always equal to either channel selectors or message parameters.
						
						// Channel parameters to specify the context of the collaboration.
						int v = 1;
						for (Selector selector: ch.getSelectors()) {
							if (selector.getExpression() instanceof Variable) {
								Variable selVar = (Variable) selector.getExpression();
								resInputParams.add(langSpec.newVariableDeclaration(selVar.getType(), selVar.getName()));
								mainInputParams.add(langSpec.newVariableDeclaration(selVar.getType(), selVar.getName()));
							} else if (selector.getExpression() instanceof Term) {
								Term var = (Term) selector.getExpression();
								resInputParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v));								
								mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v));																	
							}
							v++;
						}
						if (ch.getParent() != null) {
							for (Selector selector: ch.getParent().getAllSelectors()) {
								if (selector.getExpression() instanceof Variable) {
									Variable selVar = (Variable) selector.getExpression();
									mainInputParams.add(langSpec.newVariableDeclaration(selVar.getType(), selVar.getName()));
								} else if (selector.getExpression() instanceof Term) {
									Term var = (Term) selector.getExpression();
									mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v));																	
								}
								v++;
							}
						}
						// Message parameters to carry the data-flows.
						for (Map.Entry<Position, Variable> varEnt: message.getVariables().entrySet()) {
							Variable var = varEnt.getValue();
							String refVarName = null;
							for (ChannelMember refCm: ((DataTransferChannel) ch).getReferenceChannelMembers()) {
								Expression varExp = refCm.getStateTransition().getMessageExpression().getSubTerm(varEnt.getKey());
								if (varExp != null && varExp instanceof Variable) {
									if (refCm.getStateTransition().getCurStateExpression().contains(varExp)) {
										refVarName = refCm.getResource().getLeafResourceName();
										break;
									}
								}
							}
							if (refVarName != null) {
								// var has come from a reference resource.
								resInputParams.add(langSpec.newVariableDeclaration(var.getType(), refVarName));
							} else {
								// var has not come from a reference resource.
								resInputParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName()));
								boolean bExists = false;
								for (VariableDeclaration mainParam: mainInputParams) {
									if (mainParam.getName().equals(var.getName()) ) {
										bExists = true;
										break;
									}
								}
								if (!bExists) {
									mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName()));
								}
								if (!platformSpec.isMonolithic() && !resourcePath.contains("{" + var.getName()+ "}")) {
									VariableDeclaration param = langSpec.newVariableDeclaration(var.getType(), var.getName());
									((RestApiSpecific) platformSpec).addFormParamAnnotation(param, var.getName());
									rootInputParams.add(param);
								}
							}
						}
						if (platformSpec.isMonolithic()
								|| (resourceNode.getResourceHierarchy().getParent() != null && resourceNode.getResourceHierarchy().getParent().getParent() != null)) {
							String inputMethodName = ((Term) message).getSymbol().getImplName();
							if (((DataTransferChannel) ch).getOutputChannelMembers().size() > 1) {
								inputMethodName += _for + getComponentName(out.getResource().getResourceHierarchy(), langSpec);
							}
							if (component != null) {
								// A component is created for this resource.
								for (MethodDeclaration method: component.getMethods()) {
									if (method.getName().equals(inputMethodName)) {
										input = method;
										break;
									}
								}
								if (input == null) {
									input = langSpec.newMethodDeclaration(inputMethodName, false, null, resInputParams);
									component.addMethod(input);
								}
							} else if (parentComponent != null) {
								// No component is created for this resource.
								for (MethodDeclaration method: parentComponent.getMethods()) {
									if (method.getName().equals(inputMethodName)) {
										input = method;
										break;
									}
								}
								if (input == null) {
									input = langSpec.newMethodDeclaration(inputMethodName, false, null, resInputParams);
									parentComponent.addMethod(input);
								}
							}
						}
						
						// Declare the accessor in the main component to call the input method. (for monolithic application)
						if (platformSpec.hasMain()) {
							String messageSymbol = ((Term) message).getSymbol().getImplName();
							inputAccessor = getMethod(mainComponent, messageSymbol);
							if (inputAccessor == null) {
								inputAccessor = langSpec.newMethodDeclaration(messageSymbol, false, null, mainInputParams);
								mainComponent.addMethod(inputAccessor);
							} else {
								// Add type to a parameter without type.
								if (inputAccessor.getParameters() != null) {
									for (VariableDeclaration param: inputAccessor.getParameters()) {
										if (param.getType() == null) {
											for (VariableDeclaration p: mainInputParams) {
												if (param.getName().equals(p.getName()) && p.getType() != null) {
													param.setType(p.getType());
												}
											}
										}
									}
								}
							}
						}
						
						// For the root resource. (for REST API)
						if (!platformSpec.isMonolithic()) {
							if (priorMemberForInputChannel.get(ch) == null || out == priorMemberForInputChannel.get(ch)) {
								// If out is the receiver of the input event.
								priorMemberForInputChannel.put(ch, out);
								String messageSymbol = ((Term) message).getSymbol().getImplName();
								declareInputAccessorInTheRootResource(messageSymbol, rootInputParams, out, resourcePath,
										rootComponent, platformSpec, langSpec);
							}
						}
					} else if (message instanceof Variable) {
						// Declare an input method in this component.
						ArrayList<VariableDeclaration> resInputParams = new ArrayList<>();
						ArrayList<VariableDeclaration> mainInputParams = new ArrayList<>();
						int v = 1;
						if (out.getResource().getLastParam() != null) {
							Expression param = out.getResource().getLastParam();
							if (param instanceof Variable) {
								Variable var = (Variable) param;
								resInputParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName()));								
								mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName()));								
							} else if (param instanceof Term) {
								Term var = (Term) param;
								resInputParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v));								
								mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v));								
							}
							v++;
						}
						if (out.getResource().getParent() != null) {
							for (Expression param: out.getResource().getParent().getPathParams()) {
								if (param instanceof Variable) {
									Variable var = (Variable) param;
									mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName()));								
								} else if (param instanceof Term) {
									Term var = (Term) param;
									mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v));								
								}
								v++;
							}
						}
						if (platformSpec.isMonolithic()
								|| (resourceNode.getResourceHierarchy().getParent() != null && resourceNode.getResourceHierarchy().getParent().getParent() != null)) {
							String inputMethodName = ((Variable) message).getName();
							if (((DataTransferChannel) ch).getOutputChannelMembers().size() > 1) {
								inputMethodName += _for + getComponentName(out.getResource().getResourceHierarchy(), langSpec);
							}
							if (component != null) {
								// A component is created for this resource.
								for (MethodDeclaration method: component.getMethods()) {
									if (method.getName().equals(inputMethodName)) {
										input = method;
										break;
									}
								}
								if (input == null) {
									if (resInputParams.size() == 0) {
										input = langSpec.newMethodDeclaration(inputMethodName, null);
									} else {
										input = langSpec.newMethodDeclaration(inputMethodName, false, null, resInputParams);							
									}
									component.addMethod(input);
								}
							} else if (parentComponent != null) {
								// No component is created for this resource.
								for (MethodDeclaration method: parentComponent.getMethods()) {
									if (method.getName().equals(inputMethodName)) {
										input = method;
										break;
									}
								}
								if (input == null) {
									if (resInputParams.size() == 0) {
										input = langSpec.newMethodDeclaration(inputMethodName, null);
									} else {
										input = langSpec.newMethodDeclaration(inputMethodName, false, null, resInputParams);							
									}
									parentComponent.addMethod(input);
								}
							}
						}
												
						// Declare the accessor in the main component to call the input method. (for monolithic application)
						if (platformSpec.hasMain()) {
							String messageSymbol = ((Variable) message).getName();
							inputAccessor = getMethod(mainComponent, messageSymbol, mainInputParams);
							if (inputAccessor == null) {
								if (mainInputParams.size() == 0) {
									inputAccessor = langSpec.newMethodDeclaration(messageSymbol, null);
								} else {
									inputAccessor = langSpec.newMethodDeclaration(messageSymbol, false, null, mainInputParams);
								}
								mainComponent.addMethod(inputAccessor);
							}
						}
						
						// For the root resource. (for REST API)
						if (!platformSpec.isMonolithic()) {
							if (priorMemberForInputChannel.get(ch) == null || out == priorMemberForInputChannel.get(ch)) {
								// If out is the receiver of the input event.
								priorMemberForInputChannel.put(ch, out);
								ArrayList<VariableDeclaration> rootInputParams = new ArrayList<>();
								String resourcePath = getGetterResourcePathAndPathParams(out.getResource(), rootInputParams, platformSpec, langSpec);
								if (resourcePath.indexOf('/') > 0) {
									resourcePath = resourcePath.substring(resourcePath.indexOf('/'));
								} else {
									resourcePath = "";
								}
								String messageSymbol = ((Variable) message).getName();
								declareInputAccessorInTheRootResource(messageSymbol, rootInputParams, out, resourcePath,
										rootComponent, platformSpec, langSpec);
							}
						}
					}
					
					// Add an invocation to the accessor method.
					if (inputAccessor != null) {
						for (ChannelMember rc: ((DataTransferChannel) ch).getReferenceChannelMembers()) {
							// For each reference channel member, get the current state of the reference side resource by pull data transfer.
							ResourcePath ref = rc.getResource();
							if (!out.getResource().equals(ref)) {
								String refVarName = ref.getLeafResourceName();
								Expression refGetter = getPullAccessor(platformSpec).getDirectStateAccessorFor(ref, null);
								String[] sideEffects = new String[] {""};
								String refExp = refGetter.toImplementation(sideEffects);
								String refTypeName = ref.getResourceStateType().getInterfaceTypeName();
								inputAccessor.addFirstStatement(sideEffects[0] + refTypeName + " " + refVarName + " = " + refExp + ";");
							}
						}
						Expression resExp = getPullAccessor(platformSpec).getDirectStateAccessorFor(out.getResource(), null);
						List<String> args = new ArrayList<>();
						if (resExp instanceof Term) {
							// to access the parent
							if (((Term) resExp).getChildren().size() > 1 && ((Term) resExp).getChild(1) instanceof Variable) {
								args.add(((Variable)((Term) resExp).getChild(1)).getName());
							}
							resExp = ((Term) resExp).getChild(0);
						}
						String resourceAccess = resExp.toImplementation(new String[] {""});
						// Values of channel parameters.
						for (Selector selector: ch.getAllSelectors()) {
							if (selector.getExpression() instanceof Variable) {
								Variable selVar = (Variable) selector.getExpression();
								if (!args.contains(selVar.getName())) {
									args.add(selVar.getName());
								}
							}
						}
						// Values of message parameters.
						if (message instanceof Term) {
							for (Map.Entry<Position, Variable> varEnt: message.getVariables().entrySet()) {
								String refVarName = null;
								for (ChannelMember rc: ((DataTransferChannel) ch).getReferenceChannelMembers()) {
									Expression varExp = rc.getStateTransition().getMessageExpression().getSubTerm(varEnt.getKey());
									if (varExp != null && rc.getStateTransition().getCurStateExpression().contains(varExp)) {
										refVarName = rc.getResource().getLeafResourceName();
										break;
									}
								}
								if (refVarName != null) {
									if (!args.contains(refVarName)) {
										args.add(refVarName);
									}
								} else {
									if (!args.contains(varEnt.getValue().getName())) args.add(varEnt.getValue().getName());									
								}
							}
						}
						inputAccessor.addStatement(langSpec.getMethodInvocation(resourceAccess, input.getName(), args) + langSpec.getStatementDelimiter());
					}
					
					if (input != null) {
						// Add a statement to update the state field to the input method.
						try {
							Expression updateExp = ((DataTransferChannel) ch).deriveUpdateExpressionOf(out, getRefAccessor(platformSpec)).getKey();
							// Replace Json constructor with a constructor of a descendant resource.
							ResourceHierarchy outRes = out.getResource().getResourceHierarchy();
							if (outRes.getChildren().size() == 1 && outRes.getChildren().iterator().next().getNumParameters() > 0) {
								ResourceHierarchy descendantRes = outRes;
								Set<ResourceHierarchy> children = descendantRes.getChildren();
								do {
									descendantRes = children.iterator().next();
									if (generatesComponent(descendantRes)) {
										inputStatements.put(input, new AbstractMap.SimpleEntry<>(updateExp, new AbstractMap.SimpleEntry<>(outRes, descendantRes)));
										updateExp = null;
										break;
									}
									children = descendantRes.getChildren();
								} while (children != null && children.size() == 1);
							}
							// Add statements to the input method.
							if (updateExp != null) {
								String[] sideEffects = new String[] {""};
								String newState = updateExp.toImplementation(sideEffects);
								ResourceHierarchy resource = resourceNode.getResourceHierarchy();
								if (generatesComponent(resource)) {
									String updateStatement;
									if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) {
										updateStatement = sideEffects[0];	
										if (updateStatement.endsWith("\n")) {
											updateStatement = updateStatement.substring(0, updateStatement.length() - 1);
										}
									} else {
										updateStatement = sideEffects[0] + langSpec.getFieldAccessor(fieldOfResourceState) + langSpec.getAssignment() + newState + langSpec.getStatementDelimiter();
									}
									input.addFirstStatement(updateStatement);
								} else {
									String updateStatement = "";
									if (sideEffects[0] != null) {
										updateStatement = sideEffects[0];	
										String resourceName = langSpec.toVariableName(getComponentName(resource, langSpec));
										updateStatement = updateStatement.replace(langSpec.getFieldAccessor(fieldOfResourceState), langSpec.getFieldAccessor(resourceName));
										if (updateStatement.endsWith("\n")) {
											updateStatement = updateStatement.substring(0, updateStatement.length() - 1);
										}
									}
									if (DataConstraintModel.typeList.isAncestorOf(resourceNode.getParent().getResourceStateType())) {
										Term selector = new Term(DataConstraintModel.set);
										selector.addChild(new Constant(langSpec.getFieldAccessor(fieldOfResourceState)));
										selector.addChild(new Variable(input.getParameters().get(input.getParameters().size() - 2).getName()));
										selector.addChild(new Constant(newState));
										String[] sideEffects2 = new String[] {""};
										String newList = selector.toImplementation(sideEffects2);
										updateStatement += sideEffects2[0];
									} else if (DataConstraintModel.typeMap.isAncestorOf(resourceNode.getParent().getResourceStateType())) {
										Term selector = new Term(DataConstraintModel.insert);
										selector.addChild(new Constant(langSpec.getFieldAccessor(fieldOfResourceState)));
										selector.addChild(new Variable(input.getParameters().get(input.getParameters().size() - 2).getName()));
										selector.addChild(new Constant(newState));
										String[] sideEffects2 = new String[] {""};
										String newMap = selector.toImplementation(sideEffects2);
										updateStatement += sideEffects2[0];
									} else if (!(updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect())) {
										String resourceName = langSpec.toVariableName(getComponentName(resource, langSpec));
										updateStatement += langSpec.getFieldAccessor(resourceName) + langSpec.getAssignment() + newState + langSpec.getStatementDelimiter();
									}
									if (updateStatement != null) {
										input.addFirstStatement(updateStatement);
									}
								}
							}
						} catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork
								| InvalidMessage | UnificationFailed | ValueUndefined e) {
							e.printStackTrace();
						}
												
						// Add an invocation to an update method (for a chain of update method invocations).
						boolean hasUpdateMethodinvoked = false;
						for (Edge resToCh: resourceNode.getOutEdges()) {
							DataFlowEdge dOut = (DataFlowEdge) resToCh;
							ChannelNode directDstChNode = (ChannelNode) resToCh.getDestination();
							DataTransferChannel directDstCh = directDstChNode.getChannel();
							// Check if the input resource is outside of the channel scope.
							boolean outsideInputResource2 = false;
							ChannelMember in = null;
							Set<ChannelMember> outsideInputMembers2 = new HashSet<>();
							for (ChannelMember cm: directDstCh.getInputChannelMembers()) {
								if (resourceNode.getOutSideResources().contains(cm.getResource())) {
									if (cm.isOutside()) {
										outsideInputResource2 = true;	// Regarded as pull transfer.
									}
									in = cm;
								}
								if (cm.isOutside()) {
									outsideInputMembers2.add(cm);
								}
							}
							// Should take into account the channel hierarchy.
							Set<ChannelNode> ancestorDstChannels = directDstChNode.getAncestors();
							Set<ChannelNode> descendantDstChannels = directDstChNode.getDescendants();
							Set<Edge> outEdges = new HashSet<>();
							outEdges.addAll(directDstChNode.getOutEdges());
							for (ChannelNode ancestorDst: ancestorDstChannels) {
								outEdges.addAll(ancestorDst.getOutEdges());
							}
							for (ChannelNode descendantDst: descendantDstChannels) {
								outEdges.addAll(descendantDst.getOutEdges());
							}
							for (Edge chToRes: outEdges) {
								// For each data transfer to dstNode:ResourceNode.
								ResourceNode dstNode = ((ResourceNode) chToRes.getDestination());
								ChannelNode chNode2 = (ChannelNode) chToRes.getSource();
								DataTransferChannel ch2 = chNode2.getChannel();
								// Check if the output resource is outside of the channel scope.
								boolean outsideOutputResource2 = false;
								ChannelMember out2 = null;
								for (ChannelMember cm: ch2.getOutputChannelMembers()) {
									if (dstNode.getInSideResources().contains(cm.getResource())) {
										out2 = cm;
										if (cm.isOutside()) {
											outsideOutputResource2 = true;
											break;
										}
									}
								}
								// Also take into account the channel hierarchy to determine push/pull transfer.
								if (descendantDstChannels.contains(chNode2)) {
									outsideOutputResource2 = true;	// Regarded as (broadcasting) push transfer.
								}
								if (ancestorDstChannels.contains(chNode2)) {
									outsideInputResource2 = true;	// Regarded as (collecting) pull transfer.
								}
								if ((((PushPullAttribute) dOut.getAttribute()).getSelectedOption() == PushPullValue.PUSH && !outsideInputResource2) || outsideOutputResource2) {
									// PUSH transfer
									
									// Calculate in-degree (PUSH transfer) of the destination resource.
									Set<Edge> inEdges = new HashSet<>();
									inEdges.addAll(directDstChNode.getInEdges());
									for (ChannelNode ancestorSrc: ancestorDstChannels) {
										inEdges.addAll(ancestorSrc.getInEdges());
									}
									for (ChannelNode descendantSrc: descendantDstChannels) {
										inEdges.addAll(descendantSrc.getInEdges());
									}
									int inDegree = 0;
									for (Edge resToCh2: inEdges) {
										DataFlowEdge df =(DataFlowEdge) resToCh2;
										if (((PushPullAttribute) df.getAttribute()).getSelectedOption() == PushPullValue.PUSH) {
											inDegree++;
										}
									}
									boolean addForStatement = false;
									String forVarName = null;
									if (descendantDstChannels.contains(chNode2)) {
										// For hierarchical channels (broadcasting push transfer).
										if (ch2.getSelectors() != null && ch2.getSelectors().size() > 0) {
											Expression selExp = ch2.getSelectors().get(0).getExpression();
											Type selType = null;
											if (selExp instanceof Variable) {
												selType = ((Variable) selExp).getType();
												forVarName = ((Variable) selExp).getName();
												ChannelMember insideChMem = null;
												for (ChannelMember cm :ch2.getInputChannelMembers()) {
													if (!cm.isOutside()) {
														insideChMem = cm;
														break;
													}
												}
												if (insideChMem == null) {
													for (ChannelMember cm :ch2.getReferenceChannelMembers()) {
														if (!cm.isOutside()) {
															insideChMem = cm;
															break;
														}
													}
												}
												if (insideChMem == null) {
													for (ChannelMember cm :ch2.getOutputChannelMembers()) {
														if (!cm.isOutside()) {
															insideChMem = cm;
															break;
														}
													}
												}
												ResourcePath insideResPath = insideChMem.getResource();
												while (insideResPath.getParent() != null && (insideResPath.getLastParam() == null || !insideResPath.getLastParam().equals(selExp))) {
													insideResPath = insideResPath.getParent();
												}
												insideResPath = insideResPath.getParent();
												if (insideResPath != null) {
													String parent = null;
													if (platformSpec.isMonolithic() 
															|| insideResPath.getCommonPrefix(resourceNode.getOutSideResource(directDstCh)) != null 
															|| !platformSpec.isDifferentTreesAsDifferentServices()) {
														if (!platformSpec.isMonolithic() && generatesComponent(insideResPath.getResourceHierarchy())) {
															Expression getter = getPullAccessor(platformSpec).getDirectStateAccessorFor(insideResPath, resourceNode.getOutSideResource(directDstCh));
															Term valueGetter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD));
															valueGetter.addChild(getter);
															parent = valueGetter.toImplementation(new String[] {});
														} else {
															parent = getPullAccessor(platformSpec).getDirectStateAccessorFor(insideResPath, resourceNode.getOutSideResource(directDstCh)).toImplementation(new String[] {});
														}
													} else {
														// for REST API
														parent = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(insideResPath.getResourceHierarchy()));
													}
													if (selType.equals(DataConstraintModel.typeInt)) {
														// make a for loop (for a list) for broadcasting.
														input.addStatement(langSpec.getForStatementForList(forVarName, parent));
														addForStatement = true;
													} else if (selType.equals(DataConstraintModel.typeString)) {
														// make a for loop (for a map) for broadcasting.
														input.addStatement(langSpec.getForStatementForMap(forVarName, DataConstraintModel.typeString.getInterfaceTypeName(), parent));
														addForStatement = true;
													}
													if (!platformSpec.isMonolithic() 
															&& insideResPath.getCommonPrefix(resourceNode.getOutSideResource(directDstCh)) == null 
															&& platformSpec.isDifferentTreesAsDifferentServices()) {
														// for REST API
														Type parentResType = insideResPath.getResourceStateType();
														String parentResName = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(insideResPath.getResourceHierarchy()));
														String parentResPath = insideResPath.toString().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\"");
														generatePullDataTransfer(input, parentResName, parentResPath, parentResType, true, platformSpec, langSpec);
													}
												}
											} else if (selExp instanceof Term) {
												// not supported.
											}
										}
									}
									// Get the value of reference member to call the update method.
									List<Map.Entry<Type, Map.Entry<String, String>>> refParams = new ArrayList<>();
									Map<MethodDeclaration, Set<ResourcePath>> referredResources = new HashMap<>(); 
									Set<ResourcePath> referredSet = referredResources.get(input);
									for (ChannelMember rc: ch2.getReferenceChannelMembers()) {
										ResourcePath ref = rc.getResource();
										if (referredSet == null) {
											referredSet = new HashSet<>();
											referredResources.put(input, referredSet);
										}
										if (!resourceNode.getOutSideResources().contains(ref)) {
											String refVarName = langSpec.toVariableName(getComponentName(ref.getResourceHierarchy(), langSpec));
											if (!referredSet.contains(ref)) {
												referredSet.add(ref);
												ResourcePath srcRes = in.getResource();
												if (!generatesComponent(srcRes.getResourceHierarchy())) {
													srcRes = srcRes.getParent();
												}
												Expression refGetter = getPullAccessor(platformSpec).getDirectStateAccessorFor(ref, srcRes);
												String[] sideEffects = new String[] {""};
												String refExp = refGetter.toImplementation(sideEffects);
												String refTypeName = ref.getResourceStateType().getInterfaceTypeName();
												input.addStatement(sideEffects[0] + langSpec.getVariableDeclaration(refTypeName, refVarName) + langSpec.getAssignment() + refExp + langSpec.getStatementDelimiter());
											}
											refParams.add(new AbstractMap.SimpleEntry<>(ref.getResourceStateType(),
													  new AbstractMap.SimpleEntry<>(refVarName,
																					refVarName)));
										}
									}
									// Update fields to refer to outside resources.
									if (platformSpec.isMonolithic()) {
										try {
											Map<ChannelMember, Entry<ResourcePath, Set<ChannelMember>>> resourcePaths = ch2.fillOutsideResourcePaths(out2, getPullAccessor(platformSpec));
											if (resourcePaths != null && resourcePaths.size() > 0) {
												for (ChannelMember outsideMember: resourcePaths.keySet()) {
													ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey();
													if (!generatesComponent(outsidePath.getResourceHierarchy())) {
														outsidePath = outsidePath.getParent();
													}
													String outsideResName = langSpec.toVariableName(getComponentName(outsidePath.getResourceHierarchy(), langSpec));
													Expression outsideExp = getPullAccessor(platformSpec).getDirectStateAccessorFor(outsidePath, null);
													if (generatesComponent(outsidePath.getResourceHierarchy())) {
														outsideExp = ((Term) outsideExp).getChild(0);
													}
													if (outsideExp instanceof Field) {
														outsideExp = new Variable(((Field) outsideExp).getSymbol().getName(), ((Field) outsideExp).getType());
													} else if (outsideExp instanceof Term) {
														for (Entry<Position, Field> fieldEnt: ((Term) outsideExp).getSubTerms(Field.class).entrySet()) {
															Position pos = fieldEnt.getKey();
															Field field = fieldEnt.getValue();
															Variable var = new Variable(field.getSymbol().getName(), field.getType());
															((Term) outsideExp).replaceSubTerm(pos, var);
														}
													}
													String[] sideEffects = new String[] {""};
													String outsideAccessor = outsideExp.toImplementation(sideEffects);
													input.addStatement(langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter());		// change the reference field.
												}
											}
										} catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork
												| InvalidMessage | UnificationFailed | ValueUndefined e) {
											e.printStackTrace();
										}
									}
									// Values of path parameters to call the update method.
									List<Map.Entry<Type, Map.Entry<String, String>>> pathParams = new ArrayList<>();								
									for (Expression pathParam: out2.getResource().getPathParams()) {
										if (pathParam instanceof Variable) {
											Variable pathVar = (Variable) pathParam;
											pathParams.add(new AbstractMap.SimpleEntry<>(pathVar.getType(),
													   new AbstractMap.SimpleEntry<>(pathVar.getName(),
															   						 pathVar.getName())));
										}
									}
									// Values of channel parameters to call the update method.
									List<Map.Entry<Type, Map.Entry<String, String>>> params = new ArrayList<>();
									for (Selector selector: ch2.getAllSelectors()) {
										if (selector.getExpression() instanceof Variable) {
											Variable selVar = (Variable) selector.getExpression();
											params.add(new AbstractMap.SimpleEntry<>(selVar.getType(), 
													   new AbstractMap.SimpleEntry<>(selVar.getName(), 
															   						 selVar.getName())));
										}
									}
									// Value of the source side (input side) resource to call the update method.
									ResourceHierarchy srcRes = resourceNode.getResourceHierarchy();
									if (generatesComponent(srcRes)) {
										params.add(new AbstractMap.SimpleEntry<>(srcRes.getResourceStateType(), 
												   new AbstractMap.SimpleEntry<>(langSpec.toVariableName(srcRes.getResourceName()), 
																				 langSpec.getFieldAccessor(fieldOfResourceState))));
									} else {
										params.add(new AbstractMap.SimpleEntry<>(srcRes.getResourceStateType(),
												   new AbstractMap.SimpleEntry<>(langSpec.toVariableName(srcRes.getResourceName()),
														   						 langSpec.getFieldAccessor(langSpec.toVariableName(srcRes.getResourceName())))));
										srcRes = srcRes.getParent();
									}
									params.addAll(refParams);
									// Call the update method.
									String updateMethodName = null;
									ResourceHierarchy dstRes = dstNode.getResourceHierarchy();
									if (!generatesComponent(dstRes)) {
										updateMethodName = updateMethodPrefix + getComponentName(dstRes, langSpec) + from + resComponentName;									
										dstRes = dstRes.getParent();
									} else {
										updateMethodName = updateMethodPrefix + from + resComponentName;									
									}
									String dstCompName = langSpec.toVariableName(getComponentName(dstRes, langSpec));
									if (outsideOutputResource2 
											|| (!platformSpec.isMonolithic() && in.getResource().getCommonPrefix(out2.getResource()) == null && platformSpec.isDifferentTreesAsDifferentServices())) {
										// Inter-servces
										if (!platformSpec.isMonolithic()) {
											// REST API
											RestApiSpecific restApiSpec = (RestApiSpecific) platformSpec;
											String httpMethod = null;
											if (out2.getStateTransition().isRightUnary()) {
												httpMethod = "put";									
											} else {
												httpMethod = "post";
											}
											String[] sideEffects = new String[] {""};
											List<String> pathParamsUrl = new ArrayList<>();
											for (Expression pathExp: out2.getResource().getPathParams()) {
												pathParamsUrl.add("\" + " + pathExp.toImplementation(sideEffects) + " + \"");
											}
											String resName2 = langSpec.toVariableName(resComponentName);
											if (inDegree <= 1) {
												resName2 = null;
											}
											Map<ChannelMember, Entry<ResourcePath, Set<ChannelMember>>> filledPaths = null;
											try {
												filledPaths = ch2.fillOutsideResourcePaths(out2, getPushAccessor(platformSpec));
											} catch (ParameterizedIdentifierIsFutureWork
													| ResolvingMultipleDefinitionIsFutureWork | InvalidMessage
													| UnificationFailed | ValueUndefined e) {
												e.printStackTrace();
											}
											String dstPath = null;
											if (filledPaths != null && filledPaths.get(out2) != null) {
												ResourcePath filledDstPath = filledPaths.get(out2).getKey();
												dstPath = filledDstPath.toString().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\"");
											} else {
												dstPath = dstRes.toResourcePath(pathParamsUrl);
											}
											// Call the update method.
											if (!hasUpdateMethodinvoked) {
												// The first call to an update method in this method
												input.addStatement(restApiSpec.getHttpMethodParamsConstructionStatement(srcRes.getResourceName(), params, true));
												input.addStatement(langSpec.getVariableDeclaration(DataConstraintModel.typeString.getInterfaceTypeName(), "result")
																			+ langSpec.getAssignment() + restApiSpec.getHttpMethodCallStatement(restApiSpec.getBaseURL(), dstPath, resName2, httpMethod));
												hasUpdateMethodinvoked = true;
											} else {
												// After the second time of call to update methods in this method
												input.addStatement(restApiSpec.getHttpMethodParamsConstructionStatement(srcRes.getResourceName(), params, false));
												input.addStatement("result" + langSpec.getAssignment() + restApiSpec.getHttpMethodCallStatement(restApiSpec.getBaseURL(), dstPath, resName2, httpMethod));
											}
										} else {
											// Use the reference field to refer to outside destination resource.									
											List<String> args = new ArrayList<>();
											for (Map.Entry<Type, Map.Entry<String, String>> paramEnt: pathParams) {
												args.add(paramEnt.getValue().getValue());
											}
											for (Map.Entry<Type, Map.Entry<String, String>> paramEnt: params) {
												args.add(paramEnt.getValue().getValue());
											}
											input.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, args)
													+ langSpec.getStatementDelimiter());	// this.dst.updateDstFromSrc(value, refParams);
										}
									} else {
										// Intra-service
										// The destination resource is not outside.
										List<String> args = new ArrayList<>();
										for (Map.Entry<Type, Map.Entry<String, String>> paramEnt: pathParams) {
											args.add(paramEnt.getValue().getValue());
										}
										for (Map.Entry<Type, Map.Entry<String, String>> paramEnt: params) {
											args.add(paramEnt.getValue().getValue());
										}
										if (srcRes != dstRes) {
											input.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, args)
													+ langSpec.getStatementDelimiter());	// this.dst.updateDstFromSrc(value, refParams);
										} else {
											input.addStatement(langSpec.getMethodInvocation(updateMethodName, args)
													+ langSpec.getStatementDelimiter());	// this.updateDstFromSrc(value, refParams);
										}
									}
									if (addForStatement) {
										// Close the for loop.
										input.addStatement(langSpec.getEndForStatement(forVarName));
									}
								}
							}
							if (outsideInputMembers2.size() > 0) {
								if (!generatesComponent(resourceNode.getResourceHierarchy())) {
									ResourcePath srcRes2 = resourceNode.getOutSideResource(directDstCh);
									for (ChannelMember out2: directDstCh.getOutputChannelMembers()) {
										if (!generatesComponent(out2.getResource().getResourceHierarchy())) {
											ResourcePath dstRes2 = out2.getResource();
											if (srcRes2.getParent().equals(dstRes2.getParent())) {
												Map<ChannelMember, Entry<ResourcePath, Set<ChannelMember>>> resourcePaths = null;
												try {
													resourcePaths = directDstCh.fillOutsideResourcePaths(out2, getPullAccessor(platformSpec));
													if (resourcePaths != null && resourcePaths.size() > 0) {
														for (ChannelMember outsideMember: outsideInputMembers2) {
															for (ChannelMember dependedMember: resourcePaths.get(outsideMember).getValue()) {
																if (dependedMember.getResource().equals(srcRes2)) {
																	// An outside input resource path depends on srcRes.
																	ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey();
																	if (!generatesComponent(outsidePath.getResourceHierarchy())) {
																		outsidePath = outsidePath.getParent();
																	}
																	String outsideResName = langSpec.toVariableName(getComponentName(outsidePath.getResourceHierarchy(), langSpec));
																	Expression outsideExp = getPullAccessor(platformSpec).getDirectStateAccessorFor(outsidePath, null);
																	if (generatesComponent(outsidePath.getResourceHierarchy())) {
																		outsideExp = ((Term) outsideExp).getChild(0);
																	}
																	String[] sideEffects = new String[] {""};
																	String outsideAccessor = outsideExp.toImplementation(sideEffects);
																	input.addStatement(langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter());		// change the reference field.
																	// Update constructor.
																	String initializingStatement = langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter();
																	if (component != null) {
																		MethodDeclaration constructor = getConstructor(component);
																		constructor.addStatement(initializingStatement);		// initialize the reference field.
																	} else {
																		constructorStatements.add(initializingStatement);		// initialize the reference field.
																	}
																}
															}
														}
													}
												} catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork
														| InvalidMessage | UnificationFailed | ValueUndefined e) {
													e.printStackTrace();
												}
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}
		return new AbstractMap.SimpleEntry<>(constructorStatements, inputStatements);
	}

	protected boolean isPut(ChannelMember cm) {
		return cm.getStateTransition().isRightUnary();
	}

	protected boolean isDelete(ChannelMember cm) {
		boolean isDelete = false;
		Expression nextExp = cm.getStateTransition().getNextStateExpression();
		if (nextExp instanceof Term) {
			Symbol rootSymbol = ((Term) nextExp).getSymbol();
			if (rootSymbol.equals(DataConstraintModel.delete) || rootSymbol.equals(DataConstraintModel.remove)) {
				isDelete = true;
			} else if (rootSymbol.equals(DataConstraintModel.cond)) {
				Expression childExp = ((Term) nextExp).getChild(1);
				if (childExp instanceof Term) {
					rootSymbol = ((Term) childExp).getSymbol();
					if (rootSymbol.equals(DataConstraintModel.delete) || rootSymbol.equals(DataConstraintModel.remove)) {
						isDelete = true;
					}
				}
				childExp = ((Term) nextExp).getChild(2);
				if (childExp instanceof Term) {
					rootSymbol = ((Term) childExp).getSymbol();
					if (rootSymbol.equals(DataConstraintModel.delete) || rootSymbol.equals(DataConstraintModel.remove)) {
						isDelete = true;
					}
				}
			}
		}
		return isDelete;
	}

	protected void declareGetterAccessorInTheRootResource(ResourceNode resourceNode, TypeDeclaration rootComponent, 
			IPlatformSpecific platformSpec, ILanguageSpecific langSpec) {
		if (resourceNode.getResourceHierarchy().getParent() != null) {
			// For a non-root resource
			MethodDeclaration getterAccessor = null;
			List<VariableDeclaration> mainGetterParams = new ArrayList<>();
			String resourcePath = getGetterResourcePathAndPathParams(resourceNode.getPrimaryResourcePath(), mainGetterParams, platformSpec, langSpec);
			if (resourcePath.indexOf('/') > 0) {
				resourcePath = resourcePath.substring(resourcePath.indexOf('/'));
			} else {
				resourcePath = "";
			}
			if (mainGetterParams.size() > 0) {
				getterAccessor = langSpec.newMethodDeclaration(getterPrefix + getComponentName(resourceNode.getResourceHierarchy(), langSpec) + methoNameOfResourceState,
													false,
													getImplStateType(resourceNode.getResourceHierarchy(), langSpec),
													mainGetterParams);
			} else {
				getterAccessor = langSpec.newMethodDeclaration(getterPrefix + getComponentName(resourceNode.getResourceHierarchy(), langSpec) + methoNameOfResourceState,
													getImplStateType(resourceNode.getResourceHierarchy(), langSpec));
			}
			getterAccessor.setBody(new Block());
			ResourcePath resPath = resourceNode.getPrimaryResourcePath();
			Expression getState = getPullAccessor(platformSpec).getDirectStateAccessorFor(resPath, resPath.getRoot());
			getterAccessor.getBody().addStatement(langSpec.getReturnStatement(getState.toImplementation(new String[] {null})) + langSpec.getStatementDelimiter());
			if (!platformSpec.isMonolithic()) {
				((RestApiSpecific) platformSpec).addGetAnnotations(getterAccessor);
				if (resourcePath.length() > 0) {
					((RestApiSpecific) platformSpec).addPathAnnotation(getterAccessor, resourcePath);
				}
			}
			rootComponent.addMethod(getterAccessor);
		}
	}

	protected void declareUpdateAccessorInTheRootResource(ResourceNode resourceNode, String updateMethodName,
			DataTransferChannel ch, ChannelMember cm, ResourcePath srcResPath, ResourcePath dstResPath, TypeDeclaration rootComponent, 
			int inDegree, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) {
		ArrayList<VariableDeclaration> parameters;
		VariableDeclaration param;
		parameters = new ArrayList<>();
		String resourcePath = getUpdateResourcePathAndPathParams(dstResPath, parameters, true, platformSpec, langSpec);	// Path parameters to identify the self resource.
		for (Selector selector: ch.getAllSelectors()) {
			if (selector.getExpression() instanceof Variable) {
				Variable selVar = (Variable) selector.getExpression();
				VariableDeclaration chParam = langSpec.newVariableDeclaration(selVar.getType(), selVar.getName());
				if (!platformSpec.isMonolithic()) {
					((RestApiSpecific) platformSpec).addFormParamAnnotation(chParam, selVar.getName());
				}
				parameters.add(chParam);	// A channel parameter to specify the context of the collaboration.
			}
		}
		Type srcType = srcResPath.getResourceStateType();
		String srcResName = langSpec.toVariableName(getComponentName(srcResPath.getResourceHierarchy(), langSpec));
		param = langSpec.newVariableDeclaration(srcType, srcResName);
		if (!platformSpec.isMonolithic()) ((RestApiSpecific) platformSpec).addFormParamAnnotation(param, srcResName);
		parameters.add(param);	// The state of the source resource to carry the data-flow.
		for (ResourcePath refRes: ch.getReferenceResources()) {
			if (!refRes.equals(resourceNode.getInSideResource(ch))) {
				String refName = langSpec.toVariableName(getComponentName(refRes.getResourceHierarchy(), langSpec));
				param = langSpec.newVariableDeclaration(refRes.getResourceStateType(), refName);
				if (!platformSpec.isMonolithic()) {
					((RestApiSpecific) platformSpec).addFormParamAnnotation(param, refName);
				}
				parameters.add(param);
			}
		}
		MethodDeclaration updateAccessor = langSpec.newMethodDeclaration(updateMethodName, false, null, parameters);
		if (!platformSpec.isMonolithic()) {
			if (isPut(cm)) {
				((RestApiSpecific) platformSpec).addPutAnnotations(updateAccessor);
			} else {
				if (!isDelete(cm)) {
					((RestApiSpecific) platformSpec).addPostAnnotations(updateAccessor);
				} else {
					((RestApiSpecific) platformSpec).addDeleteAnnotations(updateAccessor);
				}
			}
		}
		if (inDegree > 1) {
			 // For each source resource, a child resource is defined in the destination resource so that its state can be updated separately.
			resourcePath += "/" + langSpec.toVariableName(srcResName);
		}
		if (!platformSpec.isMonolithic()) ((RestApiSpecific) platformSpec).addPathAnnotation(updateAccessor, resourcePath);
		
		// To make the accessor call the update method.
		Expression resExp = getPullAccessor(platformSpec).getDirectStateAccessorFor(cm.getResource(), cm.getResource().getRoot());
		List<String> args = new ArrayList<>();
		if (resExp instanceof Term) {
			// to access the parent
			if (((Term) resExp).getChildren().size() > 1 && ((Term) resExp).getChild(1) instanceof Variable) {
				args.add(((Variable)((Term) resExp).getChild(1)).getName());
			}
			resExp = ((Term) resExp).getChild(0);
		}
		String resourceAccess = resExp.toImplementation(new String[] {""});
		int v = 0;
		for (VariableDeclaration var: updateAccessor.getParameters()) {
			if (v < cm.getResource().getPathParams().size()) {
				if (cm.getResource().getPathParams().get(v) instanceof Variable) {
					args.add(((Variable) cm.getResource().getPathParams().get(v)).getName());
				} else if (cm.getResource().getPathParams().get(v) instanceof JsonAccessor) {
					args.add(((JsonAccessor) cm.getResource().getPathParams().get(v)).toImplementation(new String[] {}));	// ToDo.
				}
			} else {
				args.add(var.getName());
			}
			v++;
		}
		updateAccessor.addStatement(langSpec.getMethodInvocation(resourceAccess, updateMethodName, args));
		rootComponent.addMethod(updateAccessor);
	}

	protected void declareInputAccessorInTheRootResource(String inputMethodName,
			ArrayList<VariableDeclaration> rootInputParams, ChannelMember cm, String resourcePath,
			TypeDeclaration rootComponent, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) {
		MethodDeclaration rootInputAccessor;
		rootInputAccessor = langSpec.newMethodDeclaration(inputMethodName, false, null, rootInputParams);
		if (!platformSpec.isMonolithic()) {
			if (isPut(cm)) {
				((RestApiSpecific) platformSpec).addPutAnnotations(rootInputAccessor);
			} else {
				if (!isDelete(cm)) {
					((RestApiSpecific) platformSpec).addPostAnnotations(rootInputAccessor);
				} else {
					((RestApiSpecific) platformSpec).addDeleteAnnotations(rootInputAccessor);
				}
			}
			if (resourcePath.length() > 0) {
				((RestApiSpecific) platformSpec).addPathAnnotation(rootInputAccessor, resourcePath);
			}
		}
		rootComponent.addMethod(rootInputAccessor);
	}

	protected String getGetterResourcePathAndPathParams(ResourcePath resPath, List<VariableDeclaration> pathParams, 
			IPlatformSpecific platformSpec, ILanguageSpecific langSpec) {
		int v = 1;
		List<String> params = new ArrayList<>();
		for (Expression pathParam: resPath.getPathParams()) {
			if (pathParam instanceof Variable) {
				Variable var = (Variable) pathParam;
				String paramName = var.getName();
				params.add("{" + paramName + "}");
				VariableDeclaration param = langSpec.newVariableDeclaration(var.getType(), paramName);
				if (!platformSpec.isMonolithic()) ((RestApiSpecific) platformSpec).addPathParamAnnotation(param, paramName);
				pathParams.add(param);
			} else if (pathParam instanceof Term) {
				Term var = (Term) pathParam;
				String paramName = "v" + v;
				params.add("{" + paramName + "}");
				VariableDeclaration param = langSpec.newVariableDeclaration(var.getType(), paramName);
				if (!platformSpec.isMonolithic()) ((RestApiSpecific) platformSpec).addPathParamAnnotation(param, paramName);
				pathParams.add(param);
			}
		}
		return resPath.getResourceHierarchy().toResourcePath(params);
	}
	
	protected String getUpdateResourcePathAndPathParams(ResourcePath resPath, ArrayList<VariableDeclaration> pathParams, boolean isRestAPI, 
			IPlatformSpecific platformSpec, ILanguageSpecific langSpec) {
		int v = 1;
		List<String> params = new ArrayList<>();
		for (Expression pathParam: resPath.getPathParams()) {
			if (pathParam instanceof Variable) {
				Variable var = (Variable) pathParam;
				String paramName = null;
				if (isRestAPI) {
					paramName = var.getName();					
				} else {
					paramName = "self" + (v > 1 ? v : "");
				}
				params.add("{" + paramName  + "}");
				VariableDeclaration param = langSpec.newVariableDeclaration(var.getType(), paramName);
				if (isRestAPI) ((RestApiSpecific) platformSpec).addPathParamAnnotation(param, paramName);
				pathParams.add(param);
			} else if (pathParam instanceof Term) {
				Term var = (Term) pathParam;
				String paramName = null;
				if (isRestAPI) {
					paramName = "v" + v;					
				} else {
					paramName = "self" + (v > 1 ? v : "");
				}
				params.add("{" + paramName  + "}");
				VariableDeclaration param = langSpec.newVariableDeclaration(var.getType(), paramName);
				if (isRestAPI) ((RestApiSpecific) platformSpec).addPathParamAnnotation(param, paramName);
				pathParams.add(param);
			}
			v++;
		}
		return resPath.getResourceHierarchy().toResourcePath(params);
	}
	
	protected String getInputMethodResourcePathAndPathParams(ResourcePath resPath, ArrayList<VariableDeclaration> rootInputParams,
			IPlatformSpecific platformSpec, ILanguageSpecific langSpec) {
		int v = 1;
		List<String> params = new ArrayList<>();
		if (resPath.getLastParam() != null) {
			Expression pathParam = resPath.getLastParam();
			if (pathParam instanceof Variable) {
				Variable var = (Variable) pathParam;
				String paramName = var.getName();
				params.add("{" + paramName  + "}");
				VariableDeclaration param = langSpec.newVariableDeclaration(var.getType(), paramName);
				if (!platformSpec.isMonolithic()) ((RestApiSpecific) platformSpec).addPathParamAnnotation(param, paramName);
				rootInputParams.add(param);
			} else if (pathParam instanceof Term) {
				Term var = (Term) pathParam;
				String paramName = "v" + v;
				params.add("{" + paramName  + "}");
				VariableDeclaration param = langSpec.newVariableDeclaration(var.getType(), paramName);
				if (!platformSpec.isMonolithic()) ((RestApiSpecific) platformSpec).addPathParamAnnotation(param, paramName);
				rootInputParams.add(param);
			}
			v++;
		}
		if (resPath.getParent() != null) {
			for (Expression pathParam: resPath.getParent().getPathParams()) {
				if (pathParam instanceof Variable) {
					Variable var = (Variable) pathParam;
					String paramName = var.getName();
					params.add("{" + paramName  + "}");
					VariableDeclaration param = langSpec.newVariableDeclaration(var.getType(), paramName);
					if (!platformSpec.isMonolithic()) ((RestApiSpecific) platformSpec).addPathParamAnnotation(param, paramName);
					rootInputParams.add(param);
				} else if (pathParam instanceof Term) {
					Term var = (Term) pathParam;
					String paramName = "v" + v;
					params.add("{" + paramName  + "}");
					VariableDeclaration param = langSpec.newVariableDeclaration(var.getType(), paramName);
					if (!platformSpec.isMonolithic()) ((RestApiSpecific) platformSpec).addPathParamAnnotation(param, paramName);
					rootInputParams.add(param);
				}
				v++;
			}
		}
		return resPath.getResourceHierarchy().toResourcePath(params);
	}
	
	private void generatePullDataTransfer(MethodDeclaration methodBody, String fromResourceName, String fromResourcePath, Type fromResourceType, boolean doesAddFirst,
			IPlatformSpecific platformSpec, ILanguageSpecific langSpec) {
		RestApiSpecific restApiSpec = (RestApiSpecific) platformSpec;
		String varName = new String(fromResourceName);
		String respTypeName = fromResourceType.getInterfaceTypeName();
		String respImplTypeName = fromResourceType.getImplementationTypeName();
		String respConverter = "";
		if (DataConstraintModel.typeList.isAncestorOf(fromResourceType) && fromResourceType != DataConstraintModel.typeList) {
			Type compType = TypeInference.getListComponentType(fromResourceType);
			if (DataConstraintModel.typeTuple.isAncestorOf(compType)) {
				varName += "_json";
				String mapTypeName = convertFromEntryToMapType(compType, langSpec);
				respTypeName = "List<" + mapTypeName + ">";
				respConverter += langSpec.getVariableDeclaration(fromResourceType.getInterfaceTypeName(), fromResourceName) + langSpec.getAssignment() + langSpec.getConstructorInvocation(fromResourceType.getImplementationTypeName(), null) + langSpec.getStatementDelimiter() + "\n";
				respConverter += "for (" + mapTypeName + " i: " + varName + ") {\n";
				respConverter += "\t" + fromResourceName + ".add(" + getCodeForConversionFromMapToTuple(compType, "i", langSpec) + ")" + langSpec.getStatementDelimiter() + "\n";
				respConverter += "}";
				restApiSpec.addJsonException(methodBody);
			} else if (DataConstraintModel.typeMap.isAncestorOf(compType)) {
				// To do.
			}
		} else if (DataConstraintModel.typeTuple.isAncestorOf(fromResourceType)) {
			varName += "_json";
			respTypeName = convertFromEntryToMapType(fromResourceType, langSpec);
			respConverter += langSpec.getVariableDeclaration(fromResourceType.getInterfaceTypeName(), fromResourceName) + langSpec.getAssignment() + getCodeForConversionFromMapToTuple(fromResourceType, varName, langSpec) + langSpec.getStatementDelimiter();
			respImplTypeName = "HashMap";
		} else if (DataConstraintModel.typePair.isAncestorOf(fromResourceType)) {
			varName += "_json";
			respTypeName = convertFromEntryToMapType(fromResourceType, langSpec);
			respConverter += langSpec.getVariableDeclaration(fromResourceType.getInterfaceTypeName(), fromResourceName) + langSpec.getAssignment() + getCodeForConversionFromMapToPair(fromResourceType, varName, langSpec) + langSpec.getStatementDelimiter();
			respImplTypeName = "HashMap";
		} else if (DataConstraintModel.typeMap.isAncestorOf(fromResourceType)) {
			varName += "_json";
			respTypeName = convertFromEntryToMapType(fromResourceType, langSpec);
			respConverter += langSpec.getVariableDeclaration(fromResourceType.getInterfaceTypeName(), fromResourceName) + langSpec.getAssignment() + langSpec.getConstructorInvocation(fromResourceType.getImplementationTypeName(), null) + langSpec.getStatementDelimiter() + "\n";
			respConverter += getCodeForConversionFromMapToMap(fromResourceType, varName, fromResourceName, langSpec);
			respImplTypeName = "HashMap";
		}
		if (doesAddFirst) {
			if (respConverter.length() > 0) {
				methodBody.addFirstStatement(respConverter);
			}
			methodBody.addFirstStatement(langSpec.getVariableDeclaration(respTypeName, varName) + langSpec.getAssignment() + restApiSpec.getHttpMethodCallWithResponseStatement(restApiSpec.getBaseURL(), fromResourcePath, getterPrefix, respImplTypeName));
		} else {
			methodBody.addStatement(langSpec.getVariableDeclaration(respTypeName, varName) + langSpec.getAssignment() + restApiSpec.getHttpMethodCallWithResponseStatement(restApiSpec.getBaseURL(), fromResourcePath, getterPrefix, respImplTypeName));
			if (respConverter.length() > 0) {
				methodBody.addStatement(respConverter);
			}
		}
	}
	
	private String convertFromEntryToMapType(Type type, ILanguageSpecific langSpec) {
		String mapTypeName = null;
		if (DataConstraintModel.typePair.isAncestorOf(type)) {
			Type compType = TypeInference.getPairComponentType(type);
			String wrapperType = DataConstraintModel.getWrapperType(compType);
			if (wrapperType != null) {
				mapTypeName = langSpec.newMapType(DataConstraintModel.typeString, wrapperType).getInterfaceTypeName();
			} else {
				mapTypeName = langSpec.newMapType(DataConstraintModel.typeString, compType.getInterfaceTypeName()).getInterfaceTypeName();
			}
		} else if (DataConstraintModel.typeMap.isAncestorOf(type)) {
			List<Type> compTypes = TypeInference.getMapComponentTypes(type);
			String wrapperType = DataConstraintModel.getWrapperType(compTypes.get(1));
			if (wrapperType != null) {
				mapTypeName = langSpec.newMapType(DataConstraintModel.typeString, wrapperType).getInterfaceTypeName();
			} else {
				mapTypeName = langSpec.newMapType(DataConstraintModel.typeString, compTypes.get(1).getInterfaceTypeName()).getInterfaceTypeName();
			}
		} else {
			mapTypeName = type.getInterfaceTypeName();
			mapTypeName = mapTypeName.replace(DataConstraintModel.typeTuple.getInterfaceTypeName(), DataConstraintModel.typeMap.getInterfaceTypeName());
			for (int idx = mapTypeName.indexOf("<", 0); idx >= 0; idx = mapTypeName.indexOf("<", idx + 1)) {		// Java specific
				int to = mapTypeName.indexOf(",", idx);																// Java specific
				if (to > idx) {
					mapTypeName = mapTypeName.substring(0, idx + 1) + DataConstraintModel.typeString.getInterfaceTypeName() + mapTypeName.substring(to);		// All elements except for the last one have the string type.
				}
			}
		}
		return mapTypeName;
	}

	private String getCodeForConversionFromMapToTuple(Type tupleType, String mapVar, ILanguageSpecific langSpec) {
		String decoded = "$x";
		List<Type> elementsTypes = TypeInference.getTupleComponentTypes(tupleType);
		String elementBase = mapVar;
		for (Type elmType: elementsTypes.subList(0, elementsTypes.size() - 1)) {
			elementBase = langSpec.getFirstEntryFromMapExp(elementBase);											// elementBase.entrySet().iterator().next()
			if (elmType == DataConstraintModel.typeBoolean
					|| elmType == DataConstraintModel.typeInt
					|| elmType == DataConstraintModel.typeLong
					|| elmType == DataConstraintModel.typeFloat
					|| elmType == DataConstraintModel.typeDouble) {
				String getKey = langSpec.getMethodInvocation(elementBase, DataConstraintModel.fst.getImplName());	// elementBase.getKey()
				String elmVal = langSpec.getStringToValueExp(elmType.getImplementationTypeName(), getKey);			// Integer.parseInt(elementBase.getKey())
				decoded = decoded.replace("$x", langSpec.getPairExp(elmVal, "$x"));									// new AbstractMap.SimpleEntry<>(Integer.parseInt(elementBase.getKey()), $x)
			} else if (elmType == DataConstraintModel.typeString) {
				String getKey = langSpec.getMethodInvocation(elementBase, DataConstraintModel.fst.getImplName());	// elementBase.getKey()
				decoded = decoded.replace("$x", langSpec.getPairExp(getKey, "$x"));									// new AbstractMap.SimpleEntry<>(elementBase.getKey(), $x)
			} else {
				// To do.
			}
			elementBase = langSpec.getMethodInvocation(elementBase, DataConstraintModel.snd.getImplName());			// elementBase.getValue()
		}
		decoded = decoded.replace("$x", elementBase);
		return decoded;
	}
	
	private String getCodeForConversionFromMapToPair(Type pairType, String mapVar, ILanguageSpecific langSpec) {
		String decoded = "$x";
		decoded = decoded.replace("$x", "new Pair<>(" + mapVar + ".get(\"left\"), $x)");
		decoded = decoded.replace("$x", mapVar + ".get(\"right\")");
		return decoded;
	}

	private String getCodeForConversionFromMapToMap(Type mapType, String mapVal, String mapVar, ILanguageSpecific langSpec) {
		List<Type> elementsTypes = TypeInference.getMapComponentTypes(mapType);
		Type keyType = elementsTypes.get(0);
//		Type valType = elementsTypes.get(1);
		String decoded = "";
		if (keyType == DataConstraintModel.typeBoolean
				|| keyType == DataConstraintModel.typeInt
				|| keyType == DataConstraintModel.typeLong
				|| keyType == DataConstraintModel.typeFloat
				|| keyType == DataConstraintModel.typeDouble) {
			String keyVal = langSpec.getStringToValueExp(keyType.getImplementationTypeName(), "k");
			String getInvocation = langSpec.getMethodInvocation(mapVal, DataConstraintModel.lookup.getImplName(), List.of(keyVal));
			decoded += langSpec.getForStatementForMap("k", DataConstraintModel.typeString.getInterfaceTypeName(), mapVal) + "\n";
			decoded += "\t" + langSpec.getMethodInvocation(mapVar, DataConstraintModel.insert.getImplName(), List.of(keyVal, getInvocation)) + langSpec.getStatementDelimiter() + "\n";
			decoded += langSpec.getEndForStatement("k");
		} else if (keyType == DataConstraintModel.typeString) {
			decoded += mapVar + langSpec.getAssignment() + mapVal + langSpec.getStatementDelimiter();
		}
		return decoded;
	}
}