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