package generators; import java.util.AbstractMap; 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.Position; 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"); public static boolean differentTreesAsDifferentServices = true; 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 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<>(); // ArrayList<ResourceNode> resources = StoreResourceCheck(graph); Collection<ResourceNode> resources = graph.getResourceNodes(); Map<ResourceHierarchy, TypeDeclaration> resourceComponents = new HashMap<>(); Map<ResourceHierarchy, MethodDeclaration> resourceConstructors = new HashMap<>(); List<Map.Entry<ResourceHierarchy, MethodDeclaration>> getters = new ArrayList<>(); Map<ResourceHierarchy, Map<String, MethodDeclaration>> updates = new HashMap<>(); Map<ResourceHierarchy, Map<String, MethodDeclaration>> inputs = new HashMap<>(); List<Map.Entry<ResourceHierarchy, FieldDeclaration>> fields = new ArrayList<>(); Map<ResourceHierarchy, Set<ResourceHierarchy>> childGetters = new HashMap<>(); Map<ResourceHierarchy, MethodDeclaration> getterAccessors = new HashMap<>(); Map<ResourceHierarchy, MethodDeclaration> inputAccessors = new HashMap<>(); Map<ResourceHierarchy, Map<String, VariableDeclaration>> constructorParams = new HashMap<>(); Map<Channel, ChannelMember> priorMemberForInputChannel = new HashMap<>(); // For each resource node. for (Node n : resources) { ResourceNode resourceNode = (ResourceNode) n; TypeDeclaration component = null; if (generatesComponent(resourceNode.getResourceHierarchy())) { String resourceName = getComponentName(resourceNode.getResourceHierarchy()); component = resourceComponents.get(resourceNode.getResourceHierarchy()); if (component == null) { // Add compilation unit for each resource. component = new TypeDeclaration(resourceName); if (resourceNode.getResourceHierarchy().getParent() == null) { // For a root node. component.addAnnotation(new Annotation("Component")); component.addAnnotation(new Annotation("Path", "\"/" + resourceNode.getResourceName() + "\"")); } resourceComponents.put(resourceNode.getResourceHierarchy(), component); CompilationUnit cu = new CompilationUnit(component); cu.addImport(new ImportDeclaration("java.util.*")); if (resourceNode.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 the field to store the state in the type of each resource. if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { ResourceHierarchy res = resourceNode.getResourceHierarchy(); Set<ResourceHierarchy> children = res.getChildren(); if (children == null || children.size() == 0) { // leaf resource. Type fieldType = getImplStateType(res); component.addField(new FieldDeclaration(fieldType, "value", getInitializer(res))); Map<String, VariableDeclaration> nameToParam = constructorParams.get(res); if (nameToParam == null) { nameToParam = new HashMap<>(); constructorParams.put(resourceNode.getResourceHierarchy(), nameToParam); } String varName = toVariableName(resourceName); if (nameToParam.get(varName) == null) { nameToParam.put(varName, new VariableDeclaration(fieldType, varName)); } } else { ResourceHierarchy child = children.iterator().next(); if (children.size() == 1 && child.getNumParameters() > 0) { // map or list. component.addField(new FieldDeclaration(getImplStateType(res), "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 method to obtain the resource state in the component of each resource. MethodDeclaration stateGetter = new MethodDeclaration("getValue", getImplStateType(resourceNode.getResourceHierarchy())); if (resourceNode.getResourceHierarchy().getParent() == null) { // Since this getter is also an accessor. stateGetter.addAnnotation(new Annotation("Produces", "MediaType.APPLICATION_JSON")); stateGetter.addAnnotation(new Annotation("GET")); } component.addMethod(stateGetter); } if (component != null) { // Declare the getter methods to obtain the children resources. Set<ResourceHierarchy> children = childGetters.get(resourceNode.getResourceHierarchy()); if (children == null) { children = new HashSet<>(); childGetters.put(resourceNode.getResourceHierarchy(), children); } for (ResourceNode child: resourceNode.getChildren()) { if (generatesComponent(child.getResourceHierarchy())) { // The child generates a component. if (!children.contains(child.getResourceHierarchy())) { children.add(child.getResourceHierarchy()); List<VariableDeclaration> pathParams = new ArrayList<>(); int v = 1; for (Expression pathParam: child.getPrimaryResourcePath().getPathParams()) { if (pathParam instanceof Variable) { Variable var = (Variable) pathParam; pathParams.add(new VariableDeclaration(var.getType(), var.getName())); } else if (pathParam instanceof Term) { Term var = (Term) pathParam; pathParams.add(new VariableDeclaration(var.getType(), "v" + v)); } v++; } String childCompName = getComponentName(child.getResourceHierarchy()); Type childType = new Type(childCompName, childCompName); MethodDeclaration childGetter = null; if (pathParams.size() == 0) { childGetter = new MethodDeclaration("get" + childCompName, childType); } else { childGetter = new MethodDeclaration("get" + childCompName, false, childType, pathParams); } component.addMethod(childGetter); } } } } // // 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 the state field and reference fields in the parent component. boolean bDeclareClientField = false; if (component == null) { // Declare reference fields for push/pull data transfer. boolean noPullTransfer = true; for (Edge resToCh : resourceNode.getOutEdges()) { DataFlowEdge re = (DataFlowEdge) resToCh; DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); for (Edge chToRes: re.getDestination().getOutEdges()) { ResourceHierarchy dstRes = ((ResourceNode) chToRes.getDestination()).getResourceHierarchy(); // Check if the output resource is outside of the channel scope. boolean outsideOutputResource = false; for (ChannelMember cm: ch.getOutputChannelMembers()) { if (((ResourceNode) chToRes.getDestination()).getInSideResources().contains(cm.getResource()) && cm.isOutside()) { outsideOutputResource = true; // Regarded as push transfer. break; } } if (outsideOutputResource) { // Declare a field in the parent component to refer to the destination resource of push transfer. if (!generatesComponent(dstRes)) { dstRes = dstRes.getParent(); } String dstResName = getComponentName(dstRes); // if (resourceNode.getOutSideResource().getCommonPrefix(dstRes) == null && differentTreesAsDifferentServices) { // Inter-service if (!bDeclareClientField) { // Declare a client field to connect to the destination resource of push transfer. FieldDeclaration clientField = new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()"); fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), clientField)); bDeclareClientField = true; } // } else { // // Inner-service // // Declare a field to directly refer to the destination resource of push transfer. // if (resourceNode.getParent().getResourceHierarchy() != dstRes.getResourceHierarchy()) { // FieldDeclaration refFieldForPush = new FieldDeclaration(new Type(dstResName, dstResName), toVariableName(dstResName)); // fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), refFieldForPush)); // } // } } } } for (Edge chToRes : resourceNode.getInEdges()) { for (Edge resToCh: chToRes.getSource().getInEdges()) { DataFlowEdge re = (DataFlowEdge) resToCh; DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(ch); // Check if the input resource is outside of the channel scope. boolean outsideInputResource = false; for (ChannelMember cm: ch.getInputChannelMembers()) { if (cm.getResource().equals(srcRes) && cm.isOutside()) { outsideInputResource = true; // Regarded as pull transfer. break; } } if (outsideInputResource) { // 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()); // if (resourceNode.getOutSideResource().getCommonPrefix(srcRes) == null && differentTreesAsDifferentServices) { // Inter-service if (!bDeclareClientField) { // Declare a client field to connect to the source resource of pull transfer. FieldDeclaration clientField = new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()"); fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), clientField)); bDeclareClientField = true; } // } else { // // Inner-service // // Declare a field to directly refer to the source resource of pull transfer. // if (resourceNode.getParent().getResourceHierarchy() != srcRes.getResourceHierarchy()) { // FieldDeclaration refFieldForPull = new FieldDeclaration(new Type(srcResName, srcResName), toVariableName(srcResName)); // fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), refFieldForPull)); // } // } noPullTransfer = false; } } } // Declare the state field in the parent component. ResourceHierarchy res = resourceNode.getResourceHierarchy(); if (((StoreAttribute) resourceNode.getAttribute()).isStored() && noPullTransfer && res.getNumParameters() == 0) { String resName = getComponentName(res); FieldDeclaration stateField = new FieldDeclaration(res.getResourceStateType(), toVariableName(resName)); fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), stateField)); Map<String, VariableDeclaration> nameToParam = constructorParams.get(resourceNode.getParent().getResourceHierarchy()); if (nameToParam == null) { nameToParam = new HashMap<>(); constructorParams.put(resourceNode.getParent().getResourceHierarchy(), nameToParam); } String varName = toVariableName(resName); if (nameToParam.get(varName) == null) { nameToParam.put(varName, new VariableDeclaration(res.getResourceStateType(), varName)); } } } // Declare the getter method to obtain the resource state in the parent component. if (component == null) { // No component is created for this resource. String getterName = "get" + getComponentName(resourceNode.getResourceHierarchy()); boolean bExists = false; for (Map.Entry<ResourceHierarchy, MethodDeclaration> entry: getters) { if (entry.getKey() == resourceNode.getParent().getResourceHierarchy() && entry.getValue().getName().equals(getterName)) { bExists = true; break; } } if (!bExists) { List<VariableDeclaration> pathParams = new ArrayList<>(); int v = 1; for (Selector pathParam: resourceNode.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++; } Type resType = getImplStateType(resourceNode.getResourceHierarchy()); MethodDeclaration stateGetter = null; if (pathParams.size() == 0) { stateGetter = new MethodDeclaration(getterName, resType); } else { stateGetter = new MethodDeclaration(getterName, false, resType, pathParams); } getters.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), stateGetter)); } } // Declare the getter accessor in the root resource. if (resourceNode.getResourceHierarchy().getParent() != null) { // For a non-root resource MethodDeclaration getterAccessor = null; List<VariableDeclaration> mainGetterParams = new ArrayList<>(); String resourcePath = getGetterResourcePathAndPathParams(resourceNode.getPrimaryResourcePath(), mainGetterParams); if (resourcePath.indexOf('/') > 0) { resourcePath = resourcePath.substring(resourcePath.indexOf('/')); } else { resourcePath = ""; } if (mainGetterParams.size() > 0) { getterAccessor = new MethodDeclaration("get" + getComponentName(resourceNode.getResourceHierarchy()) + "Value", false, getImplStateType(resourceNode.getResourceHierarchy()), mainGetterParams); } else { getterAccessor = new MethodDeclaration("get" + getComponentName(resourceNode.getResourceHierarchy()) + "Value", getImplStateType(resourceNode.getResourceHierarchy())); } getterAccessor.setBody(new Block()); ResourcePath resPath = resourceNode.getPrimaryResourcePath(); Expression getState = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(resPath, resPath.getRoot()); getterAccessor.getBody().addStatement("return " + getState.toImplementation(new String[] {null}) + ";"); getterAccessor.addAnnotation(new Annotation("Produces", "MediaType.APPLICATION_JSON")); getterAccessor.addAnnotation(new Annotation("GET")); if (resourcePath.length() > 0) { getterAccessor.addAnnotation(new Annotation("Path", "\"" + resourcePath + "\"")); } getterAccessors.put(resourceNode.getResourceHierarchy(), getterAccessor); } // Declare a client field and update methods from other resources. for (Edge resToCh: resourceNode.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().equals(resourceNode.getOutSideResource(ch)) && cm.isOutside()) { outsideInputResource = true; // Regarded as pull transfer. break; } } for (ChannelMember cm: ch.getOutputChannelMembers()) { ResourcePath dstRes = cm.getResource(); // Check if the output resource is outside of the channel scope. boolean outsideOutputResource = cm.isOutside(); if (!bDeclareClientField && ((((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource)) { // For push transfer. if (!generatesComponent(dstRes.getResourceHierarchy())) { dstRes = dstRes.getParent(); } String dstResName = getComponentName(dstRes.getResourceHierarchy()); if (outsideOutputResource || (resourceNode.getOutSideResource(ch).getCommonPrefix(dstRes) == null && differentTreesAsDifferentServices)) { // Inter-service if (!bDeclareClientField) { // Declare a client field to connect to the destination resource of push transfer. FieldDeclaration clientField = new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()"); if (component != null) { // A component is created for this resource. component.addField(clientField); } else { // No component is created for this resource. fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), clientField)); } bDeclareClientField = true; } } else { // Inner-service // Declare a field to directly refer to the destination resource of push transfer. FieldDeclaration dstRefField = new FieldDeclaration(new Type(dstResName, dstResName), toVariableName(dstResName)); if (component != null) { // A component is created for this resource. if (resourceNode.getResourceHierarchy() != dstRes.getResourceHierarchy()) { component.addField(dstRefField); } } else { // No component is created for this resource. if (resourceNode.getParent().getResourceHierarchy() != dstRes.getResourceHierarchy()) { fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), dstRefField)); } } } } } } for (Edge chToRes : resourceNode.getInEdges()) { for (Edge resToCh: chToRes.getSource().getInEdges()) { DataFlowEdge re = (DataFlowEdge) resToCh; DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(ch); // Check if the input resource is outside of the channel scope. boolean outsideInputResource = false; for (ChannelMember cm: ch.getInputChannelMembers()) { if (cm.getResource().equals(srcRes) && cm.isOutside()) { outsideInputResource = true; // Regarded as pull transfer. break; } } // Check if the output resource is outside of the channel scope. boolean outsideOutputResource = false; ChannelMember out = null; for (ChannelMember cm: ch.getOutputChannelMembers()) { if (resourceNode.getInSideResources().contains(cm.getResource())) { out = cm; if (cm.isOutside()) { outsideOutputResource = true; // Regarded as push transfer. break; } } } String srcResName = getComponentName(srcRes.getResourceHierarchy()); Type srcType = srcRes.getResourceStateType(); if (!generatesComponent(srcRes.getResourceHierarchy())) { srcRes = srcRes.getParent(); } if ((((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { // For pull transfer. if (outsideInputResource || (resourceNode.getInSideResource(ch).getCommonPrefix(srcRes) == null && differentTreesAsDifferentServices)) { // Inter-service if (!bDeclareClientField) { // Declare a client field to connect to the source resource of pull transfer. FieldDeclaration clientField = new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()"); if (component != null) { // A component is created for this resource. component.addField(clientField); } else { // No component is created for this resource. fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), clientField)); } bDeclareClientField = true; } } else { // Inner-service // Declare a field to directly refer to the source resource of pull transfer. FieldDeclaration srcRefField = new FieldDeclaration(new Type(srcResName, srcResName), toVariableName(srcResName)); if (component != null) { // A component is created for this resource. if (resourceNode.getResourceHierarchy() != srcRes.getResourceHierarchy()) { component.addField(srcRefField); } } else { // No component is created for this resource. if (resourceNode.getParent().getResourceHierarchy() != srcRes.getResourceHierarchy()) { fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), srcRefField)); } } } } else { // For push transfer. boolean hasRestAPI = false; boolean isRestAPI = false; if (outsideOutputResource || (resourceNode.getInSideResource(ch).getCommonPrefix(srcRes) == null && differentTreesAsDifferentServices)) { // Inter-service hasRestAPI = true; if (resourceNode.getParent() == null) { // A root resource isRestAPI = true; } } // Declare an update method in the type of the destination resource. ArrayList<VariableDeclaration> params = new ArrayList<>(); getUpdateResourcePathAndPathParams(out.getResource(), params, isRestAPI); // Path parameters to identify the self resource. for (Selector selector: ch.getAllSelectors()) { if (selector.getExpression() instanceof Variable) { Variable selVar = (Variable) selector.getExpression(); VariableDeclaration chParam = new VariableDeclaration(selVar.getType(), selVar.getName()); if (isRestAPI) chParam.addAnnotation(new Annotation("FormParam", "\"" + selVar.getName() + "\"")); params.add(chParam); // A channel parameter to specify the context of the collaboration. } } String srcName = toVariableName(srcResName); VariableDeclaration param = new VariableDeclaration(srcType, srcName); if (isRestAPI) param.addAnnotation(new Annotation("FormParam", "\"" + srcName + "\"")); params.add(param); // The state of the source resource to carry the data-flow. for (ResourcePath refRes: ch.getReferenceResources()) { if (!refRes.equals(resourceNode.getInSideResource(ch))) { param = new VariableDeclaration(refRes.getResourceStateType(), refRes.getLeafResourceName()); if (isRestAPI) param.addAnnotation(new Annotation("FormParam", "\"" + refRes.getLeafResourceName() + "\"")); params.add(param); } } MethodDeclaration update = null; if (component != null) { // A component is created for this resource. update = new MethodDeclaration("updateFrom" + srcResName, false, typeVoid, params); } else { // No component is created for this resource. String resourceName = getComponentName(resourceNode.getResourceHierarchy()); update = new MethodDeclaration("update" + resourceName + "From" + srcResName, false, typeVoid, params); } // Determine whether the update method is put or post. boolean isPut = false; for (ChannelMember cm: ch.getOutputChannelMembers()) { if (resourceNode.getInSideResources().contains(cm.getResource())) { if (cm.getStateTransition().isRightUnary()) { isPut = true; } else { isPut = false; } } } if (isRestAPI) { if (isPut) { update.addAnnotation(new Annotation("PUT")); } else { update.addAnnotation(new Annotation("POST")); } } // Calculate in-degree of the destination resource. Set<ResourceHierarchy> inResources = new HashSet<>(); for (ResourceNode rn: graph.getResourceNodes(out.getResource().getResourceHierarchy())) { // ResourceNodes that have the same ResourceHierarchy. for (Edge chToRes2: rn.getInEdges()) { for (Edge resToCh2: chToRes2.getSource().getInEdges()) { inResources.add(((ResourceNode) resToCh2.getSource()).getResourceHierarchy()); } } } int inDegree = inResources.size(); if (inDegree > 1 || (inDegree == 1 && ch.getInputChannelMembers().iterator().next().getStateTransition().isRightPartial())) { // Declare a field to cache the state of the source resource in the type of the destination resource. ResourceHierarchy cacheRes = ((ResourceNode) re.getSource()).getResourceHierarchy(); FieldDeclaration cacheField = new FieldDeclaration(cacheRes.getResourceStateType(), srcName, getInitializer(cacheRes)); if (component != null) { // A component is created for this resource. component.addField(cacheField); } else { // No component is created for this resource. fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), cacheField)); } if (inDegree > 1) { // For each source resource, a child resource is defined in the destination resource so that its state can be updated separately. if (isRestAPI) update.addAnnotation(new Annotation("Path", "\"/" + srcName + "\"")); } } if (component != null) { // A component is created for this resource. component.addMethod(update); } else { // No component is created for this resource. String updateMethodName = update.getName(); Map<String, MethodDeclaration> nameToMethod = updates.get(resourceNode.getParent().getResourceHierarchy()); if (nameToMethod == null) { nameToMethod = new HashMap<>(); updates.put(resourceNode.getParent().getResourceHierarchy(), nameToMethod); } if (nameToMethod.get(updateMethodName) == null) { nameToMethod.put(updateMethodName, update); } } if (hasRestAPI && !isRestAPI) { // Declare an update accessor method in the type of root resource. String updateMethodName = update.getName(); params = new ArrayList<>(); String resourcePath = getUpdateResourcePathAndPathParams(out.getResource(), params, true); // Path parameters to identify the self resource. for (Selector selector: ch.getAllSelectors()) { if (selector.getExpression() instanceof Variable) { Variable selVar = (Variable) selector.getExpression(); VariableDeclaration chParam = new VariableDeclaration(selVar.getType(), selVar.getName()); chParam.addAnnotation(new Annotation("FormParam", "\"" + selVar.getName() + "\"")); params.add(chParam); // A channel parameter to specify the context of the collaboration. } } param = new VariableDeclaration(srcType, srcName); param.addAnnotation(new Annotation("FormParam", "\"" + srcName + "\"")); params.add(param); // The state of the source resource to carry the data-flow. for (ResourcePath refRes: ch.getReferenceResources()) { if (!refRes.equals(resourceNode.getInSideResource(ch))) { param = new VariableDeclaration(refRes.getResourceStateType(), refRes.getLeafResourceName()); param.addAnnotation(new Annotation("FormParam", "\"" + refRes.getLeafResourceName() + "\"")); params.add(param); } } MethodDeclaration updateAccessor = new MethodDeclaration(updateMethodName, false, typeVoid, params); if (isPut) { updateAccessor.addAnnotation(new Annotation("PUT")); } else { updateAccessor.addAnnotation(new Annotation("POST")); } if (inDegree > 1) { // For each source resource, a child resource is defined in the destination resource so that its state can be updated separately. resourcePath += "/" + toVariableName(srcResName); } updateAccessor.addAnnotation(new Annotation("Path", "\"" + resourcePath + "\"")); Map<String, MethodDeclaration> nameToMethod = updates.get(resourceNode.getResourceHierarchy().getRoot()); if (nameToMethod == null) { nameToMethod = new HashMap<>(); updates.put(resourceNode.getResourceHierarchy().getRoot(), nameToMethod); } if (nameToMethod.get(updateMethodName) == null) { nameToMethod.put(updateMethodName, updateAccessor); } } } } } // Declare the input method in each resource and the root resource. for (Channel ch : model.getInputChannels()) { for (ChannelMember cm : ((DataTransferChannel) ch).getOutputChannelMembers()) { if (!cm.isOutside()) { if (priorMemberForInputChannel.get(ch) == null) { priorMemberForInputChannel.put(ch, cm); // The receiver of the input event when multiple output resources are defined for the channel. } } } for (ChannelMember cm : ((DataTransferChannel) ch).getOutputChannelMembers()) { if (resourceNode.getInSideResources().contains(cm.getResource())) { Expression message = cm.getStateTransition().getMessageExpression(); if (message instanceof Term) { // In each resource. ArrayList<VariableDeclaration> resInputParams = new ArrayList<>(); ArrayList<VariableDeclaration> rootInputParams = new ArrayList<>(); String resourcePath = getInputMethodResourcePathAndPathParams(cm.getResource(), resInputParams, rootInputParams); // Path parameters for the input REST API. if (resourcePath.indexOf('/') > 0) { resourcePath = resourcePath.substring(resourcePath.indexOf('/')); } else { resourcePath = ""; } // The path parameters are not to be passed to the input method of each resource (resInputParams) // because they are always equal to either channel selectors or message parameters. // Channel parameters to specify the context of the collaboration. for (Selector selector: ch.getAllSelectors()) { if (selector.getExpression() instanceof Variable) { Variable selVar = (Variable) selector.getExpression(); VariableDeclaration chParam = new VariableDeclaration(selVar.getType(), selVar.getName()); resInputParams.add(chParam); } } // Message parameters to carry the data-flows. for (Map.Entry<Position, Variable> varEnt: message.getVariables().entrySet()) { Variable var = varEnt.getValue(); String refVarName = null; for (ChannelMember refCm: ((DataTransferChannel) ch).getReferenceChannelMembers()) { Expression varExp = refCm.getStateTransition().getMessageExpression().getSubTerm(varEnt.getKey()); if (varExp != null && varExp instanceof Variable) { if (refCm.getStateTransition().getCurStateExpression().contains(varExp)) { refVarName = refCm.getResource().getLeafResourceName(); break; } } } if (refVarName != null) { // var has come from a reference resource. VariableDeclaration param = new VariableDeclaration(var.getType(), refVarName); resInputParams.add(param); } else { // var has not come from reference resource. String paramName = var.getName(); VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); resInputParams.add(param); if (!resourcePath.contains("{" + paramName+ "}")) { param = new VariableDeclaration(var.getType(), paramName); param.addAnnotation(new Annotation("FormParam", "\"" + paramName + "\"")); rootInputParams.add(param); } } } if (resourceNode.getResourceHierarchy().getParent() != null && resourceNode.getResourceHierarchy().getParent().getParent() != null) { String inputMethodName = ((Term) message).getSymbol().getImplName(); if (((DataTransferChannel) ch).getOutputChannelMembers().size() > 1) { inputMethodName += "For" + getComponentName(cm.getResource().getResourceHierarchy()); } MethodDeclaration input = new MethodDeclaration(inputMethodName, false, typeVoid, resInputParams); if (component != null) { // A component is created for this resource. component.addMethod(input); } else { // No component is created for this resource. Map<String, MethodDeclaration> nameToMethod = inputs.get(resourceNode.getParent().getResourceHierarchy()); if (nameToMethod == null) { nameToMethod = new HashMap<>(); inputs.put(resourceNode.getParent().getResourceHierarchy(), nameToMethod); } if (nameToMethod.get(inputMethodName) == null) { nameToMethod.put(inputMethodName, input); } } } // For the root resource. if (priorMemberForInputChannel.get(ch) ==null || cm == priorMemberForInputChannel.get(ch)) { // If cm is the receiver of the input event. priorMemberForInputChannel.put(ch, cm); String messageSymbol = ((Term) message).getSymbol().getImplName(); MethodDeclaration inputAccessor = new MethodDeclaration(messageSymbol, false, typeVoid, rootInputParams); if (cm.getStateTransition().isRightUnary()) { inputAccessor.addAnnotation(new Annotation("PUT")); } else { inputAccessor.addAnnotation(new Annotation("POST")); } if (resourcePath.length() > 0) { inputAccessor.addAnnotation(new Annotation("Path", "\"" + resourcePath + "\"")); } inputAccessors.put(resourceNode.getResourceHierarchy(), inputAccessor); } } else if (message instanceof Variable) { // In each resource. ArrayList<VariableDeclaration> resInputParams = new ArrayList<>(); int v = 1; if (cm.getResource().getLastParam() != null) { Expression pathParam = cm.getResource().getLastParam(); if (pathParam instanceof Variable) { Variable var = (Variable) pathParam; String paramName = var.getName(); VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); resInputParams.add(param); } else if (pathParam instanceof Term) { Term var = (Term) pathParam; String paramName = "v" + v; VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); resInputParams.add(param); } v++; } if (cm.getResource().getResourceHierarchy().getParent() != null && cm.getResource().getResourceHierarchy().getParent().getParent() != null) { String inputMethodName = ((Variable) message).getName(); if (((DataTransferChannel) ch).getOutputChannelMembers().size() > 1) { inputMethodName += "For" + getComponentName(cm.getResource().getResourceHierarchy()); } MethodDeclaration input = new MethodDeclaration(inputMethodName, false, typeVoid, null); if (component != null) { // A component is created for this resource. component.addMethod(input); } else { // No component is created for this resource. Map<String, MethodDeclaration> nameToMethod = inputs.get(cm.getResource().getParent().getResourceHierarchy()); if (nameToMethod == null) { nameToMethod = new HashMap<>(); inputs.put(cm.getResource().getParent().getResourceHierarchy(), nameToMethod); } if (nameToMethod.get(inputMethodName) == null) { nameToMethod.put(inputMethodName, input); } } } // For the root resource. if (priorMemberForInputChannel.get(ch) ==null || cm == priorMemberForInputChannel.get(ch)) { // If cm is the receiver of the input event. priorMemberForInputChannel.put(ch, cm); ArrayList<VariableDeclaration> rootInputParams = new ArrayList<>(); String resourcePath = getGetterResourcePathAndPathParams(cm.getResource(), rootInputParams); if (resourcePath.indexOf('/') > 0) { resourcePath = resourcePath.substring(resourcePath.indexOf('/')); } else { resourcePath = ""; } String messageSymbol = ((Variable) message).getName(); MethodDeclaration inputAccessor = new MethodDeclaration(messageSymbol, false, typeVoid, rootInputParams); if (cm.getStateTransition().isRightUnary()) { inputAccessor.addAnnotation(new Annotation("PUT")); } else { inputAccessor.addAnnotation(new Annotation("POST")); } if (resourcePath.length() > 0) { inputAccessor.addAnnotation(new Annotation("Path", "\"" + resourcePath + "\"")); } inputAccessors.put(resourceNode.getResourceHierarchy(), inputAccessor); } } } } } } // Add leaf getter methods to the parent components. for (Map.Entry<ResourceHierarchy, MethodDeclaration> entry: getters) { resourceComponents.get(entry.getKey()).addMethod(entry.getValue()); } // Add leaf update methods to the parent components. for (Map.Entry<ResourceHierarchy, Map<String, MethodDeclaration>> entry: updates.entrySet()) { for (MethodDeclaration update: entry.getValue().values()) { resourceComponents.get(entry.getKey()).addMethod(update); } } // Add leaf input methods to the parent components. for (Map.Entry<ResourceHierarchy, Map<String, MethodDeclaration>> entry: inputs.entrySet()) { for (MethodDeclaration input: entry.getValue().values()) { resourceComponents.get(entry.getKey()).addMethod(input); } } // Add leaf reference fields to the parent components. for (Map.Entry<ResourceHierarchy, FieldDeclaration> entry: fields) { ResourceHierarchy resource = entry.getKey(); FieldDeclaration field = entry.getValue(); TypeDeclaration component = resourceComponents.get(resource); boolean existsField = false; for (FieldDeclaration fld: component.getFields()) { if (fld.getName().equals(field.getName())) { existsField = true; break; } } if (!existsField) { component.addField(field); if (field.getType().equals(typeClient)) { for (CompilationUnit cu: codes) { if (cu.types().contains(component)) { cu.addImport(new ImportDeclaration("javax.ws.rs.client.*")); break; } } } } } // Add constructor parameters to the ancestor components. for (ResourceNode root: graph.getRootResourceNodes()) { addConstructorParameters(root.getResourceHierarchy(), resourceComponents, resourceConstructors, constructorParams); } // Add accessors. for (ResourceHierarchy rootRes: model.getResourceHierarchies()) { if (rootRes.getParent() == null) { // root resource TypeDeclaration rootComponent = resourceComponents.get(rootRes); // Add getter accessors. for (ResourceHierarchy res: getterAccessors.keySet()) { if (rootRes.isAncestorOf(res)) { rootComponent.addMethod(getterAccessors.get(res)); } } // Add input accessors. for (ResourceHierarchy res: inputAccessors.keySet()) { if (rootRes.isAncestorOf(res)) { rootComponent.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 List<VariableDeclaration> addConstructorParameters(ResourceHierarchy resource, Map<ResourceHierarchy, TypeDeclaration> resourceComponents, Map<ResourceHierarchy, MethodDeclaration> resourceConstructors, Map<ResourceHierarchy, Map<String, VariableDeclaration>> constructorParams) { List<VariableDeclaration> params = new ArrayList<>(); for (ResourceHierarchy child: resource.getChildren()) { params.addAll(addConstructorParameters(child, resourceComponents, resourceConstructors, constructorParams)); } if (constructorParams.get(resource) != null) { for (VariableDeclaration param: constructorParams.get(resource).values()) { params.add(param); } } 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) { constructor.addParameter(param); constructor.getBody().addStatement("this." + toVariableName(param.getName()) + " = " + toVariableName(param.getName()) + ";"); } } } if (resource.getNumParameters() > 0) params.clear(); return params; } private static String getGetterResourcePathAndPathParams(ResourcePath resPath, List<VariableDeclaration> pathParams) { 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 = new VariableDeclaration(var.getType(), paramName); param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\"")); pathParams.add(param); } else if (pathParam instanceof Term) { Term var = (Term) pathParam; String paramName = "v" + v; params.add("{" + paramName + "}"); VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\"")); pathParams.add(param); } v++; } return resPath.getResourceHierarchy().toResourcePath(params); } private static String getUpdateResourcePathAndPathParams(ResourcePath resPath, ArrayList<VariableDeclaration> rootParams, boolean isRestAPI) { 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 = new VariableDeclaration(var.getType(), paramName); if (isRestAPI) param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\"")); rootParams.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 = new VariableDeclaration(var.getType(), paramName); if (isRestAPI) param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\"")); rootParams.add(param); } v++; } return resPath.getResourceHierarchy().toResourcePath(params); } private static String getInputMethodResourcePathAndPathParams(ResourcePath resPath, ArrayList<VariableDeclaration> resInputParams, ArrayList<VariableDeclaration> rootInputParams) { 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 = new VariableDeclaration(var.getType(), paramName); param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\"")); rootInputParams.add(param); } else if (pathParam instanceof Term) { Term var = (Term) pathParam; String paramName = "v" + v; params.add("{" + paramName + "}"); VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); param.addAnnotation(new Annotation("PathParam", "\"" + 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 = new VariableDeclaration(var.getType(), paramName); param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\"")); rootInputParams.add(param); } else if (pathParam instanceof Term) { Term var = (Term) pathParam; String paramName = "v" + v; params.add("{" + paramName + "}"); VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\"")); rootInputParams.add(param); } v++; } } return resPath.getResourceHierarchy().toResourcePath(params); } private static String getInitializer(ResourceHierarchy res) { Type stateType = res.getResourceStateType(); String initializer = null; if (res.getInitialValue() != null) { initializer = res.getInitialValue().toImplementation(new String[] {""}); } else { 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 " + 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(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); } // use the cached value as the current state return new Field(targetRes.getLeafResourceName(), targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } @Override public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { ResourcePath targetRes = target.getResource(); return new Parameter(targetRes.getLeafResourceName(), 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); } return null; } }; static public IResourceStateAccessor pullAccessor = 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(targetRes.getLeafResourceName(), 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(targetRes.getLeafResourceName(), 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("value", targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } // for reference channel member return new Parameter(targetRes.getLeafResourceName(), targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } else { // access from an ancestor or outside of the hierarchy 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 v = 1; while (!pathStack.empty()) { curPath = pathStack.pop(); String typeName = getComponentName(curPath.getResourceHierarchy()); if (getter == null && fromRes == 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; } } }; static public IResourceStateAccessor refAccessor = 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.getLeafResourceName(), targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } @Override public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { ResourcePath targetRes = target.getResource(); return new Parameter(targetRes.getLeafResourceName(), 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); } return null; } }; }