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

import java.util.ArrayList;
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 code.ast.Annotation;
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.DataFlowEdge;
import models.dataFlowModel.DataFlowGraph;
import models.dataFlowModel.ResourceNode;
import models.dataFlowModel.ChannelNode;
import models.dataFlowModel.StoreAttribute;

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

	public static String getMainTypeName() {
		return mainTypeName;
	}

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

	public static void resetMainTypeName() {
		JerseyCodeGenerator.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 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.
					return new Type("List", "ArrayList<>", "List<" + getComponentName(child) + ">", DataConstraintModel.typeList);
				} else if (DataConstraintModel.typeMap.isAncestorOf(res.getResourceStateType())) {
					// map.
					return new Type("Map", "HashMap<>", "Map<String, " + getComponentName(child) + ">", DataConstraintModel.typeMap);
				}
				return null;
			} else {
				// class
				return res.getResourceStateType();
			}
		}
	}


	static public ArrayList<CompilationUnit> doGenerate(DataFlowGraph graph, DataTransferModel model) {
		ArrayList<CompilationUnit> codes = new ArrayList<>();
//		ArrayList<ResourceNode> resources = StoreResourceCheck(graph);
		Collection<ResourceNode> resources = graph.getResourceNodes();
		Map<ResourceHierarchy, TypeDeclaration> resourceTypes = new HashMap<>();
		Map<ResourceHierarchy, MethodDeclaration> getterAccessors = new HashMap<>();
		Map<ResourceHierarchy, MethodDeclaration> inputAccessors = new HashMap<>();

		// For each resource node.
		for (Node n : resources) {
			ResourceNode rn = (ResourceNode) n;			
			if (rn.getResourceHierarchy().getChildren().size() == 0 && rn.getResourceHierarchy().getNumParameters() == 0) {
				// Don't create new component for a simple leaf resource.
				continue;
			}
			String resourceName = getComponentName(rn.getResourceHierarchy());

			TypeDeclaration type = resourceTypes.get(rn.getResourceHierarchy());
			if (type == null) {
				// Add compilation unit for each resource.
				type = new TypeDeclaration(resourceName);
				if (rn.getResourceHierarchy().getParent() == null) {
					// For a root node.
					type.addAnnotation(new Annotation("Component"));
					type.addAnnotation(new Annotation("Path", "\"/" + rn.getResourceName() + "\""));
				}
				resourceTypes.put(rn.getResourceHierarchy(), type);
				
				CompilationUnit cu = new CompilationUnit(type);
				cu.addImport(new ImportDeclaration("java.util.*"));
				if (rn.getResourceHierarchy().getParent() == null) {
					// For a root node.
					cu.addImport(new ImportDeclaration("javax.ws.rs.*"));
					cu.addImport(new ImportDeclaration("javax.ws.rs.client.*"));
					cu.addImport(new ImportDeclaration("javax.ws.rs.core.*"));
					cu.addImport(new ImportDeclaration("org.springframework.stereotype.Component"));
					cu.addImport(new ImportDeclaration("com.fasterxml.jackson.databind.ObjectMapper"));
					cu.addImport(new ImportDeclaration("com.fasterxml.jackson.core.JsonProcessingException"));
				}
				codes.add(cu);
			}
			
			// Declare a client field and update methods from other resources.
			if (rn.getResourceHierarchy().getParent() == null) {
				// For a root resource
				boolean bDeclareClientField = false;
				for (Edge resToCh: rn.getOutEdges()) {
					DataFlowEdge re = (DataFlowEdge) resToCh;
					if (!bDeclareClientField && ((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) {
						// Declare a client field to connect to the destination resource of push transfer.
						type.addField(new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()"));
						bDeclareClientField = true;
					}
				}
				for (Edge chToRes : rn.getInEdges()) {
					for (Edge resToCh: chToRes.getSource().getInEdges()) {
						DataFlowEdge re = (DataFlowEdge) resToCh;
						ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource();
						String srcResName = getComponentName(srcRes.getResourceHierarchy());
						if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) {
							if (!bDeclareClientField) {
								// Declare a client field to connect to the source resource of pull transfer.
								type.addField(new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()"));
								bDeclareClientField = true;
							}
						} else {
							// Declare an update method in the type of the destination resource.
							ArrayList<VariableDeclaration> vars = new ArrayList<>();
							String srcName = srcRes.getResourceName();
							Type srcType = srcRes.getResourceStateType();
							VariableDeclaration param = new VariableDeclaration(srcType, srcName);
							param.addAnnotation(new Annotation("FormParam", "\"" + srcName + "\""));
							vars.add(param);
							for (ResourcePath refRes: ((ChannelNode) re.getDestination()).getChannel().getReferenceResources()) {
								if (!refRes.equals(rn.getOutSideResource())) {
									param = new VariableDeclaration(refRes.getResourceStateType(), refRes.getResourceName());
									param.addAnnotation(new Annotation("FormParam", "\"" + refRes.getResourceName() + "\""));
									vars.add(param);						
								}
							}
							MethodDeclaration update = new MethodDeclaration("update" + srcResName, false, typeVoid, vars);
							for (ChannelMember cm: ((ChannelNode) re.getDestination()).getChannel().getOutputChannelMembers()) {
								if (rn.getInSideResources().contains(cm.getResource())) {
									if (cm.getStateTransition().isRightUnary()) {
										update.addAnnotation(new Annotation("PUT"));
									} else {
										update.addAnnotation(new Annotation("POST"));
									}
								}
							}
							if (re.getDestination().getIndegree() > 1) {
								 // For each source resource, a child resource is defined in the destination resource so that its state can be updated separately.
								update.addAnnotation(new Annotation("Path", "\"/" + srcName + "\""));
								// Declare a field to cash the state of the source resource in the type of the destination resource.
								ResourcePath cashRes = ((ResourceNode) re.getSource()).getOutSideResource();
								type.addField(new FieldDeclaration(cashRes.getResourceStateType(), srcName, getInitializer(cashRes)));
							}
							type.addMethod(update);
						}
					}
				}
			}
			
//			// Declare a client field to connect to the source resource of reference transfer.
//			if (!bDeclareClientField) {
//				for (ChannelGenerator cg : model.getChannelGenerators()) {
//					DataflowChannelGenerator dcg = ((DataflowChannelGenerator) cg);
//					for (ChannelMember cm : dcg.getOutputChannelMembers()) {
//						if (cm.getIdentifierTemplate().getResourceName().equals(type.getTypeName().toLowerCase())) {
//							if (dcg.getReferenceChannelMembers().size() > 0) {
//								// If there exists one or more reference channel member.
//								type.addField(new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()"));
//								bDeclareClientField = true;
//								break;
//							}
//						}
//					}
//					if (bDeclareClientField) break;
//				}
//			}
			
			// Declare input methods in resources.
			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> rootParams = new ArrayList<>();
							String resourcePath = "\"";
							int v = 1;
							for (Selector selector: rn.getAllSelectors()) {
								if (selector.getExpression() instanceof Variable) {
									Variable var = (Variable) selector.getExpression();
									String paramName = var.getName();
									VariableDeclaration param = new VariableDeclaration(var.getType(), paramName);
									param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\""));
									rootParams.add(param);
									resourcePath += "/{" + paramName + "}";
								} else if (selector.getExpression() instanceof Term) {
									Term var = (Term) selector.getExpression();
									String paramName = "v" + v;
									VariableDeclaration param = new VariableDeclaration(var.getType(), paramName);
									param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\""));
									rootParams.add(param);
									resourcePath += "/{" + paramName + "}";
								}
								v++;
							}
							resourcePath += "\"";
							ArrayList<VariableDeclaration> resParams = new ArrayList<>();
							for (Variable var: message.getVariables().values()) {
								String paramName = var.getName();
								VariableDeclaration param = new VariableDeclaration(var.getType(), paramName);
								resParams.add(param);
								param = new VariableDeclaration(var.getType(), paramName);
								param.addAnnotation(new Annotation("FormParam", "\"" + paramName + "\""));
								rootParams.add(param);
							}
							
							if (rn.getResourceHierarchy().getParent() != null) {
								MethodDeclaration input = new MethodDeclaration(((Term) cm.getStateTransition().getMessageExpression()).getSymbol().getImplName(),
																					false, typeVoid, resParams);
								type.addMethod(input);
							}
							
							// For the root resource.
							String str = ((Term) cm.getStateTransition().getMessageExpression()).getSymbol().getImplName();
							MethodDeclaration inputAccessor = new MethodDeclaration(str, false, typeVoid, rootParams);
							if (cm.getStateTransition().isRightUnary()) {
								inputAccessor.addAnnotation(new Annotation("PUT"));
							} else {
								inputAccessor.addAnnotation(new Annotation("POST"));
							}
							if (ch.getAllSelectors().size() > 0) {
								inputAccessor.addAnnotation(new Annotation("Path", resourcePath));
							}
							inputAccessors.put(rn.getResourceHierarchy(), inputAccessor);
						} else if (message instanceof Variable) {
							// In each resource.
							if (rn.getResourceHierarchy().getParent() != null) {
								MethodDeclaration input = new MethodDeclaration(((Variable) cm.getStateTransition().getMessageExpression()).getName(),
																					false, typeVoid, null);
								type.addMethod(input);
							}
							
							// For the root resource.
							ArrayList<VariableDeclaration> rootParams = new ArrayList<>();
							String resourcePath = "\"";
							int v = 1;
							for (Selector selector: rn.getAllSelectors()) {
								if (selector.getExpression() instanceof Variable) {
									Variable var = (Variable) selector.getExpression();
									String paramName = var.getName();
									VariableDeclaration param = new VariableDeclaration(var.getType(), paramName);
									param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\""));
									rootParams.add(param);
									resourcePath += "/{" + paramName + "}";
								} else if (selector.getExpression() instanceof Term) {
									Term var = (Term) selector.getExpression();
									String paramName = "v" + v;
									VariableDeclaration param = new VariableDeclaration(var.getType(), paramName);
									param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\""));
									rootParams.add(param);
									resourcePath += "/{" + paramName + "}";
								}
								v++;
							}
							resourcePath += "\"";
							String str = ((Term) cm.getStateTransition().getMessageExpression()).getSymbol().getImplName();
							MethodDeclaration inputAccessor = new MethodDeclaration(str, false, typeVoid, rootParams);
							if (cm.getStateTransition().isRightUnary()) {
								inputAccessor.addAnnotation(new Annotation("PUT"));
							} else {
								inputAccessor.addAnnotation(new Annotation("POST"));
							}
							if (ch.getAllSelectors().size() > 0) {
								inputAccessor.addAnnotation(new Annotation("Path", resourcePath));
							}
							inputAccessors.put(rn.getResourceHierarchy(), inputAccessor);
						}
					}
				}
			}
			
			// 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.addField(new FieldDeclaration(getImplStateType(res.getResourceHierarchy()), "value", getInitializer(res)));
				} else {
					ResourceHierarchy child = children.iterator().next();
					if (children.size() == 1 && child.getNumParameters() > 0) {
						// map or list.
						type.addField(new FieldDeclaration(getImplStateType(res.getResourceHierarchy()), "value", getInitializer(res)));
					} else {
						// class
						for (ResourceHierarchy c: children) {
							String childTypeName = getComponentName(c);
							Type childType = null;
							if ((c.getChildren() == null || c.getChildren().size() == 0) && c.getNumParameters() == 0) {
								// The child does not have a component.
								childType = c.getResourceStateType();
							} else {
								// The child has a component.
								childType = new Type(childTypeName, childTypeName);
							}
							String fieldName = childTypeName.substring(0, 1).toLowerCase() + childTypeName.substring(1);
							type.addField(new FieldDeclaration(childType, fieldName, getInitializer(res)));							
						}
					}
				}
			}
			
			// Declare the getter method to obtain the state in the type of each resource.
			MethodDeclaration stateGetter = new MethodDeclaration("getValue", getImplStateType(rn.getResourceHierarchy()));
			if (rn.getResourceHierarchy().getParent() == null) {
				// Since this getter is also an accessor.
				stateGetter.addAnnotation(new Annotation("Produces", "MediaType.APPLICATION_JSON"));
				stateGetter.addAnnotation(new Annotation("GET"));
			}
			type.addMethod(stateGetter);
			
			// Declare the getter methods to obtain a child of the resource.
			for (ResourceNode child: rn.getChildren()) {
				List<VariableDeclaration> pathParams = new ArrayList<>();
				int v = 1;
				for (Selector pathParam: child.getSelectors()) {
					if (pathParam.getExpression() instanceof Variable) {
						Variable var = (Variable) pathParam.getExpression();
						pathParams.add(new VariableDeclaration(var.getType(), var.getName()));
					} else if (pathParam.getExpression() instanceof Term) {
						Term var = (Term) pathParam.getExpression();
						pathParams.add(new VariableDeclaration(var.getType(), "v" + v));
					}
					v++;
				}
				String childTypeName = getComponentName(child.getResourceHierarchy());
				Type childType = null;
				if ((child.getResourceHierarchy().getChildren() == null || child.getResourceHierarchy().getChildren().size() == 0)
						&& child.getResourceHierarchy().getNumParameters() == 0) {
					// The child does not have a component.
					childType = child.getResourceStateType();
				} else {
					// The child has a component.
					childType = new Type(childTypeName, childTypeName);
				}
				MethodDeclaration childGetter = null;
				if (pathParams.size() == 0) {
					childGetter = new MethodDeclaration("get" + childTypeName, childType);
				} else {
					childGetter = new MethodDeclaration("get" + childTypeName, false, childType, pathParams);
				}
				type.addMethod(childGetter);
			}
			
			// Declare the getter accessor to obtain the resource state in the root resource.
			if (rn.getResourceHierarchy().getParent() != null) {
				// For a non-root resource
				MethodDeclaration getterAccessor = null;
				List<VariableDeclaration> params = new ArrayList<>();
				String resourcePath = "\"";
				int v = 1;
				for (Selector pathParam: rn.getAllSelectors()) {
					if (pathParam.getExpression() instanceof Variable) {
						Variable var = (Variable) pathParam.getExpression();
						String paramName = var.getName();
						VariableDeclaration param = new VariableDeclaration(var.getType(), paramName);
						param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\""));
						params.add(param);								
						resourcePath += "/{" + paramName + "}";
					} else if (pathParam.getExpression() instanceof Term) {
						Term var = (Term) pathParam.getExpression();
						String paramName = "v" + v;
						VariableDeclaration param = new VariableDeclaration(var.getType(), paramName);
						param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\""));
						params.add(param);								
						resourcePath += "/{" + paramName + "}";
					}
					v++;
				}
				resourcePath += "\"";
				if (params.size() > 0) {
					getterAccessor = new MethodDeclaration("get" + getComponentName(rn.getResourceHierarchy()) + "Value",
														false,
														getImplStateType(rn.getResourceHierarchy()),
														params);
				} else {
					getterAccessor = new MethodDeclaration("get" + getComponentName(rn.getResourceHierarchy()) + "Value",
														getImplStateType(rn.getResourceHierarchy()));
				}
				getterAccessor.setBody(new Block());
				Expression getState = JerseyCodeGenerator.pullAccessor.getCurrentStateAccessorFor(rn.getOutSideResource(), rn.getOutSideResource().getRoot());
				getterAccessor.getBody().addStatement("return " + getState.toImplementation(new String[] {null}) + ";");
				
				getterAccessor.addAnnotation(new Annotation("Produces", "MediaType.APPLICATION_JSON"));
				getterAccessor.addAnnotation(new Annotation("GET"));
				if (rn.getSelectors().size() > 0) {
					getterAccessor.addAnnotation(new Annotation("Path", resourcePath));
				}
				getterAccessors.put(rn.getResourceHierarchy(), getterAccessor);
			}
		}
		
		for (ResourceHierarchy rootRes: model.getResourceHierarchies()) {
			if (rootRes.getParent() == null) {
				// root resource
				TypeDeclaration rootType = resourceTypes.get(rootRes);
				// Add getter accessors.
				for (ResourceHierarchy res: getterAccessors.keySet()) {
					if (rootRes.isAncestorOf(res)) {
						rootType.addMethod(getterAccessors.get(res));
					}
				}
				// Add inout accessors.
				for (ResourceHierarchy res: inputAccessors.keySet()) {
					if (rootRes.isAncestorOf(res)) {
						rootType.addMethod(inputAccessors.get(res));
					}
				}
			}
		}
		
		// Declare the Pair class.
		boolean isCreatedPair = false;
		for(Node n : resources) {
			ResourceNode rn = (ResourceNode) n;
			if(isCreatedPair) continue;
			if(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);
				}
				
//				MethodDeclaration toStr = new MethodDeclaration("toString", false, DataConstraintModel.typeString, null);
//				block = new Block();
//				block.addStatement("return \"{\\\"\" + left + \"\\\":\\\"\" + right + \"\\\"}\";");
//				toStr.setBody(block);
//				type.addMethod(toStr);
			
				CompilationUnit	cu = new CompilationUnit(type);
				cu.addImport(new ImportDeclaration("java.util.*"));
				codes.add(cu);
				
				isCreatedPair = true;
			}
		}
				
		return codes;
	}

	private static String getInitializer(ResourcePath resId) {
		Type stateType = resId.getResourceStateType();
		String initializer = null;
		if (resId.getResourceHierarchy().getInitialValue() != null) {
			initializer = resId.getResourceHierarchy().getInitialValue().toImplementation(new String[] {""});
		} else {
			if (DataConstraintModel.typeList.isAncestorOf(stateType)) {
				initializer = "new " + resId.getResourceStateType().getImplementationTypeName() + "()";
			} else if (DataConstraintModel.typeMap.isAncestorOf(stateType)) {
				initializer = "new " + resId.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 " + field.getType().getImplementationTypeName() + "()";
					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;
	}

	static public IResourceStateAccessor pushAccessor = new IResourceStateAccessor() {
		@Override
		public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) {
			if (target.equals(from)) {
				return new Field("value",
						target.getResourceStateType() != null ? target.getResourceStateType()
								: DataConstraintModel.typeInt);
			}
			return null;
		}

		@Override
		public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) {
			return new Parameter(target.getResourceName(),
					target.getResourceStateType() != null ? target.getResourceStateType()
							: DataConstraintModel.typeInt);
		}
	};
	static public IResourceStateAccessor pullAccessor = new IResourceStateAccessor() {
		@Override
		public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) {
			if (from != null && !from.getResourceHierarchy().isAncestorOf(target.getResourceHierarchy())) {
				if (target.equals(from)) {
					return new Field("value",
							target.getResourceStateType() != null ? target.getResourceStateType()
									: DataConstraintModel.typeInt);
				}
				// for reference channel member
				return new Parameter(target.getResourceName(),
						target.getResourceStateType() != null ? target.getResourceStateType()
								: DataConstraintModel.typeInt);
			} else {
				// access from an ancestor or outside of the hierarchy
				Stack<ResourcePath> pathStack = new Stack<>();
				ResourcePath curPath = target;
				do {
					if (from != null && curPath.equals(from)) break;
					pathStack.push(curPath);
					curPath = curPath.getParent();					
				} while (curPath != null);
				// iterate from the `from' resource
				Term getter = null;
				int v = 1;
				while (!pathStack.empty()) {
					curPath = pathStack.pop();
					String typeName = getComponentName(curPath.getResourceHierarchy());
					if (getter == null && from == null) {
						// root resource
						String fieldName = typeName.substring(0, 1).toLowerCase() + typeName.substring(1);
						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 (target.getResourceHierarchy().getNumParameters() > 0 
						|| (target.getResourceHierarchy().getChildren() != null && target.getResourceHierarchy().getChildren().size() > 0)) {
					Term newGetter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD));
					newGetter.addChild(getter);
					getter = newGetter;
				}
				return getter;
			}
		}

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