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.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, 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[] {""};
ResourcePath srcRes = in.getResource();
if (!generatesComponent(srcRes.getResourceHierarchy())) {
srcRes = srcRes.getParent();
}
if (!platformSpec.isMonolithic()
&& (rc.isOutside() || (ref.getCommonPrefix(srcRes) == null && platformSpec.isDifferentTreesAsDifferentServices()))) {
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 {
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 {
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 (!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[] {""};
ResourcePath dstRes = out.getResource();
if (!generatesComponent(dstRes.getResourceHierarchy())) {
dstRes = dstRes.getParent();
}
if (!platformSpec.isMonolithic()
&& (rc.isOutside() || (ref.getCommonPrefix(dstRes) == null && platformSpec.isDifferentTreesAsDifferentServices()))) {
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 {
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());
}
}
}
}
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;
}
}