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

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

import code.ast.Block;
import code.ast.CompilationUnit;
import code.ast.FieldDeclaration;
import code.ast.ImportDeclaration;
import code.ast.MethodDeclaration;
import code.ast.TypeDeclaration;
import code.ast.VariableDeclaration;
import models.Edge;
import models.Node;
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.DataTransferModel;
import models.dataFlowModel.DataTransferChannel;
import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor;
import models.dataFlowModel.PushPullAttribute;
import models.dataFlowModel.PushPullValue;
import models.dataFlowModel.ChannelNode;
import models.dataFlowModel.DataFlowEdge;
import models.dataFlowModel.DataFlowGraph;
import models.dataFlowModel.ResourceNode;
import models.dataFlowModel.StoreAttribute;

/**
 * Generator for plain Java prototypes
 * 
 * @author Nitta
 *
 */
public class JavaCodeGenerator {
	public static final Type typeVoid = new Type("Void", "void");
	private static String defaultMainTypeName = "Main";
	static String mainTypeName = defaultMainTypeName;

	public static String getMainTypeName() {
		return mainTypeName;
	}

	public static void setMainTypeName(String mainTypeName) {
		JavaCodeGenerator.mainTypeName = mainTypeName;
	}

	public static void resetMainTypeName() {
		JavaCodeGenerator.mainTypeName = defaultMainTypeName;
	}

	public static String getComponentName(ResourceHierarchy res) {
		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 name.substring(0, 1).toUpperCase() + name.substring(1);
	}
	
	public static String toVariableName(String name) {
		return name.substring(0, 1).toLowerCase() + name.substring(1);
	}
	
	public static Type getImplStateType(ResourceHierarchy res) {
		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 new Type("List", "ArrayList<>", "List<" + getComponentName(child) + ">", DataConstraintModel.typeList);
					} else {
						return new Type("List", "ArrayList<>", "List<" + getImplStateType(child).getImplementationTypeName() + ">", DataConstraintModel.typeList);
					}
				} else if (DataConstraintModel.typeMap.isAncestorOf(res.getResourceStateType())) {
					// map.
					if (generatesComponent(child)) {
						return new Type("Map", "HashMap<>", "Map<String, " + getComponentName(child) + ">", DataConstraintModel.typeMap);
					} else {
						return new Type("Map", "HashMap<>", "Map<String, " + getImplStateType(child).getImplementationTypeName() + ">", DataConstraintModel.typeMap);
					}
				}
				return null;
			} else {
				// class
				return res.getResourceStateType();
			}
		}
	}
	
	public static boolean generatesComponent(ResourceHierarchy res) {
		return res.getParent() == null || !(res.getChildren() == null || res.getChildren().size() == 0);
//		Type resType = res.getResourceStateType();
//		return DataConstraintModel.typeJson.isAncestorOf(resType) || DataConstraintModel.typeList.isAncestorOf(resType) || DataConstraintModel.typeMap.isAncestorOf(resType);
	}

	static public ArrayList<CompilationUnit> doGenerate(DataFlowGraph graph, DataTransferModel model) {
		ArrayList<CompilationUnit> codes = new ArrayList<>();
		Map<ResourceHierarchy, TypeDeclaration> resourceComponents = new HashMap<>();
		Map<ResourceHierarchy, MethodDeclaration> resourceConstructors = new HashMap<>();
		List<Map.Entry<ResourceHierarchy, MethodDeclaration>> getters = new ArrayList<>();
		List<Map.Entry<ResourceHierarchy, MethodDeclaration>> inputs = new ArrayList<>();
		List<Map.Entry<ResourceHierarchy, FieldDeclaration>> fields = new ArrayList<>();
		List<Map.Entry<ResourceHierarchy, VariableDeclaration>> constructorParams = new ArrayList<>();

		Map<ResourceHierarchy, Set<ResourceHierarchy>> dependedRootComponentGraph = getDependedRootComponentGraph(model);
		ArrayList<ResourceNode> resources = determineResourceOrder(graph, dependedRootComponentGraph);
		TypeDeclaration mainComponent = new TypeDeclaration(mainTypeName);
		CompilationUnit mainCU = new CompilationUnit(mainComponent);
		mainCU.addImport(new ImportDeclaration("java.util.*"));
		codes.add(mainCU);
		
		// Declare the constructor of the main type. 
		MethodDeclaration mainConstructor = new MethodDeclaration(mainTypeName, true);
		mainComponent.addMethod(mainConstructor);
		
		// For each resource node.
		for (ResourceNode rn: resources) {
			TypeDeclaration component = null;
			if (generatesComponent(rn.getResourceHierarchy())) {
				boolean f = false;
				String resourceName = getComponentName(rn.getResourceHierarchy());

				component = resourceComponents.get(rn.getResourceHierarchy());
				if (component == null) {
					// Add compilation unit for each resource.
					component = new TypeDeclaration(resourceName);
					resourceComponents.put(rn.getResourceHierarchy(), component);
					CompilationUnit cu = new CompilationUnit(component);
					cu.addImport(new ImportDeclaration("java.util.*"));
					codes.add(cu);
					
					// Declare the field to refer to each resource in the main type.
					Set<ResourceHierarchy> depends = new HashSet<>();
					Set<ResourcePath> refs = new HashSet<>();
					if (rn.getResourceHierarchy().getParent() == null) {
						// For a root resource
						String fieldInitializer = "new " + resourceName + "(";
						for (Edge resToCh: rn.getOutEdges()) {
							DataFlowEdge re = (DataFlowEdge) resToCh;
							if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) {
								for (Edge chToRes: re.getDestination().getOutEdges()) {
									ResourcePath dstRes = ((ResourceNode) chToRes.getDestination()).getOutSideResource();
									String resName = getComponentName(dstRes.getResourceHierarchy());
									depends.add(dstRes.getResourceHierarchy());
									fieldInitializer += toVariableName(resName) + ",";
									f = true;
								}
							}
						}
						for (Edge chToRes : rn.getInEdges()) {
							for (Edge resToCh: chToRes.getSource().getInEdges()) {
								DataFlowEdge re = (DataFlowEdge) resToCh;
								ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource();
								String resName = getComponentName(srcRes.getResourceHierarchy());
								if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) {
									depends.add(srcRes.getResourceHierarchy());
									fieldInitializer += toVariableName(resName) + ",";
									f = true;
								} else {
									if (re.getDestination().getIndegree() > 1) {
										// Declare a field to cash the state of the source resource in the type of the destination resource.
										ResourcePath cashRes = ((ResourceNode) re.getSource()).getOutSideResource();
										component.addField(new FieldDeclaration(
												cashRes.getResourceStateType(), ((ResourceNode) re.getSource()).getOutSideResource().getResourceName(), getInitializer(cashRes)));
									}
								}
							}
						}
						for (ResourceHierarchy dependedRes: dependedRootComponentGraph.keySet()) {
							for (ResourceHierarchy dependingRes: dependedRootComponentGraph.get(dependedRes)) {
								if (rn.getResourceHierarchy().equals(dependingRes)) {
									// Declare a field to refer to outside resources.
									depends.add(dependedRes);
									String resName = getComponentName(dependedRes);
									fieldInitializer += toVariableName(resName) + ",";
									f = true;
								}
							}
						}
						for (Channel ch : model.getChannels()) {
							DataTransferChannel c = (DataTransferChannel) ch;
							if (c.getInputResources().contains(rn.getOutSideResource())) {
								for (ResourcePath res: c.getReferenceResources()) {
									if (!refs.contains(res) && !depends.contains(res.getResourceHierarchy())) {
										refs.add(res);
										String refResName = res.getResourceName();
										fieldInitializer += toVariableName(refResName) + ",";
										f = true;
									}
								}
							}
						}
						if (f) fieldInitializer = fieldInitializer.substring(0, fieldInitializer.length() - 1);
						fieldInitializer += ")";
						FieldDeclaration field = new FieldDeclaration(new Type(resourceName, resourceName), rn.getResourceName());
						mainComponent.addField(field);
						Block mainConstructorBody = mainConstructor.getBody();
						if (mainConstructorBody == null) {
							mainConstructorBody = new Block();
							mainConstructor.setBody(mainConstructorBody);
						}
						mainConstructorBody.addStatement(rn.getResourceName() + " = " + fieldInitializer + ";");
					}
					
					// Declare a constructor, fields and update methods in the type of each resource.
					MethodDeclaration constructor = new MethodDeclaration(resourceName, true);
					Block block = new Block();
					depends = new HashSet<>();
					for (Edge resToCh : rn.getOutEdges()) {
						DataFlowEdge re = (DataFlowEdge) resToCh;
						DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel();
						// Check if the input resource is outside of the channel scope.
						boolean outsideInputResource = false;
						for (ChannelMember cm: ch.getInputChannelMembers()) {
							if (cm.getResource().getResourceHierarchy().equals(rn.getResourceHierarchy()) && 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 (cm.getResource().getResourceHierarchy().equals(rn.getOutSideResource().getResourceHierarchy()) && cm.isOutside()) {
								outsideOutputResource = true;	// Regarded as push transfer.
								break;
							}
						}
						if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) {
							// Declare a field to refer to the destination resource of push transfer.
							for (Edge chToRes: re.getDestination().getOutEdges()) {
								ResourcePath dstRes = ((ResourceNode) chToRes.getDestination()).getOutSideResource();
								if (!generatesComponent(dstRes.getResourceHierarchy())) {
									dstRes = dstRes.getParent();
								}
								String dstResName = getComponentName(dstRes.getResourceHierarchy());
								depends.add(dstRes.getResourceHierarchy());
								component.addField(new FieldDeclaration(new Type(dstResName, dstResName), toVariableName(dstResName)));
								constructor.addParameter(new VariableDeclaration(new Type(dstResName, dstResName), toVariableName(dstResName)));
								block.addStatement("this." + toVariableName(dstResName) + " = " + toVariableName(dstResName) + ";");
								if (outsideOutputResource) {
									if (dstRes.getParent() != null) {
										// Reference to root resource.
										ResourcePath dstRootRes = dstRes.getRoot();
										String dstRootResName = getComponentName(dstRootRes.getResourceHierarchy());
										component.addField(new FieldDeclaration(new Type(dstRootResName, dstRootResName), toVariableName(dstRootResName)));
										constructor.addParameter(new VariableDeclaration(new Type(dstRootResName, dstRootResName), toVariableName(dstRootResName)));
										block.addStatement("this." + toVariableName(dstRootResName) + " = " + toVariableName(dstRootResName) + ";");
									}
								}
							}
						}
					}
					for (Edge chToRes : rn.getInEdges()) {
						for (Edge resToCh: chToRes.getSource().getInEdges()) {
							DataFlowEdge re = (DataFlowEdge) resToCh;
							ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource();
							DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel();
							// Check if the input resource is outside of the channel scope.
							boolean outsideInputResource = false;
							for (ChannelMember cm: ch.getInputChannelMembers()) {
								if (cm.getResource().getResourceHierarchy().equals(srcRes.getResourceHierarchy()) && 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 (cm.getResource().getResourceHierarchy().equals(rn.getOutSideResource().getResourceHierarchy()) && cm.isOutside()) {
									outsideOutputResource = true;	// Regarded as push transfer.
									break;
								}
							}
							String srcResName = null;
							ResourcePath srcRes2 = srcRes;
							if (generatesComponent(srcRes.getResourceHierarchy())) {
								srcResName = getComponentName(srcRes.getResourceHierarchy());
							} else {
								srcRes2 = srcRes.getParent();
								srcResName = getComponentName(srcRes2.getResourceHierarchy());
							}
							if ((((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) {
								// Declare a field to refer to the source resource of pull transfer.
								depends.add(srcRes2.getResourceHierarchy());
								component.addField(new FieldDeclaration(new Type(srcResName, srcResName), toVariableName(srcResName)));
								constructor.addParameter(new VariableDeclaration(new Type(srcResName, srcResName), toVariableName(srcResName)));
								block.addStatement("this." + toVariableName(srcResName) + " = " + toVariableName(srcResName) + ";");
								if (outsideInputResource) {
									if (srcRes2.getParent() != null) {
										// Reference to root resource.
										ResourcePath srcRootRes = srcRes2.getRoot();
										String srcRootResName = getComponentName(srcRootRes.getResourceHierarchy());
										component.addField(new FieldDeclaration(new Type(srcRootResName, srcRootResName), toVariableName(srcRootResName)));
										constructor.addParameter(new VariableDeclaration(new Type(srcRootResName, srcRootResName), toVariableName(srcRootResName)));
										block.addStatement("this." + toVariableName(srcRootResName) + " = " + toVariableName(srcRootResName) + ";");
									}
								}
							} else {
								// Declare an update method in the type of the destination resource.
								ArrayList<VariableDeclaration> vars = new ArrayList<>();
								vars.add(new VariableDeclaration(srcRes.getResourceStateType(), srcRes.getResourceName()));
								DataTransferChannel c = ((ChannelNode) resToCh.getDestination()).getChannel();
								for (ResourcePath ref: c.getReferenceResources()) {
									if (!ref.equals(rn.getOutSideResource())) {
										vars.add(new VariableDeclaration(ref.getResourceStateType(), ref.getResourceName()));
									}
								}
								component.addMethod(new MethodDeclaration("update" + srcResName, false, typeVoid, vars));
							}
						}
					}
					// Declare a field to refer to outside resources.
					if (rn.getParent() == null) {
						for (ResourceHierarchy dependedRes: dependedRootComponentGraph.keySet()) {
							for (ResourceHierarchy dependingRes: dependedRootComponentGraph.get(dependedRes)) {
								if (rn.getResourceHierarchy().equals(dependingRes)) {
									// Declare a field to refer to outside resources.
									depends.add(dependedRes);
									String resName = getComponentName(dependedRes);
									component.addField(new FieldDeclaration(new Type(resName, resName), toVariableName(resName)));
									constructor.addParameter(new VariableDeclaration(new Type(resName, resName), toVariableName(resName)));
									block.addStatement("this." + toVariableName(resName) + " = " + toVariableName(resName) + ";");
								}
							}
						}
					}
					// Declare a field to refer to the reference resource.
					refs = new HashSet<>();
					for (Channel ch : model.getChannels()) {
						DataTransferChannel c = (DataTransferChannel) ch;
						if (c.getInputResources().contains(rn.getOutSideResource())) {
							for (ResourcePath res: c.getReferenceResources()) {
								if (!refs.contains(res) && !depends.contains(res.getResourceHierarchy())) {
									refs.add(res);
									String refResName = getComponentName(res.getResourceHierarchy());
									component.addField(new FieldDeclaration(new Type(refResName, refResName), res.getResourceName()));
									constructor.addParameter(new VariableDeclaration(new Type(refResName, refResName), res.getResourceName()));						
									block.addStatement("this." + res.getResourceName() + " = " + res.getResourceName() + ";");
								}
							}
						}
					}
					constructor.setBody(block);
					if (constructor.getParameters() != null) {
						component.addMethod(constructor);
						resourceConstructors.put(rn.getResourceHierarchy(), constructor);
					}
				}
								
				// Declare the field to store the state in the type of each resource.
				if (((StoreAttribute) rn.getAttribute()).isStored()) {
					ResourcePath res = rn.getOutSideResource();
					Set<ResourceHierarchy> children = rn.getResourceHierarchy().getChildren();
					if (children == null || children.size() == 0) {
						// leaf resource.
						Type fieldType = getImplStateType(res.getResourceHierarchy());
						component.addField(new FieldDeclaration(fieldType, "value", getInitializer(res)));
						constructorParams.add(new AbstractMap.SimpleEntry<>(rn.getResourceHierarchy(), new VariableDeclaration(fieldType, toVariableName(resourceName))));
					} else {
						ResourceHierarchy child = children.iterator().next();
						if (children.size() == 1 && child.getNumParameters() > 0) {
							// map or list.
							component.addField(new FieldDeclaration(getImplStateType(res.getResourceHierarchy()), "value", getInitializer(res)));
						} else {
							// class
							for (ResourceHierarchy c: children) {
								String childTypeName = getComponentName(c);
								Type childType = null;
								if (generatesComponent(c)) {
									// The child has a component.
									childType = new Type(childTypeName, childTypeName);
									String fieldName = toVariableName(childTypeName);
									component.addField(new FieldDeclaration(childType, fieldName, getInitializer(res)));							
								}
							}
						}
					}
				}
				
				// Declare the getter methods to obtain the children resources.
				for (ResourceNode child: rn.getChildren()) {
					if (generatesComponent(child.getResourceHierarchy())) {
						// A component for the child is generated.
						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(new VariableDeclaration(var.getType(), var.getName()));
							} else if (param.getExpression() instanceof Term) {
								Term var = (Term) param.getExpression();
								params.add(new VariableDeclaration(var.getType(), "v" + v));
							}
							v++;
						}
						String childCompName = getComponentName(child.getResourceHierarchy());
						Type childType = new Type(childCompName, childCompName);
						MethodDeclaration childGetter = null;
						if (params.size() == 0) {
							childGetter = new MethodDeclaration("get" + childCompName, childType);
						} else {
							childGetter = new MethodDeclaration("get" + childCompName, false, childType, params);
						}
						component.addMethod(childGetter);
					}
				}
			}
			
			// Declare the state field and reference fields in the parent component.
			if (component == null) {
				// Declare reference fields for push/pull data transfer.
				boolean noPullTransfer = true;
				for (Edge resToCh : rn.getOutEdges()) {
					DataFlowEdge re = (DataFlowEdge) resToCh;
					DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel();
					for (Edge chToRes: re.getDestination().getOutEdges()) {
						ResourcePath dstRes = ((ResourceNode) chToRes.getDestination()).getOutSideResource();
						boolean outsideOutputResource = false;
						for (ChannelMember cm: ch.getOutputChannelMembers()) {
							if (cm.getResource().getResourceHierarchy().equals(dstRes.getResourceHierarchy()) && cm.isOutside()) {
								outsideOutputResource = true;	// Regarded as push transfer.
								break;
							}
						}
						if (outsideOutputResource) {		// This logic may be incorrect. (ToDo)
							// Declare a field in the parent component to refer to the destination resource of push transfer.
							if (!generatesComponent(dstRes.getResourceHierarchy())) {
								dstRes = dstRes.getParent();
							}
							String dstResName = getComponentName(dstRes.getResourceHierarchy());
							FieldDeclaration refFieldForPush = new FieldDeclaration(new Type(dstResName, dstResName), toVariableName(dstResName));
							fields.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), refFieldForPush));
							if (dstRes.getParent() != null) {
								// Reference to root resource.
								ResourcePath dstRootRes = dstRes.getRoot();
								String dstRootResName = getComponentName(dstRootRes.getResourceHierarchy());
								Type dstRootResType = new Type(dstRootResName, dstRootResName);
								FieldDeclaration refRootFieldForPush = new FieldDeclaration(dstRootResType, toVariableName(dstRootResName));
								fields.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), refRootFieldForPush));
								constructorParams.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), new VariableDeclaration(dstRootResType, toVariableName(dstRootResName))));
							}
						}
					}
				}
				for (Edge chToRes : rn.getInEdges()) {
					for (Edge resToCh: chToRes.getSource().getInEdges()) {
						DataFlowEdge re = (DataFlowEdge) resToCh;
						ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource();
						DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel();
						boolean outsideInputResource = false;
						for (ChannelMember cm: ch.getInputChannelMembers()) {
							if (cm.getResource().getResourceHierarchy().equals(srcRes.getResourceHierarchy()) && cm.isOutside()) {
								outsideInputResource = true;	// Regarded as pull transfer.
								break;
							}
						}
						if (outsideInputResource) {			// This logic may be incorrect. (ToDo)
							// Declare a field in the parent component to refer to the source resource of pull transfer.
							if (!generatesComponent(srcRes.getResourceHierarchy())) {
								srcRes = srcRes.getParent();
							}
							String srcResName = getComponentName(srcRes.getResourceHierarchy());
							FieldDeclaration refFieldForPull = new FieldDeclaration(new Type(srcResName, srcResName), toVariableName(srcResName));
							fields.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), refFieldForPull));
							if (srcRes.getParent() != null) {
								// Reference to root resource.
								ResourcePath srcRootRes = srcRes.getRoot();
								String srcRootResName = getComponentName(srcRootRes.getResourceHierarchy());
								Type srcRootResType = new Type(srcRootResName, srcRootResName);
								FieldDeclaration refRootFieldForPull = new FieldDeclaration(srcRootResType, toVariableName(srcRootResName));
								fields.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), refRootFieldForPull));
								constructorParams.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), new VariableDeclaration(srcRootResType, toVariableName(srcRootResName))));
							}
							noPullTransfer = false;
						}
					}
				}
				// Declare the state field in the parent component.
				ResourceHierarchy res = rn.getOutSideResource().getResourceHierarchy();
				if (((StoreAttribute) rn.getAttribute()).isStored() && noPullTransfer && res.getNumParameters() == 0) {
					String resName = getComponentName(res);
					FieldDeclaration stateField = new FieldDeclaration(res.getResourceStateType(), toVariableName(resName));
					fields.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), stateField));
					constructorParams.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), new VariableDeclaration(res.getResourceStateType(), toVariableName(resName))));
				}
			}
			
			// Declare the getter method in this component to obtain the resource state.
			if (component != null) {
				// A component is created for this resource.
				MethodDeclaration stateGetter = new MethodDeclaration("getValue", getImplStateType(rn.getResourceHierarchy()));
				component.addMethod(stateGetter);
			} else {
				// No component is created for this resource.			
				List<VariableDeclaration> getterParams = new ArrayList<>();
				int v = 1;
				for (Selector param: rn.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++;
				}
				String resCompName = getComponentName(rn.getResourceHierarchy());
				Type resType = getImplStateType(rn.getResourceHierarchy());
				MethodDeclaration stateGetter = null;
				if (getterParams.size() == 0) {
					stateGetter = new MethodDeclaration("get" + resCompName, resType);
				} else {
					stateGetter = new MethodDeclaration("get" + resCompName, false, resType, getterParams);
				}
				getters.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), stateGetter));
			}
			
			// Declare the accessor method in the main type to call the getter method.
			MethodDeclaration accessor = null;
			List<VariableDeclaration> mainGetterParams = new ArrayList<>();
			int v = 1;
			for (Selector param: rn.getAllSelectors()) {
				if (param.getExpression() instanceof Variable) {
					Variable var = (Variable) param.getExpression();
					mainGetterParams.add(new VariableDeclaration(var.getType(), var.getName()));
				} else if (param.getExpression() instanceof Term) {
					Term var = (Term) param.getExpression();
					mainGetterParams.add(new VariableDeclaration(var.getType(), "v" + v));
				}
				v++;
			}
			if (mainGetterParams.size() > 0) {
				accessor = new MethodDeclaration("get" + getComponentName(rn.getResourceHierarchy()),
													false,
													getImplStateType(rn.getResourceHierarchy()),
													mainGetterParams);
			} else {
				accessor = new MethodDeclaration("get" + getComponentName(rn.getResourceHierarchy()),
													getImplStateType(rn.getResourceHierarchy()));
			}
			accessor.setBody(new Block());
			Expression getState = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(rn.getOutSideResource(), null);
			accessor.getBody().addStatement("return " + getState.toImplementation(new String[] {null}) + ";");
			mainComponent.addMethod(accessor);
			
			// Declare the input method in this component and the main component.
			for (Channel ch : model.getIOChannels()) {
				for (ChannelMember cm : ((DataTransferChannel) ch).getOutputChannelMembers()) {
					if (rn.getInSideResources().contains(cm.getResource())) {
						Expression message = cm.getStateTransition().getMessageExpression();
						if (message instanceof Term) {
							// In each resource.
							ArrayList<VariableDeclaration> resInputParams = new ArrayList<>();
							ArrayList<VariableDeclaration> mainInputParams = new ArrayList<>();
							v = 1;
							if (cm.getResource().getLastParam() != null) {
								Expression param = cm.getResource().getLastParam();
								if (param instanceof Variable) {
									Variable var = (Variable) param;
									resInputParams.add(new VariableDeclaration(var.getType(), var.getName()));								
									mainInputParams.add(new VariableDeclaration(var.getType(), var.getName()));								
								} else if (param instanceof Term) {
									Term var = (Term) param;
									resInputParams.add(new VariableDeclaration(var.getType(), "v" + v));								
									mainInputParams.add(new VariableDeclaration(var.getType(), "v" + v));								
								}
								v++;
							}
							if (cm.getResource().getParent() != null) {
								for (Expression param: cm.getResource().getParent().getPathParams()) {
									if (param instanceof Variable) {
										Variable var = (Variable) param;
										mainInputParams.add(new VariableDeclaration(var.getType(), var.getName()));								
									} else if (param instanceof Term) {
										Term var = (Term) param;
										mainInputParams.add(new VariableDeclaration(var.getType(), "v" + v));								
									}
									v++;
								}
							}
							for (Variable var: message.getVariables().values()) {
								resInputParams.add(new VariableDeclaration(var.getType(), var.getName()));
								mainInputParams.add(new VariableDeclaration(var.getType(), var.getName()));
							}
							MethodDeclaration input = new MethodDeclaration(
															((Term) cm.getStateTransition().getMessageExpression()).getSymbol().getImplName(),
															false, typeVoid, resInputParams);
							if (component != null) {
								// A component is created for this resource.
								component.addMethod(input);
							} else {
								// No component is created for this resource.
								inputs.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), input));
							}
							
							// In the main type.
							String str = ((Term) cm.getStateTransition().getMessageExpression()).getSymbol().getImplName();
							input = getMethod(mainComponent, str, mainInputParams);
							if (input == null) {
								input = new MethodDeclaration(str, false, typeVoid, mainInputParams);
								mainComponent.addMethod(input);
							} else {
								// Add type to a parameter without type.
								for (VariableDeclaration param: input.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) {
							// In each resource.
							ArrayList<VariableDeclaration> resInputParams = new ArrayList<>();
							ArrayList<VariableDeclaration> mainInputParams = new ArrayList<>();
							v = 1;
							if (cm.getResource().getLastParam() != null) {
								Expression param = cm.getResource().getLastParam();
								if (param instanceof Variable) {
									Variable var = (Variable) param;
									resInputParams.add(new VariableDeclaration(var.getType(), var.getName()));								
									mainInputParams.add(new VariableDeclaration(var.getType(), var.getName()));								
								} else if (param instanceof Term) {
									Term var = (Term) param;
									resInputParams.add(new VariableDeclaration(var.getType(), "v" + v));								
									mainInputParams.add(new VariableDeclaration(var.getType(), "v" + v));								
								}
								v++;
							}
							if (cm.getResource().getParent() != null) {
								for (Expression param: cm.getResource().getParent().getPathParams()) {
									if (param instanceof Variable) {
										Variable var = (Variable) param;
										mainInputParams.add(new VariableDeclaration(var.getType(), var.getName()));								
									} else if (param instanceof Term) {
										Term var = (Term) param;
										mainInputParams.add(new VariableDeclaration(var.getType(), "v" + v));								
									}
									v++;
								}
							}
							MethodDeclaration input = null;
							if (resInputParams.size() > 0) {
								input = new MethodDeclaration(
										((Variable) cm.getStateTransition().getMessageExpression()).getName(),
										false, typeVoid, resInputParams);
							} else {
								input = new MethodDeclaration(
										((Variable) cm.getStateTransition().getMessageExpression()).getName(),
										false, typeVoid, null);								
							}
							if (component != null) {
								// A component is created for this resource.
								component.addMethod(input);
							} else {
								// No component is created for this resource.
								inputs.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), input));
							}
							
							// In the main type.
							String str = ((Variable) cm.getStateTransition().getMessageExpression()).getName();
							input = getMethod(mainComponent, str, mainInputParams);
							if (input == null) {
								if (mainInputParams.size() > 0) {
									input = new MethodDeclaration(str, false, typeVoid, mainInputParams);
								} else {
									input = new MethodDeclaration(str, false, typeVoid, null);
								}
								mainComponent.addMethod(input);
							}
						}
					}
				}
			}
		}
		
		// Add leaf getter methods to the parent components.
		for (Map.Entry<ResourceHierarchy, MethodDeclaration> entry: getters) {
			resourceComponents.get(entry.getKey()).addMethod(entry.getValue());
		}
		
		// Add leaf input methods to the parent components.
		for (Map.Entry<ResourceHierarchy, MethodDeclaration> entry: inputs) {
			resourceComponents.get(entry.getKey()).addMethod(entry.getValue());
		}
		
		// Add leaf reference fields to the parent components.
		for (Map.Entry<ResourceHierarchy, FieldDeclaration> entry: fields) {
			boolean existsField = false;
			for (FieldDeclaration field: resourceComponents.get(entry.getKey()).getFields()) {
				if (field.getName().equals(entry.getValue().getName())) {
					existsField = true;
					break;
				}
			}
			if (!existsField) {
				resourceComponents.get(entry.getKey()).addField(entry.getValue());
			}
		}
		
		// Add constructor parameters to the ancestor components.
		for (ResourceNode root: graph.getRootResourceNodes()) {
			addConstructorParameters(root.getResourceHierarchy(), resourceComponents, resourceConstructors, constructorParams);
		}

		// Declare the Pair class.
		boolean isCreatedPair = false;
		for (ResourceNode rn : resources) {
			if (isCreatedPair) continue;
			if (rn.getResourceStateType() != null && model.getType("Pair").isAncestorOf(rn.getResourceStateType())) {
				TypeDeclaration type = new TypeDeclaration("Pair<T>");
				type.addField(new FieldDeclaration(new Type("Double", "T"), "left"));
				type.addField(new FieldDeclaration(new Type("Double", "T"), "right"));
				
				MethodDeclaration constructor = new MethodDeclaration("Pair", true);
				constructor.addParameter(new VariableDeclaration(new Type("Double", "T"), "left"));
				constructor.addParameter(new VariableDeclaration(new Type("Double", "T"), "right"));
				Block block = new Block();
				block.addStatement("this.left = left;");
				block.addStatement("this.right = right;");
				constructor.setBody(block);
				type.addMethod(constructor);
			
				for(FieldDeclaration field : type.getFields()) {
					MethodDeclaration getter = new MethodDeclaration(
						"get" + field.getName().substring(0,1).toUpperCase() + field.getName().substring(1),
						new Type("Double","T"));
					getter.setBody(new Block());
					getter.getBody().addStatement("return " + field.getName() + ";");
					type.addMethod(getter);
				}
			
				CompilationUnit	cu = new CompilationUnit(type);
				cu.addImport(new ImportDeclaration("java.util.*"));
				codes.add(cu);
				
				isCreatedPair = true;
			}
		}
		
//		HashSet<String> tmps = new HashSet<>();
//		HashSet<String> cont = new HashSet<>();
//		for (MethodDeclaration method : mainType.getMethods()) {
//			if (!tmps.contains(method.getName()))
//				tmps.add(method.getName());
//			else
//				cont.add(method.getName());
//		}
//		for (MethodDeclaration method : mainType.getMethods()) {
//			if (cont.contains(method.getName())) {
//				method.setName(method.getName() + method.getParameters().get(0).getName().substring(0, 1).toUpperCase()
//						+ method.getParameters().get(0).getName().substring(1));
//			}
//		}
		return codes;
	}

	private static Map<ResourceHierarchy, Set<ResourceHierarchy>> getDependedRootComponentGraph(DataTransferModel model) {
		Map<ResourceHierarchy, Set<ResourceHierarchy>> dependedComponentGraph = new HashMap<>();
		for (Channel ch: model.getChannels()) {
			DataTransferChannel dtCh = (DataTransferChannel) ch;
			Set<ResourceHierarchy> inRes = new HashSet<>();
			Set<ResourceHierarchy> outRes = new HashSet<>();
			for (ChannelMember cm: dtCh.getChannelMembers()) {
				if (cm.isOutside()) {
					outRes.add(cm.getResource().getResourceHierarchy());
				} else {
					inRes.add(cm.getResource().getResourceHierarchy());
				}
			}
			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);
						}
						dependings.add(in.getRoot());
					}
				}
			}
		}
		return dependedComponentGraph;
	}

	static private ArrayList<ResourceNode> determineResourceOrder(DataFlowGraph graph, Map<ResourceHierarchy, Set<ResourceHierarchy>> dependedRootComponentGraph) {
		ArrayList<ResourceNode> resources = new ArrayList<>();
		Set<ResourceNode> visited = new HashSet<>();
		for (Node n : graph.getResourceNodes()) {
			ResourceNode resNode = (ResourceNode) n;
			topologicalSort(resNode, graph, dependedRootComponentGraph, visited, resources);
		}
		return resources;
	}

	static private void topologicalSort(ResourceNode curResNode, DataFlowGraph graph, Map<ResourceHierarchy, Set<ResourceHierarchy>> dependedRootComponentGraph, Set<ResourceNode> visited, List<ResourceNode> orderedList) {
		if (visited.contains(curResNode)) return;
		visited.add(curResNode);
		// For each incoming PUSH transfer.
		for (Edge chToRes: curResNode.getInEdges()) {
			for (Edge resToCh: chToRes.getSource().getInEdges()) {
				DataFlowEdge re = (DataFlowEdge) resToCh;
				if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) {
					topologicalSort((ResourceNode) re.getSource(), graph, dependedRootComponentGraph, visited, orderedList);
				}
			}
		}
		// For each outgoing PULL transfer.
		for (Edge resToCh: curResNode.getOutEdges()) {
			DataFlowEdge de = (DataFlowEdge) resToCh;
			if (((PushPullAttribute) de.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) {
				for (Edge chToRes : resToCh.getDestination().getOutEdges()) {
					topologicalSort((ResourceNode) chToRes.getDestination(), graph, dependedRootComponentGraph, visited, orderedList);
				}
			}
		}
		// For each depending root node.
		if (dependedRootComponentGraph.get(curResNode.getResourceHierarchy()) != null) {
			for (ResourceHierarchy dependingRes: dependedRootComponentGraph.get(curResNode.getResourceHierarchy())) {
				for (ResourceNode root: graph.getRootResourceNodes()) {
					if (root.getResourceHierarchy().equals(dependingRes)) {
						topologicalSort(root, graph, dependedRootComponentGraph, visited, orderedList);
					}
				}
			}
		}
		// For each reference resource.
		for (Node n: graph.getResourceNodes()) {		
			ResourceNode resNode = (ResourceNode) n;
			for (Edge resToCh : resNode.getOutEdges()) {
				ChannelNode chNode = (ChannelNode) resToCh.getDestination();
				for (ChannelMember m: chNode.getChannel().getReferenceChannelMembers()) {
					if (m.getResource().equals(curResNode.getOutSideResource())) {
						topologicalSort(resNode, graph, dependedRootComponentGraph, visited, orderedList);
					}
				}
			}
		}
		orderedList.add(0, curResNode);
	}

	private static List<VariableDeclaration> addConstructorParameters(ResourceHierarchy resource,
			Map<ResourceHierarchy, TypeDeclaration> resourceComponents,
			Map<ResourceHierarchy, MethodDeclaration> resourceConstructors,
			List<Entry<ResourceHierarchy, VariableDeclaration>> constructorParams) {
		List<VariableDeclaration> params = new ArrayList<>();
		for (ResourceHierarchy child: resource.getChildren()) {
			params.addAll(addConstructorParameters(child, resourceComponents, resourceConstructors, constructorParams));
		}
		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);
					constructor = new MethodDeclaration(resourceName, true);
					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("this." + toVariableName(param.getName()) + " = " + toVariableName(param.getName()) + ";");
					}
				}
			}
		}
		if (resource.getNumParameters() > 0) params.clear();
		return params;
	}

	private static String getInitializer(ResourcePath res) {
		Type stateType = res.getResourceStateType();
		String initializer = null;
		if (res.getResourceHierarchy().getInitialValue() != null) {
			initializer = res.getResourceHierarchy().getInitialValue().toImplementation(new String[] {""});
		} else if (stateType != null) {
			if (DataConstraintModel.typeList.isAncestorOf(stateType)) {
				initializer = "new " + res.getResourceStateType().getImplementationTypeName() + "()";
			} else if (DataConstraintModel.typeMap.isAncestorOf(stateType)) {
				initializer = "new " + res.getResourceStateType().getImplementationTypeName() + "()";
			}
		}
		return initializer;
	}

	static public ArrayList<String> getCodes(ArrayList<TypeDeclaration> codeTree) {
		ArrayList<String> codes = new ArrayList<>();
		for (TypeDeclaration type : codeTree) {
			codes.add("public class " + type.getTypeName() + "{");
			for (FieldDeclaration field : type.getFields()) {
				if (type.getTypeName() != mainTypeName) {
					String cons = "\t" + "private " + field.getType().getInterfaceTypeName() + " "
							+ field.getName();
					if (DataConstraintModel.isListType(field.getType()))
						cons += " = new ArrayList<>()";
					cons += ";";
					codes.add(cons);
				} else {
					String cons = "\t" + "private " + field.getType().getInterfaceTypeName() + " "
							+ field.getName() + " = new " + field.getType().getTypeName() + "(";
					cons += ");";
					codes.add(cons);
				}
			}
			codes.add("");
			for (MethodDeclaration method : type.getMethods()) {
				String varstr = "\t" + "public " + method.getReturnType().getInterfaceTypeName() + " "
						+ method.getName() + "(";
				if (method.getParameters() != null) {
					for (VariableDeclaration var : method.getParameters()) {
						varstr += var.getType().getInterfaceTypeName() + " " + var.getName() + ",";
					}
					if (!method.getParameters().isEmpty())
						varstr = varstr.substring(0, varstr.length() - 1);
				}
				if (method.getBody() != null) {
					for (String str : method.getBody().getStatements()) {
						codes.add("\t\t" + str + ";");
					}
				}
				codes.add(varstr + ")" + "{");
				codes.add("\t" + "}");
				codes.add("");
			}
			codes.add("}");
			codes.add("");
		}
		return codes;
	}

	private static MethodDeclaration getMethod(TypeDeclaration type, String methodName, List<VariableDeclaration> params) {
		for (MethodDeclaration m: type.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;
	}

	static public IResourceStateAccessor pushAccessor = 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("value",
						targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
								: DataConstraintModel.typeInt);
			}
			// for reference channel member
			return new Parameter(targetRes.getResourceName(),
					targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
							: DataConstraintModel.typeInt);
		}

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

		@Override
		public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) {
			if (fromRes != null && targetRes.equals(fromRes)) {
				return new Field("value",
						targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
								: DataConstraintModel.typeInt);
			}
			// for reference channel member
			return new Parameter(targetRes.getResourceName(),
					targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
							: DataConstraintModel.typeInt);
		}
	};
	
	static public IResourceStateAccessor pullAccessor = 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());
				if (generatesComponent(targetRes.getResourceHierarchy())) {
					getter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD));
					getter.addChild(new Field(toVariableName(targetComponentName), targetRes.getResourceStateType()));
				} else {
					String parentName = toVariableName(getComponentName(targetRes.getResourceHierarchy().getParent()));
					Type parentType = targetRes.getResourceHierarchy().getParent().getResourceStateType();
					getter = new Term(new Symbol("get" + 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());
				if (generatesComponent(targetRes.getResourceHierarchy())) {
					getter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD));
					getter.addChild(new Field(toVariableName(targetComponentName), targetRes.getResourceStateType()));
				} else {
					String parentName = toVariableName(getComponentName(targetRes.getResourceHierarchy().getParent()));
					Type parentType = targetRes.getResourceHierarchy().getParent().getResourceStateType();
					getter = new Term(new Symbol("get" + 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("value",
							targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
									: DataConstraintModel.typeInt);
				}
				// for reference channel member
				Term getter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD));
				getter.addChild(new Field(targetRes.getResourceName(), targetRes.getResourceStateType()));
				return getter;
			} else {
				// access from the outside of the hierarchy
				Stack<ResourcePath> pathStack = new Stack<>();
				ResourcePath curPath = targetRes;
				do {
					pathStack.push(curPath);
					curPath = curPath.getParent();					
				} while (curPath != null);
				// iterate from the root resource
				Term getter = null;
				int v = 1;
				while (!pathStack.empty()) {
					curPath = pathStack.pop();
					String typeName = getComponentName(curPath.getResourceHierarchy());
					if (getter == null) {
						// root resource
						String fieldName = toVariableName(typeName);
						getter = new Field(fieldName, new Type(typeName, typeName));
					} else {
						Term newGetter = new Term(new Symbol("get" + typeName, -1, Symbol.Type.METHOD));
						newGetter.addChild(getter);
						if (curPath.getResourceHierarchy().getNumParameters() > 0) {
							Variable var = null;
							Expression param = curPath.getLastParam();
							if (param instanceof Variable) {
								var = (Variable) param;
							} else if (param instanceof Term) {
								var = new Variable("v" + v, ((Term) param).getType());
							}
							if (var != null) {
								newGetter.addChild(var);
								newGetter.getSymbol().setArity(2);
							}
							v++;
						}
						getter = newGetter;
					}
				}
				if (generatesComponent(targetRes.getResourceHierarchy())) {
					Term newGetter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD));
					newGetter.addChild(getter);
					getter = newGetter;
				}
				return getter;
			}
		}
	};
}