package generators;

import java.util.AbstractMap;
import java.util.ArrayList;
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 java.util.Set;
import java.util.AbstractMap.SimpleEntry;

import code.ast.Block;
import code.ast.CompilationUnit;
import code.ast.FieldDeclaration;
import code.ast.MethodDeclaration;
import code.ast.TypeDeclaration;
import code.ast.VariableDeclaration;
import models.Edge;
import models.Node;
import models.algebra.Constant;
import models.algebra.Expression;
import models.algebra.Field;
import models.algebra.InvalidMessage;
import models.algebra.Parameter;
import models.algebra.ParameterizedIdentifierIsFutureWork;
import models.algebra.Position;
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.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, ArrayList<ResourceNode> components, Map<ResourceHierarchy, Set<ResourceHierarchy>> dependedRootComponentGraph, 
			TypeDeclaration mainComponent, MethodDeclaration mainConstructor, ArrayList<CompilationUnit> codes, ILanguageSpecific langSpec) {
		Map<ResourceHierarchy, TypeDeclaration> resourceComponents = new HashMap<>();
		Map<ResourceHierarchy, MethodDeclaration> resourceConstructors = new HashMap<>();
		List<Map.Entry<ResourceHierarchy, VariableDeclaration>> constructorParams = new ArrayList<>();
		List<Map.Entry<ResourceHierarchy, String>> constructorStatements = new ArrayList<>();
		Map<MethodDeclaration, Map.Entry<Expression, ResourceHierarchy>> updateStatements = new HashMap<>();
		
		// 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);
					resourceComponents.put(resourceNode.getResourceHierarchy(), component);
					CompilationUnit cu = langSpec.newCompilationUnit(component);
					codes.add(cu);
					
					// Declare the constructor.
					MethodDeclaration constructor = declareConstructor(resourceNode, component, dependedRootComponentGraph, depends, langSpec);
					
					if (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, langSpec);
					
					// Declare the accessor method in the main component to call the getter method.
					declareAccessorInMainComponent(mainComponent, resourceNode, stateGetter, langSpec);
					
					// Declare the getter methods in this resource to obtain the children resources.
					declareChildGetterMethod(resourceNode, component, langSpec);
				}
			}
		}
		
		// For each components (2nd pass).
		for (Node componentNode: components) {
			// Declare this resource.
			ResourceNode resourceNode = (ResourceNode) componentNode;
			Type resStateType = getImplStateType(resourceNode.getResourceHierarchy(), langSpec);
			TypeDeclaration component = null;
			TypeDeclaration parentComponent = null;
			if (generatesComponent(resourceNode.getResourceHierarchy())) {
				component = resourceComponents.get(resourceNode.getResourceHierarchy());				
			}
			if (resourceNode.getResourceHierarchy().getParent() != null) {
				parentComponent = resourceComponents.get(resourceNode.getResourceHierarchy().getParent());
			}
			
			// Declare cache fields and update methods in this resource.
			Map.Entry<List<String>, Map<MethodDeclaration, Map.Entry<Expression, ResourceHierarchy>>> initStatementsAndUpdateUpdates = declareCacheFieldsAndUpdateMethods(resourceNode, component, parentComponent, 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, ResourceHierarchy>> entry: initStatementsAndUpdateUpdates.getValue().entrySet()) {
				updateStatements.put(entry.getKey(), entry.getValue());
			}
			
			// Declare the fields to refer to other resources in the parent/this component, and the state field in the parent component.
			declareFieldsToReferToOtherResourcesAndStateFieldInParentComponent(resourceNode, component, parentComponent, constructorParams, langSpec);
			
			// Declare the getter method in this resource to obtain the state.
			if (component == null) {
				MethodDeclaration stateGetter = declareStateGetterMethodInParent(resourceNode, parentComponent, resStateType, langSpec);
				
				if (stateGetter != null) {
					// Declare the accessor method in the main component to call the getter method.
					declareAccessorInMainComponent(mainComponent, resourceNode, stateGetter, langSpec);
				}
			}
			
			// Declare input methods in this component and the main component.
			Map.Entry<List<String>, Map<MethodDeclaration, Map.Entry<Expression, ResourceHierarchy>>> initStatementsAndInputUpdates = declareInputMethodsInThisAndMainComponents(resourceNode, component, parentComponent, mainComponent, model, 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, 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 childRes = updateStatements.get(method).getValue();
			TypeDeclaration childComponent = resourceComponents.get(childRes);
			addUpdateStatementWithConstructorInvocationToMethod(method, updateExp, childRes, childComponent, langSpec);
		}
	}

	private static List<VariableDeclaration> addConstructorParameters(ResourceHierarchy resource, Map<ResourceHierarchy, TypeDeclaration> resourceComponents,
			Map<ResourceHierarchy, MethodDeclaration> resourceConstructors, List<Entry<ResourceHierarchy, VariableDeclaration>> constructorParams, ILanguageSpecific langSpec) {
		List<VariableDeclaration> params = new ArrayList<>();
		for (ResourceHierarchy child: resource.getChildren()) {
			params.addAll(addConstructorParameters(child, resourceComponents, resourceConstructors, constructorParams, langSpec));
		}
		for (Entry<ResourceHierarchy, VariableDeclaration> paramEnt: constructorParams) {
			if (paramEnt.getKey().equals(resource)) {
				params.add(paramEnt.getValue());
			}
		}
		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 childRes, TypeDeclaration childComponent, ILanguageSpecific langSpec) {
		Type replacedJsonType = childRes.getResourceStateType();
		String replacingClassName = getComponentName(childRes, langSpec);
		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().equals(replacedJsonType)) {
				MethodDeclaration childConstructor = getConstructor(childComponent);
				List<String> params = new ArrayList<>();
				for (VariableDeclaration var: childConstructor.getParameters()) {
					JsonAccessor jsonMember = new JsonAccessor(DataConstraintModel.dot);
					jsonMember.addChild(jsonTerm);
					jsonMember.addChild(new Constant("\"" + var.getName() + "\""));
					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();
			}
		}
		String[] sideEffects = new String[] {""};
		String newState = exp.toImplementation(sideEffects);
		String updateStatement;
		if (exp instanceof Term && ((Term) exp).getSymbol().isImplWithSideEffect()) {
			updateStatement = sideEffects[0];	
		} 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()).getOptions().get(0) == 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()).getOptions().get(0) != 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, List<Map.Entry<ResourceHierarchy, 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);
		} 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.getFieldInitializer(resStateType, resourceNode.getResourceHierarchy().getInitialValue()));
						component.addField(stateField);
					}
				}
			}
		}
	}

	private void declareFieldsToReferToOtherResourcesAndStateFieldInParentComponent(ResourceNode resourceNode, TypeDeclaration component, TypeDeclaration parentComponent, List<Map.Entry<ResourceHierarchy, VariableDeclaration>> constructorParams, ILanguageSpecific langSpec) {
		// Declare reference fields for push data transfer.
		boolean noPullTransfer = true;
		for (Edge resToCh : resourceNode.getOutEdges()) {
			DataFlowEdge re = (DataFlowEdge) resToCh;
			DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel();
			// Check if the source 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: re.getDestination().getOutEdges()) {
				if (chToRes.getDestination() instanceof ResourceNode) {
					ResourceHierarchy dstRes = ((ResourceNode) chToRes.getDestination()).getResourceHierarchy();
					// Check if the destination 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) re.getAttribute()).getOptions().get(0) == 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 (component != null) {
							// A component is created for this resource.
							if (resourceNode.getResourceHierarchy() != dstRes) {
								component.addField(refFieldForPush);
								if (!outsideOutputResource) {
									constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy(), refVarForPush));
								}
							}
						} else {
							// No component is created for this resource.
							if (resourceNode.getParent().getResourceHierarchy() != dstRes) {
								parentComponent.addField(refFieldForPush);
								if (!outsideOutputResource) {
									constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), 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);
										constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy(), 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);
										constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), refRootVarForPush));
									}
								}
							}
						}
					}
				}
			}
		}
		// Declare reference fields for pull data transfer.
		for (Edge chToRes : resourceNode.getInEdges()) {
			for (Edge resToCh: chToRes.getSource().getInEdges()) {
				DataFlowEdge re = (DataFlowEdge) resToCh;
				DataTransferChannel ch = ((ChannelNode) re.getDestination()).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;
					}
				}
				if ((((PushPullAttribute) re.getAttribute()).getOptions().get(0) != 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 (component != null) {
						// A component is created for this resource.
						if (resourceNode.getResourceHierarchy() != srcRes.getResourceHierarchy()) {
							component.addField(refFieldForPull);
							if (!outsideInputResource) {
								constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy(), refVarForPull));
							}
						}
					} else {
						// No component is created for this resource.
						if (resourceNode.getParent().getResourceHierarchy() != srcRes.getResourceHierarchy()) {
							parentComponent.addField(refFieldForPull);
							if (!outsideInputResource) {
								constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), 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);
									constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy(), 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);
									constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), 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);
					constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), langSpec.newVariableDeclaration(res.getResourceStateType(), resName)));
				}
			}
		}
	}
	
	
	private MethodDeclaration declareStateGetterMethod(ResourceNode resourceNode, TypeDeclaration component, Type resStateType, ILanguageSpecific langSpec) {
		// Declare the getter method of the resource state.
		MethodDeclaration stateGetter = langSpec.newMethodDeclaration(getterOfResourceState, resStateType);
		component.addMethod(stateGetter);
		
		if (((StoreAttribute) resourceNode.getAttribute()).isStored()) {
			fillStateGetterMethod(stateGetter, resourceNode.getResourceHierarchy(), resStateType, langSpec);
		} else {	
			// invocations to other getter methods when at least one incoming data-flow edges is PULL-style.
			addOtherGetterInvocationsToStateGatter(stateGetter, resourceNode, langSpec);
		}
		
		return stateGetter;
	}
	
	private MethodDeclaration declareStateGetterMethodInParent(ResourceNode resourceNode, TypeDeclaration parentComponent, Type resStateType, ILanguageSpecific langSpec) {
		// Check duplication.
		String getterName = getterPrefix + getComponentName(resourceNode.getResourceHierarchy(), langSpec);
		for (MethodDeclaration method: parentComponent.getMethods()) {
			if (method.getName().equals(getterName)) return null;
		}
		
		// Declare the getter method of the resource state.
		List<VariableDeclaration> getterParams = new ArrayList<>();
		int v = 1;
		for (Selector param: resourceNode.getSelectors()) {
			if (param.getExpression() instanceof Variable) {
				Variable var = (Variable) param.getExpression();
				getterParams.add(new VariableDeclaration(var.getType(), var.getName()));
			} else if (param.getExpression() instanceof Term) {
				Term var = (Term) param.getExpression();
				getterParams.add(new VariableDeclaration(var.getType(), "v" + v));
			}
			v++;
		}
		MethodDeclaration stateGetter = null;
		if (getterParams.size() == 0) {
			stateGetter = new MethodDeclaration(getterName, resStateType);
		} else {
			stateGetter = new MethodDeclaration(getterName, false, resStateType, getterParams);
		}
		if (parentComponent != null) {
			parentComponent.addMethod(stateGetter);
		}
		
		if (((StoreAttribute) resourceNode.getAttribute()).isStored()) {
			fillChildGetterMethod(stateGetter, resourceNode.getResourceHierarchy(), resourceNode.getResourceHierarchy().getParent().getResourceStateType(), langSpec);
		} else {	
			addOtherGetterInvocationsToStateGatter(stateGetter, resourceNode, langSpec);
		}
		
		return stateGetter;
	}

	private void addOtherGetterInvocationsToStateGatter(MethodDeclaration stateGetter, ResourceNode resourceNode,
			ILanguageSpecific langSpec) {
		boolean isContainedPush = false;
		DataTransferChannel ch = null;
		HashMap<ChannelMember, IResourceStateAccessor> inputResourceToStateAccessor = new HashMap<>();
		for (Edge chToRes: resourceNode.getInEdges()) {
			DataTransferChannel 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()).getOptions().get(0) == PushPullValue.PUSH) {
					// PUSH transfer
					isContainedPush = true;
					inputResourceToStateAccessor.put(in, getPushAccessor());
				} else {
					// PULL transfer
					inputResourceToStateAccessor.put(in, getPullAccessor());
					ch = ((ChannelNode) resToCh.getDestination()).getChannel();		// pull containing input side channel is always one.
				}
			}
		}
		// for reference channel members.
		for (ChannelMember c: ch.getReferenceChannelMembers()) {
			inputResourceToStateAccessor.put(c, getPullAccessor());			// by pull data transfer
		}
		
		// generate a return statement.
		try {
			for (ChannelMember out: ch.getOutputChannelMembers()) {
				if (resourceNode.getInSideResources().contains(out.getResource())) {
					String[] sideEffects = new String[] {""};
					if (!isContainedPush) {
						// All incoming edges are in PULL-style.
						String curState = ch.deriveUpdateExpressionOf(out, getPullAccessor()).getKey().toImplementation(sideEffects);
						stateGetter.addStatement(sideEffects[0] + langSpec.getReturnStatement(curState) + langSpec.getStatementDelimiter());
					} else {
						// At least one incoming edge is in PUSH-style.
						String curState = ch.deriveUpdateExpressionOf(out, getPullAccessor(), inputResourceToStateAccessor).getKey().toImplementation(sideEffects);
						stateGetter.addStatement(sideEffects[0] + langSpec.getReturnStatement(curState) + langSpec.getStatementDelimiter());
					}
					break;
				}
			}
		} catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork
				| InvalidMessage | UnificationFailed | ValueUndefined e) {
			e.printStackTrace();
		}
	}
	
	private void declareChildGetterMethod(ResourceNode resourceNode, TypeDeclaration component, ILanguageSpecific langSpec) {
		// Declare the getter methods in this resource to obtain the children resources.
		Set<ResourceHierarchy> children = new HashSet<>();
		for (ResourceNode child: resourceNode.getChildren()) {
			if (generatesComponent(child.getResourceHierarchy())) {
				// A component for the child is generated.
				if (!children.contains(child.getResourceHierarchy())) {
					children.add(child.getResourceHierarchy());
					List<VariableDeclaration> params = new ArrayList<>();
					int v = 1;
					for (Selector param: child.getSelectors()) {
						if (param.getExpression() instanceof Variable) {
							Variable var = (Variable) param.getExpression();
							params.add(langSpec.newVariableDeclaration(var.getType(), var.getName()));
						} else if (param.getExpression() instanceof Term) {
							Term var = (Term) param.getExpression();
							params.add(langSpec.newVariableDeclaration(var.getType(), "v" + v));
						}
						v++;
					}
					String childCompName = getComponentName(child.getResourceHierarchy(), langSpec);
					Type childType = new Type(childCompName, childCompName);
					MethodDeclaration childGetter = null;
					if (params.size() == 0) {
						childGetter = langSpec.newMethodDeclaration(getterPrefix + childCompName, childType);
					} else {
						childGetter = langSpec.newMethodDeclaration(getterPrefix + childCompName, false, childType, params);
					}
					
					fillChildGetterMethod(childGetter, child.getResourceHierarchy(), resourceNode.getResourceStateType(), langSpec);
					component.addMethod(childGetter);
				}
			}
		}
	}
	
	private Map.Entry<List<String>, Map<MethodDeclaration, Map.Entry<Expression, ResourceHierarchy>>> declareCacheFieldsAndUpdateMethods(ResourceNode resourceNode, TypeDeclaration component, TypeDeclaration parentComponent, 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, ResourceHierarchy>> updateStatements = new HashMap<>();
		for (Edge chToRes: resourceNode.getInEdges()) {
			for (Edge resToCh: chToRes.getSource().getInEdges()) {
				DataTransferChannel ch = ((ChannelNode) resToCh.getDestination()).getChannel();
				DataFlowEdge re = (DataFlowEdge) resToCh;
				ResourcePath srcResPath = ((ResourceNode) re.getSource()).getOutSideResource(ch);
				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: ch.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;
				ResourcePath dstResPath = null;
				for (ChannelMember cm: ch.getOutputChannelMembers()) {
					if (resourceNode.getInSideResources().contains(cm.getResource())) {
						dstResPath = cm.getResource();
						if (cm.isOutside()) {
							outsideOutputResource = true;	// Regarded as push transfer.
							break;
						}
					}
				}
				if ((((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) {
					// for push data transfer
					
					// Declare an update method in the type of the destination resource.
					ArrayList<VariableDeclaration> parameters = new ArrayList<>();
					int v = 1;
					for (Expression exp: dstResPath.getPathParams()) {
						if (exp instanceof Variable) {
							Variable pathVar = (Variable) exp;
							String varName = "self" + (v > 1 ? v : "");
//							String varName = pathVar.getName();
							VariableDeclaration pathParam = langSpec.newVariableDeclaration(pathVar.getType(), varName);
							parameters.add(pathParam);	// A path parameter to identify the self resource.
						}
						v++;
					}
					for (Selector selector: ch.getAllSelectors()) {
						if (selector.getExpression() instanceof Variable) {
							Variable selVar = (Variable) selector.getExpression();
							VariableDeclaration chParam = langSpec.newVariableDeclaration(selVar.getType(), selVar.getName());
							parameters.add(chParam);	// A channel parameter to specify the context of the collaboration.
						}
					}
					parameters.add(langSpec.newVariableDeclaration(srcResPath.getResourceStateType(), srcResPath.getLeafResourceName()));			// The state of the source resource to carry the data-flow.			
					// For the refs.
					for (ResourcePath ref: ch.getReferenceResources()) {
						if (!resourceNode.getInSideResources().contains(ref)) {
							parameters.add(langSpec.newVariableDeclaration(ref.getResourceStateType(), ref.getLeafResourceName()));
						}
					}
					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);
							component.addMethod(update);
						}
					} 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);
							parentComponent.addMethod(update);
						}
					}
					
					// Add a statement to update the state field
					if (((StoreAttribute) resourceNode.getAttribute()).isStored()) {
						try {
							for (ChannelMember out: ch.getOutputChannelMembers()) {
								if (resourceNode.getInSideResources().contains(out.getResource())) {
									Expression updateExp = null;
									if (ch.getReferenceChannelMembers().size() == 0) {
										updateExp = ch.deriveUpdateExpressionOf(out, getPushAccessor()).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());
										}
										for (ChannelMember c: ch.getReferenceChannelMembers()) {
											inputResourceToStateAccessor.put(c, getRefAccessor());
										}
										updateExp = ch.deriveUpdateExpressionOf(out, getPushAccessor(), 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 childRes = outRes.getChildren().iterator().next();
										Type childStateType = childRes.getResourceStateType();
										if (DataConstraintModel.typeJson.isAncestorOf(childStateType)) {
											updateStatements.put(update, new AbstractMap.SimpleEntry<>(updateExp, childRes));
											break;
										}
									}
									// 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];
										} 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 (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();
										}
									}
									if (numOfOutResourcesWithTheSameHierarchy == 1) {
										update.addFirstStatement(updateStatement);
									} else {
										Term conditions = null;
										int i = 1;
										Map<ChannelMember, Entry<ResourcePath, Set<ChannelMember>>> resourcePaths = ch.fillOutsideResourcePaths(out, JerseyCodeGenerator.pushAccessor);
										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 : "")), 
														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 (resToCh.getDestination().getIndegree() > 1 
							|| (resToCh.getDestination().getIndegree() == 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. 
							FieldDeclaration cacheField = langSpec.newFieldDeclaration(
																	srcResPath.getResourceStateType(), 
																	srcResPath.getLeafResourceName(), 
																	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 out: ch.getOutputChannelMembers()) {
							if (resourceNode.getInSideResources().contains(out.getResource())) {
								try {
									resourcePaths = ch.fillOutsideResourcePaths(out, getPullAccessor());
								} 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().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).
					for (Edge resToCh2: resourceNode.getOutEdges()) {
						DataFlowEdge dOut = (DataFlowEdge) resToCh2;
						DataTransferChannel ch2 = ((ChannelNode) resToCh2.getDestination()).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: ch2.getInputChannelMembers()) {
							if (resourceNode.getOutSideResources().contains(cm.getResource())) {
								if (cm.isOutside()) {
									outsideInputResource2 = true;	// Regarded as pull transfer.
								}
								in = cm;
							}
							if (cm.isOutside()) {
								outsideInputMembers2.add(cm);
							}
						}
						for (Edge chToRes2: resToCh2.getDestination().getOutEdges()) {
							ResourceNode dstNode = ((ResourceNode) chToRes2.getDestination());
							// Check if the output resource is outside of the channel scope.
							boolean outsideOutputResource2 = false;
							ChannelMember out = null;
							for (ChannelMember cm: ch2.getOutputChannelMembers()) {
								if (dstNode.getInSideResources().contains(cm.getResource())) {
									out = cm;
									if (cm.isOutside()) {
										outsideOutputResource2 = true;
										break;
									}
								}
							}
							if ((((PushPullAttribute) dOut.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource2) || outsideOutputResource2) {
								// PUSH transfer
								List<String> params = new ArrayList<>();								
								// Values of path parameters.
								for (Expression pathParam: out.getResource().getPathParams()) {
									if (pathParam instanceof Variable) {
										Variable pathVar = (Variable) pathParam;
										params.add(pathVar.getName());
									}
								}
								// Values of channel parameters.
								for (Selector selector: ch.getAllSelectors()) {
									if (selector.getExpression() instanceof Variable) {
										Variable selVar = (Variable) selector.getExpression();
										params.add(selVar.getName());
									}
								}
								// Value of the source side (input side) resource.
								ResourceHierarchy srcRes2 = resourceNode.getResourceHierarchy();
								if (generatesComponent(srcRes2)) {
									params.add(langSpec.getFieldAccessor(fieldOfResourceState));
								} else {
									params.add(langSpec.getFieldAccessor(langSpec.toVariableName(srcRes2.getResourceName())));
									srcRes2 = srcRes2.getParent();									
								}
								Map<MethodDeclaration, Set<ResourcePath>> referredResources = new HashMap<>(); 
								Set<ResourcePath> referredSet = referredResources.get(update);
								for (ChannelMember rc: ch2.getReferenceChannelMembers()) {
									// to get the value of reference member.
									ResourcePath ref = rc.getResource();
									if (referredSet == null) {
										referredSet = new HashSet<>();
										referredResources.put(update, referredSet);
									}
									if (!resourceNode.getInSideResources().contains(ref)) {
										String refVarName = ref.getLeafResourceName();
										if (!referredSet.contains(ref)) {
											referredSet.add(ref);
											Expression refGetter = getPullAccessor().getCurrentStateAccessorFor(rc, in);
											String[] sideEffects = new String[] {""};
											String refExp = refGetter.toImplementation(sideEffects);
											String refTypeName = ref.getResourceStateType().getInterfaceTypeName();
											update.addStatement(sideEffects[0] + langSpec.getVariableDeclaration(refTypeName, refVarName) + langSpec.getAssignment() + refExp + langSpec.getStatementDelimiter());
										}
										params.add(refVarName);
									}
								}
								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) {
									// The destination resource is not outside.
									if (srcRes2 != dstRes) {
										update.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, params) 
												+ langSpec.getStatementDelimiter());	// this.dst.updateDstFromSrc(value, refParams);
									} else {
										update.addStatement(langSpec.getMethodInvocation(updateMethodName, params)
												+ langSpec.getStatementDelimiter());	// this.updateDstFromSrc(value, refParams);
									}
								} else {
									// Use the reference field to refer to outside destination resource.									
									update.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, params) 
											+ langSpec.getStatementDelimiter());	// this.dst.updateDstFromSrc(value, refParams);
								}
							}
						}
						if (outsideInputMembers2.size() > 0) {
							if (!generatesComponent(resourceNode.getResourceHierarchy())) {
								// srcRes2 does not have a component.
								ResourcePath srcRes2 = resourceNode.getOutSideResource(ch2);
								for (ChannelMember out: ch2.getOutputChannelMembers()) {
									if (!generatesComponent(out.getResource().getResourceHierarchy())) {
										// Also dstRes2 does not have a component.
										ResourcePath dstRes2 = out.getResource();
										if (srcRes2.getParent().equals(dstRes2.getParent())) {
											Map<ChannelMember, Entry<ResourcePath, Set<ChannelMember>>> resourcePaths = null;
											try {
												resourcePaths = ch2.fillOutsideResourcePaths(out, getPullAccessor());
												if (resourcePaths != null && resourcePaths.size() > 0) {
													for (ChannelMember outsideMember: outsideInputMembers2) {
														for (ChannelMember dependingMember: resourcePaths.get(outsideMember).getValue()) {
															if (dependingMember.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().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);
																}
															}
														}
													}
												}
											} 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, ResourceHierarchy>>> declareInputMethodsInThisAndMainComponents(ResourceNode resourceNode, TypeDeclaration component,
			TypeDeclaration parentComponent, TypeDeclaration mainComponent, DataTransferModel model, ILanguageSpecific langSpec) {
		// Declare input methods.
		String resName = resourceNode.getResourceName();
		String resComponentName = langSpec.toComponentName(resName);
		List<String> constructorStatements = new ArrayList<>();
		Map<MethodDeclaration, Map.Entry<Expression, ResourceHierarchy>> inputStatements = new HashMap<>();
		for (Channel ch : model.getInputChannels()) {
			for (ChannelMember out : ((DataTransferChannel) ch).getOutputChannelMembers()) {
				if (resourceNode.getInSideResources().contains(out.getResource())) {
					Expression message = out.getStateTransition().getMessageExpression();
					MethodDeclaration input = null;
					MethodDeclaration inputAccessor = null;
					if (message instanceof Term) {
						// Declare an input method in this component.
						ArrayList<VariableDeclaration> resInputParams = new ArrayList<>();
						ArrayList<VariableDeclaration> mainInputParams = new ArrayList<>();
						// 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()));
								}
							}
						}
						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.
						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());
											}
										}
									}
								}
							}
						}
					} 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++;
							}
						}
						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.
						String messageSymbol = ((Variable) message).getName();
						inputAccessor = getMethod(mainComponent, messageSymbol);
						if (inputAccessor == null) {
							if (mainInputParams.size() == 0) {
								inputAccessor = langSpec.newMethodDeclaration(messageSymbol, null);
							} else {
								inputAccessor = langSpec.newMethodDeclaration(messageSymbol, false, null, mainInputParams);
							}
							mainComponent.addMethod(inputAccessor);
						}
					}
					
					// 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 = JavaCodeGenerator.pullAccessor.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().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[] {null});
						// 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()).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 childRes = outRes.getChildren().iterator().next();
								Type childStateType = childRes.getResourceStateType();
								if (DataConstraintModel.typeJson.isAncestorOf(childStateType)) {
									inputStatements.put(input, new AbstractMap.SimpleEntry<>(updateExp, childRes));
									updateExp = null;
								}
							}
							// 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];	
									} 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 (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).
						for (Edge resToCh: resourceNode.getOutEdges()) {
							DataFlowEdge dOut = (DataFlowEdge) resToCh;
							DataTransferChannel ch2 = ((ChannelNode) resToCh.getDestination()).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: ch2.getInputChannelMembers()) {
								if (resourceNode.getOutSideResources().contains(cm.getResource())) {
									if (cm.isOutside()) {
										outsideInputResource2 = true;	// Regarded as pull transfer.
									}
									in = cm;
								}
								if (cm.isOutside()) {
									outsideInputMembers2.add(cm);
								}
							}
							for (Edge chToRes: resToCh.getDestination().getOutEdges()) {
								ResourceNode dstNode = ((ResourceNode) chToRes.getDestination());
								// 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;
										}
									}
								}
								if ((((PushPullAttribute) dOut.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource2) || outsideOutputResource2) {
									// PUSH transfer
									Map<MethodDeclaration, Set<ResourcePath>> referredResources = new HashMap<>(); 
									List<String> params = new ArrayList<>();
									// Values of path parameters.
									for (Expression pathParam: out2.getResource().getPathParams()) {
										if (pathParam instanceof Variable) {
											Variable pathVar = (Variable) pathParam;
											params.add(pathVar.getName());
										}
									}
									// Values of channel parameters.
									for (Selector selector: ch2.getAllSelectors()) {
										if (selector.getExpression() instanceof Variable) {
											Variable selVar = (Variable) selector.getExpression();
											params.add(selVar.getName());
										}
									}
									// Value of the source side (input side) resource.
									ResourceHierarchy srcRes = resourceNode.getResourceHierarchy();
									if (generatesComponent(srcRes)) {
										params.add(langSpec.getFieldAccessor(fieldOfResourceState));
									} else {
										params.add(langSpec.getFieldAccessor(langSpec.toVariableName(srcRes.getResourceName())));
										srcRes = srcRes.getParent();
									}
									Set<ResourcePath> referredSet = referredResources.get(input);
									for (ChannelMember rc: ch2.getReferenceChannelMembers()) {
										// to get the value of reference member.
										ResourcePath ref = rc.getResource();
										if (referredSet == null) {
											referredSet = new HashSet<>();
											referredResources.put(input, referredSet);
										}
										if (!resourceNode.getOutSideResources().contains(ref)) {
											String refVarName = ref.getLeafResourceName();
											if (!referredSet.contains(ref)) {
												referredSet.add(ref);
												Expression refGetter = getPullAccessor().getCurrentStateAccessorFor(rc, in);
												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());
											}
											params.add(refVarName);
										}
									}
									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) {
										// The destination resource is not outside.
										if (srcRes != dstRes) {
											input.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, params)
													+ langSpec.getStatementDelimiter());	// this.dst.updateDstFromSrc(value, refParams);
										} else {
											input.addStatement(langSpec.getMethodInvocation(updateMethodName, params)
													+ langSpec.getStatementDelimiter());	// this.updateDstFromSrc(value, refParams);
										}
									} else {
										// Use the reference field to refer to outside destination resource.									
										input.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, params)
												+ langSpec.getStatementDelimiter());	// this.dst.updateDstFromSrc(value, refParams);
									}
								}
							}
							if (outsideInputMembers2.size() > 0) {
								if (!generatesComponent(resourceNode.getResourceHierarchy())) {
									ResourcePath srcRes2 = resourceNode.getOutSideResource(ch2);
									for (ChannelMember out2: ch2.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 = ch2.fillOutsideResourcePaths(out2, getPullAccessor());
													if (resourcePaths != null && resourcePaths.size() > 0) {
														for (ChannelMember outsideMember: outsideInputMembers2) {
															for (ChannelMember dependingMember: resourcePaths.get(outsideMember).getValue()) {
																if (dependingMember.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().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);
																	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);
	}
}
