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.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import algorithms.TypeInference; import java.util.Set; import java.util.Stack; import code.ast.Annotation; import code.ast.Block; import code.ast.CodeUtil; 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.InvalidMessage; import models.algebra.Parameter; import models.algebra.ParameterizedIdentifierIsFutureWork; import models.algebra.Position; import models.algebra.Symbol; import models.algebra.Term; import models.algebra.Type; import models.algebra.UnificationFailed; import models.algebra.ValueUndefined; import models.algebra.Variable; import models.dataConstraintModel.Channel; import models.dataConstraintModel.ChannelMember; import models.dataConstraintModel.DataConstraintModel; import models.dataConstraintModel.JsonAccessor; import models.dataConstraintModel.JsonTerm; import models.dataConstraintModel.ResourceHierarchy; import models.dataConstraintModel.ResourcePath; import models.dataConstraintModel.Selector; 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.ResolvingMultipleDefinitionIsFutureWork; import models.dataFlowModel.ResourceNode; import models.dataFlowModel.ChannelNode; import models.dataFlowModel.StoreAttribute; import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor; public class CodeGeneratorFromDataFlowGraph extends CodeGenerator { public void generateCodeFromFlowGraph(DataTransferModel model, IFlowGraph flowGraph, Collection<ResourceNode> components, ArrayList<CompilationUnit> codes, Map<ResourceHierarchy, Set<ResourceHierarchy>> dependedRootComponentGraph, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { Map<ResourceHierarchy, TypeDeclaration> resourceComponents = new HashMap<>(); Map<ResourceHierarchy, MethodDeclaration> resourceConstructors = new HashMap<>(); Map<ResourceHierarchy, Map<String, VariableDeclaration>> constructorParams = new HashMap<>(); List<Map.Entry<ResourceHierarchy, String>> constructorStatements = new ArrayList<>(); Map<MethodDeclaration, Map.Entry<Expression, Map.Entry<ResourceHierarchy, ResourceHierarchy>>> updateStatements = new HashMap<>(); Map<ResourceHierarchy, Set<ResourceHierarchy>> descendantGetters = new HashMap<>(); TypeDeclaration mainComponent = null; MethodDeclaration mainConstructor = null; if (platformSpec.hasMain()) { // Add the main component. if (getMainTypeName() == null) { setMainTypeName(langSpec.getMainComponentName()); } mainComponent = langSpec.newTypeDeclaration(getMainTypeName()); mainConstructor = mainComponent.createConstructor(); CompilationUnit mainCU = langSpec.newCompilationUnit(mainComponent); codes.add(mainCU); } // For each components (1st pass). for (Node componentNode: components) { ResourceNode resourceNode = (ResourceNode) componentNode; TypeDeclaration component = null; if (generatesComponent(resourceNode.getResourceHierarchy())) { // A component will be generated for this resource. String resourceName = getComponentName(resourceNode.getResourceHierarchy(), langSpec); Type resStateType = getImplStateType(resourceNode.getResourceHierarchy(), langSpec); component = resourceComponents.get(resourceNode.getResourceHierarchy()); List<ResourceHierarchy> depends = new ArrayList<>(); if (component == null) { // Add compilation unit for this component. component = langSpec.newTypeDeclaration(resourceName); if (!platformSpec.isMonolithic() && resourceNode.getResourceHierarchy().getParent() == null) { // For each root node, add component annotations. ((RestApiSpecific) platformSpec).addComponentAnnotations(component, resourceNode.getResourceName()); } resourceComponents.put(resourceNode.getResourceHierarchy(), component); CompilationUnit cu = langSpec.newCompilationUnit(component); if (!platformSpec.isMonolithic() && resourceNode.getResourceHierarchy().getParent() == null) { // For each root node, add platform specific imports. ((RestApiSpecific) platformSpec).addPlatformSpecificImports(cu); } codes.add(cu); if (platformSpec.isMonolithic()) { // For monolithic applications (components are tightly coupled and must be built together). // Declare the constructor. MethodDeclaration constructor = declareConstructor(resourceNode, component, dependedRootComponentGraph, depends, langSpec); if (platformSpec.hasMain() && resourceNode.getResourceHierarchy().getParent() == null) { // For each root resource // Update the main component for this component. updateMainComponent(mainComponent, mainConstructor, componentNode, constructor, depends, langSpec); } // Declare the fields to refer to reference resources. declareFieldsToReferenceResources(model, resourceNode, component, constructor, depends, langSpec); if (constructor.getParameters() == null || constructor.getParameters().size() == 0) { component.removeMethod(constructor); } else { resourceConstructors.put(resourceNode.getResourceHierarchy(), constructor); } } // Declare the field to store the state in this resource. if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { declareStateField(resourceNode, resourceName, component, resStateType, constructorParams, langSpec); } // Declare the getter method in this resource to obtain the state. MethodDeclaration stateGetter = declareStateGetterMethod(resourceNode, component, resStateType, platformSpec, langSpec); // Declare the accessor method in the main component to call the getter method. if (platformSpec.hasMain()) { declareAccessorInMainComponent(mainComponent, resourceNode, stateGetter, platformSpec, langSpec); } } if (component != null) { // (#1) Declare the getter methods in this resource to obtain the descendant resources. (complementary to #2) declareDescendantGetterMethods(resourceNode, component, descendantGetters, langSpec); } } } // For each components (2nd pass). Map<Channel, ChannelMember> priorMemberForInputChannel = new HashMap<>(); Set<ResourceHierarchy> generatedResources = new HashSet<>(); for (Node componentNode: components) { // Declare this resource. ResourceNode resourceNode = (ResourceNode) componentNode; Type resStateType = getImplStateType(resourceNode.getResourceHierarchy(), langSpec); TypeDeclaration component = null; TypeDeclaration parentComponent = null; TypeDeclaration rootComponent = null; if (generatesComponent(resourceNode.getResourceHierarchy())) { component = resourceComponents.get(resourceNode.getResourceHierarchy()); } if (resourceNode.getResourceHierarchy().getParent() != null) { parentComponent = resourceComponents.get(resourceNode.getResourceHierarchy().getParent()); } rootComponent = resourceComponents.get(resourceNode.getResourceHierarchy().getRoot()); // Declare cache fields and update methods in this resource, and an update accessor method in the type of root resource. Map.Entry<List<String>, Map<MethodDeclaration, Map.Entry<Expression, Map.Entry<ResourceHierarchy, ResourceHierarchy>>>> initStatementsAndUpdateUpdates = declareCacheFieldsAndUpdateMethods(resourceNode, component, parentComponent, rootComponent, platformSpec, langSpec); if (component == null) { // Constructor statements were not added to any component because no component had been generated. for (String statement: initStatementsAndUpdateUpdates.getKey()) { constructorStatements.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy().getParent(), statement)); } } for (Map.Entry<MethodDeclaration, Map.Entry<Expression, Map.Entry<ResourceHierarchy, ResourceHierarchy>>> entry: initStatementsAndUpdateUpdates.getValue().entrySet()) { updateStatements.put(entry.getKey(), entry.getValue()); } // Declare the fields to refer to other resources for push/pull transfer in the parent/this component, and the state field in the parent component. declareFieldsToReferToOtherResourcesAndStateFieldInParentComponent(resourceNode, component, parentComponent, constructorParams, platformSpec, langSpec); // (#2) Declare the getter method to obtain the resource state in an ancestor resource. (complementary to #1) if (component == null) { MethodDeclaration stateGetter = declareStateGetterMethodInAncestor(resourceNode, resourceComponents, resStateType, platformSpec, langSpec); if (stateGetter != null && platformSpec.hasMain()) { // Declare the accessor method in the main component to call the getter method. declareAccessorInMainComponent(mainComponent, resourceNode, stateGetter, platformSpec, langSpec); } } if (!platformSpec.isMonolithic() && !generatedResources.contains(resourceNode.getResourceHierarchy())) { // Declare the getter accessor in the root resource. declareGetterAccessorInTheRootResource(resourceNode, rootComponent, platformSpec, langSpec); } // Declare input methods in this component and the main component. if (!generatedResources.contains(resourceNode.getResourceHierarchy())) { Map.Entry<List<String>, Map<MethodDeclaration, Map.Entry<Expression, Map.Entry<ResourceHierarchy, ResourceHierarchy>>>> initStatementsAndInputUpdates = declareInputMethodsInThisAndMainComponents(resourceNode, component, parentComponent, mainComponent, rootComponent, model, priorMemberForInputChannel, platformSpec, langSpec); if (component == null) { // Constructor statements were not added to any component because no component had been generated. for (String statement: initStatementsAndInputUpdates.getKey()) { constructorStatements.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy().getParent(), statement)); } } for (Map.Entry<MethodDeclaration, Map.Entry<Expression, Map.Entry<ResourceHierarchy, ResourceHierarchy>>> entry: initStatementsAndInputUpdates.getValue().entrySet()) { updateStatements.put(entry.getKey(), entry.getValue()); } } generatedResources.add(resourceNode.getResourceHierarchy()); } // Add constructor parameters to the ancestor components. for (ResourceNode root: flowGraph.getRootResourceNodes()) { addConstructorParameters(root.getResourceHierarchy(), resourceComponents, resourceConstructors, constructorParams, langSpec); } // Add constructor statements. for (Map.Entry<ResourceHierarchy, String> entry: constructorStatements) { resourceConstructors.get(entry.getKey()).addStatement(entry.getValue()); } // Add update statements. for (MethodDeclaration method: updateStatements.keySet()) { Expression updateExp = updateStatements.get(method).getKey(); ResourceHierarchy resource = updateStatements.get(method).getValue().getKey(); ResourceHierarchy descendantRes = updateStatements.get(method).getValue().getValue(); TypeDeclaration descendantComponent = resourceComponents.get(descendantRes); addUpdateStatementWithConstructorInvocationToMethod(method, updateExp, resource, descendantRes, descendantComponent, langSpec); } } private static List<VariableDeclaration> addConstructorParameters(ResourceHierarchy resource, Map<ResourceHierarchy, TypeDeclaration> resourceComponents, Map<ResourceHierarchy, MethodDeclaration> resourceConstructors, Map<ResourceHierarchy, Map<String, VariableDeclaration>> constructorParams, ILanguageSpecific langSpec) { List<VariableDeclaration> params = new ArrayList<>(); for (ResourceHierarchy child: resource.getChildren()) { params.addAll(addConstructorParameters(child, resourceComponents, resourceConstructors, constructorParams, langSpec)); } 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, langSpec); constructor = langSpec.newMethodDeclaration(resourceName, true, null, null); Block body = new Block(); constructor.setBody(body); resourceComponents.get(resource).addMethod(constructor); resourceConstructors.put(resource, constructor); } } if (constructor != null) { for (VariableDeclaration param: params) { boolean existsParam = false; if (constructor.getParameters() != null) { for (VariableDeclaration constParam: constructor.getParameters()) { if (constParam.getName().equals(param.getName())) { existsParam = true; break; } } } if (!existsParam) { constructor.addParameter(param); constructor.getBody().addStatement(langSpec.getFieldAccessor(langSpec.toVariableName(param.getName())) + langSpec.getAssignment() + langSpec.toVariableName(param.getName()) + langSpec.getStatementDelimiter()); } } } } if (resource.getNumParameters() > 0) params.clear(); return params; } private void addUpdateStatementWithConstructorInvocationToMethod(MethodDeclaration method, Expression exp, ResourceHierarchy resource, ResourceHierarchy descendantRes, TypeDeclaration descendantComponent, ILanguageSpecific langSpec) { // Replace each json term in exp with the corresponding constructor invocation. Type replacedJsonType = descendantRes.getResourceStateType(); String replacingClassName = getComponentName(descendantRes, langSpec); Type descendantType = new Type(replacingClassName, replacingClassName); Map<Position, Term> subTerms = ((Term) exp).getSubTerms(Term.class); Iterator<Entry<Position, Term>> termEntItr = subTerms.entrySet().iterator(); while (termEntItr.hasNext()) { Entry<Position, Term> termEnt = termEntItr.next(); Term jsonTerm = termEnt.getValue(); if (jsonTerm.getType() != null) { if (jsonTerm.getType().equals(replacedJsonType)) { if (jsonTerm instanceof JsonTerm || jsonTerm.getSymbol().equals(DataConstraintModel.addMember)) { MethodDeclaration childConstructor = getConstructor(descendantComponent); List<String> params = new ArrayList<>(); if (childConstructor != null) { for (VariableDeclaration var: childConstructor.getParameters()) { // Extract the argument of each constructor parameter from jsonTerm. JsonAccessor jsonMember = new JsonAccessor(DataConstraintModel.dot); jsonMember.addChild(jsonTerm); jsonMember.addChild(new Constant(var.getName(), DataConstraintModel.typeString)); Expression param = jsonMember.reduce(); // Reduce {"name": "foo", age: 25}.name => "foo" if (param != null) { if (param instanceof Term) { if (((Term) param).getType() == null) { ((Term) param).setType(var.getType()); } } else if (param instanceof Variable) { if (((Variable) param).getType() == null) { ((Variable) param).setType(var.getType()); } } params.add(param.toImplementation(null)); } else { params.add(var.getName()); } } } ((Term) exp).replaceSubTerm(termEnt.getKey(), new Constant(langSpec.getConstructorInvocation(replacingClassName, params))); subTerms = ((Term) exp).getSubTerms(Term.class); termEntItr = subTerms.entrySet().iterator(); } else { jsonTerm.setType(descendantType); } } else { Type oldType = jsonTerm.getType(); Type newType = new Type(oldType.getTypeName(), oldType.getImplementationTypeName().replace(replacedJsonType.getInterfaceTypeName(), replacingClassName), oldType.getInterfaceTypeName().replace(replacedJsonType.getInterfaceTypeName(), replacingClassName)); for (Type parent: oldType.getParentTypes()) { newType.addParentType(parent); } jsonTerm.setType(newType); } } } // Replace the type of the state field. Type fieldType = getImplStateType(resource, langSpec); if (exp instanceof Term) { ((Term) exp).setType(fieldType); for (Map.Entry<Position, Variable> varEnt: ((Term) exp).getVariables().entrySet()) { if (varEnt.getValue().getName().equals(fieldOfResourceState)) { varEnt.getValue().setType(fieldType); } } } else if (exp instanceof Variable) { ((Variable) exp).setType(fieldType); } String[] sideEffects = new String[] {""}; String newState = exp.toImplementation(sideEffects); String updateStatement; if (exp instanceof Term && ((Term) exp).getSymbol().isImplWithSideEffect()) { updateStatement = sideEffects[0]; if (updateStatement.endsWith("\n")) { updateStatement = updateStatement.substring(0, updateStatement.length() - 1); } } else { updateStatement = sideEffects[0] + langSpec.getFieldAccessor(fieldOfResourceState) + langSpec.getAssignment() + newState + langSpec.getStatementDelimiter(); } method.addFirstStatement(updateStatement); } private MethodDeclaration declareConstructor(ResourceNode resourceNode, TypeDeclaration component, Map<ResourceHierarchy, Set<ResourceHierarchy>> dependedRootComponentGraph, List<ResourceHierarchy> depends, ILanguageSpecific langSpec) { // Declare a constructor in each component. String resourceName = getComponentName(resourceNode.getResourceHierarchy(), langSpec); MethodDeclaration constructor = langSpec.newMethodDeclaration(resourceName, true, null, null); Block block = new Block(); constructor.setBody(block); component.addMethod(constructor); for (Edge resToCh: resourceNode.getOutEdges()) { DataTransferChannel ch = ((ChannelNode) resToCh.getDestination()).getChannel(); // Check if the input resource is outside of the channel scope. boolean outsideInputResource = false; for (ChannelMember cm: ch.getInputChannelMembers()) { if (resourceNode.getOutSideResources().contains(cm.getResource()) && cm.isOutside()) { outsideInputResource = true; // Regarded as pull transfer. break; } } for (Edge chToRes: resToCh.getDestination().getOutEdges()) { if (chToRes.getDestination() instanceof ResourceNode) { 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 ((((PushPullAttribute) ((DataFlowEdge) resToCh).getAttribute()).getSelectedOption() == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) { // for PUSH transfer // ResourceHierarchy dstRes = addReference(component, constructor, ((ResourceNode) chToRes.getDestination()).getOutSideResource().getResourceHierarchy(), langSpec); // if (outsideOutputResource) { // if (dstRes != null && dstRes.getParent() != null) { // // Reference to root resource. // addReference(component, constructor, dstRes.getRoot(), langSpec); // } // } if (!generatesComponent(dstRes)) { dstRes = dstRes.getParent(); } if (!depends.contains(dstRes)) depends.add(dstRes); } } } } for (Edge chToRes: resourceNode.getInEdges()) { for (Edge resToCh: chToRes.getSource().getInEdges()) { ResourceHierarchy srcRes = ((ResourceNode) resToCh.getSource()).getResourceHierarchy(); DataTransferChannel ch = ((ChannelNode) resToCh.getDestination()).getChannel(); // Check if the input resource is outside of the channel scope. boolean outsideInputResource = false; for (ChannelMember cm: ch.getInputChannelMembers()) { if (((ResourceNode) resToCh.getSource()).getOutSideResources().contains(cm.getResource()) && cm.isOutside()) { outsideInputResource = true; // Regarded as pull transfer. break; } } // Check if the output resource is outside of the channel scope. boolean outsideOutputResource = false; for (ChannelMember cm: ch.getOutputChannelMembers()) { if (resourceNode.getInSideResources().contains(cm.getResource()) && cm.isOutside()) { outsideOutputResource = true; // Regarded as push transfer. break; } } if ((((PushPullAttribute) ((DataFlowEdge) resToCh).getAttribute()).getSelectedOption() != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { // for PULL transfer // srcRes = addReference(component, constructor, ((ResourceNode) resToCh.getSource()).getOutSideResource().getResourceHierarchy(), langSpec); // if (outsideInputResource) { // if (srcRes != null & srcRes.getParent() != null) { // // Reference to root resource. // addReference(component, constructor, srcRes.getRoot(), langSpec); // } // } if (!generatesComponent(srcRes)) { srcRes = srcRes.getParent(); } if (!depends.contains(srcRes)) depends.add(srcRes); } } } // Declare a field to refer to outside resources. if (resourceNode.getParent() == null) { for (ResourceHierarchy dependedRes: dependedRootComponentGraph.keySet()) { for (ResourceHierarchy dependingRes: dependedRootComponentGraph.get(dependedRes)) { if (resourceNode.getResourceHierarchy().equals(dependingRes)) { // Declare a field to refer to outside resources. depends.add(dependedRes); addReference(component, constructor, dependedRes, langSpec); } } } } return constructor; } private void declareStateField(ResourceNode resourceNode, String resourceName, TypeDeclaration component, Type resStateType, Map<ResourceHierarchy, Map<String, VariableDeclaration>> constructorParams, ILanguageSpecific langSpec) { Set<ResourceHierarchy> children = resourceNode.getResourceHierarchy().getChildren(); if (children == null || children.size() == 0) { // leaf resource. FieldDeclaration stateField = langSpec.newFieldDeclaration(resStateType, fieldOfResourceState, langSpec.getFieldInitializer(resStateType, resourceNode.getResourceHierarchy().getInitialValue())); component.addField(stateField); // Add a parameter to initialize the state field to the constructor. // Map<String, VariableDeclaration> nameToParam = constructorParams.get(resourceNode.getResourceHierarchy()); // if (nameToParam == null) { // nameToParam = new HashMap<>(); // constructorParams.put(resourceNode.getResourceHierarchy(), nameToParam); // } // String varName = fieldOfResourceState; // if (nameToParam.get(varName) == null) { // nameToParam.put(varName, langSpec.newVariableDeclaration(resStateType, varName)); // } } else { ResourceHierarchy child = children.iterator().next(); if (children.size() == 1 && child.getNumParameters() > 0) { // map or list. FieldDeclaration stateField = langSpec.newFieldDeclaration(resStateType, fieldOfResourceState, langSpec.getFieldInitializer(resStateType, resourceNode.getResourceHierarchy().getInitialValue())); component.addField(stateField); } else { // class for (ResourceHierarchy c: children) { String childTypeName = getComponentName(c, langSpec); Type childType = null; if (generatesComponent(c)) { // The child has a component. childType = new Type(childTypeName, childTypeName); String fieldName = langSpec.toVariableName(childTypeName); FieldDeclaration stateField = langSpec.newFieldDeclaration(childType, fieldName, langSpec.getConstructorInvocation(childTypeName, new ArrayList<>())); component.addField(stateField); } } } } } private void declareFieldsToReferToOtherResourcesAndStateFieldInParentComponent(ResourceNode resourceNode, TypeDeclaration component, TypeDeclaration parentComponent, Map<ResourceHierarchy, Map<String, VariableDeclaration>> constructorParams, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { // Declare reference fields for push data transfer. boolean noPullTransfer = true; for (Edge resToCh : resourceNode.getOutEdges()) { DataFlowEdge re = (DataFlowEdge) resToCh; ChannelNode directDstChNode = (ChannelNode) re.getDestination(); DataTransferChannel directDstCh = directDstChNode.getChannel(); // Check if the source resource is outside of the channel scope. boolean outsideInputResource = false; for (ChannelMember cm: directDstCh.getInputChannelMembers()) { if (cm.getResource().equals(resourceNode.getOutSideResource(directDstCh)) && cm.isOutside()) { outsideInputResource = true; // Regarded as pull transfer. break; } } // Should take into account the channel hierarchy. Set<ChannelNode> ancestorDstChannels = directDstChNode.getAncestors(); Set<ChannelNode> descendantDstChannels = directDstChNode.getDescendants(); Set<Edge> outEdges = new HashSet<>(); outEdges.addAll(directDstChNode.getOutEdges()); for (ChannelNode ancestorDst: ancestorDstChannels) { outEdges.addAll(ancestorDst.getOutEdges()); } for (ChannelNode descendantDst: descendantDstChannels) { outEdges.addAll(descendantDst.getOutEdges()); } for (Edge chToRes: outEdges) { if (chToRes.getDestination() instanceof ResourceNode) { ResourceHierarchy dstRes = ((ResourceNode) chToRes.getDestination()).getResourceHierarchy(); ChannelNode chNode = (ChannelNode) chToRes.getSource(); DataTransferChannel ch = chNode.getChannel(); // Check if the destination resource is outside of the channel scope. boolean outsideOutputResource = false; ChannelMember out = null; for (ChannelMember cm: ch.getOutputChannelMembers()) { if (((ResourceNode) chToRes.getDestination()).getInSideResources().contains(cm.getResource())) { out = cm; if (cm.isOutside()) { outsideOutputResource = true; // Regarded as push transfer. break; } } } ResourcePath dstResPath = out.getResource(); // Also take into account the channel hierarchy to determine push/pull transfer. if (descendantDstChannels.contains(chNode)) { outsideOutputResource = true; // Regarded as (broadcasting) push transfer. } if (ancestorDstChannels.contains(chNode)) { outsideInputResource = true; // Regarded as (collecting) pull transfer. } if ((((PushPullAttribute) re.getAttribute()).getSelectedOption() == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) { // Declare a field in the parent or this component to refer to the destination resource of push transfer. if (!generatesComponent(dstRes)) { dstRes = dstRes.getParent(); } String dstResName = getComponentName(dstRes, langSpec); FieldDeclaration refFieldForPush = langSpec.newFieldDeclaration(new Type(dstResName, dstResName), langSpec.toVariableName(dstResName)); VariableDeclaration refVarForPush = langSpec.newVariableDeclaration(new Type(dstResName, dstResName), langSpec.toVariableName(dstResName)); if (!platformSpec.isMonolithic() && (outsideOutputResource || (resourceNode.getOutSideResource(ch).getCommonPrefix(dstResPath) == null && platformSpec.isDifferentTreesAsDifferentServices()))) { // Inter-service (for REST API) if (parentComponent != null && !((RestApiSpecific) platformSpec).hasHttpClientFieldDeclaration(parentComponent)) { // Declare a client field to connect to the destination resource of push transfer. ((RestApiSpecific) platformSpec).addHttpClientFieldDeclaration(parentComponent); } } else { // Monolithic or Inner-service if (component != null) { // A component is created for this resource. if (resourceNode.getResourceHierarchy() != dstRes) { component.addField(refFieldForPush); if (!outsideOutputResource) { Map<String, VariableDeclaration> nameToParam = constructorParams.get(resourceNode.getResourceHierarchy()); if (nameToParam == null) { nameToParam = new HashMap<>(); constructorParams.put(resourceNode.getResourceHierarchy(), nameToParam); } if (nameToParam.get(dstResName) == null) { nameToParam.put(dstResName, refVarForPush); } } } } else { // No component is created for this resource. if (resourceNode.getParent().getResourceHierarchy() != dstRes && parentComponent != null) { parentComponent.addField(refFieldForPush); if (!outsideOutputResource) { Map<String, VariableDeclaration> nameToParam = constructorParams.get(resourceNode.getParent().getResourceHierarchy()); if (nameToParam == null) { nameToParam = new HashMap<>(); constructorParams.put(resourceNode.getParent().getResourceHierarchy(), nameToParam); } if (nameToParam.get(dstResName) == null) { nameToParam.put(dstResName, refVarForPush); } } } } if (outsideOutputResource) { // When the reference to the destination resource can vary. if (dstRes.getParent() != null) { // Reference to its root resource. String dstRootResName = getComponentName(dstRes.getRoot(), langSpec); Type dstRootResType = new Type(dstRootResName, dstRootResName); dstRootResName = langSpec.toVariableName(dstRootResName); FieldDeclaration refRootFieldForPush = langSpec.newFieldDeclaration(dstRootResType, dstRootResName); VariableDeclaration refRootVarForPush = langSpec.newVariableDeclaration(dstRootResType, dstRootResName); if (component != null) { // A component is created for this resource. boolean existsField = false; for (FieldDeclaration field: component.getFields()) { if (dstRootResName.equals(field.getName())) { existsField = true; break; } } if (!existsField) { component.addField(refRootFieldForPush); Map<String, VariableDeclaration> nameToParam = constructorParams.get(resourceNode.getResourceHierarchy()); if (nameToParam == null) { nameToParam = new HashMap<>(); constructorParams.put(resourceNode.getResourceHierarchy(), nameToParam); } if (nameToParam.get(dstRootResName) == null) { nameToParam.put(dstRootResName, refRootVarForPush); } } } else { // No component is created for this resource. boolean existsField = false; for (FieldDeclaration field: parentComponent.getFields()) { if (dstRootResName.equals(field.getName())) { existsField = true; break; } } if (!existsField) { parentComponent.addField(refRootFieldForPush); Map<String, VariableDeclaration> nameToParam = constructorParams.get(resourceNode.getParent().getResourceHierarchy()); if (nameToParam == null) { nameToParam = new HashMap<>(); constructorParams.put(resourceNode.getParent().getResourceHierarchy(), nameToParam); } if (nameToParam.get(dstRootResName) == null) { nameToParam.put(dstRootResName, refRootVarForPush); } } } } } } } } } } // Declare reference fields for pull data transfer. for (Edge chToRes : resourceNode.getInEdges()) { ChannelNode directSrcChNode = (ChannelNode) chToRes.getSource(); DataTransferChannel directSrcCh = directSrcChNode.getChannel(); // Should take into account the channel hierarchy. Set<ChannelNode> ancestorSrcChannels = directSrcChNode.getAncestors(); Set<ChannelNode> descendantSrcChannels = directSrcChNode.getDescendants(); Set<Edge> inEdges = new HashSet<>(); inEdges.addAll(directSrcChNode.getInEdges()); for (ChannelNode ancestorSrc: ancestorSrcChannels) { inEdges.addAll(ancestorSrc.getInEdges()); } for (ChannelNode descendantSrc: descendantSrcChannels) { inEdges.addAll(descendantSrc.getInEdges()); } for (Edge resToCh: inEdges) { DataFlowEdge re = (DataFlowEdge) resToCh; ChannelNode chNode = (ChannelNode) re.getDestination(); DataTransferChannel ch = chNode.getChannel(); ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(ch); // Check if the source 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; for (ChannelMember cm: ch.getOutputChannelMembers()) { if (resourceNode.getInSideResources().contains(cm.getResource()) && cm.isOutside()) { outsideOutputResource = true; // Regarded as push transfer. break; } } // Also take into account the channel hierarchy to determine push/pull transfer. if (descendantSrcChannels.contains(chNode)) { outsideInputResource = true; // Regarded as (collecting) pull transfer. } if (ancestorSrcChannels.contains(chNode)) { outsideOutputResource = true; // Regarded as (broadcasting) push transfer. } if ((((PushPullAttribute) re.getAttribute()).getSelectedOption() != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { noPullTransfer = false; // Declare a field in the parent/this component to refer to the source resource of pull transfer. if (!generatesComponent(srcRes.getResourceHierarchy())) { srcRes = srcRes.getParent(); } String srcResName = getComponentName(srcRes.getResourceHierarchy(), langSpec); FieldDeclaration refFieldForPull = langSpec.newFieldDeclaration(new Type(srcResName, srcResName), langSpec.toVariableName(srcResName)); VariableDeclaration refVarForPull = langSpec.newVariableDeclaration(new Type(srcResName, srcResName), langSpec.toVariableName(srcResName)); if (!platformSpec.isMonolithic() && (outsideInputResource || (resourceNode.getInSideResource(ch).getCommonPrefix(srcRes) == null && platformSpec.isDifferentTreesAsDifferentServices()))) { // Inter-service (for REST API) if (parentComponent != null && !((RestApiSpecific) platformSpec).hasHttpClientFieldDeclaration(parentComponent)) { // Declare a client field to connect to the destination resource of push transfer. ((RestApiSpecific) platformSpec).addHttpClientFieldDeclaration(parentComponent); } } else { // Monolithic or Inner-service (for REST API) // Declare a field to directly refer to the source resource of pull transfer. if (component != null) { // A component is created for this resource. if (resourceNode.getResourceHierarchy() != srcRes.getResourceHierarchy()) { component.addField(refFieldForPull); if (!outsideInputResource) { Map<String, VariableDeclaration> nameToParam = constructorParams.get(resourceNode.getResourceHierarchy()); if (nameToParam == null) { nameToParam = new HashMap<>(); constructorParams.put(resourceNode.getResourceHierarchy(), nameToParam); } if (nameToParam.get(srcResName) == null) { nameToParam.put(srcResName, refVarForPull); } } } } else { // No component is created for this resource. if (resourceNode.getParent().getResourceHierarchy() != srcRes.getResourceHierarchy() && parentComponent != null) { parentComponent.addField(refFieldForPull); if (!outsideInputResource) { Map<String, VariableDeclaration> nameToParam = constructorParams.get(resourceNode.getParent().getResourceHierarchy()); if (nameToParam == null) { nameToParam = new HashMap<>(); constructorParams.put(resourceNode.getParent().getResourceHierarchy(), nameToParam); } if (nameToParam.get(srcResName) == null) { nameToParam.put(srcResName, refVarForPull); } } } } if (outsideInputResource) { // When the reference to the source resource can vary. if (srcRes.getParent() != null) { // Reference to its root resource. String srcRootResName = getComponentName(srcRes.getRoot().getResourceHierarchy(), langSpec); Type srcRootResType = new Type(srcRootResName, srcRootResName); srcRootResName = langSpec.toVariableName(srcRootResName); FieldDeclaration refRootFieldForPull = langSpec.newFieldDeclaration(srcRootResType, srcRootResName); VariableDeclaration refRootVarForPull = langSpec.newVariableDeclaration(srcRootResType, srcRootResName); if (component != null) { // A component is created for this resource. boolean existsField = false; for (FieldDeclaration field: component.getFields()) { if (srcRootResName.equals(field.getName())) { existsField = true; break; } } if (!existsField) { component.addField(refRootFieldForPull); Map<String, VariableDeclaration> nameToParam = constructorParams.get(resourceNode.getResourceHierarchy()); if (nameToParam == null) { nameToParam = new HashMap<>(); constructorParams.put(resourceNode.getResourceHierarchy(), nameToParam); } if (nameToParam.get(srcRootResName) == null) { nameToParam.put(srcRootResName, refRootVarForPull); } } } else { // No component is created for this resource. boolean existsField = false; for (FieldDeclaration field: parentComponent.getFields()) { if (srcRootResName.equals(field.getName())) { existsField = true; break; } } if (!existsField) { parentComponent.addField(refRootFieldForPull); Map<String, VariableDeclaration> nameToParam = constructorParams.get(resourceNode.getParent().getResourceHierarchy()); if (nameToParam == null) { nameToParam = new HashMap<>(); constructorParams.put(resourceNode.getParent().getResourceHierarchy(), nameToParam); } if (nameToParam.get(srcRootResName) == null) { nameToParam.put(srcRootResName, refRootVarForPull); } } } } } } } } } // Declare the state field in the parent component. if (component == null) { ResourceHierarchy res = resourceNode.getResourceHierarchy(); if (((StoreAttribute) resourceNode.getAttribute()).isStored() && noPullTransfer && res.getNumParameters() == 0) { String resName = langSpec.toVariableName(getComponentName(res, langSpec)); boolean existsField = false; for (FieldDeclaration field: parentComponent.getFields()) { if (resName.equals(field.getName())) { existsField = true; break; } } if (!existsField) { FieldDeclaration stateField = langSpec.newFieldDeclaration(res.getResourceStateType(), resName); parentComponent.addField(stateField); Map<String, VariableDeclaration> nameToParam = constructorParams.get(resourceNode.getParent().getResourceHierarchy()); if (nameToParam == null) { nameToParam = new HashMap<>(); constructorParams.put(resourceNode.getParent().getResourceHierarchy(), nameToParam); } if (nameToParam.get(resName) == null) { nameToParam.put(resName, langSpec.newVariableDeclaration(res.getResourceStateType(), resName)); } } } } } private MethodDeclaration declareStateGetterMethod(ResourceNode resourceNode, TypeDeclaration component, Type resStateType, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { // Declare the getter method of the resource state. MethodDeclaration stateGetter = langSpec.newMethodDeclaration(getterOfResourceState, resStateType); if (!platformSpec.isMonolithic() && resourceNode.getResourceHierarchy().getParent() == null) { // Since this getter is also an accessor. ((RestApiSpecific) platformSpec).addGetAnnotations(stateGetter); } component.addMethod(stateGetter); boolean hasDescendantIn = hasDescendantInput(resourceNode); if (((StoreAttribute) resourceNode.getAttribute()).isStored() && !hasDescendantIn) { fillStateGetterMethod(stateGetter, resourceNode.getResourceHierarchy(), resStateType, platformSpec, langSpec); } else { // invocations to other getter methods when at least one incoming data-flow edges is PULL-style. if (addOtherGetterInvocationsToStateGatter(stateGetter, resourceNode, platformSpec, langSpec)) { // Declare a client field to connect to the destination resource of push transfer. if (!((RestApiSpecific) platformSpec).hasHttpClientFieldDeclaration(component)) { ((RestApiSpecific) platformSpec).addHttpClientFieldDeclaration(component); } } } return stateGetter; } private MethodDeclaration declareStateGetterMethodInAncestor(ResourceNode resourceNode, Map<ResourceHierarchy, TypeDeclaration> resourceComponents, Type resStateType, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { // Search an ancestor in which the getter method is declared. ResourceNode ancestorNode = resourceNode; ResourceNode childNode = null; Stack<ResourceNode> ancestors = new Stack<>(); do { ancestors.push(ancestorNode); childNode = ancestorNode; ancestorNode = ancestorNode.getParent(); } while (!generatesComponent(ancestorNode.getResourceHierarchy())); TypeDeclaration ancestorComponent = resourceComponents.get(ancestorNode.getResourceHierarchy()); List<VariableDeclaration> getterParams = new ArrayList<>(); int v = 1; while (ancestors.size() > 0) { ResourceNode curAncestor = ancestors.pop(); Expression param = curAncestor.getPrimaryResourcePath().getLastParam(); if (param instanceof Variable) { Variable var = (Variable) param; getterParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); } else if (param instanceof Term) { Term var = (Term) param; getterParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v)); } v++; } // Check duplication. String getterName = getterPrefix + getComponentName(resourceNode.getResourceHierarchy(), langSpec); for (MethodDeclaration method: ancestorComponent.getMethods()) { if (method.getName().equals(getterName) && (method.getParameters() == null ? 0 : method.getParameters().size()) == getterParams.size()) return null; } // Declare the getter method of the resource state. MethodDeclaration stateGetter = null; if (getterParams.size() == 0) { stateGetter = langSpec.newMethodDeclaration(getterName, resStateType); } else { stateGetter = langSpec.newMethodDeclaration(getterName, false, resStateType, getterParams); } if (ancestorComponent != null) { ancestorComponent.addMethod(stateGetter); } boolean hasDescendantIn = hasDescendantInput(resourceNode); if (((StoreAttribute) resourceNode.getAttribute()).isStored() && !hasDescendantIn) { fillDescendantGetterMethod(stateGetter, resourceNode.getResourceHierarchy(), childNode.getResourceHierarchy(), ancestorNode.getResourceHierarchy(), ancestorComponent, langSpec); } else { if (addOtherGetterInvocationsToStateGatter(stateGetter, resourceNode, platformSpec, langSpec)) { // Declare a client field to connect to the destination resource of push transfer. if (ancestorComponent != null && !((RestApiSpecific) platformSpec).hasHttpClientFieldDeclaration(ancestorComponent)) { ((RestApiSpecific) platformSpec).addHttpClientFieldDeclaration(ancestorComponent); } } } return stateGetter; } private boolean hasDescendantInput(ResourceNode resourceNode) { boolean hasDescendantIn = false; outer: for (Edge chToRes: resourceNode.getInEdges()) { ChannelNode chNode = (ChannelNode) chToRes.getSource(); Set<ChannelNode> descendantChannels = chNode.getDescendants(); for (ChannelNode descendantCh: descendantChannels) { if (descendantCh.getIndegree() > 0) { hasDescendantIn = true; break outer; } } } return hasDescendantIn; } private boolean addOtherGetterInvocationsToStateGatter(MethodDeclaration stateGetter, ResourceNode resourceNode, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { boolean bDeclareClientField = false; try { // Data transfer on the same channel hierarchy. boolean isContainedPush = false; DataTransferChannel ch = null; DataTransferChannel ch2 = null; HashMap<ChannelMember, IResourceStateAccessor> inputResourceToStateAccessor = new HashMap<>(); for (Edge chToRes: resourceNode.getInEdges()) { ch2 = ((ChannelNode) chToRes.getSource()).getChannel(); for (Edge resToCh: chToRes.getSource().getInEdges()) { DataFlowEdge dIn = (DataFlowEdge) resToCh; ChannelMember in = null; for (ChannelMember cm: ch2.getInputChannelMembers()) { if (((ResourceNode) dIn.getSource()).getOutSideResources().contains(cm.getResource())) { in = cm; break; } } if (((PushPullAttribute) dIn.getAttribute()).getSelectedOption() == PushPullValue.PUSH) { // PUSH transfer isContainedPush = true; inputResourceToStateAccessor.put(in, getPushAccessor(platformSpec)); } else { // PULL transfer inputResourceToStateAccessor.put(in, getPullAccessor(platformSpec)); ch = ((ChannelNode) resToCh.getDestination()).getChannel(); // pull containing input side channel is at most one. if (!platformSpec.isMonolithic() && !in.isOutside() && in.getResource().getCommonPrefix(resourceNode.getInSideResource(ch2)) == null && platformSpec.isDifferentTreesAsDifferentServices()) { // for REST API ResourcePath srcResPath = in.getResource(); String srcResourceName = langSpec.toVariableName(getComponentName(srcResPath.getResourceHierarchy(), langSpec)); Type srcResourceType = srcResPath.getResourceStateType(); List<String> pathParams = new ArrayList<>(); for (Expression pathExp: srcResPath.getPathParams()) { String[] sideEffects = new String[] {""}; pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); } generatePullDataTransfer(stateGetter, srcResourceName, srcResPath.getResourceHierarchy().toResourcePath(pathParams), srcResourceType, true, platformSpec, langSpec); bDeclareClientField = true; } } } } if (ch == null) { ch = ch2; } ChannelMember out = null; for (ChannelMember cm: ch.getOutputChannelMembers()) { if (resourceNode.getInSideResources().contains(cm.getResource())) { out = cm; break; } } // for reference channel members. ResourcePath dstResPath = resourceNode.getInSideResource(ch); for (ChannelMember rc: ch.getReferenceChannelMembers()) { inputResourceToStateAccessor.put(rc, getPullAccessor(platformSpec)); // by pull data transfer ResourcePath refResPath = rc.getResource(); if (!platformSpec.isMonolithic() && (rc.isOutside() || (refResPath.getCommonPrefix(dstResPath) == null && platformSpec.isDifferentTreesAsDifferentServices()))) { // for REST API String refResourceName = langSpec.toVariableName(getComponentName(refResPath.getResourceHierarchy(), langSpec)); Type refResourceType = refResPath.getResourceStateType(); List<String> pathParams = new ArrayList<>(); for (Expression pathExp: refResPath.getPathParams()) { String[] sideEffects = new String[] {""}; pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); } generatePullDataTransfer(stateGetter, refResourceName, refResPath.getResourceHierarchy().toResourcePath(pathParams), refResourceType, true, platformSpec, langSpec); bDeclareClientField = true; } } // Construct the base message. Map.Entry<Map<ChannelMember, Entry<ResourcePath, Set<ChannelMember>>>, Term> resourcePathsAndMessage; if (!isContainedPush) { // All incoming edges are in PULL-style. resourcePathsAndMessage = ch.fillOutsideResourcePaths(out, getPullAccessor(platformSpec), null); } else { // At least one incoming edge is in PUSH-style. resourcePathsAndMessage = ch.fillOutsideResourcePaths(out, getPullAccessor(platformSpec), inputResourceToStateAccessor); } Map<ChannelMember, Entry<ResourcePath, Set<ChannelMember>>> resourcePaths = resourcePathsAndMessage.getKey(); Term messageTerm = resourcePathsAndMessage.getValue(); // Data transfer from path depending resource. for (Entry<ChannelMember, Entry<ResourcePath, Set<ChannelMember>>> pathEnt: resourcePaths.entrySet()) { ChannelMember cm = pathEnt.getKey(); ResourcePath srcResPath = pathEnt.getValue().getKey(); // get outside srcResPath resource state by pull data transfer. if (!platformSpec.isMonolithic() && (cm.isOutside() || (srcResPath.getCommonPrefix(dstResPath)) == null && platformSpec.isDifferentTreesAsDifferentServices())) { // for REST API // Data transfer from an outside input resource is regarded as PULL transfer. List<String> pathParams = new ArrayList<>(); for (Expression pathExp: srcResPath.getPathParams()) { String[] sideEffects = new String[] {""}; pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); } // generate a pull data transfer from a depending in/ref resource. Type srcResourceType = srcResPath.getResourceStateType(); String srcResName2 = langSpec.toVariableName(getComponentName(srcResPath.getResourceHierarchy(), langSpec)); String srcPath2 = srcResPath.toResourcePath().replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); generatePullDataTransfer(stateGetter, srcResName2, srcPath2, srcResourceType, false, platformSpec, langSpec); bDeclareClientField = true; } } // Data transfer from the descendant channel hierarchies. Stack<Iterator<Channel>> channelItrStack = new Stack<>(); DataTransferChannel curChannel = ch; if (curChannel.getChildren() != null && curChannel.getChildren().size() > 0) { // retrieve descendant channels recursively. Iterator<Channel> chItr = curChannel.getChildren().iterator(); do { if (!chItr.hasNext()) { chItr = channelItrStack.pop(); } else { curChannel = (DataTransferChannel) chItr.next(); // generate pull data transfers. Set<ChannelMember> chMems = new HashSet<>(curChannel.getInputChannelMembers()); chMems.addAll(curChannel.getReferenceChannelMembers()); for (ChannelMember cm2: chMems) { if (resourcePaths == null || !resourcePaths.keySet().contains(cm2)) { // not a depending channel member. ResourcePath src2 = cm2.getResource(); Type srcResType2 = src2.getResourceStateType(); String srcResName2 = langSpec.toVariableName(getComponentName(src2.getResourceHierarchy(), langSpec)); if (platformSpec.isMonolithic()) { String srcGetter = getPullAccessor(platformSpec).getDirectStateAccessorFor(src2, resourceNode.getInSideResource(curChannel)).toImplementation(new String[] {}); stateGetter.addStatement(langSpec.getVariableDeclaration(srcResType2.getInterfaceTypeName(), srcResName2) + langSpec.getAssignment() + srcGetter + langSpec.getStatementDelimiter()); } else { String srcPath2 = src2.toResourcePath().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); generatePullDataTransfer(stateGetter, srcResName2, srcPath2, srcResType2, false, platformSpec, langSpec); bDeclareClientField = true; } } else { // a depending channel member. ResourcePath src2 = resourcePaths.get(cm2).getKey(); // get outside src2 resource state by pull data transfer. if (cm2.isOutside() || src2.getCommonPrefix(resourceNode.getInSideResource(curChannel)) == null) { // generate a pull data transfer from a depending in/ref resource. Type srcResType2 = src2.getResourceStateType(); String srcResName2 = langSpec.toVariableName(getComponentName(src2.getResourceHierarchy(), langSpec)); if (platformSpec.isMonolithic()) { String dependingGetter = getPullAccessor(platformSpec).getDirectStateAccessorFor(src2, resourceNode.getInSideResource(curChannel)).toImplementation(new String[] {}); stateGetter.addStatement(langSpec.getVariableDeclaration(srcResType2.getInterfaceTypeName(), srcResName2) + langSpec.getAssignment() + dependingGetter + langSpec.getStatementDelimiter()); } else { String srcPath2 = src2.toResourcePath().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); generatePullDataTransfer(stateGetter, srcResName2, srcPath2, srcResType2, false, platformSpec, langSpec); bDeclareClientField = true; } } } } // collect the message constraints by a descendant channel. List<Variable> varsForSideEffects = new ArrayList<>(); int v = 0; resourcePathsAndMessage = curChannel.fillOutsideResourcePaths(out, getPullAccessor(platformSpec), null); if (resourcePathsAndMessage != null) { resourcePaths = resourcePathsAndMessage.getKey(); Term messageTermSub = resourcePathsAndMessage.getValue(); for (Entry<Position, Field> fieldEnt: ((Term) messageTermSub).getSubTerms(Field.class).entrySet()) { Position pos = fieldEnt.getKey(); Field field = fieldEnt.getValue(); Variable var = new Variable(field.getSymbol().getName(), field.getType()); ((Term) messageTermSub).replaceSubTerm(pos, var); } for (Map.Entry<Position, Term> subTermEnt: messageTermSub.getSubTerms(Term.class).entrySet()) { Term subTerm = subTermEnt.getValue(); if (!(subTerm instanceof Constant) && subTerm.getSymbol().isImplWithSideEffect()) { Variable var = new Variable("v" + v, subTerm.getType()); varsForSideEffects.add(var); v++; // Add a side effect statement within the loop Position pos = new Position(); pos.addHeadOrder(0); subTerm.replaceSubTerm(pos, var); String[] sideEffects = new String[] {""}; String curState = messageTermSub.toImplementation(sideEffects); stateGetter.addStatement(sideEffects[0].replaceAll("\n", "")); // Cancel the side effects in the return value. pos = subTermEnt.getKey(); messageTermSub.replaceSubTerm(pos, var); } } if (messageTerm == null) { messageTerm = messageTermSub; } else { messageTerm = (Term) messageTerm.unify(messageTermSub); } if (messageTerm == null) { throw new UnificationFailed(); } } // enclosed by a for loop (for data collecting pull transfer) Expression selExp = curChannel.getSelectors().get(0).getExpression(); Type selType = null; String forVarName = null; if (selExp instanceof Variable) { selType = ((Variable) selExp).getType(); forVarName = ((Variable) selExp).getName(); ChannelMember insideChMem = null; for (ChannelMember cm2 :curChannel.getInputChannelMembers()) { if (!cm2.isOutside()) { insideChMem = cm2; break; } } if (insideChMem == null) { for (ChannelMember cm2 :curChannel.getReferenceChannelMembers()) { if (!cm2.isOutside()) { insideChMem = cm2; break; } } } ResourcePath insideResPath = insideChMem.getResource(); while (insideResPath.getParent() != null && (insideResPath.getLastParam() == null || !insideResPath.getLastParam().equals(selExp))) { insideResPath = insideResPath.getParent(); } insideResPath = insideResPath.getParent(); if (insideResPath != null) { String parent = null; if (platformSpec.isMonolithic() || insideResPath.getCommonPrefix(dstResPath) != null || !platformSpec.isDifferentTreesAsDifferentServices()) { if (!platformSpec.isMonolithic() && generatesComponent(insideResPath.getResourceHierarchy())) { Expression parentGetter = getPullAccessor(platformSpec).getDirectStateAccessorFor(insideResPath, dstResPath); Term valueGetter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); valueGetter.addChild(parentGetter); parent = valueGetter.toImplementation(new String[] {}); } else { parent = getPullAccessor(platformSpec).getDirectStateAccessorFor(insideResPath, dstResPath).toImplementation(new String[] {}); } } else { // for REST API parent = langSpec.toVariableName(getComponentName(insideResPath.getResourceHierarchy(), langSpec)); } if (selType.equals(DataConstraintModel.typeInt)) { // make a for loop (for a list) for data collecting. stateGetter.addFirstStatement(langSpec.getForStatementForList(forVarName, parent)); } else if (selType.equals(DataConstraintModel.typeString)) { // make a for loop (for a map) for data collecting. stateGetter.addFirstStatement(langSpec.getForStatementForMap(forVarName, DataConstraintModel.typeString.getInterfaceTypeName(), parent)); } if (!platformSpec.isMonolithic() && insideResPath.getCommonPrefix(dstResPath) == null && platformSpec.isDifferentTreesAsDifferentServices()) { // for REST API Type parentResType = insideResPath.getResourceStateType(); String parentResName = langSpec.toVariableName(getComponentName(insideResPath.getResourceHierarchy(), langSpec)); String parentResPath = insideResPath.toResourcePath().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); generatePullDataTransfer(stateGetter, parentResName, parentResPath, parentResType, true, platformSpec, langSpec); bDeclareClientField = true; } } } // initialize the variables to hold side effects within the loop for (Variable var: varsForSideEffects) { stateGetter.addFirstStatement(langSpec.getVariableDeclaration(var.getType().getInterfaceTypeName(), var.getName()) + langSpec.getAssignment() + langSpec.getConstructorInvocation(var.getType().getImplementationTypeName(), null) + langSpec.getStatementDelimiter()); } // end of the loop stateGetter.addStatement("}"); if (curChannel.getChildren() != null && curChannel.getChildren().size() > 0) { channelItrStack.push(chItr); chItr = curChannel.getChildren().iterator(); } } } while (!channelItrStack.isEmpty()); } // generate a return statement. String[] sideEffects = new String[] {""}; String curState = ch.deriveUpdateExpressionOf(out, messageTerm, getPullAccessor(platformSpec)).toImplementation(sideEffects); stateGetter.addStatement(sideEffects[0] + langSpec.getReturnStatement(curState) + langSpec.getStatementDelimiter()); } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage | UnificationFailed | ValueUndefined e) { e.printStackTrace(); } return bDeclareClientField; } private void declareDescendantGetterMethods(ResourceNode resourceNode, TypeDeclaration component, Map<ResourceHierarchy, Set<ResourceHierarchy>> descendantGetters, ILanguageSpecific langSpec) { // Declare the getter methods in this resource to obtain descendant resources. Set<ResourceHierarchy> descendants = descendantGetters.get(resourceNode.getResourceHierarchy()); if (descendants == null) { descendants = new HashSet<>(); descendantGetters.put(resourceNode.getResourceHierarchy(), descendants); } for (ResourceNode child: resourceNode.getChildren()) { // A descendant of the child may generate a component. List<VariableDeclaration> params = new ArrayList<>(); int v = 1; ResourceNode descendant = child; Set<ResourceNode> childNodes; do { Expression param = descendant.getPrimaryResourcePath().getLastParam(); if (param != null) { if (param instanceof Variable) { Variable var = (Variable) param; params.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); } else if (param instanceof Term) { Term var = (Term) param; params.add(langSpec.newVariableDeclaration(var.getType(), "v" + v)); } v++; } if (generatesComponent(descendant.getResourceHierarchy())) { // If the descendant generates a component. if (!descendants.contains(descendant.getResourceHierarchy())) { descendants.add(descendant.getResourceHierarchy()); String descendantCompName = getComponentName(descendant.getResourceHierarchy(), langSpec); Type descendantType = new Type(descendantCompName, descendantCompName); MethodDeclaration descendantGetter = null; if (params.size() == 0) { descendantGetter = langSpec.newMethodDeclaration(getterPrefix + descendantCompName, descendantType); } else { descendantGetter = langSpec.newMethodDeclaration(getterPrefix + descendantCompName, false, descendantType, params); } fillDescendantGetterMethod(descendantGetter, descendant.getResourceHierarchy(), child.getResourceHierarchy(), resourceNode.getResourceHierarchy(), component, langSpec); component.addMethod(descendantGetter); } break; } childNodes = descendant.getChildren(); } while (childNodes != null && childNodes.size() == 1 && (descendant = childNodes.iterator().next()) != null); } } private Map.Entry<List<String>, Map<MethodDeclaration, Map.Entry<Expression, Map.Entry<ResourceHierarchy, ResourceHierarchy>>>> declareCacheFieldsAndUpdateMethods(ResourceNode resourceNode, TypeDeclaration component, TypeDeclaration parentComponent, TypeDeclaration rootComponent, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { // Declare cash fields and update methods in the component. String resComponentName = getComponentName(resourceNode.getResourceHierarchy(), langSpec); List<String> constructorStatements = new ArrayList<>(); Map<MethodDeclaration, Map.Entry<Expression, Map.Entry<ResourceHierarchy, ResourceHierarchy>>> updateStatements = new HashMap<>(); for (Edge chToRes: resourceNode.getInEdges()) { ChannelNode directSrcChannel = (ChannelNode) chToRes.getSource(); DataTransferChannel ch = directSrcChannel.getChannel(); // Should take into account the channel hierarchy. Set<ChannelNode> ancestorSrcChannels = directSrcChannel.getAncestors(); Set<ChannelNode> descendantSrcChannels = directSrcChannel.getDescendants(); Set<Edge> inEdges = new HashSet<>(); inEdges.addAll(directSrcChannel.getInEdges()); for (ChannelNode ancestorSrc: ancestorSrcChannels) { inEdges.addAll(ancestorSrc.getInEdges()); } for (ChannelNode descendantSrc: descendantSrcChannels) { inEdges.addAll(descendantSrc.getInEdges()); } for (Edge resToCh: inEdges) { // For each data transfer from srcResPath:ResourcePath to resourceNode:ResourceNode. DataFlowEdge re = (DataFlowEdge) resToCh; ChannelNode indirectSrcChNode = (ChannelNode) re.getDestination(); DataTransferChannel indirectSrcCh = indirectSrcChNode.getChannel(); ResourcePath srcResPath = ((ResourceNode) re.getSource()).getOutSideResource(indirectSrcCh); String srcResComponentName = getComponentName(srcResPath.getResourceHierarchy(), langSpec); String srcResName = langSpec.toVariableName(srcResComponentName); // Check if the input resource is outside of the channel scope. boolean outsideInputResource = false; for (ChannelMember cm: indirectSrcCh.getInputChannelMembers()) { if (cm.getResource().equals(srcResPath) && 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; ResourcePath dstResPath = null; for (ChannelMember cm: ch.getOutputChannelMembers()) { if (resourceNode.getInSideResources().contains(cm.getResource())) { out = cm; dstResPath = cm.getResource(); if (cm.isOutside()) { outsideOutputResource = true; // Regarded as push transfer. break; } } } // Also take into account the channel hierarchy to determine push/pull transfer. if (ancestorSrcChannels.contains(indirectSrcChNode)) { outsideOutputResource = true; // Regarded as (broadcasting) push transfer. } if (descendantSrcChannels.contains(indirectSrcChNode)) { outsideInputResource = true; // Regarded as (collecting) pull transfer. } if ((((PushPullAttribute) re.getAttribute()).getSelectedOption() == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) { // For push data transfer boolean hasRestAPI = false; boolean isRestAPI = false; if (!platformSpec.isMonolithic() && (outsideOutputResource || (resourceNode.getInSideResource(ch).getCommonPrefix(srcResPath) == null && platformSpec.isDifferentTreesAsDifferentServices()))) { // Inter-service hasRestAPI = true; if (component != null && !((RestApiSpecific) platformSpec).hasHttpClientFieldDeclaration(component)) { // Declare a client field to connect to the destination resource of push transfer. ((RestApiSpecific) platformSpec).addHttpClientFieldDeclaration(component); } if (resourceNode.getParent() == null) { // A root resource isRestAPI = true; } } // Declare an update method in the type of the destination resource. ArrayList<VariableDeclaration> parameters = new ArrayList<>(); getUpdateResourcePathAndPathParams(dstResPath, parameters, isRestAPI, platformSpec, langSpec); // Path parameters to identify the self resource. for (Selector selector: ch.getAllSelectors()) { if (selector.getExpression() instanceof Variable) { Variable selVar = (Variable) selector.getExpression(); VariableDeclaration chParam = langSpec.newVariableDeclaration(selVar.getType(), selVar.getName()); if (isRestAPI) ((RestApiSpecific) platformSpec).addFormParamAnnotation(chParam, selVar.getName()); parameters.add(chParam); // A channel parameter to specify the context of the collaboration. } } VariableDeclaration param = langSpec.newVariableDeclaration(srcResPath.getResourceStateType(), srcResName); if (isRestAPI) ((RestApiSpecific) platformSpec).addFormParamAnnotation(param, srcResName); parameters.add(param); // The state of the source resource to carry the data-flow. // For the refs. for (ResourcePath ref: ch.getReferenceResources()) { if (!resourceNode.getInSideResources().contains(ref)) { String refName = langSpec.toVariableName(getComponentName(ref.getResourceHierarchy(), langSpec)); param = langSpec.newVariableDeclaration(ref.getResourceStateType(), refName); if (isRestAPI) ((RestApiSpecific) platformSpec).addFormParamAnnotation(param, refName); parameters.add(param); } } MethodDeclaration update = null; if (component != null) { for (MethodDeclaration method: component.getMethods()) { if (method.getName().equals(updateMethodPrefix + from + srcResComponentName)) { update = method; break; } } if (update == null) { update = langSpec.newMethodDeclaration(updateMethodPrefix + from + srcResComponentName, false, null, parameters); } } else if (parentComponent != null) { for (MethodDeclaration method: parentComponent.getMethods()) { if (method.getName().equals(updateMethodPrefix + resComponentName + from + srcResComponentName)) { update = method; break; } } if (update == null) { update = langSpec.newMethodDeclaration(updateMethodPrefix + resComponentName + from + srcResComponentName, false, null, parameters); } } // Calculate in-degree (PUSH transfer) of the destination resource. int inDegree = 0; for (Edge resToCh2: inEdges) { DataFlowEdge df =(DataFlowEdge) resToCh2; if (((PushPullAttribute) df.getAttribute()).getSelectedOption() == PushPullValue.PUSH) { inDegree++; } } if (isRestAPI) { // Determine whether the update method is put or post or delete. if (isPut(out)) { ((RestApiSpecific) platformSpec).addPutAnnotations(update); } else { if (!isDelete(out)) { ((RestApiSpecific) platformSpec).addPostAnnotations(update); } else { ((RestApiSpecific) platformSpec).addDeleteAnnotations(update); } } if (inDegree > 1) { // If incoming edges are multiple, then a child resource for each source resource is defined in the destination resource so that its state can be updated separately. if (isRestAPI) ((RestApiSpecific) platformSpec).addPathAnnotation(update, "/" + srcResName); } } if (update != null) { if (component != null) { // A component is created for this resource. component.addMethod(update); } else if (parentComponent != null) { // No component is created for this resource. parentComponent.addMethod(update); } } // For a post/put REST API. if (hasRestAPI) { if (!isRestAPI) { // If not a root resource. // Declare an update accessor method in the type of root resource. declareUpdateAccessorInTheRootResource(resourceNode, update.getName(), ch, out, srcResPath, dstResPath, rootComponent, inDegree, platformSpec, langSpec); } // to convert a json param to a tuple, pair or map object. for (VariableDeclaration jsonParam: update.getParameters()) { Type paramType = jsonParam.getType(); String paramName = jsonParam.getName(); String paramTypeName = paramType.getInterfaceTypeName(); String strTypeName = DataConstraintModel.typeString.getInterfaceTypeName(); String paramConverter = ""; if (DataConstraintModel.typeList.isAncestorOf(paramType) && paramType != DataConstraintModel.typeList) { Type compType = TypeInference.getListComponentType(paramType); if (DataConstraintModel.typeTuple.isAncestorOf(compType)) { jsonParam.setType(DataConstraintModel.typeListStr); jsonParam.setName(paramName + "_json"); paramConverter += langSpec.getVariableDeclaration(paramTypeName, paramName) + langSpec.getAssignment() + langSpec.getConstructorInvocation(paramType.getImplementationTypeName(), null) + langSpec.getStatementDelimiter() + "\n"; paramConverter += langSpec.getForStatementForCollection("str", strTypeName, jsonParam.getName()) + "\n"; String mapTypeName = convertFromEntryToMapType(compType, langSpec); paramConverter += "\t" + ((RestApiSpecific) platformSpec).getConversionFromJsonString("str", "i", mapTypeName) + langSpec.getStatementDelimiter() + "\n"; paramConverter += "\t" + langSpec.getMethodInvocation(paramName, DataConstraintModel.append.getImplName(), List.of(getCodeForConversionFromMapToTuple(compType, "i", langSpec))) + langSpec.getStatementDelimiter() + "\n"; paramConverter += langSpec.getEndForStatement("str"); ((RestApiSpecific) platformSpec).addJsonException(update); } else if (DataConstraintModel.typePair.isAncestorOf(compType)) { jsonParam.setType(DataConstraintModel.typeListStr); jsonParam.setName(paramName + "_json"); paramConverter += langSpec.getVariableDeclaration(paramTypeName, paramName) + langSpec.getAssignment() + langSpec.getConstructorInvocation(paramType.getImplementationTypeName(), null) + langSpec.getStatementDelimiter() + "\n"; paramConverter += langSpec.getForStatementForCollection("str", strTypeName, jsonParam.getName()) + "\n"; String mapTypeName = convertFromEntryToMapType(compType, langSpec); paramConverter += "\t" + ((RestApiSpecific) platformSpec).getConversionFromJsonString("str", "i", mapTypeName) + langSpec.getStatementDelimiter() + "\n"; paramConverter += "\t" + langSpec.getMethodInvocation(paramName, DataConstraintModel.append.getImplName(), List.of(getCodeForConversionFromMapToPair(compType, "i", langSpec))) + langSpec.getStatementDelimiter() + "\n"; paramConverter += langSpec.getEndForStatement("str"); ((RestApiSpecific) platformSpec).addJsonException(update); } else if (DataConstraintModel.typeMap.isAncestorOf(compType)) { jsonParam.setType(DataConstraintModel.typeListStr); // To do. } } else if (DataConstraintModel.typeTuple.isAncestorOf(paramType)) { jsonParam.setType(DataConstraintModel.typeString); jsonParam.setName(paramName + "_json"); paramConverter += langSpec.getVariableDeclaration(paramTypeName, paramName) + langSpec.getStatementDelimiter() + "\n"; paramConverter += langSpec.getOpeningScoreDelimiter() + "\n"; String mapTypeName = convertFromEntryToMapType(paramType, langSpec); paramConverter += "\t" + ((RestApiSpecific) platformSpec).getConversionFromJsonString(paramName + "_json", "i", mapTypeName) + langSpec.getStatementDelimiter() + "\n"; paramConverter += "\t" + paramName + langSpec.getAssignment() + getCodeForConversionFromMapToTuple(paramType, "i", langSpec) + langSpec.getStatementDelimiter() + "\n"; paramConverter += langSpec.getClosingScoreDelimiter(); ((RestApiSpecific) platformSpec).addJsonException(update); } else if (DataConstraintModel.typePair.isAncestorOf(paramType)) { jsonParam.setType(DataConstraintModel.typeString); jsonParam.setName(paramName + "_json"); paramConverter += langSpec.getVariableDeclaration(paramTypeName, paramName) + langSpec.getStatementDelimiter() + "\n"; paramConverter += langSpec.getOpeningScoreDelimiter() + "\n"; String mapTypeName = convertFromEntryToMapType(paramType, langSpec); paramConverter += "\t" + ((RestApiSpecific) platformSpec).getConversionFromJsonString(paramName + "_json", "i", mapTypeName) + langSpec.getStatementDelimiter() + "\n"; paramConverter += "\t" + paramName + langSpec.getAssignment() + getCodeForConversionFromMapToPair(paramType, "i", langSpec) + langSpec.getStatementDelimiter() + "\n"; paramConverter += langSpec.getClosingScoreDelimiter(); ((RestApiSpecific) platformSpec).addJsonException(update); } else if (DataConstraintModel.typeMap.isAncestorOf(paramType)) { jsonParam.setType(DataConstraintModel.typeString); jsonParam.setName(paramName + "_json"); paramConverter += langSpec.getVariableDeclaration(paramTypeName, paramName) + langSpec.getAssignment() + langSpec.getConstructorInvocation(paramType.getImplementationTypeName(), null) + langSpec.getStatementDelimiter() + "\n"; paramConverter += langSpec.getOpeningScoreDelimiter() + "\n"; String mapTypeName = convertFromEntryToMapType(paramType, langSpec); paramConverter += "\t" + ((RestApiSpecific) platformSpec).getConversionFromJsonString(paramName + "_json", "i", mapTypeName) + langSpec.getStatementDelimiter() + "\n"; paramConverter += "\t" + getCodeForConversionFromMapToMap(paramType, "i", paramName, langSpec) + "\n"; paramConverter += langSpec.getClosingScoreDelimiter(); ((RestApiSpecific) platformSpec).addJsonException(update); } if (paramConverter.length() > 0 && !update.getBody().getStatements().contains(paramConverter)) { update.addFirstStatement(paramConverter); } } } // Add a statement to update the state field if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { try { if (resourceNode.getInSideResources().contains(out.getResource())) { Term unifiedMassage = null; for (ChannelNode srcChNode: ancestorSrcChannels) { DataTransferChannel abcestorSrcCh = (DataTransferChannel) srcChNode.getChannel(); Term message = abcestorSrcCh.fillOutsideResourcePaths(out, getPushAccessor(platformSpec), null).getValue(); if (unifiedMassage == null) { unifiedMassage = message; } else { unifiedMassage = (Term) unifiedMassage.unify(message); } } Expression updateExp = null; if (ch.getReferenceChannelMembers().size() == 0) { Term message = ch.fillOutsideResourcePaths(out, getPushAccessor(platformSpec), null).getValue(); if (unifiedMassage == null) { unifiedMassage = message; } else { unifiedMassage = (Term) unifiedMassage.unify(message); } updateExp = ch.deriveUpdateExpressionOf(out, unifiedMassage, getPushAccessor(platformSpec)); } else { // if there exists one or more reference channel member. HashMap<ChannelMember, IResourceStateAccessor> inputResourceToStateAccessor = new HashMap<>(); for (ChannelMember in: ch.getInputChannelMembers()) { inputResourceToStateAccessor.put(in, getPushAccessor(platformSpec)); } for (ChannelMember ref: ch.getReferenceChannelMembers()) { inputResourceToStateAccessor.put(ref, getRefAccessor(platformSpec)); } Term message = ch.fillOutsideResourcePaths(out, getPushAccessor(platformSpec), inputResourceToStateAccessor).getValue(); if (unifiedMassage == null) { unifiedMassage = message; } else { unifiedMassage = (Term) unifiedMassage.unify(message); } updateExp = ch.deriveUpdateExpressionOf(out, unifiedMassage, getPushAccessor(platformSpec)); } // Replace Json constructor with a constructor of the child resource. ResourceHierarchy outRes = out.getResource().getResourceHierarchy(); if (outRes.getChildren().size() == 1 && outRes.getChildren().iterator().next().getNumParameters() > 0) { ResourceHierarchy descendantRes = outRes; Set<ResourceHierarchy> children = descendantRes.getChildren(); do { descendantRes = children.iterator().next(); if (generatesComponent(descendantRes)) { updateStatements.put(update, new AbstractMap.SimpleEntry<>(updateExp, new AbstractMap.SimpleEntry<>(outRes, descendantRes))); updateExp = null; break; } children = descendantRes.getChildren(); } while (children != null && children.size() == 1); } // Replace the type of the state field. Type fieldType = getImplStateType(outRes, langSpec); if (updateExp instanceof Term) { ((Term) updateExp).setType(fieldType); for (Map.Entry<Position, Variable> varEnt: ((Term) updateExp).getVariables().entrySet()) { if (varEnt.getValue().getName().equals(fieldOfResourceState)) { varEnt.getValue().setType(fieldType); } } } else if (updateExp instanceof Variable) { ((Variable) updateExp).setType(fieldType); } // Add statements to the update method. String[] sideEffects = new String[] {""}; String newState = updateExp.toImplementation(sideEffects); int numOfOutResourcesWithTheSameHierarchy = 0; for (ResourcePath outResPath: ch.getOutputResources()) { if (outResPath.getResourceHierarchy().equals(outRes)) { numOfOutResourcesWithTheSameHierarchy++; } } String updateStatement = ""; if (generatesComponent(outRes)) { if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { updateStatement = sideEffects[0]; if (updateStatement.endsWith("\n")) { updateStatement = updateStatement.substring(0, updateStatement.length() - 1); } } else { updateStatement = sideEffects[0] + langSpec.getFieldAccessor(fieldOfResourceState) + langSpec.getAssignment() + newState + langSpec.getStatementDelimiter(); // this.value = ... } } else { if (sideEffects[0] != null) { updateStatement = sideEffects[0]; String resourceName = langSpec.toVariableName(getComponentName(outRes, langSpec)); updateStatement = updateStatement.replace(langSpec.getFieldAccessor(fieldOfResourceState), langSpec.getFieldAccessor(resourceName)); if (updateStatement.endsWith("\n")) { updateStatement = updateStatement.substring(0, updateStatement.length() - 1); } } if (DataConstraintModel.typeList.isAncestorOf(resourceNode.getParent().getResourceStateType())) { Term selector = new Term(DataConstraintModel.set); selector.addChild(new Constant(langSpec.getFieldAccessor(fieldOfResourceState))); selector.addChild(new Variable(update.getParameters().get(update.getParameters().size() - 2).getName())); selector.addChild(new Constant(newState)); String[] sideEffects2 = new String[] {""}; String newList = selector.toImplementation(sideEffects2); updateStatement += sideEffects2[0]; } else if (DataConstraintModel.typeMap.isAncestorOf(resourceNode.getParent().getResourceStateType())) { Term selector = new Term(DataConstraintModel.insert); selector.addChild(new Constant(langSpec.getFieldAccessor(fieldOfResourceState))); selector.addChild(new Variable(update.getParameters().get(update.getParameters().size() - 2).getName())); selector.addChild(new Constant(newState)); String[] sideEffects2 = new String[] {""}; String newMap = selector.toImplementation(sideEffects2); updateStatement += sideEffects2[0]; } else if (!(updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect())) { String resourceName = langSpec.toVariableName(getComponentName(outRes, langSpec)); updateStatement += langSpec.getFieldAccessor(resourceName) + langSpec.getAssignment() + newState + langSpec.getStatementDelimiter(); } } // add an update statement of the state of dst side resource. if (numOfOutResourcesWithTheSameHierarchy == 1) { update.addFirstStatement(updateStatement); } else { Term conditions = null; int i = 1; Map<ChannelMember, Entry<ResourcePath, Set<ChannelMember>>> resourcePaths = ch.fillOutsideResourcePaths(out, getPushAccessor(platformSpec)); for (Expression pathParam: out.getResource().getPathParams()) { if (pathParam instanceof Variable) { String selfParamName = ((Variable) pathParam).getName(); Expression arg = null; for (Selector selector: ch.getAllSelectors()) { if (selector.getExpression() instanceof Variable) { Variable selVar = (Variable) selector.getExpression(); if (selVar.getName().equals(selfParamName)) { arg = selVar; break; } } } if (arg == null) { ResourcePath filledPath = resourcePaths.get(out).getKey(); arg = filledPath.getPathParams().get(i - 1); } Term condition = new Term(DataConstraintModel.eq, new Expression[] { new Parameter("self" + (i > 1 ? i : ""), DataConstraintModel.typeString), arg}); if (conditions == null) { conditions = condition; } else { conditions = new Term(DataConstraintModel.and, new Expression[] { conditions, condition}); } } i++; } update.addFirstStatement(langSpec.getIfStatement(conditions, updateStatement)); } } } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage | UnificationFailed | ValueUndefined e1) { e1.printStackTrace(); } } // Declare the field to cache the state of the source resource in the type of the destination resource. if (inDegree > 1 || (ch.getInputChannelMembers().size() == 1 && ch.getInputChannelMembers().iterator().next().getStateTransition().isRightPartial())) { // If incoming edges are multiple, or the current state of an input member is needed. if (langSpec.declareField()) { // Declare the cache field. String cacheFieldName = langSpec.toVariableName(getComponentName(srcResPath.getResourceHierarchy(), langSpec)); FieldDeclaration cacheField = langSpec.newFieldDeclaration( srcResPath.getResourceStateType(), cacheFieldName, langSpec.getFieldInitializer(srcResPath.getResourceStateType(), srcResPath.getResourceHierarchy().getInitialValue())); if (component != null) { component.addField(cacheField); } else if (parentComponent != null){ parentComponent.addField(cacheField); } } // Update the cache field. String cacheStatement = langSpec.getFieldAccessor(langSpec.toVariableName(srcResName)) + langSpec.getAssignment() + langSpec.toVariableName(srcResName) + langSpec.getStatementDelimiter(); if (update.getBody() == null || !update.getBody().getStatements().contains(cacheStatement)) { update.addStatement(cacheStatement); } } // Update and initialize a field to refer to an outside input resource for PULL transfer. if (platformSpec.isMonolithic()) { // For a monolithic application. Set<ChannelMember> outsideInputMembers = new HashSet<>(); for (ChannelMember cm: ch.getInputChannelMembers()) { if (cm.isOutside()) { outsideInputMembers.add(cm); } } if (outsideInputMembers.size() > 0) { Map<ChannelMember, Entry<ResourcePath, Set<ChannelMember>>> resourcePaths = null; for (ChannelMember out1: ch.getOutputChannelMembers()) { if (resourceNode.getInSideResources().contains(out1.getResource())) { try { resourcePaths = ch.fillOutsideResourcePaths(out1, getPullAccessor(platformSpec)); } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage | UnificationFailed | ValueUndefined e) { e.printStackTrace(); } break; } } if (resourcePaths != null && resourcePaths.size() > 0) { for (ChannelMember outsideMember: outsideInputMembers) { for (ChannelMember dependingMember: resourcePaths.get(outsideMember).getValue()) { if (dependingMember.getResource().equals(srcResPath)) { // An outside input resource path depends on srcRes. ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey(); String outsideResName = langSpec.toVariableName(getComponentName(outsidePath.getResourceHierarchy(), langSpec)); Expression outsideExp = getPullAccessor(platformSpec).getDirectStateAccessorFor(outsidePath, null); if (generatesComponent(outsidePath.getResourceHierarchy())) { outsideExp = ((Term) outsideExp).getChild(0); } Expression nextExp = dependingMember.getStateTransition().getNextStateExpression(); if (nextExp != null && outsideExp instanceof Term) { if (nextExp instanceof Variable) { outsideExp = ((Term) outsideExp).substitute((Variable) nextExp, new Field(langSpec.toVariableName(getComponentName(dependingMember.getResource().getResourceHierarchy(), langSpec)))); } else { // ToDo. } } String[] sideEffects = new String[] {""}; String outsideAccessor = outsideExp.toImplementation(sideEffects); String updateReference = langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter(); update.addStatement(updateReference); // Update the reference field. // Update constructor. if (component != null) { MethodDeclaration constructor = getConstructor(component); constructor.addStatement(updateReference); // Initialize the reference field. } else if (parentComponent != null){ constructorStatements.add(updateReference); } } } } } } } // Add an invocation to another update method (for a chain of update method invocations). boolean hasUpdateMethodinvoked = false; for (Edge resToCh2: resourceNode.getOutEdges()) { DataFlowEdge dOut = (DataFlowEdge) resToCh2; ChannelNode directDstChNode = (ChannelNode) resToCh2.getDestination(); DataTransferChannel directDstCh = directDstChNode.getChannel(); // Check if the input resource is outside of the channel scope. boolean outsideInputResource2 = false; ChannelMember in = null; Set<ChannelMember> outsideInputMembers2 = new HashSet<>(); for (ChannelMember cm: directDstCh.getInputChannelMembers()) { if (cm.getResource().equals(resourceNode.getOutSideResource(directDstCh))) { if (cm.isOutside()) { outsideInputResource2 = true; // Regarded as pull transfer. } in = cm; } if (cm.isOutside()) { outsideInputMembers2.add(cm); } } // Should take into account the channel hierarchy. Set<ChannelNode> ancestorDstChannels = directDstChNode.getAncestors(); Set<ChannelNode> descendantDstChannels = directDstChNode.getDescendants(); Set<Edge> outEdges = new HashSet<>(); outEdges.addAll(directDstChNode.getOutEdges()); for (ChannelNode ancestorDst: ancestorDstChannels) { outEdges.addAll(ancestorDst.getOutEdges()); } for (ChannelNode descendantDst: descendantDstChannels) { outEdges.addAll(descendantDst.getOutEdges()); } for (Edge chToRes2: outEdges) { // For each data transfer to dstNode:ResourceNode. ResourceNode dstNode = ((ResourceNode) chToRes2.getDestination()); ChannelNode chNode2 = (ChannelNode) chToRes2.getSource(); DataTransferChannel ch2 = chNode2.getChannel(); // Check if the output resource is outside of the channel scope. boolean outsideOutputResource2 = false; ChannelMember out1 = null; for (ChannelMember cm: ch2.getOutputChannelMembers()) { if (dstNode.getInSideResources().contains(cm.getResource())) { out1 = cm; if (cm.isOutside()) { outsideOutputResource2 = true; break; } } } // Also take into account the channel hierarchy to determine push/pull transfer. if (descendantDstChannels.contains(chNode2)) { outsideOutputResource2 = true; // Regarded as (broadcasting) push transfer. } if (ancestorDstChannels.contains(chNode2)) { outsideInputResource2 = true; // Regarded as (collecting) pull transfer. } if ((((PushPullAttribute) dOut.getAttribute()).getSelectedOption() == PushPullValue.PUSH && !outsideInputResource2) || outsideOutputResource2) { // PUSH transfer boolean addForStatement = false; String forVarName = null; if (descendantDstChannels.contains(chNode2)) { // For hierarchical channels (broadcasting push transfer). if (ch2.getSelectors() != null && ch2.getSelectors().size() > 0) { Expression selExp = ch2.getSelectors().get(0).getExpression(); Type selType = null; if (selExp instanceof Variable) { selType = ((Variable) selExp).getType(); forVarName = ((Variable) selExp).getName(); ChannelMember insideChMem = null; for (ChannelMember cm :ch2.getInputChannelMembers()) { if (!cm.isOutside()) { insideChMem = cm; break; } } if (insideChMem == null) { for (ChannelMember cm :ch2.getReferenceChannelMembers()) { if (!cm.isOutside()) { insideChMem = cm; break; } } } if (insideChMem == null) { for (ChannelMember cm :ch2.getOutputChannelMembers()) { if (!cm.isOutside()) { insideChMem = cm; break; } } } ResourcePath insideResPath = insideChMem.getResource(); while (insideResPath.getParent() != null && (insideResPath.getLastParam() == null || !insideResPath.getLastParam().equals(selExp))) { insideResPath = insideResPath.getParent(); } insideResPath = insideResPath.getParent(); if (insideResPath != null) { String parent = null; if (platformSpec.isMonolithic() || insideResPath.getCommonPrefix(resourceNode.getOutSideResource(directDstCh)) != null || !platformSpec.isDifferentTreesAsDifferentServices()) { if (!platformSpec.isMonolithic() && generatesComponent(insideResPath.getResourceHierarchy())) { Expression getter = getPullAccessor(platformSpec).getDirectStateAccessorFor(insideResPath, resourceNode.getOutSideResource(directDstCh)); Term valueGetter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); valueGetter.addChild(getter); parent = valueGetter.toImplementation(new String[] {}); } else { parent = getPullAccessor(platformSpec).getDirectStateAccessorFor(insideResPath, resourceNode.getOutSideResource(directDstCh)).toImplementation(new String[] {}); } } else { // for REST API parent = langSpec.toVariableName(getComponentName(insideResPath.getResourceHierarchy(), langSpec)); } if (selType.equals(DataConstraintModel.typeInt)) { // make a for loop (for a list) for broadcasting. update.addStatement(langSpec.getForStatementForList(forVarName, parent)); addForStatement = true; } else if (selType.equals(DataConstraintModel.typeString)) { // make a for loop (for a map) for broadcasting. update.addStatement(langSpec.getForStatementForMap(forVarName, DataConstraintModel.typeString.getInterfaceTypeName(), parent)); addForStatement = true; } if (!platformSpec.isMonolithic() && insideResPath.getCommonPrefix(resourceNode.getOutSideResource(directDstCh)) == null && platformSpec.isDifferentTreesAsDifferentServices()) { // for REST API Type parentResType = insideResPath.getResourceStateType(); String parentResName = langSpec.toVariableName(getComponentName(insideResPath.getResourceHierarchy(), langSpec)); String parentResPath = insideResPath.toResourcePath().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); generatePullDataTransfer(update, parentResName, parentResPath, parentResType, true, platformSpec, langSpec); } } } else if (selExp instanceof Term) { // not supported. } } } // Get the value of reference member to call the update method. List<Map.Entry<Type, Map.Entry<String, String>>> refParams = new ArrayList<>(); Map<MethodDeclaration, Set<ResourcePath>> referredResources = new HashMap<>(); Set<ResourcePath> referredSet = referredResources.get(update); for (ChannelMember rc: ch2.getReferenceChannelMembers()) { ResourcePath ref = rc.getResource(); if (referredSet == null) { referredSet = new HashSet<>(); referredResources.put(update, referredSet); } if (!resourceNode.getInSideResources().contains(ref)) { String refVarName = langSpec.toVariableName(getComponentName(ref.getResourceHierarchy(), langSpec)); Type refResourceType = ref.getResourceStateType(); if (!referredSet.contains(ref)) { referredSet.add(ref); String[] sideEffects = new String[] {""}; if (!platformSpec.isMonolithic() && rc.isOutside()) { List<String> pathParams = new ArrayList<>(); for (Expression pathExp: ref.getPathParams()) { pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); } generatePullDataTransfer(update, refVarName, ref.getResourceHierarchy().toResourcePath(pathParams), refResourceType, false, platformSpec, langSpec); } else { ResourcePath srcRes = in.getResource(); if (!generatesComponent(srcRes.getResourceHierarchy())) { srcRes = srcRes.getParent(); } Expression refGetter = getPullAccessor(platformSpec).getDirectStateAccessorFor(ref, srcRes); String refExp = refGetter.toImplementation(sideEffects); String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); update.addStatement(sideEffects[0] + langSpec.getVariableDeclaration(refTypeName, refVarName) + langSpec.getAssignment() + refExp + langSpec.getStatementDelimiter()); } } refParams.add(new AbstractMap.SimpleEntry<>(ref.getResourceStateType(), new AbstractMap.SimpleEntry<>(refVarName, refVarName))); } } List<Map.Entry<Type, Map.Entry<String, String>>> pathParams = new ArrayList<>(); if (platformSpec.isMonolithic()) { // Update fields to refer to outside resources. ResourcePath filledOutsideResourcePath = null; try { Map<ChannelMember, Entry<ResourcePath, Set<ChannelMember>>> resourcePaths = ch2.fillOutsideResourcePaths(out1, getPullAccessor(platformSpec)); if (resourcePaths != null && resourcePaths.size() > 0) { for (ChannelMember outsideMember: resourcePaths.keySet()) { ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey(); if (out1.equals(outsideMember)) { filledOutsideResourcePath = outsidePath; } if (!generatesComponent(outsidePath.getResourceHierarchy())) { outsidePath = outsidePath.getParent(); } String outsideResName = langSpec.toVariableName(getComponentName(outsidePath.getResourceHierarchy(), langSpec)); Expression outsideExp = getPullAccessor(platformSpec).getDirectStateAccessorFor(outsidePath, null); if (generatesComponent(outsidePath.getResourceHierarchy())) { outsideExp = ((Term) outsideExp).getChild(0); } if (outsideExp instanceof Field) { outsideExp = new Variable(((Field) outsideExp).getSymbol().getName(), ((Field) outsideExp).getType()); } else if (outsideExp instanceof Term) { for (Entry<Position, Field> fieldEnt: ((Term) outsideExp).getSubTerms(Field.class).entrySet()) { Position pos = fieldEnt.getKey(); Field field = fieldEnt.getValue(); Variable var = new Variable(field.getSymbol().getName(), field.getType()); ((Term) outsideExp).replaceSubTerm(pos, var); } } String[] sideEffects = new String[] {""}; String outsideAccessor = outsideExp.toImplementation(sideEffects); update.addStatement(langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter()); // change the reference field. } } } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage | UnificationFailed | ValueUndefined e) { e.printStackTrace(); } // Values of path parameters to call the update method. if (filledOutsideResourcePath == null) { filledOutsideResourcePath = out1.getResource(); } for (Expression pathParam: filledOutsideResourcePath.getPathParams()) { if (pathParam instanceof Variable) { Variable pathVar = (Variable) pathParam; pathParams.add(new AbstractMap.SimpleEntry<>(pathVar.getType(), new AbstractMap.SimpleEntry<>(pathVar.getName(), pathVar.getName()))); } else if (pathParam instanceof Constant) { Constant pathVar = (Constant) pathParam; pathParams.add(new AbstractMap.SimpleEntry<>(pathVar.getType(), new AbstractMap.SimpleEntry<>(pathVar.getSymbol().getName(), pathVar.getSymbol().getName()))); } } } else { // Values of path parameters to call the update method. for (Expression pathParam: out1.getResource().getPathParams()) { if (pathParam instanceof Variable) { Variable pathVar = (Variable) pathParam; pathParams.add(new AbstractMap.SimpleEntry<>(pathVar.getType(), new AbstractMap.SimpleEntry<>(pathVar.getName(), pathVar.getName()))); } } } // Values of channel parameters to call the update method. List<Map.Entry<Type, Map.Entry<String, String>>> params = new ArrayList<>(); for (Selector selector: ch2.getAllSelectors()) { if (selector.getExpression() instanceof Variable) { Variable selVar = (Variable) selector.getExpression(); params.add(new AbstractMap.SimpleEntry<>(selVar.getType(), new AbstractMap.SimpleEntry<>(selVar.getName(), selVar.getName()))); } } // Value of the source side (input side) resource to call the update method. ResourceHierarchy srcRes2 = resourceNode.getResourceHierarchy(); if (generatesComponent(srcRes2)) { params.add(new AbstractMap.SimpleEntry<>(srcRes2.getResourceStateType(), new AbstractMap.SimpleEntry<>(langSpec.toVariableName(srcRes2.getResourceName()), langSpec.getFieldAccessor(fieldOfResourceState)))); } else { params.add(new AbstractMap.SimpleEntry<>(srcRes2.getResourceStateType(), new AbstractMap.SimpleEntry<>(langSpec.toVariableName(srcRes2.getResourceName()), langSpec.getFieldAccessor(langSpec.toVariableName(srcRes2.getResourceName()))))); srcRes2 = srcRes2.getParent(); } params.addAll(refParams); // Call the update method. String updateMethodName = null; ResourceHierarchy dstRes = dstNode.getResourceHierarchy(); if (!generatesComponent(dstRes)) { updateMethodName = updateMethodPrefix + getComponentName(dstRes, langSpec) + from + resComponentName; dstRes = dstRes.getParent(); } else { updateMethodName = updateMethodPrefix + from + resComponentName; } String dstCompName = langSpec.toVariableName(getComponentName(dstRes, langSpec)); if (outsideOutputResource2 || (!platformSpec.isMonolithic() && in.getResource().getCommonPrefix(out1.getResource()) == null && platformSpec.isDifferentTreesAsDifferentServices())) { // Inter-servces if (!platformSpec.isMonolithic()) { // REST API RestApiSpecific restApiSpec = (RestApiSpecific) platformSpec; String httpMethod = null; if (out1.getStateTransition().isRightUnary()) { httpMethod = "put"; } else { httpMethod = "post"; } String[] sideEffects = new String[] {""}; List<String> pathParamsUrl = new ArrayList<>(); for (Expression pathExp: out1.getResource().getPathParams()) { pathParamsUrl.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); } String resName = langSpec.toVariableName(resComponentName); if (inDegree <= 1) { resName = null; } Map<ChannelMember, Entry<ResourcePath, Set<ChannelMember>>> filledPaths = null; try { filledPaths = ch2.fillOutsideResourcePaths(out1, getPushAccessor(platformSpec)); } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage | UnificationFailed | ValueUndefined e) { e.printStackTrace(); } String dstPath = null; if (filledPaths != null && filledPaths.get(out1) != null) { ResourcePath filledDstPath = filledPaths.get(out1).getKey(); dstPath = filledDstPath.toResourcePath().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); } else { dstPath = dstRes.toResourcePath(pathParamsUrl); } // Call the update method. if (!hasUpdateMethodinvoked) { // The first call to an update method in this method update.addStatement(restApiSpec.getHttpMethodParamsConstructionStatement(srcRes2.getResourceName(), params, true)); update.addStatement(langSpec.getVariableDeclaration(DataConstraintModel.typeString.getInterfaceTypeName(), "result") + langSpec.getAssignment() + restApiSpec.getHttpMethodCallStatement(restApiSpec.getBaseURL(), dstPath, resName, httpMethod)); hasUpdateMethodinvoked = true; } else { // After the second time of call to update methods in this method update.addStatement(restApiSpec.getHttpMethodParamsConstructionStatement(srcRes2.getResourceName(), params, false)); update.addStatement("result" + langSpec.getAssignment() + restApiSpec.getHttpMethodCallStatement(restApiSpec.getBaseURL(), dstPath, resName, httpMethod)); } } else { // Use the reference field to refer to outside destination resource. List<String> args = new ArrayList<>(); for (Map.Entry<Type, Map.Entry<String, String>> paramEnt: pathParams) { args.add(paramEnt.getValue().getValue()); } for (Map.Entry<Type, Map.Entry<String, String>> paramEnt: params) { args.add(paramEnt.getValue().getValue()); } update.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, args) + langSpec.getStatementDelimiter()); // this.dst.updateDstFromSrc(value, refParams); } } else { // Intra-service // The destination resource is not outside. List<String> args = new ArrayList<>(); for (Map.Entry<Type, Map.Entry<String, String>> paramEnt: pathParams) { args.add(paramEnt.getValue().getValue()); } for (Map.Entry<Type, Map.Entry<String, String>> paramEnt: params) { args.add(paramEnt.getValue().getValue()); } if (srcRes2 != dstRes) { update.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, args) + langSpec.getStatementDelimiter()); // this.dst.updateDstFromSrc(value, refParams); } else { update.addStatement(langSpec.getMethodInvocation(updateMethodName, args) + langSpec.getStatementDelimiter()); // this.updateDstFromSrc(value, refParams); } } if (addForStatement) { // Close the for loop update.addStatement(langSpec.getEndForStatement(forVarName)); } } } if (outsideInputMembers2.size() > 0) { if (!generatesComponent(resourceNode.getResourceHierarchy())) { // srcRes2 does not have a component. ResourcePath srcRes2 = resourceNode.getOutSideResource(directDstCh); for (Edge chToRes2: outEdges) { ChannelNode chNode2 = (ChannelNode) chToRes2.getSource(); DataTransferChannel ch2 = chNode2.getChannel(); for (ChannelMember out2: ch2.getOutputChannelMembers()) { if (!generatesComponent(out2.getResource().getResourceHierarchy())) { // Also dstRes2 does not have a component. ResourcePath dstRes2 = out2.getResource(); if (srcRes2.getParent().equals(dstRes2.getParent())) { Map<ChannelMember, Entry<ResourcePath, Set<ChannelMember>>> resourcePaths = null; try { resourcePaths = ch2.fillOutsideResourcePaths(out2, getPullAccessor(platformSpec)); if (resourcePaths != null && resourcePaths.size() > 0) { for (ChannelMember outsideMember: outsideInputMembers2) { for (ChannelMember dependedMember: resourcePaths.get(outsideMember).getValue()) { if (dependedMember.getResource().equals(srcRes2)) { // An outside input resource path depends on srcRes. ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey(); if (!generatesComponent(outsidePath.getResourceHierarchy())) { outsidePath = outsidePath.getParent(); } String outsideResName = langSpec.toVariableName(getComponentName(outsidePath.getResourceHierarchy(), langSpec)); Expression outsideExp = getPullAccessor(platformSpec).getDirectStateAccessorFor(outsidePath, null); if (generatesComponent(outsidePath.getResourceHierarchy())) { outsideExp = ((Term) outsideExp).getChild(0); } String[] sideEffects = new String[] {""}; String outsideAccessor = outsideExp.toImplementation(sideEffects); String updateReference = langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter(); update.addStatement(updateReference); // Update the reference field. // Update constructor. if (component != null) { MethodDeclaration constructor = getConstructor(component); constructor.addStatement(updateReference); // Initialize the reference field. } else if (parentComponent != null) { constructorStatements.add(updateReference); } } } } } } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage | UnificationFailed | ValueUndefined e) { e.printStackTrace(); } } } } } } } } } } } return new AbstractMap.SimpleEntry<>(constructorStatements, updateStatements); } private Map.Entry<List<String>, Map<MethodDeclaration, Map.Entry<Expression, Map.Entry<ResourceHierarchy, ResourceHierarchy>>>> declareInputMethodsInThisAndMainComponents(ResourceNode resourceNode, TypeDeclaration component, TypeDeclaration parentComponent, TypeDeclaration mainComponent, TypeDeclaration rootComponent, DataTransferModel model, Map<Channel, ChannelMember> priorMemberForInputChannel, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { // Declare input methods. String resName = resourceNode.getResourceName(); String resComponentName = langSpec.toComponentName(resName); List<String> constructorStatements = new ArrayList<>(); Map<MethodDeclaration, Map.Entry<Expression, Map.Entry<ResourceHierarchy, ResourceHierarchy>>> inputStatements = new HashMap<>(); 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 out: ((DataTransferChannel) ch).getOutputChannelMembers()) { if (resourceNode.getInSideResources().contains(out.getResource())) { Expression message = out.getStateTransition().getMessageExpression(); MethodDeclaration input = null; MethodDeclaration mainInputAccessor = null; MethodDeclaration rootInputAccessor = null; if (message instanceof Term) { // Declare an input method in this component. ArrayList<VariableDeclaration> resInputParams = new ArrayList<>(); ArrayList<VariableDeclaration> mainInputParams = new ArrayList<>(); ArrayList<VariableDeclaration> rootInputParams = new ArrayList<>(); String resourcePath = null; if (!platformSpec.isMonolithic()) { resourcePath = getInputMethodResourcePathAndPathParams(out.getResource(), rootInputParams, platformSpec, langSpec); // 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. int v = 1; for (Selector selector: ch.getSelectors()) { if (selector.getExpression() instanceof Variable) { Variable selVar = (Variable) selector.getExpression(); resInputParams.add(langSpec.newVariableDeclaration(selVar.getType(), selVar.getName())); mainInputParams.add(langSpec.newVariableDeclaration(selVar.getType(), selVar.getName())); } else if (selector.getExpression() instanceof Term) { Term var = (Term) selector.getExpression(); resInputParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v)); mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v)); } v++; } if (ch.getParent() != null) { for (Selector selector: ch.getParent().getAllSelectors()) { if (selector.getExpression() instanceof Variable) { Variable selVar = (Variable) selector.getExpression(); mainInputParams.add(langSpec.newVariableDeclaration(selVar.getType(), selVar.getName())); } else if (selector.getExpression() instanceof Term) { Term var = (Term) selector.getExpression(); mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v)); } v++; } } // 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. resInputParams.add(langSpec.newVariableDeclaration(var.getType(), refVarName)); } else { // var has not come from a reference resource. resInputParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); boolean bExists = false; for (VariableDeclaration mainParam: mainInputParams) { if (mainParam.getName().equals(var.getName()) ) { bExists = true; break; } } if (!bExists) { mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); } if (!platformSpec.isMonolithic() && !resourcePath.contains("{" + var.getName()+ "}")) { VariableDeclaration param = langSpec.newVariableDeclaration(var.getType(), var.getName()); ((RestApiSpecific) platformSpec).addFormParamAnnotation(param, var.getName()); rootInputParams.add(param); } } } if (platformSpec.isMonolithic() || (resourceNode.getResourceHierarchy().getParent() != null && resourceNode.getResourceHierarchy().getParent().getParent() != null)) { String inputMethodName = ((Term) message).getSymbol().getImplName(); if (((DataTransferChannel) ch).getOutputChannelMembers().size() > 1) { inputMethodName += _for + getComponentName(out.getResource().getResourceHierarchy(), langSpec); } if (component != null) { // A component is created for this resource. for (MethodDeclaration method: component.getMethods()) { if (method.getName().equals(inputMethodName)) { input = method; break; } } if (input == null) { input = langSpec.newMethodDeclaration(inputMethodName, false, null, resInputParams); component.addMethod(input); } } else if (parentComponent != null) { // No component is created for this resource. for (MethodDeclaration method: parentComponent.getMethods()) { if (method.getName().equals(inputMethodName)) { input = method; break; } } if (input == null) { input = langSpec.newMethodDeclaration(inputMethodName, false, null, resInputParams); parentComponent.addMethod(input); } } } // Declare the accessor in the main component to call the input method. (for monolithic application) if (platformSpec.hasMain()) { String messageSymbol = ((Term) message).getSymbol().getImplName(); mainInputAccessor = getMethod(mainComponent, messageSymbol); if (mainInputAccessor == null) { mainInputAccessor = langSpec.newMethodDeclaration(messageSymbol, false, null, mainInputParams); mainComponent.addMethod(mainInputAccessor); } else { // Add type to a parameter without type. if (mainInputAccessor.getParameters() != null) { for (VariableDeclaration param: mainInputAccessor.getParameters()) { if (param.getType() == null) { for (VariableDeclaration p: mainInputParams) { if (param.getName().equals(p.getName()) && p.getType() != null) { param.setType(p.getType()); } } } } } } } // For the root resource. (for REST API) if (!platformSpec.isMonolithic()) { if (priorMemberForInputChannel.get(ch) == null || out == priorMemberForInputChannel.get(ch)) { // If out is the receiver of the input event. priorMemberForInputChannel.put(ch, out); String messageSymbol = ((Term) message).getSymbol().getImplName(); rootInputAccessor = declareInputAccessorInTheRootResource(messageSymbol, rootInputParams, out, resourcePath, rootComponent, platformSpec, langSpec); if (input == null) { input = rootInputAccessor; rootInputAccessor = null; } } } } else if (message instanceof Variable) { // Declare an input method in this component. ArrayList<VariableDeclaration> resInputParams = new ArrayList<>(); ArrayList<VariableDeclaration> mainInputParams = new ArrayList<>(); int v = 1; if (out.getResource().getLastParam() != null) { Expression param = out.getResource().getLastParam(); if (param instanceof Variable) { Variable var = (Variable) param; resInputParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); } else if (param instanceof Term) { Term var = (Term) param; resInputParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v)); mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v)); } v++; } if (out.getResource().getParent() != null) { for (Expression param: out.getResource().getParent().getPathParams()) { if (param instanceof Variable) { Variable var = (Variable) param; mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); } else if (param instanceof Term) { Term var = (Term) param; mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v)); } v++; } } if (platformSpec.isMonolithic() || (resourceNode.getResourceHierarchy().getParent() != null && resourceNode.getResourceHierarchy().getParent().getParent() != null)) { String inputMethodName = ((Variable) message).getName(); if (((DataTransferChannel) ch).getOutputChannelMembers().size() > 1) { inputMethodName += _for + getComponentName(out.getResource().getResourceHierarchy(), langSpec); } if (component != null) { // A component is created for this resource. for (MethodDeclaration method: component.getMethods()) { if (method.getName().equals(inputMethodName)) { input = method; break; } } if (input == null) { if (resInputParams.size() == 0) { input = langSpec.newMethodDeclaration(inputMethodName, null); } else { input = langSpec.newMethodDeclaration(inputMethodName, false, null, resInputParams); } component.addMethod(input); } } else if (parentComponent != null) { // No component is created for this resource. for (MethodDeclaration method: parentComponent.getMethods()) { if (method.getName().equals(inputMethodName)) { input = method; break; } } if (input == null) { if (resInputParams.size() == 0) { input = langSpec.newMethodDeclaration(inputMethodName, null); } else { input = langSpec.newMethodDeclaration(inputMethodName, false, null, resInputParams); } parentComponent.addMethod(input); } } } // Declare the accessor in the main component to call the input method. (for monolithic application) if (platformSpec.hasMain()) { String messageSymbol = ((Variable) message).getName(); mainInputAccessor = getMethod(mainComponent, messageSymbol, mainInputParams); if (mainInputAccessor == null) { if (mainInputParams.size() == 0) { mainInputAccessor = langSpec.newMethodDeclaration(messageSymbol, null); } else { mainInputAccessor = langSpec.newMethodDeclaration(messageSymbol, false, null, mainInputParams); } mainComponent.addMethod(mainInputAccessor); } } // For the root resource. (for REST API) if (!platformSpec.isMonolithic()) { if (priorMemberForInputChannel.get(ch) == null || out == priorMemberForInputChannel.get(ch)) { // If out is the receiver of the input event. priorMemberForInputChannel.put(ch, out); ArrayList<VariableDeclaration> rootInputParams = new ArrayList<>(); String resourcePath = getGetterResourcePathAndPathParams(out.getResource(), rootInputParams, platformSpec, langSpec); if (resourcePath.indexOf('/') > 0) { resourcePath = resourcePath.substring(resourcePath.indexOf('/')); } else { resourcePath = ""; } String messageSymbol = ((Variable) message).getName(); rootInputAccessor = declareInputAccessorInTheRootResource(messageSymbol, rootInputParams, out, resourcePath, rootComponent, platformSpec, langSpec); if (input == null) { input = rootInputAccessor; rootInputAccessor = null; } } } } // Add an invocation to the accessor method. if (mainInputAccessor != null) { if (platformSpec.hasMain()) { // For an application with a main component, the reference resource is accessed from the main component. for (ChannelMember rc: ((DataTransferChannel) ch).getReferenceChannelMembers()) { // For each reference channel member, get the current state of the reference resource by pull data transfer. ResourcePath ref = rc.getResource(); if (!out.getResource().equals(ref)) { String refVarName = ref.getLeafResourceName(); Expression refGetter = getPullAccessor(platformSpec).getDirectStateAccessorFor(ref, null); String[] sideEffects = new String[] {""}; String refExp = refGetter.toImplementation(sideEffects); String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); mainInputAccessor.addFirstStatement(sideEffects[0] + langSpec.getVariableDeclaration(refTypeName, refVarName) + langSpec.getAssignment() + refExp + langSpec.getStatementDelimiter()); } } } Expression resExp = getPullAccessor(platformSpec).getDirectStateAccessorFor(out.getResource(), null); List<String> args = new ArrayList<>(); if (resExp instanceof Term) { // to access the parent if (((Term) resExp).getChildren().size() > 1 && ((Term) resExp).getChild(1) instanceof Variable) { args.add(((Variable)((Term) resExp).getChild(1)).getName()); } resExp = ((Term) resExp).getChild(0); } String resourceAccess = resExp.toImplementation(new String[] {""}); // Values of channel parameters. for (Selector selector: ch.getAllSelectors()) { if (selector.getExpression() instanceof Variable) { Variable selVar = (Variable) selector.getExpression(); if (!args.contains(selVar.getName())) { args.add(selVar.getName()); } } } // Values of message parameters. if (message instanceof Term) { for (Map.Entry<Position, Variable> varEnt: message.getVariables().entrySet()) { String refVarName = null; for (ChannelMember rc: ((DataTransferChannel) ch).getReferenceChannelMembers()) { Expression varExp = rc.getStateTransition().getMessageExpression().getSubTerm(varEnt.getKey()); if (varExp != null && rc.getStateTransition().getCurStateExpression().contains(varExp)) { refVarName = rc.getResource().getLeafResourceName(); break; } } if (refVarName != null) { if (!args.contains(refVarName)) { args.add(refVarName); } } else { if (!args.contains(varEnt.getValue().getName())) args.add(varEnt.getValue().getName()); } } } mainInputAccessor.addStatement(langSpec.getMethodInvocation(resourceAccess, input.getName(), args) + langSpec.getStatementDelimiter()); } if (input != null) { // Add a statement to update the state field to the input method. try { if (!platformSpec.hasMain()) { // For an application with no main component, the reference resource is accessed from each resource. for (ChannelMember rc: ((DataTransferChannel) ch).getReferenceChannelMembers()) { // For each reference channel member, get the current state of the reference side resource by pull data transfer. ResourcePath ref = rc.getResource(); if (!out.getResource().equals(ref)) { String refResourceName = ref.getLeafResourceName(); Type refResourceType = ref.getResourceStateType(); String[] sideEffects = new String[] {""}; if (!platformSpec.isMonolithic() && rc.isOutside()) { List<String> pathParams = new ArrayList<>(); for (Expression pathExp: ref.getPathParams()) { pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); } generatePullDataTransfer(input, refResourceName, ref.getResourceHierarchy().toResourcePath(pathParams), refResourceType, true, platformSpec, langSpec); } else { ResourcePath dstRes = out.getResource(); if (!generatesComponent(dstRes.getResourceHierarchy())) { dstRes = dstRes.getParent(); } Expression refGetter = getPullAccessor(platformSpec).getDirectStateAccessorFor(ref, dstRes); String refExp = refGetter.toImplementation(sideEffects); String refTypeName = refResourceType.getInterfaceTypeName(); input.addFirstStatement(sideEffects[0] + langSpec.getVariableDeclaration(refTypeName, refResourceName) + langSpec.getAssignment() + refExp + langSpec.getStatementDelimiter()); } } } } Expression updateExp = ((DataTransferChannel) ch).deriveUpdateExpressionOf(out, getRefAccessor(platformSpec)).getKey(); // Replace Json constructor with a constructor of a descendant resource. ResourceHierarchy outRes = out.getResource().getResourceHierarchy(); if (outRes.getChildren().size() == 1 && outRes.getChildren().iterator().next().getNumParameters() > 0) { ResourceHierarchy descendantRes = outRes; Set<ResourceHierarchy> children = descendantRes.getChildren(); do { descendantRes = children.iterator().next(); if (generatesComponent(descendantRes)) { inputStatements.put(input, new AbstractMap.SimpleEntry<>(updateExp, new AbstractMap.SimpleEntry<>(outRes, descendantRes))); updateExp = null; break; } children = descendantRes.getChildren(); } while (children != null && children.size() == 1); } // Add statements to the input method. if (updateExp != null) { String[] sideEffects = new String[] {""}; String newState = updateExp.toImplementation(sideEffects); ResourceHierarchy resource = resourceNode.getResourceHierarchy(); if (generatesComponent(resource)) { String updateStatement; if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { updateStatement = sideEffects[0]; if (updateStatement.endsWith("\n")) { updateStatement = updateStatement.substring(0, updateStatement.length() - 1); } } else { updateStatement = sideEffects[0] + langSpec.getFieldAccessor(fieldOfResourceState) + langSpec.getAssignment() + newState + langSpec.getStatementDelimiter(); } input.addFirstStatement(updateStatement); } else { String updateStatement = ""; if (sideEffects[0] != null) { updateStatement = sideEffects[0]; String resourceName = langSpec.toVariableName(getComponentName(resource, langSpec)); updateStatement = updateStatement.replace(langSpec.getFieldAccessor(fieldOfResourceState), langSpec.getFieldAccessor(resourceName)); if (updateStatement.endsWith("\n")) { updateStatement = updateStatement.substring(0, updateStatement.length() - 1); } } if (DataConstraintModel.typeList.isAncestorOf(resourceNode.getParent().getResourceStateType())) { Term selector = new Term(DataConstraintModel.set); selector.addChild(new Constant(langSpec.getFieldAccessor(fieldOfResourceState))); selector.addChild(new Variable(input.getParameters().get(input.getParameters().size() - 2).getName())); selector.addChild(new Constant(newState)); String[] sideEffects2 = new String[] {""}; String newList = selector.toImplementation(sideEffects2); updateStatement += sideEffects2[0]; } else if (DataConstraintModel.typeMap.isAncestorOf(resourceNode.getParent().getResourceStateType())) { Term selector = new Term(DataConstraintModel.insert); selector.addChild(new Constant(langSpec.getFieldAccessor(fieldOfResourceState))); selector.addChild(new Variable(input.getParameters().get(input.getParameters().size() - 2).getName())); selector.addChild(new Constant(newState)); String[] sideEffects2 = new String[] {""}; String newMap = selector.toImplementation(sideEffects2); updateStatement += sideEffects2[0]; } else if (!(updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect())) { String resourceName = langSpec.toVariableName(getComponentName(resource, langSpec)); updateStatement += langSpec.getFieldAccessor(resourceName) + langSpec.getAssignment() + newState + langSpec.getStatementDelimiter(); } if (updateStatement != null) { input.addFirstStatement(updateStatement); } } } if (rootInputAccessor != null) { // In the root resource // The expression of the receiver (resource) of the input method. ResourcePath outResPath = new ResourcePath(out.getResource()); for (int i = 0; i < outResPath.getPathParams().size(); i++) { Parameter pathParam = new Parameter(rootInputAccessor.getParameters().get(i).getName()); outResPath.replacePathParam(i, pathParam, null); } Expression resExp = getPullAccessor(platformSpec).getDirectStateAccessorFor(outResPath, outResPath.getRoot()); List<String> args = new ArrayList<>(); if (resExp instanceof Term) { // to access the parent if (((Term) resExp).getChildren().size() > 1 && ((Term) resExp).getChild(1) instanceof Variable) { args.add(((Variable)((Term) resExp).getChild(1)).getName()); } resExp = ((Term) resExp).getChild(0); } String resourceAccess = resExp.toImplementation(new String[] {""}); // Values of channel parameters. for (Selector selector: ch.getAllSelectors()) { if (selector.getExpression() instanceof Variable) { Variable selVar = (Variable) selector.getExpression(); args.add(selVar.getName()); } } // Values of message parameters. if (message instanceof Term) { for (Variable mesVar: message.getVariables().values()) { args.add(mesVar.getName()); } } rootInputAccessor.addStatement(langSpec.getMethodInvocation(resourceAccess, input.getName(), args) + langSpec.getStatementDelimiter()); if (input != null && input.getThrows() != null && ((RestApiSpecific) platformSpec).hasJsonException(input)) { ((RestApiSpecific) platformSpec).addJsonException(rootInputAccessor); } } } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage | UnificationFailed | ValueUndefined e) { e.printStackTrace(); } // Add an invocation to an update method (for a chain of update method invocations). boolean hasUpdateMethodinvoked = false; for (Edge resToCh: resourceNode.getOutEdges()) { DataFlowEdge dOut = (DataFlowEdge) resToCh; ChannelNode directDstChNode = (ChannelNode) resToCh.getDestination(); DataTransferChannel directDstCh = directDstChNode.getChannel(); // Check if the input resource is outside of the channel scope. boolean outsideInputResource2 = false; ChannelMember in = null; Set<ChannelMember> outsideInputMembers2 = new HashSet<>(); for (ChannelMember cm: directDstCh.getInputChannelMembers()) { if (resourceNode.getOutSideResources().contains(cm.getResource())) { if (cm.isOutside()) { outsideInputResource2 = true; // Regarded as pull transfer. } in = cm; } if (cm.isOutside()) { outsideInputMembers2.add(cm); } } // Should take into account the channel hierarchy. Set<ChannelNode> ancestorDstChannels = directDstChNode.getAncestors(); Set<ChannelNode> descendantDstChannels = directDstChNode.getDescendants(); Set<Edge> outEdges = new HashSet<>(); outEdges.addAll(directDstChNode.getOutEdges()); for (ChannelNode ancestorDst: ancestorDstChannels) { outEdges.addAll(ancestorDst.getOutEdges()); } for (ChannelNode descendantDst: descendantDstChannels) { outEdges.addAll(descendantDst.getOutEdges()); } for (Edge chToRes: outEdges) { // For each data transfer to dstNode:ResourceNode. ResourceNode dstNode = ((ResourceNode) chToRes.getDestination()); ChannelNode chNode2 = (ChannelNode) chToRes.getSource(); DataTransferChannel ch2 = chNode2.getChannel(); // Check if the output resource is outside of the channel scope. boolean outsideOutputResource2 = false; ChannelMember out2 = null; for (ChannelMember cm: ch2.getOutputChannelMembers()) { if (dstNode.getInSideResources().contains(cm.getResource())) { out2 = cm; if (cm.isOutside()) { outsideOutputResource2 = true; break; } } } // Also take into account the channel hierarchy to determine push/pull transfer. if (descendantDstChannels.contains(chNode2)) { outsideOutputResource2 = true; // Regarded as (broadcasting) push transfer. } if (ancestorDstChannels.contains(chNode2)) { outsideInputResource2 = true; // Regarded as (collecting) pull transfer. } if ((((PushPullAttribute) dOut.getAttribute()).getSelectedOption() == PushPullValue.PUSH && !outsideInputResource2) || outsideOutputResource2) { // PUSH transfer // Calculate in-degree (PUSH transfer) of the destination resource. Set<Edge> inEdges = new HashSet<>(); inEdges.addAll(directDstChNode.getInEdges()); for (ChannelNode ancestorSrc: ancestorDstChannels) { inEdges.addAll(ancestorSrc.getInEdges()); } for (ChannelNode descendantSrc: descendantDstChannels) { inEdges.addAll(descendantSrc.getInEdges()); } int inDegree = 0; for (Edge resToCh2: inEdges) { DataFlowEdge df =(DataFlowEdge) resToCh2; if (((PushPullAttribute) df.getAttribute()).getSelectedOption() == PushPullValue.PUSH) { inDegree++; } } boolean addForStatement = false; String forVarName = null; if (descendantDstChannels.contains(chNode2)) { // For hierarchical channels (broadcasting push transfer). if (ch2.getSelectors() != null && ch2.getSelectors().size() > 0) { Expression selExp = ch2.getSelectors().get(0).getExpression(); Type selType = null; if (selExp instanceof Variable) { selType = ((Variable) selExp).getType(); forVarName = ((Variable) selExp).getName(); ChannelMember insideChMem = null; for (ChannelMember cm :ch2.getInputChannelMembers()) { if (!cm.isOutside()) { insideChMem = cm; break; } } if (insideChMem == null) { for (ChannelMember cm :ch2.getReferenceChannelMembers()) { if (!cm.isOutside()) { insideChMem = cm; break; } } } if (insideChMem == null) { for (ChannelMember cm :ch2.getOutputChannelMembers()) { if (!cm.isOutside()) { insideChMem = cm; break; } } } ResourcePath insideResPath = insideChMem.getResource(); while (insideResPath.getParent() != null && (insideResPath.getLastParam() == null || !insideResPath.getLastParam().equals(selExp))) { insideResPath = insideResPath.getParent(); } insideResPath = insideResPath.getParent(); if (insideResPath != null) { String parent = null; if (platformSpec.isMonolithic() || insideResPath.getCommonPrefix(resourceNode.getOutSideResource(directDstCh)) != null || !platformSpec.isDifferentTreesAsDifferentServices()) { if (!platformSpec.isMonolithic() && generatesComponent(insideResPath.getResourceHierarchy())) { Expression getter = getPullAccessor(platformSpec).getDirectStateAccessorFor(insideResPath, resourceNode.getOutSideResource(directDstCh)); Term valueGetter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); valueGetter.addChild(getter); parent = valueGetter.toImplementation(new String[] {}); } else { parent = getPullAccessor(platformSpec).getDirectStateAccessorFor(insideResPath, resourceNode.getOutSideResource(directDstCh)).toImplementation(new String[] {}); } } else { // for REST API parent = langSpec.toVariableName(getComponentName(insideResPath.getResourceHierarchy(), langSpec)); } if (selType.equals(DataConstraintModel.typeInt)) { // make a for loop (for a list) for broadcasting. input.addStatement(langSpec.getForStatementForList(forVarName, parent)); addForStatement = true; } else if (selType.equals(DataConstraintModel.typeString)) { // make a for loop (for a map) for broadcasting. input.addStatement(langSpec.getForStatementForMap(forVarName, DataConstraintModel.typeString.getInterfaceTypeName(), parent)); addForStatement = true; } if (!platformSpec.isMonolithic() && insideResPath.getCommonPrefix(resourceNode.getOutSideResource(directDstCh)) == null && platformSpec.isDifferentTreesAsDifferentServices()) { // for REST API Type parentResType = insideResPath.getResourceStateType(); String parentResName = langSpec.toVariableName(getComponentName(insideResPath.getResourceHierarchy(), langSpec)); String parentResPath = insideResPath.toResourcePath().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); generatePullDataTransfer(input, parentResName, parentResPath, parentResType, true, platformSpec, langSpec); } } } else if (selExp instanceof Term) { // not supported. } } } // Get the value of reference member to call the update method. List<Map.Entry<Type, Map.Entry<String, String>>> refParams = new ArrayList<>(); Map<MethodDeclaration, Set<ResourcePath>> referredResources = new HashMap<>(); Set<ResourcePath> referredSet = referredResources.get(input); for (ChannelMember rc: ch2.getReferenceChannelMembers()) { ResourcePath ref = rc.getResource(); if (referredSet == null) { referredSet = new HashSet<>(); referredResources.put(input, referredSet); } if (!resourceNode.getOutSideResources().contains(ref)) { String refVarName = langSpec.toVariableName(getComponentName(ref.getResourceHierarchy(), langSpec)); if (!referredSet.contains(ref)) { referredSet.add(ref); ResourcePath srcRes = in.getResource(); if (!generatesComponent(srcRes.getResourceHierarchy())) { srcRes = srcRes.getParent(); } Expression refGetter = getPullAccessor(platformSpec).getDirectStateAccessorFor(ref, srcRes); String[] sideEffects = new String[] {""}; String refExp = refGetter.toImplementation(sideEffects); String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); input.addStatement(sideEffects[0] + langSpec.getVariableDeclaration(refTypeName, refVarName) + langSpec.getAssignment() + refExp + langSpec.getStatementDelimiter()); } refParams.add(new AbstractMap.SimpleEntry<>(ref.getResourceStateType(), new AbstractMap.SimpleEntry<>(refVarName, refVarName))); } } List<Map.Entry<Type, Map.Entry<String, String>>> pathParams = new ArrayList<>(); if (platformSpec.isMonolithic()) { // Update fields to refer to outside resources. ResourcePath filledOutsideResourcePath = null; try { Map<ChannelMember, Entry<ResourcePath, Set<ChannelMember>>> resourcePaths = ch2.fillOutsideResourcePaths(out2, getPullAccessor(platformSpec)); if (resourcePaths != null && resourcePaths.size() > 0) { for (ChannelMember outsideMember: resourcePaths.keySet()) { ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey(); if (out2.equals(outsideMember)) { filledOutsideResourcePath = outsidePath; } if (!generatesComponent(outsidePath.getResourceHierarchy())) { outsidePath = outsidePath.getParent(); } String outsideResName = langSpec.toVariableName(getComponentName(outsidePath.getResourceHierarchy(), langSpec)); Expression outsideExp = getPullAccessor(platformSpec).getDirectStateAccessorFor(outsidePath, null); if (generatesComponent(outsidePath.getResourceHierarchy())) { outsideExp = ((Term) outsideExp).getChild(0); } if (outsideExp instanceof Field) { outsideExp = new Variable(((Field) outsideExp).getSymbol().getName(), ((Field) outsideExp).getType()); } else if (outsideExp instanceof Term) { for (Entry<Position, Field> fieldEnt: ((Term) outsideExp).getSubTerms(Field.class).entrySet()) { Position pos = fieldEnt.getKey(); Field field = fieldEnt.getValue(); Variable var = new Variable(field.getSymbol().getName(), field.getType()); ((Term) outsideExp).replaceSubTerm(pos, var); } } String[] sideEffects = new String[] {""}; String outsideAccessor = outsideExp.toImplementation(sideEffects); input.addStatement(langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter()); // change the reference field. } } } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage | UnificationFailed | ValueUndefined e) { e.printStackTrace(); } // Values of path parameters to call the update method. if (filledOutsideResourcePath == null) { filledOutsideResourcePath = out2.getResource(); } for (Expression pathParam: filledOutsideResourcePath.getPathParams()) { if (pathParam instanceof Variable) { Variable pathVar = (Variable) pathParam; pathParams.add(new AbstractMap.SimpleEntry<>(pathVar.getType(), new AbstractMap.SimpleEntry<>(pathVar.getName(), pathVar.getName()))); } else if (pathParam instanceof Constant) { Constant pathVar = (Constant) pathParam; pathParams.add(new AbstractMap.SimpleEntry<>(pathVar.getType(), new AbstractMap.SimpleEntry<>(pathVar.getSymbol().getName(), pathVar.getSymbol().getName()))); } } } else { // Values of path parameters to call the update method. for (Expression pathParam: out2.getResource().getPathParams()) { if (pathParam instanceof Variable) { Variable pathVar = (Variable) pathParam; pathParams.add(new AbstractMap.SimpleEntry<>(pathVar.getType(), new AbstractMap.SimpleEntry<>(pathVar.getName(), pathVar.getName()))); } } } // Values of channel parameters to call the update method. List<Map.Entry<Type, Map.Entry<String, String>>> params = new ArrayList<>(); for (Selector selector: ch2.getAllSelectors()) { if (selector.getExpression() instanceof Variable) { Variable selVar = (Variable) selector.getExpression(); params.add(new AbstractMap.SimpleEntry<>(selVar.getType(), new AbstractMap.SimpleEntry<>(selVar.getName(), selVar.getName()))); } } // Value of the source side (input side) resource to call the update method. ResourceHierarchy srcRes = resourceNode.getResourceHierarchy(); if (generatesComponent(srcRes)) { params.add(new AbstractMap.SimpleEntry<>(srcRes.getResourceStateType(), new AbstractMap.SimpleEntry<>(langSpec.toVariableName(srcRes.getResourceName()), langSpec.getFieldAccessor(fieldOfResourceState)))); } else { params.add(new AbstractMap.SimpleEntry<>(srcRes.getResourceStateType(), new AbstractMap.SimpleEntry<>(langSpec.toVariableName(srcRes.getResourceName()), langSpec.getFieldAccessor(langSpec.toVariableName(srcRes.getResourceName()))))); srcRes = srcRes.getParent(); } params.addAll(refParams); // Call the update method. String updateMethodName = null; ResourceHierarchy dstRes = dstNode.getResourceHierarchy(); if (!generatesComponent(dstRes)) { updateMethodName = updateMethodPrefix + getComponentName(dstRes, langSpec) + from + resComponentName; dstRes = dstRes.getParent(); } else { updateMethodName = updateMethodPrefix + from + resComponentName; } String dstCompName = langSpec.toVariableName(getComponentName(dstRes, langSpec)); if (outsideOutputResource2 || (!platformSpec.isMonolithic() && in.getResource().getCommonPrefix(out2.getResource()) == null && platformSpec.isDifferentTreesAsDifferentServices())) { // Inter-servces if (!platformSpec.isMonolithic()) { // REST API RestApiSpecific restApiSpec = (RestApiSpecific) platformSpec; String httpMethod = null; if (out2.getStateTransition().isRightUnary()) { httpMethod = "put"; } else { httpMethod = "post"; } String[] sideEffects = new String[] {""}; List<String> pathParamsUrl = new ArrayList<>(); for (Expression pathExp: out2.getResource().getPathParams()) { pathParamsUrl.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); } String resName2 = langSpec.toVariableName(resComponentName); if (inDegree <= 1) { resName2 = null; } Map<ChannelMember, Entry<ResourcePath, Set<ChannelMember>>> filledPaths = null; try { filledPaths = ch2.fillOutsideResourcePaths(out2, getPushAccessor(platformSpec)); } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage | UnificationFailed | ValueUndefined e) { e.printStackTrace(); } String dstPath = null; if (filledPaths != null && filledPaths.get(out2) != null) { ResourcePath filledDstPath = filledPaths.get(out2).getKey(); dstPath = filledDstPath.toResourcePath().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); } else { dstPath = dstRes.toResourcePath(pathParamsUrl); } // Call the update method. if (!hasUpdateMethodinvoked) { // The first call to an update method in this method input.addStatement(restApiSpec.getHttpMethodParamsConstructionStatement(srcRes.getResourceName(), params, true)); input.addStatement(langSpec.getVariableDeclaration(DataConstraintModel.typeString.getInterfaceTypeName(), "result") + langSpec.getAssignment() + restApiSpec.getHttpMethodCallStatement(restApiSpec.getBaseURL(), dstPath, resName2, httpMethod)); hasUpdateMethodinvoked = true; if (component != null && !((RestApiSpecific) platformSpec).hasHttpClientFieldDeclaration(component)) { // Declare a client field to connect to the destination resource of push transfer. ((RestApiSpecific) platformSpec).addHttpClientFieldDeclaration(component); } } else { // After the second time of call to update methods in this method input.addStatement(restApiSpec.getHttpMethodParamsConstructionStatement(srcRes.getResourceName(), params, false)); input.addStatement("result" + langSpec.getAssignment() + restApiSpec.getHttpMethodCallStatement(restApiSpec.getBaseURL(), dstPath, resName2, httpMethod)); } } else { // Use the reference field to refer to outside destination resource. List<String> args = new ArrayList<>(); for (Map.Entry<Type, Map.Entry<String, String>> paramEnt: pathParams) { args.add(paramEnt.getValue().getValue()); } for (Map.Entry<Type, Map.Entry<String, String>> paramEnt: params) { args.add(paramEnt.getValue().getValue()); } input.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, args) + langSpec.getStatementDelimiter()); // this.dst.updateDstFromSrc(value, refParams); } } else { // Intra-service // The destination resource is not outside. List<String> args = new ArrayList<>(); for (Map.Entry<Type, Map.Entry<String, String>> paramEnt: pathParams) { args.add(paramEnt.getValue().getValue()); } for (Map.Entry<Type, Map.Entry<String, String>> paramEnt: params) { args.add(paramEnt.getValue().getValue()); } if (srcRes != dstRes) { input.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, args) + langSpec.getStatementDelimiter()); // this.dst.updateDstFromSrc(value, refParams); } else { input.addStatement(langSpec.getMethodInvocation(updateMethodName, args) + langSpec.getStatementDelimiter()); // this.updateDstFromSrc(value, refParams); } } if (addForStatement) { // Close the for loop. input.addStatement(langSpec.getEndForStatement(forVarName)); } } } // Update and initialize a field to refer to an outside input resource for PULL transfer. if (platformSpec.isMonolithic()) { // For a monolithic application. if (outsideInputMembers2.size() > 0) { if (!generatesComponent(resourceNode.getResourceHierarchy())) { ResourcePath srcRes2 = resourceNode.getOutSideResource(directDstCh); for (ChannelMember out2: directDstCh.getOutputChannelMembers()) { if (!generatesComponent(out2.getResource().getResourceHierarchy())) { ResourcePath dstRes2 = out2.getResource(); if (srcRes2.getParent().equals(dstRes2.getParent())) { Map<ChannelMember, Entry<ResourcePath, Set<ChannelMember>>> resourcePaths = null; try { resourcePaths = directDstCh.fillOutsideResourcePaths(out2, getPullAccessor(platformSpec)); if (resourcePaths != null && resourcePaths.size() > 0) { for (ChannelMember outsideMember: outsideInputMembers2) { for (ChannelMember dependedMember: resourcePaths.get(outsideMember).getValue()) { if (dependedMember.getResource().equals(srcRes2)) { // An outside input resource path depends on srcRes. ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey(); if (!generatesComponent(outsidePath.getResourceHierarchy())) { outsidePath = outsidePath.getParent(); } String outsideResName = langSpec.toVariableName(getComponentName(outsidePath.getResourceHierarchy(), langSpec)); Expression outsideExp = getPullAccessor(platformSpec).getDirectStateAccessorFor(outsidePath, null); if (generatesComponent(outsidePath.getResourceHierarchy())) { outsideExp = ((Term) outsideExp).getChild(0); } String[] sideEffects = new String[] {""}; String outsideAccessor = outsideExp.toImplementation(sideEffects); input.addStatement(langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter()); // change the reference field. // Update constructor. String initializingStatement = langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter(); if (component != null) { MethodDeclaration constructor = getConstructor(component); constructor.addStatement(initializingStatement); // initialize the reference field. } else { constructorStatements.add(initializingStatement); // initialize the reference field. } } } } } } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage | UnificationFailed | ValueUndefined e) { e.printStackTrace(); } } } } } } } } } } } } return new AbstractMap.SimpleEntry<>(constructorStatements, inputStatements); } protected void declareGetterAccessorInTheRootResource(ResourceNode resourceNode, TypeDeclaration rootComponent, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { if (resourceNode.getResourceHierarchy().getParent() != null) { // For a non-root resource MethodDeclaration getterAccessor = null; List<VariableDeclaration> mainGetterParams = new ArrayList<>(); String resourcePath = getGetterResourcePathAndPathParams(resourceNode.getPrimaryResourcePath(), mainGetterParams, platformSpec, langSpec); if (resourcePath.indexOf('/') > 0) { resourcePath = resourcePath.substring(resourcePath.indexOf('/')); } else { resourcePath = ""; } if (mainGetterParams.size() > 0) { getterAccessor = langSpec.newMethodDeclaration(getterPrefix + getComponentName(resourceNode.getResourceHierarchy(), langSpec) + methoNameOfResourceState, false, getImplStateType(resourceNode.getResourceHierarchy(), langSpec), mainGetterParams); } else { getterAccessor = langSpec.newMethodDeclaration(getterPrefix + getComponentName(resourceNode.getResourceHierarchy(), langSpec) + methoNameOfResourceState, getImplStateType(resourceNode.getResourceHierarchy(), langSpec)); } getterAccessor.setBody(new Block()); ResourcePath resPath = new ResourcePath(resourceNode.getPrimaryResourcePath()); for (int i = 0; i < mainGetterParams.size(); i++) { Parameter pathParam = new Parameter(mainGetterParams.get(i).getName()); resPath.replacePathParam(i, pathParam, null); } Expression getState = getPullAccessor(platformSpec).getDirectStateAccessorFor(resPath, resPath.getRoot()); getterAccessor.getBody().addStatement(langSpec.getReturnStatement(getState.toImplementation(new String[] {null})) + langSpec.getStatementDelimiter()); if (!platformSpec.isMonolithic()) { ((RestApiSpecific) platformSpec).addGetAnnotations(getterAccessor); if (resourcePath.length() > 0) { ((RestApiSpecific) platformSpec).addPathAnnotation(getterAccessor, resourcePath); } } rootComponent.addMethod(getterAccessor); } } protected void declareUpdateAccessorInTheRootResource(ResourceNode resourceNode, String updateMethodName, DataTransferChannel ch, ChannelMember cm, ResourcePath srcResPath, ResourcePath dstResPath, TypeDeclaration rootComponent, int inDegree, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { ArrayList<VariableDeclaration> parameters; VariableDeclaration param; parameters = new ArrayList<>(); String resourcePath = getUpdateResourcePathAndPathParams(dstResPath, parameters, true, platformSpec, langSpec); // Path parameters to identify the self resource. ResourcePath resPath = new ResourcePath(dstResPath); for (int i = 0; i < parameters.size(); i++) { Parameter pathParam = new Parameter(parameters.get(i).getName()); resPath.replacePathParam(i, pathParam, null); } for (Selector selector: ch.getAllSelectors()) { if (selector.getExpression() instanceof Variable) { Variable selVar = (Variable) selector.getExpression(); VariableDeclaration chParam = langSpec.newVariableDeclaration(selVar.getType(), selVar.getName()); if (!platformSpec.isMonolithic()) { ((RestApiSpecific) platformSpec).addFormParamAnnotation(chParam, selVar.getName()); } parameters.add(chParam); // A channel parameter to specify the context of the collaboration. } } Type srcType = srcResPath.getResourceStateType(); String srcResName = langSpec.toVariableName(getComponentName(srcResPath.getResourceHierarchy(), langSpec)); param = langSpec.newVariableDeclaration(srcType, srcResName); if (!platformSpec.isMonolithic()) ((RestApiSpecific) platformSpec).addFormParamAnnotation(param, srcResName); parameters.add(param); // The state of the source resource to carry the data-flow. for (ResourcePath refRes: ch.getReferenceResources()) { if (!refRes.equals(resourceNode.getInSideResource(ch))) { String refName = langSpec.toVariableName(getComponentName(refRes.getResourceHierarchy(), langSpec)); param = langSpec.newVariableDeclaration(refRes.getResourceStateType(), refName); if (!platformSpec.isMonolithic()) { ((RestApiSpecific) platformSpec).addFormParamAnnotation(param, refName); } parameters.add(param); } } MethodDeclaration updateAccessor = langSpec.newMethodDeclaration(updateMethodName, false, null, parameters); if (!platformSpec.isMonolithic()) { if (isPut(cm)) { ((RestApiSpecific) platformSpec).addPutAnnotations(updateAccessor); } else { if (!isDelete(cm)) { ((RestApiSpecific) platformSpec).addPostAnnotations(updateAccessor); } else { ((RestApiSpecific) platformSpec).addDeleteAnnotations(updateAccessor); } } } 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 += "/" + langSpec.toVariableName(srcResName); } if (!platformSpec.isMonolithic()) ((RestApiSpecific) platformSpec).addPathAnnotation(updateAccessor, resourcePath); // To make the accessor call the update method. Expression resExp = getPullAccessor(platformSpec).getDirectStateAccessorFor(resPath, resPath.getRoot()); List<String> args = new ArrayList<>(); if (resExp instanceof Term) { // to access the parent if (((Term) resExp).getChildren().size() > 1 && ((Term) resExp).getChild(1) instanceof Variable) { args.add(((Variable)((Term) resExp).getChild(1)).getName()); } resExp = ((Term) resExp).getChild(0); } String resourceAccess = resExp.toImplementation(new String[] {""}); for (VariableDeclaration var: updateAccessor.getParameters()) { args.add(var.getName()); } updateAccessor.addStatement(langSpec.getMethodInvocation(resourceAccess, updateMethodName, args) + langSpec.getStatementDelimiter()); rootComponent.addMethod(updateAccessor); } protected MethodDeclaration declareInputAccessorInTheRootResource(String inputMethodName, ArrayList<VariableDeclaration> rootInputParams, ChannelMember cm, String resourcePath, TypeDeclaration rootComponent, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { MethodDeclaration rootInputAccessor; rootInputAccessor = langSpec.newMethodDeclaration(inputMethodName, false, null, rootInputParams); if (!platformSpec.isMonolithic()) { if (isPut(cm)) { ((RestApiSpecific) platformSpec).addPutAnnotations(rootInputAccessor); } else { if (!isDelete(cm)) { ((RestApiSpecific) platformSpec).addPostAnnotations(rootInputAccessor); } else { ((RestApiSpecific) platformSpec).addDeleteAnnotations(rootInputAccessor); } } if (resourcePath.length() > 0) { ((RestApiSpecific) platformSpec).addPathAnnotation(rootInputAccessor, resourcePath); } } rootComponent.addMethod(rootInputAccessor); return rootInputAccessor; } }