package generators; import java.util.AbstractMap; import java.util.ArrayList; 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 java.util.Set; import java.util.AbstractMap.SimpleEntry; import code.ast.Block; import code.ast.CompilationUnit; import code.ast.FieldDeclaration; import code.ast.MethodDeclaration; import code.ast.TypeDeclaration; import code.ast.VariableDeclaration; import models.Edge; import models.Node; import models.algebra.Constant; import models.algebra.Expression; import models.algebra.Field; import models.algebra.InvalidMessage; import models.algebra.Parameter; import models.algebra.ParameterizedIdentifierIsFutureWork; import models.algebra.Position; 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.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.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, DataFlowGraph flowGraph, ArrayList<ResourceNode> components, Map<ResourceHierarchy, Set<ResourceHierarchy>> dependedRootComponentGraph, TypeDeclaration mainComponent, MethodDeclaration mainConstructor, ArrayList<CompilationUnit> codes, ILanguageSpecific langSpec) { Map<ResourceHierarchy, TypeDeclaration> resourceComponents = new HashMap<>(); Map<ResourceHierarchy, MethodDeclaration> resourceConstructors = new HashMap<>(); List<Map.Entry<ResourceHierarchy, VariableDeclaration>> constructorParams = new ArrayList<>(); List<Map.Entry<ResourceHierarchy, String>> constructorStatements = new ArrayList<>(); Map<MethodDeclaration, Map.Entry<Expression, ResourceHierarchy>> updateStatements = new HashMap<>(); Map<ResourceHierarchy, Set<ResourceHierarchy>> childGetters = new HashMap<>(); // 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); resourceComponents.put(resourceNode.getResourceHierarchy(), component); CompilationUnit cu = langSpec.newCompilationUnit(component); codes.add(cu); // Declare the constructor. MethodDeclaration constructor = declareConstructor(resourceNode, component, dependedRootComponentGraph, depends, langSpec); if (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, langSpec); // Declare the accessor method in the main component to call the getter method. declareAccessorInMainComponent(mainComponent, resourceNode, stateGetter, langSpec); } if (component != null) { // Declare the getter methods in this resource to obtain the children resources. declareChildGetterMethod(resourceNode, component, childGetters, langSpec); } } } // For each components (2nd pass). for (Node componentNode: components) { // Declare this resource. ResourceNode resourceNode = (ResourceNode) componentNode; Type resStateType = getImplStateType(resourceNode.getResourceHierarchy(), langSpec); TypeDeclaration component = null; TypeDeclaration parentComponent = null; if (generatesComponent(resourceNode.getResourceHierarchy())) { component = resourceComponents.get(resourceNode.getResourceHierarchy()); } if (resourceNode.getResourceHierarchy().getParent() != null) { parentComponent = resourceComponents.get(resourceNode.getResourceHierarchy().getParent()); } // Declare cache fields and update methods in this resource. Map.Entry<List<String>, Map<MethodDeclaration, Map.Entry<Expression, ResourceHierarchy>>> initStatementsAndUpdateUpdates = declareCacheFieldsAndUpdateMethods(resourceNode, component, parentComponent, 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, ResourceHierarchy>> entry: initStatementsAndUpdateUpdates.getValue().entrySet()) { updateStatements.put(entry.getKey(), entry.getValue()); } // Declare the fields to refer to other resources in the parent/this component, and the state field in the parent component. declareFieldsToReferToOtherResourcesAndStateFieldInParentComponent(resourceNode, component, parentComponent, constructorParams, langSpec); // Declare the getter method in this resource to obtain the state. if (component == null) { MethodDeclaration stateGetter = declareStateGetterMethodInParent(resourceNode, parentComponent, resStateType, langSpec); if (stateGetter != null) { // Declare the accessor method in the main component to call the getter method. declareAccessorInMainComponent(mainComponent, resourceNode, stateGetter, langSpec); } } // Declare input methods in this component and the main component. Map.Entry<List<String>, Map<MethodDeclaration, Map.Entry<Expression, ResourceHierarchy>>> initStatementsAndInputUpdates = declareInputMethodsInThisAndMainComponents(resourceNode, component, parentComponent, mainComponent, model, 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, ResourceHierarchy>> entry: initStatementsAndInputUpdates.getValue().entrySet()) { updateStatements.put(entry.getKey(), entry.getValue()); } } // 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 childRes = updateStatements.get(method).getValue(); TypeDeclaration childComponent = resourceComponents.get(childRes); addUpdateStatementWithConstructorInvocationToMethod(method, updateExp, childRes, childComponent, langSpec); } } private static List<VariableDeclaration> addConstructorParameters(ResourceHierarchy resource, Map<ResourceHierarchy, TypeDeclaration> resourceComponents, Map<ResourceHierarchy, MethodDeclaration> resourceConstructors, List<Entry<ResourceHierarchy, VariableDeclaration>> constructorParams, ILanguageSpecific langSpec) { List<VariableDeclaration> params = new ArrayList<>(); for (ResourceHierarchy child: resource.getChildren()) { params.addAll(addConstructorParameters(child, resourceComponents, resourceConstructors, constructorParams, langSpec)); } for (Entry<ResourceHierarchy, VariableDeclaration> paramEnt: constructorParams) { if (paramEnt.getKey().equals(resource)) { params.add(paramEnt.getValue()); } } if (params.size() > 0) { MethodDeclaration constructor = resourceConstructors.get(resource); if (constructor == null) { if (resourceComponents.get(resource) != null) { String resourceName = getComponentName(resource, 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 childRes, TypeDeclaration childComponent, ILanguageSpecific langSpec) { Type replacedJsonType = childRes.getResourceStateType(); String replacingClassName = getComponentName(childRes, langSpec); 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 && jsonTerm.getType().equals(replacedJsonType)) { MethodDeclaration childConstructor = getConstructor(childComponent); List<String> params = new ArrayList<>(); for (VariableDeclaration var: childConstructor.getParameters()) { JsonAccessor jsonMember = new JsonAccessor(DataConstraintModel.dot); jsonMember.addChild(jsonTerm); jsonMember.addChild(new Constant("\"" + var.getName() + "\"")); Expression param = jsonMember.reduce(); 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(); } } String[] sideEffects = new String[] {""}; String newState = exp.toImplementation(sideEffects); String updateStatement; if (exp instanceof Term && ((Term) exp).getSymbol().isImplWithSideEffect()) { updateStatement = sideEffects[0]; } 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()).getOptions().get(0) == 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()).getOptions().get(0) != 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, List<Map.Entry<ResourceHierarchy, 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); } 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, List<Map.Entry<ResourceHierarchy, VariableDeclaration>> constructorParams, ILanguageSpecific langSpec) { // Declare reference fields for push data transfer. boolean noPullTransfer = true; for (Edge resToCh : resourceNode.getOutEdges()) { DataFlowEdge re = (DataFlowEdge) resToCh; DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); // Check if the source 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: re.getDestination().getOutEdges()) { if (chToRes.getDestination() instanceof ResourceNode) { ResourceHierarchy dstRes = ((ResourceNode) chToRes.getDestination()).getResourceHierarchy(); // Check if the destination 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) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) { // Declare a field in the parent component to refer to the destination resource of push transfer. if (!generatesComponent(dstRes)) { dstRes = dstRes.getParent(); } String dstResName = getComponentName(dstRes, langSpec); FieldDeclaration refFieldForPush = langSpec.newFieldDeclaration(new Type(dstResName, dstResName), langSpec.toVariableName(dstResName)); VariableDeclaration refVarForPush = langSpec.newVariableDeclaration(new Type(dstResName, dstResName), langSpec.toVariableName(dstResName)); if (component != null) { // A component is created for this resource. if (resourceNode.getResourceHierarchy() != dstRes) { component.addField(refFieldForPush); if (!outsideOutputResource) { constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy(), refVarForPush)); } } } else { // No component is created for this resource. if (resourceNode.getParent().getResourceHierarchy() != dstRes) { parentComponent.addField(refFieldForPush); if (!outsideOutputResource) { constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), 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); constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy(), 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); constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), refRootVarForPush)); } } } } } } } } // Declare reference fields for pull data transfer. for (Edge chToRes : resourceNode.getInEdges()) { for (Edge resToCh: chToRes.getSource().getInEdges()) { DataFlowEdge re = (DataFlowEdge) resToCh; DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(ch); // Check if the 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; } } if ((((PushPullAttribute) re.getAttribute()).getOptions().get(0) != 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 (component != null) { // A component is created for this resource. if (resourceNode.getResourceHierarchy() != srcRes.getResourceHierarchy()) { component.addField(refFieldForPull); if (!outsideInputResource) { constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy(), refVarForPull)); } } } else { // No component is created for this resource. if (resourceNode.getParent().getResourceHierarchy() != srcRes.getResourceHierarchy()) { parentComponent.addField(refFieldForPull); if (!outsideInputResource) { constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), 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); constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy(), 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); constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), 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); constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), langSpec.newVariableDeclaration(res.getResourceStateType(), resName))); } } } } private MethodDeclaration declareStateGetterMethod(ResourceNode resourceNode, TypeDeclaration component, Type resStateType, ILanguageSpecific langSpec) { // Declare the getter method of the resource state. MethodDeclaration stateGetter = langSpec.newMethodDeclaration(getterOfResourceState, resStateType); component.addMethod(stateGetter); if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { fillStateGetterMethod(stateGetter, resourceNode.getResourceHierarchy(), resStateType, langSpec); } else { // invocations to other getter methods when at least one incoming data-flow edges is PULL-style. addOtherGetterInvocationsToStateGatter(stateGetter, resourceNode, langSpec); } return stateGetter; } private MethodDeclaration declareStateGetterMethodInParent(ResourceNode resourceNode, TypeDeclaration parentComponent, Type resStateType, ILanguageSpecific langSpec) { // Check duplication. String getterName = getterPrefix + getComponentName(resourceNode.getResourceHierarchy(), langSpec); for (MethodDeclaration method: parentComponent.getMethods()) { if (method.getName().equals(getterName)) return null; } // Declare the getter method of the resource state. List<VariableDeclaration> getterParams = new ArrayList<>(); int v = 1; for (Selector param: resourceNode.getSelectors()) { if (param.getExpression() instanceof Variable) { Variable var = (Variable) param.getExpression(); getterParams.add(new VariableDeclaration(var.getType(), var.getName())); } else if (param.getExpression() instanceof Term) { Term var = (Term) param.getExpression(); getterParams.add(new VariableDeclaration(var.getType(), "v" + v)); } v++; } MethodDeclaration stateGetter = null; if (getterParams.size() == 0) { stateGetter = new MethodDeclaration(getterName, resStateType); } else { stateGetter = new MethodDeclaration(getterName, false, resStateType, getterParams); } if (parentComponent != null) { parentComponent.addMethod(stateGetter); } if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { fillChildGetterMethod(stateGetter, resourceNode.getResourceHierarchy(), resourceNode.getResourceHierarchy().getParent().getResourceStateType(), langSpec); } else { addOtherGetterInvocationsToStateGatter(stateGetter, resourceNode, langSpec); } return stateGetter; } private void addOtherGetterInvocationsToStateGatter(MethodDeclaration stateGetter, ResourceNode resourceNode, ILanguageSpecific langSpec) { boolean isContainedPush = false; DataTransferChannel ch = null; HashMap<ChannelMember, IResourceStateAccessor> inputResourceToStateAccessor = new HashMap<>(); for (Edge chToRes: resourceNode.getInEdges()) { DataTransferChannel 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()).getOptions().get(0) == PushPullValue.PUSH) { // PUSH transfer isContainedPush = true; inputResourceToStateAccessor.put(in, getPushAccessor()); } else { // PULL transfer inputResourceToStateAccessor.put(in, getPullAccessor()); ch = ((ChannelNode) resToCh.getDestination()).getChannel(); // pull containing input side channel is always one. } } } // for reference channel members. for (ChannelMember c: ch.getReferenceChannelMembers()) { inputResourceToStateAccessor.put(c, getPullAccessor()); // by pull data transfer } // generate a return statement. try { for (ChannelMember out: ch.getOutputChannelMembers()) { if (resourceNode.getInSideResources().contains(out.getResource())) { String[] sideEffects = new String[] {""}; if (!isContainedPush) { // All incoming edges are in PULL-style. String curState = ch.deriveUpdateExpressionOf(out, getPullAccessor()).getKey().toImplementation(sideEffects); stateGetter.addStatement(sideEffects[0] + langSpec.getReturnStatement(curState) + langSpec.getStatementDelimiter()); } else { // At least one incoming edge is in PUSH-style. String curState = ch.deriveUpdateExpressionOf(out, getPullAccessor(), inputResourceToStateAccessor).getKey().toImplementation(sideEffects); stateGetter.addStatement(sideEffects[0] + langSpec.getReturnStatement(curState) + langSpec.getStatementDelimiter()); } break; } } } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage | UnificationFailed | ValueUndefined e) { e.printStackTrace(); } } private void declareChildGetterMethod(ResourceNode resourceNode, TypeDeclaration component, Map<ResourceHierarchy, Set<ResourceHierarchy>> childGetters, ILanguageSpecific langSpec) { // Declare the getter methods in this resource to obtain the children resources. Set<ResourceHierarchy> children = childGetters.get(resourceNode.getResourceHierarchy()); if (children == null) { children = new HashSet<>(); childGetters.put(resourceNode.getResourceHierarchy(), children); } for (ResourceNode child: resourceNode.getChildren()) { if (generatesComponent(child.getResourceHierarchy())) { // A component for the child is generated. if (!children.contains(child.getResourceHierarchy())) { children.add(child.getResourceHierarchy()); List<VariableDeclaration> params = new ArrayList<>(); int v = 1; Expression param = child.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++; } String childCompName = getComponentName(child.getResourceHierarchy(), langSpec); Type childType = new Type(childCompName, childCompName); MethodDeclaration childGetter = null; if (params.size() == 0) { childGetter = langSpec.newMethodDeclaration(getterPrefix + childCompName, childType); } else { childGetter = langSpec.newMethodDeclaration(getterPrefix + childCompName, false, childType, params); } fillChildGetterMethod(childGetter, child.getResourceHierarchy(), resourceNode.getResourceStateType(), langSpec); component.addMethod(childGetter); } } } } private Map.Entry<List<String>, Map<MethodDeclaration, Map.Entry<Expression, ResourceHierarchy>>> declareCacheFieldsAndUpdateMethods(ResourceNode resourceNode, TypeDeclaration component, TypeDeclaration parentComponent, 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, ResourceHierarchy>> updateStatements = new HashMap<>(); for (Edge chToRes: resourceNode.getInEdges()) { for (Edge resToCh: chToRes.getSource().getInEdges()) { DataTransferChannel ch = ((ChannelNode) resToCh.getDestination()).getChannel(); DataFlowEdge re = (DataFlowEdge) resToCh; ResourcePath srcResPath = ((ResourceNode) re.getSource()).getOutSideResource(ch); 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: ch.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; ResourcePath dstResPath = null; for (ChannelMember cm: ch.getOutputChannelMembers()) { if (resourceNode.getInSideResources().contains(cm.getResource())) { dstResPath = cm.getResource(); if (cm.isOutside()) { outsideOutputResource = true; // Regarded as push transfer. break; } } } if ((((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) { // for push data transfer // Declare an update method in the type of the destination resource. ArrayList<VariableDeclaration> parameters = new ArrayList<>(); int v = 1; for (Expression exp: dstResPath.getPathParams()) { if (exp instanceof Variable) { Variable pathVar = (Variable) exp; String varName = "self" + (v > 1 ? v : ""); // String varName = pathVar.getName(); VariableDeclaration pathParam = langSpec.newVariableDeclaration(pathVar.getType(), varName); parameters.add(pathParam); // A path parameter to identify the self resource. } v++; } for (Selector selector: ch.getAllSelectors()) { if (selector.getExpression() instanceof Variable) { Variable selVar = (Variable) selector.getExpression(); VariableDeclaration chParam = langSpec.newVariableDeclaration(selVar.getType(), selVar.getName()); parameters.add(chParam); // A channel parameter to specify the context of the collaboration. } } parameters.add(langSpec.newVariableDeclaration(srcResPath.getResourceStateType(), srcResPath.getLeafResourceName())); // The state of the source resource to carry the data-flow. // For the refs. for (ResourcePath ref: ch.getReferenceResources()) { if (!resourceNode.getInSideResources().contains(ref)) { parameters.add(langSpec.newVariableDeclaration(ref.getResourceStateType(), ref.getLeafResourceName())); } } 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); component.addMethod(update); } } 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); parentComponent.addMethod(update); } } // Add a statement to update the state field if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { try { for (ChannelMember out: ch.getOutputChannelMembers()) { if (resourceNode.getInSideResources().contains(out.getResource())) { Expression updateExp = null; if (ch.getReferenceChannelMembers().size() == 0) { updateExp = ch.deriveUpdateExpressionOf(out, getPushAccessor()).getKey(); } 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()); } for (ChannelMember c: ch.getReferenceChannelMembers()) { inputResourceToStateAccessor.put(c, getRefAccessor()); } updateExp = ch.deriveUpdateExpressionOf(out, getPushAccessor(), inputResourceToStateAccessor).getKey(); } // 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 childRes = outRes.getChildren().iterator().next(); Type childStateType = childRes.getResourceStateType(); if (DataConstraintModel.typeJson.isAncestorOf(childStateType)) { updateStatements.put(update, new AbstractMap.SimpleEntry<>(updateExp, childRes)); break; } } // 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]; } 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 (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(); } } if (numOfOutResourcesWithTheSameHierarchy == 1) { update.addFirstStatement(updateStatement); } else { Term conditions = null; int i = 1; Map<ChannelMember, Entry<ResourcePath, Set<ChannelMember>>> resourcePaths = ch.fillOutsideResourcePaths(out, JerseyCodeGenerator.pushAccessor); 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 : "")), arg}); if (conditions == null) { conditions = condition; } else { conditions = new Term(DataConstraintModel.and, new Expression[] { conditions, condition}); } } i++; } update.addFirstStatement(langSpec.getIfStatement(conditions, updateStatement)); } break; } } } 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 (resToCh.getDestination().getIndegree() > 1 || (resToCh.getDestination().getIndegree() == 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. FieldDeclaration cacheField = langSpec.newFieldDeclaration( srcResPath.getResourceStateType(), srcResPath.getLeafResourceName(), 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); } } 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 out: ch.getOutputChannelMembers()) { if (resourceNode.getInSideResources().contains(out.getResource())) { try { resourcePaths = ch.fillOutsideResourcePaths(out, getPullAccessor()); } 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().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). for (Edge resToCh2: resourceNode.getOutEdges()) { DataFlowEdge dOut = (DataFlowEdge) resToCh2; DataTransferChannel ch2 = ((ChannelNode) resToCh2.getDestination()).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: ch2.getInputChannelMembers()) { if (resourceNode.getOutSideResources().contains(cm.getResource())) { if (cm.isOutside()) { outsideInputResource2 = true; // Regarded as pull transfer. } in = cm; } if (cm.isOutside()) { outsideInputMembers2.add(cm); } } for (Edge chToRes2: resToCh2.getDestination().getOutEdges()) { ResourceNode dstNode = ((ResourceNode) chToRes2.getDestination()); // Check if the output resource is outside of the channel scope. boolean outsideOutputResource2 = false; ChannelMember out = null; for (ChannelMember cm: ch2.getOutputChannelMembers()) { if (dstNode.getInSideResources().contains(cm.getResource())) { out = cm; if (cm.isOutside()) { outsideOutputResource2 = true; break; } } } if ((((PushPullAttribute) dOut.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource2) || outsideOutputResource2) { // PUSH transfer List<String> params = new ArrayList<>(); // Values of path parameters. for (Expression pathParam: out.getResource().getPathParams()) { if (pathParam instanceof Variable) { Variable pathVar = (Variable) pathParam; params.add(pathVar.getName()); } } // Values of channel parameters. for (Selector selector: ch.getAllSelectors()) { if (selector.getExpression() instanceof Variable) { Variable selVar = (Variable) selector.getExpression(); params.add(selVar.getName()); } } // Value of the source side (input side) resource. ResourceHierarchy srcRes2 = resourceNode.getResourceHierarchy(); if (generatesComponent(srcRes2)) { params.add(langSpec.getFieldAccessor(fieldOfResourceState)); } else { params.add(langSpec.getFieldAccessor(langSpec.toVariableName(srcRes2.getResourceName()))); srcRes2 = srcRes2.getParent(); } Map<MethodDeclaration, Set<ResourcePath>> referredResources = new HashMap<>(); Set<ResourcePath> referredSet = referredResources.get(update); for (ChannelMember rc: ch2.getReferenceChannelMembers()) { // to get the value of reference member. ResourcePath ref = rc.getResource(); if (referredSet == null) { referredSet = new HashSet<>(); referredResources.put(update, referredSet); } if (!resourceNode.getInSideResources().contains(ref)) { String refVarName = ref.getLeafResourceName(); if (!referredSet.contains(ref)) { referredSet.add(ref); Expression refGetter = getPullAccessor().getCurrentStateAccessorFor(rc, in); String[] sideEffects = new String[] {""}; String refExp = refGetter.toImplementation(sideEffects); String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); update.addStatement(sideEffects[0] + langSpec.getVariableDeclaration(refTypeName, refVarName) + langSpec.getAssignment() + refExp + langSpec.getStatementDelimiter()); } params.add(refVarName); } } 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) { // The destination resource is not outside. if (srcRes2 != dstRes) { update.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, params) + langSpec.getStatementDelimiter()); // this.dst.updateDstFromSrc(value, refParams); } else { update.addStatement(langSpec.getMethodInvocation(updateMethodName, params) + langSpec.getStatementDelimiter()); // this.updateDstFromSrc(value, refParams); } } else { // Use the reference field to refer to outside destination resource. update.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, params) + langSpec.getStatementDelimiter()); // this.dst.updateDstFromSrc(value, refParams); } } } if (outsideInputMembers2.size() > 0) { if (!generatesComponent(resourceNode.getResourceHierarchy())) { // srcRes2 does not have a component. ResourcePath srcRes2 = resourceNode.getOutSideResource(ch2); for (ChannelMember out: ch2.getOutputChannelMembers()) { if (!generatesComponent(out.getResource().getResourceHierarchy())) { // Also dstRes2 does not have a component. ResourcePath dstRes2 = out.getResource(); if (srcRes2.getParent().equals(dstRes2.getParent())) { Map<ChannelMember, Entry<ResourcePath, Set<ChannelMember>>> resourcePaths = null; try { resourcePaths = ch2.fillOutsideResourcePaths(out, getPullAccessor()); if (resourcePaths != null && resourcePaths.size() > 0) { for (ChannelMember outsideMember: outsideInputMembers2) { for (ChannelMember dependingMember: resourcePaths.get(outsideMember).getValue()) { if (dependingMember.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().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); } } } } } } 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, ResourceHierarchy>>> declareInputMethodsInThisAndMainComponents(ResourceNode resourceNode, TypeDeclaration component, TypeDeclaration parentComponent, TypeDeclaration mainComponent, DataTransferModel model, ILanguageSpecific langSpec) { // Declare input methods. String resName = resourceNode.getResourceName(); String resComponentName = langSpec.toComponentName(resName); List<String> constructorStatements = new ArrayList<>(); Map<MethodDeclaration, Map.Entry<Expression, ResourceHierarchy>> inputStatements = new HashMap<>(); for (Channel ch : model.getInputChannels()) { for (ChannelMember out : ((DataTransferChannel) ch).getOutputChannelMembers()) { if (resourceNode.getInSideResources().contains(out.getResource())) { Expression message = out.getStateTransition().getMessageExpression(); MethodDeclaration input = null; MethodDeclaration inputAccessor = null; if (message instanceof Term) { // Declare an input method in this component. ArrayList<VariableDeclaration> resInputParams = new ArrayList<>(); ArrayList<VariableDeclaration> mainInputParams = new ArrayList<>(); // 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())); } } } 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. String messageSymbol = ((Term) message).getSymbol().getImplName(); inputAccessor = getMethod(mainComponent, messageSymbol); if (inputAccessor == null) { inputAccessor = langSpec.newMethodDeclaration(messageSymbol, false, null, mainInputParams); mainComponent.addMethod(inputAccessor); } else { // Add type to a parameter without type. if (inputAccessor.getParameters() != null) { for (VariableDeclaration param: inputAccessor.getParameters()) { if (param.getType() == null) { for (VariableDeclaration p: mainInputParams) { if (param.getName().equals(p.getName()) && p.getType() != null) { param.setType(p.getType()); } } } } } } } else if (message instanceof Variable) { // 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++; } } 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. String messageSymbol = ((Variable) message).getName(); inputAccessor = getMethod(mainComponent, messageSymbol); if (inputAccessor == null) { if (mainInputParams.size() == 0) { inputAccessor = langSpec.newMethodDeclaration(messageSymbol, null); } else { inputAccessor = langSpec.newMethodDeclaration(messageSymbol, false, null, mainInputParams); } mainComponent.addMethod(inputAccessor); } } // Add an invocation to the accessor method. if (inputAccessor != null) { 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 refVarName = ref.getLeafResourceName(); Expression refGetter = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(ref, null); String[] sideEffects = new String[] {""}; String refExp = refGetter.toImplementation(sideEffects); String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); inputAccessor.addFirstStatement(sideEffects[0] + refTypeName + " " + refVarName + " = " + refExp + ";"); } } Expression resExp = getPullAccessor().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[] {null}); // 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()); } } } inputAccessor.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 { Expression updateExp = ((DataTransferChannel) ch).deriveUpdateExpressionOf(out, getRefAccessor()).getKey(); // 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 childRes = outRes.getChildren().iterator().next(); Type childStateType = childRes.getResourceStateType(); if (DataConstraintModel.typeJson.isAncestorOf(childStateType)) { inputStatements.put(input, new AbstractMap.SimpleEntry<>(updateExp, childRes)); updateExp = null; } } // 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]; } 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 (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); } } } } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage | UnificationFailed | ValueUndefined e) { e.printStackTrace(); } // Add an invocation to an update method (for a chain of update method invocations). for (Edge resToCh: resourceNode.getOutEdges()) { DataFlowEdge dOut = (DataFlowEdge) resToCh; DataTransferChannel ch2 = ((ChannelNode) resToCh.getDestination()).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: ch2.getInputChannelMembers()) { if (resourceNode.getOutSideResources().contains(cm.getResource())) { if (cm.isOutside()) { outsideInputResource2 = true; // Regarded as pull transfer. } in = cm; } if (cm.isOutside()) { outsideInputMembers2.add(cm); } } for (Edge chToRes: resToCh.getDestination().getOutEdges()) { ResourceNode dstNode = ((ResourceNode) chToRes.getDestination()); // 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; } } } if ((((PushPullAttribute) dOut.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource2) || outsideOutputResource2) { // PUSH transfer Map<MethodDeclaration, Set<ResourcePath>> referredResources = new HashMap<>(); List<String> params = new ArrayList<>(); // Values of path parameters. for (Expression pathParam: out2.getResource().getPathParams()) { if (pathParam instanceof Variable) { Variable pathVar = (Variable) pathParam; params.add(pathVar.getName()); } } // Values of channel parameters. for (Selector selector: ch2.getAllSelectors()) { if (selector.getExpression() instanceof Variable) { Variable selVar = (Variable) selector.getExpression(); params.add(selVar.getName()); } } // Value of the source side (input side) resource. ResourceHierarchy srcRes = resourceNode.getResourceHierarchy(); if (generatesComponent(srcRes)) { params.add(langSpec.getFieldAccessor(fieldOfResourceState)); } else { params.add(langSpec.getFieldAccessor(langSpec.toVariableName(srcRes.getResourceName()))); srcRes = srcRes.getParent(); } Set<ResourcePath> referredSet = referredResources.get(input); for (ChannelMember rc: ch2.getReferenceChannelMembers()) { // to get the value of reference member. ResourcePath ref = rc.getResource(); if (referredSet == null) { referredSet = new HashSet<>(); referredResources.put(input, referredSet); } if (!resourceNode.getOutSideResources().contains(ref)) { String refVarName = ref.getLeafResourceName(); if (!referredSet.contains(ref)) { referredSet.add(ref); Expression refGetter = getPullAccessor().getCurrentStateAccessorFor(rc, in); 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()); } params.add(refVarName); } } 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) { // The destination resource is not outside. if (srcRes != dstRes) { input.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, params) + langSpec.getStatementDelimiter()); // this.dst.updateDstFromSrc(value, refParams); } else { input.addStatement(langSpec.getMethodInvocation(updateMethodName, params) + langSpec.getStatementDelimiter()); // this.updateDstFromSrc(value, refParams); } } else { // Use the reference field to refer to outside destination resource. input.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, params) + langSpec.getStatementDelimiter()); // this.dst.updateDstFromSrc(value, refParams); } } } if (outsideInputMembers2.size() > 0) { if (!generatesComponent(resourceNode.getResourceHierarchy())) { ResourcePath srcRes2 = resourceNode.getOutSideResource(ch2); for (ChannelMember out2: ch2.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 = ch2.fillOutsideResourcePaths(out2, getPullAccessor()); if (resourcePaths != null && resourcePaths.size() > 0) { for (ChannelMember outsideMember: outsideInputMembers2) { for (ChannelMember dependingMember: resourcePaths.get(outsideMember).getValue()) { if (dependingMember.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().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); 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); } }