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

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

import algorithms.TypeInference;
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.Parameter;
import models.algebra.Symbol;
import models.algebra.Term;
import models.algebra.Type;
import models.algebra.Variable;
import models.dataConstraintModel.Channel;
import models.dataConstraintModel.ChannelMember;
import models.dataConstraintModel.DataConstraintModel;
import models.dataConstraintModel.ResourceHierarchy;
import models.dataConstraintModel.ResourcePath;
import models.dataConstraintModel.Selector;
import models.dataFlowModel.ChannelNode;
import models.dataFlowModel.DataFlowEdge;
import models.dataFlowModel.DataFlowGraph;
import models.dataFlowModel.DataTransferChannel;
import models.dataFlowModel.DataTransferModel;
import models.dataFlowModel.IFlowGraph;
import models.dataFlowModel.PushPullAttribute;
import models.dataFlowModel.PushPullValue;
import models.dataFlowModel.ResourceNode;
import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor;

/**
 * Common generator for prototypes
 * 
 * @author Nitta
 *
 */
public abstract class CodeGenerator {
	public static final String fieldOfResourceState = "value";
	public static final String methoNameOfResourceState = "Value";
	public static final String getterPrefix = "get";
	public static final String getterOfResourceState = getterPrefix + methoNameOfResourceState;		// "getValue"
	public static final String updateMethodPrefix = "update";
	public static final String from = "From";
	public static final String _for = "For";
	private static String mainTypeName = null;
	private static ILanguageSpecific langSpec = null;
	private static IPlatformSpecific platformSpec = null;
		
	public static String getMainTypeName() {
		return mainTypeName;
	}

	public static void setMainTypeName(String mainTypeName) {
		CodeGenerator.mainTypeName = mainTypeName;
	}
	
	public static void resetMainTypeName() {
		CodeGenerator.mainTypeName = null;
	}

	public static String getComponentName(ResourceHierarchy res, ILanguageSpecific langSpec) {
		String name = res.getResourceName();
		if (res.getNumParameters() > 0) {
			if (name.length() > 3 && name.endsWith("ies")) {
				name = name.substring(0, name.length() - 3) + "y";
			} else if (name.length() > 1 && name.endsWith("s")) {
				name = name.substring(0, name.length() - 1);
			} else {
				name += "Element";
			}
		}
		return langSpec.toComponentName(name);
	}
	
	public static Type getImplStateType(ResourceHierarchy res, ILanguageSpecific langSpec) {
		Set<ResourceHierarchy> children = res.getChildren();
		if (children == null || children.size() == 0) {
			// leaf resource.
			return res.getResourceStateType();
		} else {
			ResourceHierarchy child = children.iterator().next();
			if (children.size() == 1 && child.getNumParameters() > 0) {
				// map or list.
				if (DataConstraintModel.typeList.isAncestorOf(res.getResourceStateType())) {
					// list.
					if (generatesComponent(child)) {
						return langSpec.newListType(getComponentName(child, langSpec));
					} else {
						return langSpec.newListType(getImplStateType(child, langSpec).getInterfaceTypeName());
					}
				} else if (DataConstraintModel.typeMap.isAncestorOf(res.getResourceStateType())) {
					// map.
					if (generatesComponent(child)) {
						return langSpec.newMapType(DataConstraintModel.typeString, getComponentName(child, langSpec));
					} else {
						return langSpec.newMapType(DataConstraintModel.typeString, getImplStateType(child, langSpec).getInterfaceTypeName());
					}
				}
				return null;
			} else {
				// class
				return res.getResourceStateType();
			}
		}
	}
	
	public static boolean generatesComponent(ResourceHierarchy res) {
		if (res.getParent() == null) return true;
		if (res.getChildren() == null || res.getChildren().size() == 0) return false;
		if (res.getNumParameters() > 0 && res.getChildren().size() == 1 && res.getChildren().iterator().next().getNumParameters() > 0) return false;
		if (res.getChildren().size() == 1 && res.getChildren().iterator().next().getNumParameters() > 0 
				&& (res.getChildren().iterator().next().getChildren() == null || res.getChildren().iterator().next().getChildren().size() == 0)) return false;
		return true;
//		return res.getParent() == null || !(res.getChildren() == null || res.getChildren().size() == 0);
	}

	/**
	 * Generate source codes in specified language from data-flow/control-flow graph.
	 * 
	 * @param model architecture model
	 * @param flowGraph data-flow or control-flow graph
	 * @param langSpec specified language
	 * @return source codes
	 */
	public ArrayList<CompilationUnit> generateCode(DataTransferModel model, IFlowGraph flowGraph, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) {
		CodeGenerator.langSpec = langSpec;
		CodeGenerator.platformSpec = platformSpec;
		ArrayList<CompilationUnit> codes = new ArrayList<>();
		
		Map<ResourceHierarchy, Set<ResourceHierarchy>> dependedRootComponentGraph = null;
		Collection<ResourceNode> components = null;
		if (platformSpec.isMonolithic()) {
			// To build monolithic application, the dependency of the components should be resolved in advance.
			
			// Get the dependency among root nodes.
			dependedRootComponentGraph = getDependedRootComponentGraph(model);
			
			// Sort the all components.
			components = determineComponentOrder(flowGraph, dependedRootComponentGraph);
		} else {
			// Get the all components.
			components = flowGraph.getResourceNodes();
		}
		
		// Generate the other components.
		generateCodeFromFlowGraph(model, flowGraph, components, codes, dependedRootComponentGraph, platformSpec, langSpec);
		
		return codes;
	}
	
	public abstract void generateCodeFromFlowGraph(DataTransferModel model, IFlowGraph flowGraph, Collection<ResourceNode> components, ArrayList<CompilationUnit> codes, 
			Map<ResourceHierarchy, Set<ResourceHierarchy>> dependedRootComponentGraph, IPlatformSpecific platformSpec, ILanguageSpecific langSpec);
	
	private static Map<ResourceHierarchy, Set<ResourceHierarchy>> getDependedRootComponentGraph(DataTransferModel model) {
		Map<ResourceHierarchy, Set<ResourceHierarchy>> dependedComponentGraph = new HashMap<>();
		for (Channel ch: model.getChannels()) {
			Set<ResourceHierarchy> inRes = new HashSet<>();
			Set<ResourceHierarchy> outRes = new HashSet<>();
			getDependedRootComponentGraphSub(ch, inRes, outRes, true);
			if (outRes.size() > 0 && inRes.size() > 0) {
				for (ResourceHierarchy out: outRes) {
					for (ResourceHierarchy in: inRes) {
						Set<ResourceHierarchy> dependings = dependedComponentGraph.get(out.getRoot());
						if (dependings == null) {
							dependings = new HashSet<>();
							dependedComponentGraph.put(out.getRoot(), dependings);
						}
						if (!out.getRoot().equals(in.getRoot())) {
							dependings.add(in.getRoot());
						}
					}
				}
			}
		}
		return dependedComponentGraph;
	}

	private static void getDependedRootComponentGraphSub(Channel ch, Set<ResourceHierarchy> inRes, Set<ResourceHierarchy> outRes, boolean isRoot) {
		DataTransferChannel dtCh = (DataTransferChannel) ch;
		for (ChannelMember cm: dtCh.getChannelMembers()) {
			if (!isRoot && !cm.isOutside()) {
				outRes.add(cm.getResource().getResourceHierarchy());	// dependency to a descendant channel resource.
			}
			if (cm.isOutside()) {
				outRes.add(cm.getResource().getResourceHierarchy());	// dependency to an outside resource.
			} else {
				inRes.add(cm.getResource().getResourceHierarchy());	// dependency from an inside resource.
			}
		}
		for (Channel childCh: ch.getChildren()) {
			getDependedRootComponentGraphSub(childCh, inRes, outRes, false);
		}
	}
	
	private static ArrayList<ResourceNode> determineComponentOrder(IFlowGraph graph, Map<ResourceHierarchy, Set<ResourceHierarchy>> dependedRootComponentGraph) {
		ArrayList<ResourceNode> objects = new ArrayList<>();
		Set<ResourceNode> visited = new HashSet<>();
		Collection<ResourceNode> allNodes = graph.getResourceNodes();
		for (ResourceNode resNode: allNodes) {
			topologicalSort(resNode, allNodes, dependedRootComponentGraph, visited, objects);
		}
		return objects;
	}
	
	private static void topologicalSort(ResourceNode curResNode, Collection<ResourceNode> allNodes, Map<ResourceHierarchy, Set<ResourceHierarchy>> dependedRootComponentGraph, Set<ResourceNode> visited, List<ResourceNode> orderedList) {
		if (visited.contains(curResNode)) return;
		visited.add(curResNode);
		// A caller is before the callee
		
		// For each incoming PUSH transfer.		
		for (Edge chToRes: curResNode.getInEdges()) {
			for (Edge resToCh: chToRes.getSource().getInEdges()) {
				if (!(resToCh instanceof DataFlowEdge) || ((PushPullAttribute)((DataFlowEdge) resToCh).getAttribute()).getSelectedOption() == PushPullValue.PUSH) {
					topologicalSort((ResourceNode) resToCh.getSource(), allNodes, dependedRootComponentGraph, visited, orderedList);
				}
			}
		}
		// For each outgoing PULL transfer.
		if (curResNode instanceof ResourceNode) {
			for (Edge resToCh: curResNode.getOutEdges()) {
				DataFlowEdge de = (DataFlowEdge) resToCh;
				if (((PushPullAttribute) de.getAttribute()).getSelectedOption() != PushPullValue.PUSH) {
					for (Edge chToRes : resToCh.getDestination().getOutEdges()) {
						topologicalSort((ResourceNode) chToRes.getDestination(), allNodes, dependedRootComponentGraph, visited, orderedList);
					}
				}
			}
		}
		// For each depending root node.
		if (curResNode instanceof ResourceNode && dependedRootComponentGraph.get(curResNode.getResourceHierarchy()) != null) {
			for (ResourceHierarchy dependingRes: dependedRootComponentGraph.get(curResNode.getResourceHierarchy())) {
				for (ResourceNode rootNode: allNodes) {
					ResourceHierarchy rootRes = rootNode.getResourceHierarchy();
					if (rootRes.getParent() == null && rootRes.equals(dependingRes)) {
						topologicalSort(rootNode, allNodes, dependedRootComponentGraph, visited, orderedList);
					}
				}
			}
		}
		// For each reference resource.
		ResourceNode cn = null;
		if (curResNode instanceof ResourceNode) {
			cn = (ResourceNode) curResNode;
		}
		if (cn != null) {
			for (Node n: allNodes) {
				ResourceNode resNode = null;
				if (n instanceof ResourceNode) {
					resNode = (ResourceNode) n;
				}
				if (resNode != null) {
					for (Edge resToCh: resNode.getOutEdges()) {
						ChannelNode chNode = (ChannelNode) resToCh.getDestination();
						for (ChannelMember m: chNode.getChannel().getReferenceChannelMembers()) {
							if (curResNode.getOutSideResources().contains(m.getResource())) {
								topologicalSort(resNode, allNodes, dependedRootComponentGraph, visited, orderedList);
							}
						}
					}
				}
			}
		}
		orderedList.add(0, curResNode);
	}

	protected void updateMainComponent(TypeDeclaration mainType, MethodDeclaration mainConstructor, Node componentNode, 
			MethodDeclaration constructor, final List<ResourceHierarchy> depends, ILanguageSpecific langSpec) {
		// Declare the field to refer to each object in the main type.
		ResourceNode resNode = null;
		String nodeName = null;
		if (componentNode instanceof ResourceNode) {
			resNode = (ResourceNode) componentNode;
			nodeName = resNode.getResourceName();
		}
		String componentName = langSpec.toComponentName(nodeName);
		// Declare a field to refer each object.
		if (langSpec.declareField()) {
			FieldDeclaration refField = langSpec.newFieldDeclaration(new Type(componentName, componentName), nodeName);
			mainType.addField(refField);
		}
		// Add a statement to instantiate each object to the main constructor.
		List<String> parameters = new ArrayList<>();
		for (ResourceHierarchy res: depends) {
			parameters.add(res.getResourceName());
		}
		if (constructor.getParameters() != null) {
			for (VariableDeclaration var: constructor.getParameters()) {
				if (!parameters.contains(var.getName())) {
					parameters.add(var.getName());
				}
			}
		}

		Block mainConstructorBody = mainConstructor.getBody();
		if (mainConstructorBody == null) {
			mainConstructorBody = new Block();
			mainConstructor.setBody(mainConstructorBody);
		}
		mainConstructorBody.addStatement(langSpec.getFieldAccessor(nodeName) + langSpec.getAssignment() + langSpec.getConstructorInvocation(componentName, parameters) + langSpec.getStatementDelimiter());
	}

	protected ResourceHierarchy addReference(TypeDeclaration component, MethodDeclaration constructor, ResourceHierarchy dstRes, ILanguageSpecific langSpec) {
		if (!generatesComponent(dstRes)) {
			dstRes = dstRes.getParent();
		}
		String dstComponentName = getComponentName(dstRes, langSpec);
		if (dstComponentName != null) {
			String dstNodeName = langSpec.toVariableName(dstComponentName);
			if (langSpec.declareField()) {
				// Declare a field to refer to another component.
				component.addField(langSpec.newFieldDeclaration(new Type(dstComponentName, dstComponentName), dstNodeName));
			}
			// Initialize the field to refer to another component.
			constructor.addParameter(langSpec.newVariableDeclaration(new Type(dstComponentName, dstComponentName), dstNodeName));
			constructor.getBody().addStatement(langSpec.getFieldAccessor(dstNodeName) + langSpec.getAssignment() + dstNodeName + langSpec.getStatementDelimiter());
		}
		return dstRes;
	}

	protected void fillStateGetterMethod(MethodDeclaration stateGetter, ResourceHierarchy resourceHierarchy, Type resStateType, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) {
		// returns the state field when all incoming data-flow edges are PUSH-style.
		if (langSpec.isValueType(resStateType)) {
			stateGetter.addStatement(langSpec.getReturnStatement(langSpec.getFieldAccessor(fieldOfResourceState)) + langSpec.getStatementDelimiter());		// return value;
		} else {
			if (resourceHierarchy.getChildren() != null && resourceHierarchy.getChildren().size() == 1 && resourceHierarchy.getChildren().iterator().next().getNumParameters() > 0) {
				// list or map
//				if (!platformSpec.isMonolithic()) {
//					// For REST API
//					stateGetter.addStatement(langSpec.getReturnStatement(langSpec.getFieldAccessor(fieldOfResourceState)) + langSpec.getStatementDelimiter());	// return value;
//				} else {
					String implTypeName = resStateType.getImplementationTypeName();
					// copy the current state to be returned as a 'value'
					List<String> parameters = new ArrayList<>();
					parameters.add(langSpec.getFieldAccessor(fieldOfResourceState));
					stateGetter.addStatement(langSpec.getReturnStatement(langSpec.getConstructorInvocation(implTypeName, parameters)) + langSpec.getStatementDelimiter());	// return new Resource(value);
//				}
			} else {
				if (resourceHierarchy.getChildren() == null || resourceHierarchy.getChildren().size() == 0) {
//					// a leaf resource
//					if (!platformSpec.isMonolithic()) {
//						// For REST API
//						stateGetter.addStatement(langSpec.getReturnStatement(langSpec.getFieldAccessor(fieldOfResourceState)) + langSpec.getStatementDelimiter());	// return value;
//					} else {
						String implTypeName = resStateType.getImplementationTypeName();
						// copy the current state to be returned as a 'value'
						List<String> parameters = new ArrayList<>();
						parameters.add(langSpec.getFieldAccessor(fieldOfResourceState));
						stateGetter.addStatement(langSpec.getReturnStatement(langSpec.getConstructorInvocation(implTypeName, parameters)) + langSpec.getStatementDelimiter());	// return new Resource(value);
//					}
				} else {
					Term composer = null; 
					Term composerSub = new Constant(DataConstraintModel.nil);
					composerSub.setType(DataConstraintModel.typeMap);
					for (ResourceHierarchy child: resourceHierarchy.getChildren()) {
						String childTypeName = getComponentName(child, langSpec);
						String fieldName = langSpec.toVariableName(childTypeName);
						Term childGetter = null; 
						if (!generatesComponent(child)) {
							// the child is not a class
							childGetter = new Term(new Symbol(getterPrefix + childTypeName, 1, Symbol.Type.METHOD));
							childGetter.addChild(new Constant(langSpec.getSelfExp()));
						} else {
							// the child is a class
							childGetter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD));
							childGetter.addChild(new Field(fieldName, getImplStateType(child, langSpec)));
						}
						composer = new Term(DataConstraintModel.insert);
						composer.addChild(composerSub);
						composer.addChild(new Constant(fieldName, DataConstraintModel.typeString));		// key
						composer.addChild(childGetter);													// value
						composer.setType(DataConstraintModel.typeMap);
						composerSub = composer;
					}
					composer.setType(stateGetter.getReturnType());
					String[] sideEffects = new String[] {null};
					String returnValue = composer.toImplementation(sideEffects);
					if (sideEffects[0] != null) {
						stateGetter.addStatement(sideEffects[0] + langSpec.getReturnStatement(returnValue) + langSpec.getStatementDelimiter());
					} else {
						stateGetter.addStatement(langSpec.getReturnStatement(returnValue) + langSpec.getStatementDelimiter());
					}
				}
			}
		}
	}

	protected void fillDescendantGetterMethod(MethodDeclaration descendantGetter, ResourceHierarchy descendant, 
			ResourceHierarchy child, ResourceHierarchy ancestor, TypeDeclaration ancestorComponent, ILanguageSpecific langSpec) {
		// (#4) descendant getter method (the implementation must be kept consistent with #3)
		Expression selector;
		if (DataConstraintModel.typeList.isAncestorOf(ancestor.getResourceStateType())) {
			selector = new Variable(langSpec.getFieldAccessor(fieldOfResourceState));
		} else if (DataConstraintModel.typeMap.isAncestorOf(ancestor.getResourceStateType())) {
			selector = new Variable(langSpec.getFieldAccessor(fieldOfResourceState));
		} else {
			String fieldName = langSpec.toVariableName(getComponentName(child, langSpec));
			selector = new Variable(langSpec.getFieldAccessor(fieldName));
		}
		if (descendantGetter.getParameters() != null) {
			for (VariableDeclaration param: descendantGetter.getParameters()) {
				if (DataConstraintModel.typeInt.isAncestorOf(param.getType())) {
					Term newSelector = new Term(DataConstraintModel.get);
					newSelector.addChild(selector);
					newSelector.addChild(new Variable(param.getName()));
					selector = newSelector;
				} else if (DataConstraintModel.typeString.isAncestorOf(param.getType())) {
					Term newSelector = new Term(DataConstraintModel.lookup);
					newSelector.addChild(selector);
					newSelector.addChild(new Variable(param.getName()));
					selector = newSelector;
				}
			}
		}
		if (descendantGetter != null && (descendantGetter.getBody() == null || descendantGetter.getBody().getStatements().size() == 0)) {
			String[] sideEffects = new String[] {null};
			String returnValue = selector.toImplementation(sideEffects);
			if (sideEffects[0] != null) descendantGetter.addStatement(sideEffects[0]);
			descendantGetter.addStatement(langSpec.getReturnStatement(returnValue) + langSpec.getStatementDelimiter());
		}
	}

	protected void declareAccessorInMainComponent(TypeDeclaration mainComponent, ResourceNode accessRes, MethodDeclaration stateGetter, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) {
		List<VariableDeclaration> mainGetterParams = new ArrayList<>();
		int v = 1;
		for (Expression param: accessRes.getPrimaryResourcePath().getPathParams()) {
			if (param instanceof Variable) {
				Variable var = (Variable) param;
				mainGetterParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName()));
			} else if (param instanceof Term) {
				Term var = (Term) param;
				mainGetterParams.add(new VariableDeclaration(var.getType(), "v" + v));
			}
			v++;
		}
		ResourcePath accessResPath = new ResourcePath(accessRes.getPrimaryResourcePath());
		for (int i = 0; i < mainGetterParams.size(); i++) {
			Parameter pathParam = new Parameter(mainGetterParams.get(i).getName());
			accessResPath.replacePathParam(i, pathParam, null);
		}
		MethodDeclaration accessor = null;
		if (mainGetterParams.size() == 0) {
			accessor = langSpec.newMethodDeclaration(getterPrefix + getComponentName(accessRes.getResourceHierarchy(), langSpec), getImplStateType(accessRes.getResourceHierarchy(), langSpec));
		} else {
			accessor = langSpec.newMethodDeclaration(getterPrefix + getComponentName(accessRes.getResourceHierarchy(), langSpec), false, getImplStateType(accessRes.getResourceHierarchy(), langSpec), mainGetterParams);
		}
		Block block = new Block();
		Expression getState = getPullAccessor(platformSpec).getDirectStateAccessorFor(accessResPath, null);
		block.addStatement(langSpec.getReturnStatement(getState.toImplementation(new String[] {null})) + langSpec.getStatementDelimiter());
//		if (stateGetter.getParameters() == null || stateGetter.getParameters().size() == 0) {
//			block.addStatement(langSpec.getReturnStatement(langSpec.getMethodInvocation(accessRes.getResourceName(), stateGetter.getName())) + langSpec.getStatementDelimiter());
//		} else {
//			List<String> resParams = new ArrayList<>();
//			for (VariableDeclaration var: stateGetter.getParameters()) {
//				resParams.add(var.getName());
//			}
//			block.addStatement(langSpec.getReturnStatement(langSpec.getMethodInvocation(accessRes.getResourceName(), stateGetter.getName(), resParams)) + langSpec.getStatementDelimiter());
//		}
		accessor.setBody(block);
		mainComponent.addMethod(accessor);
	}

	protected void declareFieldsToReferenceResources(DataTransferModel model, ResourceNode resourceNode, TypeDeclaration component, MethodDeclaration constructor,
			final List<ResourceHierarchy> depends, ILanguageSpecific langSpec) {
		Set<ResourcePath> refs = new HashSet<>();
		for (Channel ch : model.getChannels()) {
			DataTransferChannel c = (DataTransferChannel) ch;
			if (resourceNode.getOutSideResource(c) != null) {
				for (ResourcePath res: c.getReferenceResources()) {
					if (!refs.contains(res) && !depends.contains(res.getResourceHierarchy())) {
						refs.add(res);
						String refResName = langSpec.toComponentName(res.getLeafResourceName());
						component.addField(langSpec.newFieldDeclaration(new Type(refResName, refResName), res.getLeafResourceName()));
						constructor.addParameter(langSpec.newVariableDeclaration(new Type(refResName, refResName), res.getLeafResourceName()));						
						constructor.getBody().addStatement(langSpec.getFieldAccessor(res.getLeafResourceName()) + langSpec.getAssignment() + res.getLeafResourceName() + langSpec.getStatementDelimiter());
					}
				}
			}
		}
	}
	
	protected MethodDeclaration getConstructor(TypeDeclaration component) {
		for (MethodDeclaration m: component.getMethods()) {
			if (m.isConstructor()) return m;
		}
		return null;
	}
	
	protected static List<MethodDeclaration> getGetterMethods(TypeDeclaration component, String resourceName) {
		List<MethodDeclaration> getters = new ArrayList<>();
		for (MethodDeclaration m: component.getMethods()) {
			if (m.getName().equals(getterPrefix + resourceName)) {
				getters.add(m);
			}
		}
		return getters;
	}
	
	protected MethodDeclaration getUpdateMethod(Edge inEdge, TypeDeclaration component,
			Map<Edge, Map<PushPullValue, List<ResourceNode>>> dataFlowInform, ILanguageSpecific langSpec) {
		List<ResourceNode> passedResoueces = dataFlowInform.get(inEdge).get(PushPullValue.PUSH);
		String methodName = updateMethodPrefix;
		for (ResourceNode rn: passedResoueces) {
			methodName += langSpec.toComponentName(rn.getResourceName());
		}
		return getMethod(component, methodName);
	}

	protected MethodDeclaration getInputMethod(ResourceNode resourceNode, DataTransferChannel ch, TypeDeclaration component) {
		MethodDeclaration input = null;
		for (ChannelMember out : ch.getOutputChannelMembers()) {
			if (resourceNode.getInSideResources().contains(out.getResource())) {
				Expression message = out.getStateTransition().getMessageExpression();
				if (message instanceof Term) {
					input = getMethod(component, ((Term) message).getSymbol().getImplName());
				} else if (message instanceof Variable) {
					// Declare an input method in this component.
					input = getMethod(component, ((Variable) message).getName());
				}
				break;
			}
		}
		return input;
	}
	

	protected MethodDeclaration getMethod(TypeDeclaration component, String methodName, List<VariableDeclaration> params) {
		for (MethodDeclaration m: component.getMethods()) {
			if (m.getName().equals(methodName)) {
				if (m.getParameters() == null && (params == null || params.size() == 0)) return m;
				if (m.getParameters() != null && params != null && m.getParameters().size() == params.size()) {
					boolean matchParams = true;
					for (int i = 0; i < m.getParameters().size(); i++) {
						if (!m.getParameters().get(i).getType().equals(params.get(i).getType())) {
							matchParams = false;
							break;
						}
					}
					if (matchParams) return m;
				}
			}
		}
		return null;
	}

	protected MethodDeclaration getMethod(TypeDeclaration component, String methodName) {
		for (MethodDeclaration m: component.getMethods()) {
			if (m.getName().equals(methodName)) return m;
		}
		return null;
	}	

	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 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);
	}
	
	protected 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 = langSpec.newListType(mapTypeName).getInterfaceTypeName();
				respConverter += langSpec.getVariableDeclaration(fromResourceType.getInterfaceTypeName(), fromResourceName) + langSpec.getAssignment() + langSpec.getConstructorInvocation(fromResourceType.getImplementationTypeName(), null) + langSpec.getStatementDelimiter() + "\n";
				respConverter += langSpec.getForStatementForCollection("i", mapTypeName, varName) + "\n";
				respConverter += "\t" + langSpec.getMethodInvocation(fromResourceName, DataConstraintModel.append.getImplName(), List.of(getCodeForConversionFromMapToTuple(compType, "i", langSpec))) + langSpec.getStatementDelimiter() + "\n";
				respConverter += langSpec.getEndForStatement("i") ;
				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);
			}
		}
	}
	
	protected 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;
	}

	protected 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;
	}
	
	protected 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;
	}

	protected 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;
	}

	protected IResourceStateAccessor getPushAccessor(IPlatformSpecific platformSpec) {
		if (platformSpec.isMonolithic()) {
			return new IResourceStateAccessor() {
				@Override
				public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) {
					ResourcePath targetRes= target.getResource();
					ResourcePath fromRes= from.getResource();
					if (targetRes.equals(fromRes)) {
						return new Field(fieldOfResourceState,
								targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
										: DataConstraintModel.typeInt);
					}
					// use the cached value as the current state
					return new Field(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)),
							targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
									: DataConstraintModel.typeInt);
				}
	
				@Override
				public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) {
					ResourcePath targetRes= target.getResource();
					return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)),
							targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
									: DataConstraintModel.typeInt);
				}
	
				@Override
				public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) {
					if (fromRes != null && targetRes.equals(fromRes)) {
						return new Field(fieldOfResourceState,
								targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
										: DataConstraintModel.typeInt);
					}
					// for reference channel member
					return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)),
							targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
									: DataConstraintModel.typeInt);
				}
			};
		} else {
			return new IResourceStateAccessor() {
				@Override
				public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) {
					ResourcePath targetRes = target.getResource();
					ResourcePath fromRes = from.getResource();
					if (targetRes.equals(fromRes)) {
						return new Field(fieldOfResourceState,
								targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
										: DataConstraintModel.typeInt);
					}
					// use the cached value as the current state
					return new Field(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)),
							targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
									: DataConstraintModel.typeInt);
				}

				@Override
				public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) {
					ResourcePath targetRes = target.getResource();
					return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)),
							targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
									: DataConstraintModel.typeInt);
				}

				@Override
				public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) {
					if (fromRes != null && targetRes.equals(fromRes)) {
						return new Field(fieldOfResourceState,
								targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
										: DataConstraintModel.typeInt);
					}
					return null;
				}
			};
		}
	}

	protected IResourceStateAccessor getPullAccessor(IPlatformSpecific platformSpec) {
		if (platformSpec.isMonolithic()) {
			return new IResourceStateAccessor() {
				@Override
				public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) {
					ResourcePath targetRes= target.getResource();
					if (from != null) {
						ResourcePath fromRes= from.getResource();
						if (!target.isOutside()) {
							return getDirectStateAccessorFor(targetRes, fromRes);
						}
						Term getter = null;
						String targetComponentName = getComponentName(targetRes.getResourceHierarchy(), langSpec);
						if (generatesComponent(targetRes.getResourceHierarchy())) {
							getter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD));
							getter.addChild(new Field(langSpec.toVariableName(targetComponentName), targetRes.getResourceStateType()));
						} else {
							String parentName = langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy().getParent(), langSpec));
							Type parentType = targetRes.getResourceHierarchy().getParent().getResourceStateType();
							getter = new Term(new Symbol(getterPrefix + targetComponentName, 1, Symbol.Type.METHOD));
							getter.addChild(new Field(parentName, parentType));
						}
						return getter;
					} else {
						return getDirectStateAccessorFor(targetRes, null);
					}
				}
	
				@Override
				public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) {
					ResourcePath targetRes= target.getResource();
					if (from != null) {
						ResourcePath fromRes= from.getResource();
						if (!target.isOutside()) {
							return getDirectStateAccessorFor(targetRes, fromRes);
						}
						Term getter = null;
						String targetComponentName = getComponentName(targetRes.getResourceHierarchy(), langSpec);
						if (generatesComponent(targetRes.getResourceHierarchy())) {
							getter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD));
							getter.addChild(new Field(langSpec.toVariableName(targetComponentName), targetRes.getResourceStateType()));
						} else {
							String parentName = langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy().getParent(), langSpec));
							Type parentType = targetRes.getResourceHierarchy().getParent().getResourceStateType();
							getter = new Term(new Symbol(getterPrefix + targetComponentName, 1, Symbol.Type.METHOD));
							getter.addChild(new Field(parentName, parentType));
						}
						return getter;
					} else {
						return getDirectStateAccessorFor(targetRes, null);
					}
				}
				
				@Override
				public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) {
					if (fromRes != null) {
						if (targetRes.equals(fromRes)) {
							return new Field(fieldOfResourceState,
									targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
											: DataConstraintModel.typeInt);
						}
						// for reference channel member
						if (fromRes.isAncestorOf(targetRes)) {
							Stack<ResourcePath> pathStack = new Stack<>();
							ResourcePath curPath = targetRes;
							do {
								pathStack.push(curPath);
								curPath = curPath.getParent();					
							} while (!curPath.equals(fromRes));
							// iterate from the fromRes resource
							return getRelativePath(targetRes, pathStack);
						}
						if (generatesComponent(targetRes.getResourceHierarchy())) {
							Term getter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD));
							getter.addChild(new Field(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), targetRes.getResourceStateType()));
							return getter;
						} else {
							return new Field(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), targetRes.getResourceStateType());
						}
					} else {
						// (#3) access from the outside of the hierarchy (must be kept consistent with #4)
						Stack<ResourcePath> pathStack = new Stack<>();
						ResourcePath curPath = targetRes;
						do {
							pathStack.push(curPath);
							curPath = curPath.getParent();					
						} while (curPath != null);
						// iterate from the root resource
						return getRelativePath(targetRes, pathStack);
					}
				}
	
				private Expression getRelativePath(ResourcePath targetRes, Stack<ResourcePath> pathStack) {
					ResourcePath curPath;
					Term getter = null;
					int arity = 2;
					boolean doesChainInvocations = true;
					while (!pathStack.empty()) {
						curPath = pathStack.pop();
						String typeName = getComponentName(curPath.getResourceHierarchy(), langSpec);
						if (getter == null && generatesComponent(curPath.getResourceHierarchy())) {
							// root resource
							String fieldName = langSpec.toVariableName(typeName);
							getter = new Field(fieldName, new Type(typeName, typeName));
						} else {
							if (generatesComponent(curPath.getResourceHierarchy())) {
								if (arity == 2) {
									Term newGetter = new Term(new Symbol(getterPrefix + typeName, -1, Symbol.Type.METHOD));
									newGetter.addChild(getter);
									if (curPath.getResourceHierarchy().getNumParameters() > 0) {
										Expression param = curPath.getLastParam();
										if (param != null) {
											newGetter.addChild(param);
											newGetter.getSymbol().setArity(2);
										}
									}
									getter = newGetter;
								} else {
									// add the last path parameter.
									if (curPath.getResourceHierarchy().getNumParameters() > 0) {
										Expression param = curPath.getLastParam();
										if (param != null) {
											getter.getSymbol().setArity(arity);
											getter.addChild(param);
										}
									}
								}
								arity = 2;
								doesChainInvocations = true;
							} else {
								// to get a descendant resource directly. (e.g, .todos.{year}.{month}.{day}.{id} ==> .getTodos().getTodo(year, month, day, id))
								if (doesChainInvocations) {
									Term newGetter = new Term(new Symbol(getterPrefix + typeName, -1, Symbol.Type.METHOD));
									newGetter.addChild(getter);
									getter = newGetter;
									doesChainInvocations = false;
								}
								if (curPath.getResourceHierarchy().getNumParameters() > 0) {
									// may change the symbol name
									getter.getSymbol().changeName(getterPrefix + typeName);
									// add a path parameter.
									Expression param = curPath.getLastParam();
									if (param != null) {
										getter.getSymbol().setArity(arity);
										getter.addChild(param);
										arity++;
									}
								}
							}
						}
					}
					
					if (generatesComponent(targetRes.getResourceHierarchy())) {
						Term newGetter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD));
						newGetter.addChild(getter);
						getter = newGetter;
					}
					return getter;
				}
			};
		} else {
			return new IResourceStateAccessor() {
				@Override
				public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) {
					ResourcePath targetRes = target.getResource();
					if (from != null && !target.isOutside()) {
						ResourcePath fromRes = from.getResource();
						if (targetRes.getCommonPrefix(fromRes) != null) {
							return getDirectStateAccessorFor(targetRes, fromRes);
						}
					}
					// for reference channel member
					return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)),
							targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
									: DataConstraintModel.typeInt);
				}

				@Override
				public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) {
					ResourcePath targetRes = target.getResource();
					if (from != null && !target.isOutside()) {
						ResourcePath fromRes = from.getResource();
						if (targetRes.getCommonPrefix(fromRes) != null) {
							return getDirectStateAccessorFor(targetRes, fromRes);
						}
					}
					return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)),
							targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
									: DataConstraintModel.typeInt);
				}
				
				@Override
				public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) {
					if (fromRes != null && !fromRes.getResourceHierarchy().isAncestorOf(targetRes.getResourceHierarchy())) {
						if (targetRes.equals(fromRes)) {
							return new Field(fieldOfResourceState,
									targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
											: DataConstraintModel.typeInt);
						}
						// for reference channel member
						return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)),
								targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
										: DataConstraintModel.typeInt);
					} else {
						// (#3) access from an ancestor or outside of the hierarchy (must be kept consistent with #4)
						Stack<ResourcePath> pathStack = new Stack<>();
						ResourcePath curPath = targetRes;
						do {
							if (fromRes != null && curPath.equals(fromRes)) break;
							pathStack.push(curPath);
							curPath = curPath.getParent();					
						} while (curPath != null);
						// iterate from the `from' resource
						Term getter = null;
						int arity = 2;
						boolean doesChainInvocations = true;
						while (!pathStack.empty()) {
							curPath = pathStack.pop();
							String typeName = getComponentName(curPath.getResourceHierarchy(), langSpec);
							if (getter == null && fromRes == null) {
								// root resource
								String fieldName = langSpec.toVariableName(typeName);
								getter = new Field(fieldName, new Type(typeName, typeName));
							} else {
								if (generatesComponent(curPath.getResourceHierarchy())) {
									if (arity == 2) {
										Term newGetter = new Term(new Symbol(getterPrefix + typeName, -1, Symbol.Type.METHOD));
										newGetter.addChild(getter);
										if (curPath.getResourceHierarchy().getNumParameters() > 0) {
											Expression param = curPath.getLastParam();
											if (param != null) {
												newGetter.addChild(param);
												newGetter.getSymbol().setArity(2);
											}
										}
										getter = newGetter;
									} else {
										// add the last path parameter.
										if (curPath.getResourceHierarchy().getNumParameters() > 0) {
											Expression param = curPath.getLastParam();
											if (param != null) {
												getter.getSymbol().setArity(arity);
												getter.addChild(param);
											}
										}
									}
									arity = 2;
									doesChainInvocations = true;
								} else {
									// to get a descendant resource directly. (e.g, .todos.{year}.{month}.{day}.{id} ==> .getTodos().getTodo(year, month, day, id))
									if (doesChainInvocations) {
										Term newGetter = new Term(new Symbol(getterPrefix + typeName, -1, Symbol.Type.METHOD));
										newGetter.addChild(getter);
										getter = newGetter;
										doesChainInvocations = false;
									}
									if (curPath.getResourceHierarchy().getNumParameters() > 0) {
										// may change the symbol name
										getter.getSymbol().changeName(getterPrefix + typeName);
										// add a path parameter.
										Expression param = curPath.getLastParam();
										if (param != null) {
											getter.getSymbol().setArity(arity);
											getter.addChild(param);
											arity++;
										}
									}
								}
							}
						}
						
						if (generatesComponent(targetRes.getResourceHierarchy())) {
							Term newGetter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD));
							newGetter.addChild(getter);
							getter = newGetter;
						}
						return getter;
					}
				}
			};
		}
	}
	
	protected IResourceStateAccessor getRefAccessor(IPlatformSpecific platformSpec) {
		if (platformSpec.isMonolithic()) {
			return new IResourceStateAccessor() {
				@Override
				public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) {
					ResourcePath targetRes= target.getResource();
					ResourcePath fromRes= from.getResource();
					if (targetRes.equals(fromRes)) {
						return new Field(fieldOfResourceState,
								targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
										: DataConstraintModel.typeInt);
					}
					// use the cached value as the current state
					return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)),
							targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
									: DataConstraintModel.typeInt);
				}
	
				@Override
				public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) {
					ResourcePath targetRes= target.getResource();
					return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)),
							targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
									: DataConstraintModel.typeInt);
				}
	
				@Override
				public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) {
					if (fromRes != null && targetRes.equals(fromRes)) {
						return new Field(fieldOfResourceState,
								targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
										: DataConstraintModel.typeInt);
					}
					// for reference channel member
					return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)),
							targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
									: DataConstraintModel.typeInt);
				}
			};
		} else {
			return new IResourceStateAccessor() {
				@Override
				public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) {
					ResourcePath targetRes = target.getResource();
					ResourcePath fromRes = from.getResource();
					if (targetRes.equals(fromRes)) {
						return new Field(fieldOfResourceState,
								targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
										: DataConstraintModel.typeInt);
					}
					// for reference channel member
					return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)),
							targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
									: DataConstraintModel.typeInt);
				}

				@Override
				public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) {
					ResourcePath targetRes = target.getResource();
					return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)),
							targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
									: DataConstraintModel.typeInt);
				}

				@Override
				public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) {
					if (fromRes != null && targetRes.equals(fromRes)) {
						return new Field(fieldOfResourceState,
								targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
										: DataConstraintModel.typeInt);
					}
					return null;
				}
			};
		}
	}
}