package generators;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import code.ast.CompilationUnit;
import code.ast.MethodDeclaration;
import code.ast.TypeDeclaration;
import code.ast.VariableDeclaration;
import models.Edge;
import models.algebra.Constant;
import models.algebra.Expression;
import models.algebra.Field;
import models.algebra.InvalidMessage;
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.ResourceHierarchy;
import models.dataConstraintModel.ResourcePath;
import models.dataFlowModel.DataTransferModel;
import models.dataFlowModel.DataTransferChannel;
import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor;
import models.dataFlowModel.PushPullAttribute;
import models.dataFlowModel.PushPullValue;
import models.dataFlowModel.ChannelNode;
import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork;
import models.dataFlowModel.DataFlowEdge;
import models.dataFlowModel.DataFlowGraph;
import models.dataFlowModel.ResourceNode;
import models.dataFlowModel.StoreAttribute;
public class JavaMethodBodyGenerator {
public static ArrayList<CompilationUnit> doGenerate(DataFlowGraph graph, DataTransferModel model, ArrayList<CompilationUnit> codes) {
// Create a map from type names (lower case) to their types.
Map<String, TypeDeclaration> componentMap = new HashMap<>();
for (CompilationUnit code: codes) {
for (TypeDeclaration component: code.types()) {
componentMap.put(component.getTypeName(), component);
}
}
// Generate the body of each update or getter method.
try {
Map<MethodDeclaration, Set<ResourcePath>> referredResources = new HashMap<>();
for (Edge e: graph.getEdges()) {
DataFlowEdge resToCh = (DataFlowEdge) e;
if (!resToCh.isChannelToResource()) {
PushPullAttribute pushPull = (PushPullAttribute) resToCh.getAttribute();
ResourceNode src = (ResourceNode) resToCh.getSource();
for (Edge chToRes: resToCh.getDestination().getOutEdges()) {
ResourceNode dst = (ResourceNode) chToRes.getDestination();
String srcResourceName = JavaCodeGenerator.getComponentName(src.getResourceHierarchy());
String dstResourceName = JavaCodeGenerator.getComponentName(dst.getResourceHierarchy());
TypeDeclaration srcComponent = componentMap.get(srcResourceName);
TypeDeclaration dstComponent = componentMap.get(dstResourceName);
DataTransferChannel ch = ((ChannelNode) resToCh.getDestination()).getChannel();
for (ChannelMember out: ch.getOutputChannelMembers()) {
if (dst.getInSideResources().contains(out.getResource())) {
// Check if the input resource is outside of the channel scope.
boolean outsideInputResource = false;
for (ChannelMember cm: ch.getInputChannelMembers()) {
if (cm.getResource().getResourceHierarchy().equals(src.getResourceHierarchy()) && 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 (cm.getResource().getResourceHierarchy().equals(dst.getResourceHierarchy()) && cm.isOutside()) {
outsideOutputResource = true; // Regarded as push transfer.
break;
}
}
if (pushPull.getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) {
// for push data transfer
String srcResName = null;
if (srcComponent == null) {
srcResName = srcResourceName;
} else {
srcResName = srcComponent.getTypeName();
}
if (dstComponent == null) {
String dstParentResourceName = JavaCodeGenerator.getComponentName(dst.getResourceHierarchy().getParent());
dstComponent = componentMap.get(dstParentResourceName);
}
MethodDeclaration update = getUpdateMethod(dstComponent, srcResName);
if (((StoreAttribute) dst.getAttribute()).isStored()) {
// update stored state of dst side resource (when every incoming edge is in push style)
Expression updateExp = null;
if (ch.getReferenceChannelMembers().size() == 0) {
updateExp = ch.deriveUpdateExpressionOf(out, JavaCodeGenerator.pushAccessor);
} else {
// if there exists one or more reference channel member.
HashMap<ChannelMember, IResourceStateAccessor> inputResourceToStateAccessor = new HashMap<>();
for (ChannelMember in: ch.getInputChannelMembers()) {
inputResourceToStateAccessor.put(in, JavaCodeGenerator.pushAccessor);
}
for (ChannelMember c: ch.getReferenceChannelMembers()) {
inputResourceToStateAccessor.put(c, JavaCodeGenerator.refAccessor);
}
updateExp = ch.deriveUpdateExpressionOf(out, JavaCodeGenerator.pushAccessor, inputResourceToStateAccessor);
}
// Replace Json constructor with a constructor of the child resource.
ResourceHierarchy outRes = out.getResource().getResourceHierarchy();
if (outRes.getChildren().size() == 1 && outRes.getChildren().iterator().next().getNumParameters() > 0) {
ResourceHierarchy childRes = outRes.getChildren().iterator().next();
Type childStateType = childRes.getResourceStateType();
String childComponentName = JavaCodeGenerator.getComponentName(childRes);
TypeDeclaration childComponent = componentMap.get(childComponentName);
if (DataConstraintModel.typeJson.isAncestorOf(childStateType)) {
replaceJsonTermWithConstructorInvocation(updateExp, childStateType, childComponentName, childComponent);
}
}
// Add statements to the update method.
String[] sideEffects = new String[] {""};
String curState = updateExp.toImplementation(sideEffects);
String updateStatement;
if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) {
updateStatement = sideEffects[0];
} else {
updateStatement = sideEffects[0] + "this.value = " + curState + ";";
}
if (update.getBody() == null || !update.getBody().getStatements().contains(updateStatement)) {
update.addFirstStatement(updateStatement);
}
}
if (resToCh.getDestination().getIndegree() > 1
|| (resToCh.getDestination().getIndegree() == 1 && ch.getInputChannelMembers().iterator().next().getStateTransition().isRightPartial())) {
// update a cache of src side resource (when incoming edges are multiple)
String cacheStatement = "this." + JavaCodeGenerator.toVariableName(srcResourceName) + " = " + JavaCodeGenerator.toVariableName(srcResourceName) + ";";
if (update.getBody() == null || !update.getBody().getStatements().contains(cacheStatement)) {
update.addStatement(cacheStatement);
}
}
if (((StoreAttribute) dst.getAttribute()).isStored()) {
// returns the current state stored in a field.
MethodDeclaration getter = null;
if (JavaCodeGenerator.generatesComponent(dst.getResourceHierarchy())) {
getter = getMethod(dstComponent, "getValue");
} else {
getter = getGetterMethod(dstComponent, dstResourceName);
}
if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) {
Type resourceType = dst.getResourceStateType();
if (dst.getResourceHierarchy().getNumParameters() == 0) {
if (model.isPrimitiveType(resourceType)) {
getter.addStatement("return value;");
} else {
// copy the current state to be returned as a 'value'
String implTypeName = resourceType.getImplementationTypeName();
getter.addStatement("return new " + implTypeName + "(value);");
}
} else {
String[] sideEffects = new String[] {""};
if (DataConstraintModel.typeList.isAncestorOf(dst.getParent().getResourceStateType())) {
Term selector = new Term(DataConstraintModel.get);
selector.addChild(new Field("value"));
selector.addChild(dst.getSelectors().get(dst.getSelectors().size() - 1).getExpression());
String curState = selector.toImplementation(sideEffects);
getter.addStatement(sideEffects[0] + "return " + curState + ";");
} else if (DataConstraintModel.typeMap.isAncestorOf(dst.getParent().getResourceStateType())) {
Term selector = new Term(DataConstraintModel.lookup);
selector.addChild(new Field("value"));
selector.addChild(dst.getSelectors().get(dst.getSelectors().size() - 1).getExpression());
String curState = selector.toImplementation(sideEffects);
getter.addStatement(sideEffects[0] + "return " + curState + ";");
}
}
}
}
// src side (for a chain of update method invocations)
ChannelMember in = null;
for (ChannelMember cm: ch.getInputChannelMembers()) {
if (cm.getResource().equals(src.getOutSideResource())) {
in = cm;
break;
}
}
for (MethodDeclaration srcUpdate: getUpdateMethods(srcComponent)) {
String refParams = "";
Set<ResourcePath> referredSet = referredResources.get(srcUpdate);
for (ChannelMember rc: ch.getReferenceChannelMembers()) {
// to get the value of reference member.
ResourcePath ref = rc.getResource();
if (referredSet == null) {
referredSet = new HashSet<>();
referredResources.put(srcUpdate, referredSet);
}
if (!dst.getInSideResources().contains(ref)) {
String refVarName = ref.getResourceName();
if (!referredSet.contains(ref)) {
referredSet.add(ref);
Expression refGetter = JavaCodeGenerator.pullAccessor.getCurrentStateAccessorFor(rc, in);
String[] sideEffects = new String[] {""};
String refExp = refGetter.toImplementation(sideEffects);
String refTypeName = ref.getResourceStateType().getInterfaceTypeName();
srcUpdate.addFirstStatement(sideEffects[0] + refTypeName + " " + refVarName + " = " + refExp + ";");
}
refParams += ", " + refVarName;
}
}
srcUpdate.addStatement("this." + dstResourceName + ".update" + srcComponent.getTypeName() + "(value" + refParams + ");");
}
for (MethodDeclaration srcInput: getInputMethods(srcComponent, src, model)) {
String refParams = "";
Set<ResourcePath> referredSet = referredResources.get(srcInput);
for (ChannelMember rc: ch.getReferenceChannelMembers()) {
// to get the value of reference member.
ResourcePath ref = rc.getResource();
if (referredSet == null) {
referredSet = new HashSet<>();
referredResources.put(srcInput, referredSet);
}
if (!dst.getInSideResources().contains(ref)) {
String refVarName = ref.getResourceName();
if (!referredSet.contains(ref)) {
referredSet.add(ref);
Expression refGetter = JavaCodeGenerator.pullAccessor.getCurrentStateAccessorFor(rc, in);
String[] sideEffects = new String[] {""};
String refExp = refGetter.toImplementation(sideEffects);
String refTypeName = ref.getResourceStateType().getInterfaceTypeName();
srcInput.addFirstStatement(sideEffects[0] + refTypeName + " " + refVarName + " = " + refExp + ";");
}
refParams += ", " + refVarName;
}
}
srcInput.addStatement("this." + dstResourceName + ".update" + srcComponent.getTypeName() + "(value" + refParams + ");");
}
} else if ((pushPull.getOptions().get(0) != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) {
// for pull (or push/pull) data transfer
if (dstComponent == null) {
String dstParentResourceName = JavaCodeGenerator.getComponentName(dst.getResourceHierarchy().getParent());
dstComponent = componentMap.get(dstParentResourceName);
}
MethodDeclaration getter = null;
if (JavaCodeGenerator.generatesComponent(dst.getResourceHierarchy())) {
getter = getMethod(dstComponent, "getValue");
} else {
getter = getGetterMethod(dstComponent, dstResourceName);
}
if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) {
boolean isContainedPush = false;
Map<ChannelMember, IResourceStateAccessor> inputResourceToStateAccessor = new HashMap<>();
for (Edge chToRes2: dst.getInEdges()) {
DataTransferChannel ch2 = ((ChannelNode) chToRes2.getSource()).getChannel();
for (Edge resToCh2: chToRes2.getSource().getInEdges()) {
DataFlowEdge dIn = (DataFlowEdge) resToCh2;
ChannelMember in = null;
for (ChannelMember cm: ch2.getInputChannelMembers()) {
if (cm.getResource().equals(((ResourceNode) dIn.getSource()).getOutSideResource())) {
in = cm;
break;
}
}
if (((PushPullAttribute) dIn.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) {
isContainedPush = true;
inputResourceToStateAccessor.put(in, JavaCodeGenerator.pushAccessor);
} else {
inputResourceToStateAccessor.put(in, JavaCodeGenerator.pullAccessor);
}
}
}
// for reference channel members
for (ChannelMember c: ch.getReferenceChannelMembers()) {
inputResourceToStateAccessor.put(c, JavaCodeGenerator.pullAccessor); // by pull data transfer
}
String[] sideEffects = new String[] {""};
// generate a return statement.
// An input resource is outside.
if (!isContainedPush) {
// All incoming edges are in PULL style.
String curState = ch.deriveUpdateExpressionOf(out, JavaCodeGenerator.pullAccessor).toImplementation(sideEffects);
getter.addStatement(sideEffects[0] + "return " + curState + ";");
} else {
// At least one incoming edge is in PUSH style.
String curState = ch.deriveUpdateExpressionOf(out, JavaCodeGenerator.pullAccessor, inputResourceToStateAccessor).toImplementation(sideEffects);
getter.addStatement(sideEffects[0] + "return " + curState + ";");
}
}
if (outsideInputResource) {
// Update fields to refer to outside resources.
Map<ChannelMember, Entry<ResourcePath, Set<ChannelMember>>> resourcePaths = ch.fillOutsideResourcePaths(out, JavaCodeGenerator.pullAccessor);
if (resourcePaths != null && resourcePaths.size() > 0) {
for (ChannelMember outsideMember: resourcePaths.keySet()) {
ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey();
if (!JavaCodeGenerator.generatesComponent(outsidePath.getResourceHierarchy())) {
outsidePath = outsidePath.getParent();
}
String outsideResName = JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(outsidePath.getResourceHierarchy()));
Set<ChannelMember> dependingMembers = resourcePaths.get(outsideMember).getValue();
for (ChannelMember dependingMember: dependingMembers) {
ResourcePath dependingRes = dependingMember.getResource();
ResourceNode dependingNode = null;
PushPullAttribute pushPull2 = null;
for (Edge resToCh2: resToCh.getDestination().getInEdges()) {
if (((ResourceNode) resToCh2.getSource()).getOutSideResource().equals(dependingRes)) {
dependingNode = (ResourceNode) resToCh2.getSource();
pushPull2 = (PushPullAttribute) resToCh.getAttribute();
}
}
TypeDeclaration dependingComponent = null;
if (JavaCodeGenerator.generatesComponent(dependingRes.getResourceHierarchy())) {
String dependingResourceName = JavaCodeGenerator.getComponentName(dependingRes.getResourceHierarchy());
dependingComponent = componentMap.get(dependingResourceName);
} else {
String dependingParentResourceName = JavaCodeGenerator.getComponentName(dependingRes.getParent().getResourceHierarchy());
dependingComponent = componentMap.get(dependingParentResourceName);
}
Expression outsideExp = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(outsidePath, null);
if (JavaCodeGenerator.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(JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(dependingRes.getResourceHierarchy()))));
} else {
// ToDo.
}
}
if (dstComponent == dependingComponent) {
// In the common parent.
if (dependingNode != null) { // Inspect further dependency.
for (Edge chToRes2: dependingNode.getInEdges()) {
DataTransferChannel ch2 = ((ChannelNode) chToRes2.getSource()).getChannel();
if (ch2.getInputChannelMembers().size() == 0) {
// In an input method of the parent component.
MethodDeclaration input = getInputMethod(dependingComponent, ch2.getOutputChannelMembers().iterator().next());
String[] sideEffects = new String[] {""};
String outsideAccessor = outsideExp.toImplementation(sideEffects);
input.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // change the reference field.
// Update constructor.
MethodDeclaration constructor = getConstructor(dependingComponent);
constructor.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // initialize the reference field.
} else {
boolean isPush = true;
for (Edge resToCh2: chToRes2.getSource().getInEdges()) {
if (((PushPullAttribute) resToCh2.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) {
isPush = false;
break;
}
}
if (isPush) {
String[] sideEffects = new String[] {""};
String outsideAccessor = outsideExp.toImplementation(sideEffects);
for (Edge resToCh2: chToRes2.getSource().getInEdges()) {
// In an update method of the parent component.
ResourceNode dependingResSrc = (ResourceNode) resToCh2.getSource();
MethodDeclaration update = getUpdateMethod(dependingComponent, JavaCodeGenerator.getComponentName(dependingResSrc.getResourceHierarchy()));
update.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // change the reference field.
}
// Update constructor.
MethodDeclaration constructor = getConstructor(dependingComponent);
constructor.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // initialize the reference field.
}
}
}
}
} else {
if (pushPull2.getOptions().get(0) == PushPullValue.PUSH) {
// In an update method of the destination component.
MethodDeclaration update = getUpdateMethod(dstComponent, JavaCodeGenerator.getComponentName(dependingRes.getResourceHierarchy()));
String[] sideEffects = new String[] {""};
String outsideAccessor = outsideExp.toImplementation(sideEffects);
update.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // change the reference field.
// Update constructor.
MethodDeclaration constructor = getConstructor(dstComponent);
constructor.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // initialize the reference field.
}
}
}
}
}
}
}
}
}
}
}
}
// for source nodes
TypeDeclaration mainComponent = componentMap.get(JavaCodeGenerator.mainTypeName);
for (ResourceHierarchy resource: model.getResourceHierarchies()) {
// ResourceNode resource = (ResourceNode) n;
String resourceName = JavaCodeGenerator.getComponentName(resource);
TypeDeclaration component = componentMap.get(resourceName);
if (component != null) {
// state getter method
Type resourceType = JavaCodeGenerator.getImplStateType(resource);
MethodDeclaration stateGetter = getMethod(component, "getValue");
if (stateGetter != null && (stateGetter.getBody() == null || stateGetter.getBody().getStatements().size() == 0)) {
if (model.isPrimitiveType(resourceType)) {
// primitive type
stateGetter.addStatement("return value;");
} else {
if (resource.getChildren() != null && resource.getChildren().size() == 1 && resource.getChildren().iterator().next().getNumParameters() > 0) {
// list or map
String implTypeName = resourceType.getImplementationTypeName();
// copy the current state to be returned as a 'value'
stateGetter.addStatement("return new " + implTypeName + "(value);");
} else {
if (resource.getChildren() == null || resource.getChildren().size() == 0) {
// a leaf resource
String implTypeName = resourceType.getImplementationTypeName();
stateGetter.addStatement("return new " + implTypeName + "(value);");
} else {
Term composer = null;
Term composerSub = new Constant(DataConstraintModel.nil);
composerSub.setType(DataConstraintModel.typeMap);
for (ResourceHierarchy child: resource.getChildren()) {
String childTypeName = JavaCodeGenerator.getComponentName(child);
String fieldName = JavaCodeGenerator.toVariableName(childTypeName);
Term childGetter = null;
if ((child.getChildren() == null || child.getChildren().size() == 0) && child.getNumParameters() == 0) {
// the child is not a class
childGetter = new Term(new Symbol("get" + childTypeName, 1, Symbol.Type.METHOD));
childGetter.addChild(new Constant("this"));
} else {
// the child is a class
childGetter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD));
childGetter.addChild(new Field(fieldName, JavaCodeGenerator.getImplStateType(child)));
}
composer = new Term(DataConstraintModel.insert);
composer.addChild(composerSub);
composer.addChild(new Constant("\"" + fieldName + "\"", DataConstraintModel.typeString)); // key
composer.addChild(childGetter); // value
composer.setType(DataConstraintModel.typeMap);
composerSub = composer;
}
composer.setType(stateGetter.getReturnType());
String[] sideEffects = new String[] {null};
String returnValue = composer.toImplementation(sideEffects);
if (sideEffects[0] != null) {
stateGetter.addStatement(sideEffects[0] + "return " + returnValue+ ";");
} else {
stateGetter.addStatement("return " + returnValue+ ";");
}
}
}
}
}
// child getter method
if (resource.getChildren().size() > 0) {
for (ResourceHierarchy child: resource.getChildren()) {
String methodName = "get" + JavaCodeGenerator.getComponentName(child);
MethodDeclaration childGetter = getMethod(component, methodName);
if (childGetter != null && (childGetter.getBody() == null || childGetter.getBody().getStatements().size() == 0)) {
if (DataConstraintModel.typeList.isAncestorOf(resource.getResourceStateType())) {
Term selector = new Term(DataConstraintModel.get);
selector.addChild(new Field("value"));
selector.addChild(new Variable(childGetter.getParameters().get(childGetter.getParameters().size() - 1).getName()));
selector.setType(childGetter.getReturnType());
String[] sideEffects = new String[] {null};
String returnValue = selector.toImplementation(sideEffects);
if (sideEffects[0] != null) childGetter.addStatement(sideEffects[0]);
childGetter.addStatement("return " + returnValue + ";");
} else if (DataConstraintModel.typeMap.isAncestorOf(resource.getResourceStateType())) {
Term selector = new Term(DataConstraintModel.lookup);
selector.addChild(new Field("value"));
selector.addChild(new Variable(childGetter.getParameters().get(childGetter.getParameters().size() - 1).getName()));
selector.setType(childGetter.getReturnType());
String[] sideEffects = new String[] {null};
String returnValue = selector.toImplementation(sideEffects);
if (sideEffects[0] != null) childGetter.addStatement(sideEffects[0]);
childGetter.addStatement("return " + returnValue+ ";");
} else {
String fieldName = JavaCodeGenerator.getComponentName(child);
String returnValue = JavaCodeGenerator.toVariableName(fieldName);
childGetter.addStatement("return this." + returnValue + ";");
}
}
}
}
}
// methods for input events
Map<DataTransferChannel, Set<ChannelMember>> ioChannelsAndMembers = getIOChannelsAndMembers(resource, model);
for (Map.Entry<DataTransferChannel, Set<ChannelMember>> entry: ioChannelsAndMembers.entrySet()) {
Set<ChannelMember> outs = entry.getValue();
for (ChannelMember out: outs) {
MethodDeclaration input = null;
if (JavaCodeGenerator.generatesComponent(resource)) {
// A component is generated for this resource.
input = getInputMethod(component, out);
} else {
// No component is generated for this resource.
ResourceHierarchy parent = resource.getParent();
if (parent != null) {
TypeDeclaration parentType = componentMap.get(JavaCodeGenerator.getComponentName(parent));
input = getInputMethod(parentType, out);
}
}
if (input != null) {
// In each resource
Expression updateExp = entry.getKey().deriveUpdateExpressionOf(out, JavaCodeGenerator.pushAccessor);
// Replace Json constructor with a constructor of the child resource.
ResourceHierarchy outRes = out.getResource().getResourceHierarchy();
if (outRes.getChildren().size() == 1 && outRes.getChildren().iterator().next().getNumParameters() > 0) {
ResourceHierarchy childRes = outRes.getChildren().iterator().next();
Type childStateType = childRes.getResourceStateType();
String childComponentName = JavaCodeGenerator.getComponentName(childRes);
TypeDeclaration childComponent = componentMap.get(childComponentName);
if (DataConstraintModel.typeJson.isAncestorOf(childStateType)) {
replaceJsonTermWithConstructorInvocation(updateExp, childStateType, childComponentName, childComponent);
}
}
// Add statements to the input method.
String[] sideEffects = new String[] {""};
String newState = updateExp.toImplementation(sideEffects);
if (JavaCodeGenerator.generatesComponent(resource)) {
String updateStatement;
if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) {
updateStatement = sideEffects[0];
} else {
updateStatement = sideEffects[0] + "this.value = " + newState + ";";
}
if (input.getBody() == null || !input.getBody().getStatements().contains(updateStatement)) {
input.addFirstStatement(updateStatement);
}
} else {
String updateStatement = null;
if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) {
// ToDo.
updateStatement = sideEffects[0];
} else {
if (DataConstraintModel.typeList.isAncestorOf(resource.getParent().getResourceStateType())) {
Term selector = new Term(DataConstraintModel.set);
selector.addChild(new Field("value"));
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 = sideEffects[0] + sideEffects2[0];
} else if (DataConstraintModel.typeMap.isAncestorOf(resource.getParent().getResourceStateType())) {
Term selector = new Term(DataConstraintModel.insert);
selector.addChild(new Field("value"));
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 = sideEffects[0] + sideEffects2[0];
} else {
updateStatement = sideEffects[0] + "this." + JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(resource)) + " = " + newState + ";";
}
if (updateStatement != null && (input.getBody() == null || !input.getBody().getStatements().contains(updateStatement))) {
input.addFirstStatement(updateStatement);
}
}
}
// In the main type
if (mainComponent != null) {
MethodDeclaration inputAccessor = getMethod(mainComponent, input.getName());
if (inputAccessor != null) {
Expression resExp = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(out.getResource(), null);
String args = "";
String delimiter = "";
if (resExp instanceof Term) {
// to access the parent
if (((Term) resExp).getChildren().size() > 1 && ((Term) resExp).getChild(1) instanceof Variable) {
args += delimiter + ((Variable)((Term) resExp).getChild(1)).getName();
delimiter = ", ";
}
resExp = ((Term) resExp).getChild(0);
}
String resourceAccess = resExp.toImplementation(new String[] {null});
if (out.getStateTransition().getMessageExpression() instanceof Term) {
Term message = (Term) out.getStateTransition().getMessageExpression();
for (Variable var: message.getVariables().values()) {
args += delimiter + var.getName();
delimiter = ", ";
}
}
inputAccessor.addStatement(resourceAccess + "." + input.getName() + "(" + args + ");");
}
}
}
}
}
}
} catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork
| InvalidMessage | UnificationFailed | ValueUndefined e1) {
e1.printStackTrace();
}
return codes;
}
private static void replaceJsonTermWithConstructorInvocation(Expression exp, Type replacedJsonType, String replacingClassName, TypeDeclaration childComponent) {
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().equals(replacedJsonType)) {
String constructorInvocation = "new " + replacingClassName + "(";
MethodDeclaration childConstructor = getConstructor(childComponent);
String delimiter = "";
for (VariableDeclaration var: childConstructor.getParameters()) {
JsonAccessor jsonMember = new JsonAccessor(DataConstraintModel.dot);
jsonMember.addChild(jsonTerm);
jsonMember.addChild(new Constant("\"" + var.getName() + "\""));
Expression param = jsonMember.reduce();
if (param != null) {
constructorInvocation = constructorInvocation + delimiter + param.toImplementation(new String[] {""});
} else {
constructorInvocation = constructorInvocation + delimiter + var.getName();
}
delimiter = ", ";
}
constructorInvocation += ")";
((Term) exp).replaceSubTerm(termEnt.getKey(), new Constant(constructorInvocation));
subTerms = ((Term) exp).getSubTerms(Term.class);
termEntItr = subTerms.entrySet().iterator();
}
}
}
private static MethodDeclaration getConstructor(TypeDeclaration component) {
for (MethodDeclaration m: component.getMethods()) {
if (m.isConstructor()) return m;
}
return null;
}
private static MethodDeclaration getUpdateMethod(TypeDeclaration component, String fromResName) {
for (MethodDeclaration m: component.getMethods()) {
if (m.getName().equals("update" + fromResName)) return m;
}
return null;
}
private static List<MethodDeclaration> getUpdateMethods(TypeDeclaration component) {
List<MethodDeclaration> updates = new ArrayList<>();
for (MethodDeclaration m: component.getMethods()) {
if (m.getName().startsWith("update")) {
updates.add(m);
}
}
return updates;
}
private static MethodDeclaration getGetterMethod(TypeDeclaration component, String resourceName) {
for (MethodDeclaration m: component.getMethods()) {
if (m.getName().startsWith("get" + resourceName)) return m;
}
return null;
}
private static Map<DataTransferChannel, Set<ChannelMember>> getIOChannelsAndMembers(ResourceHierarchy resource, DataTransferModel model) {
Map<DataTransferChannel, Set<ChannelMember>> ioChannelsAndMembers = new HashMap<>();
for (Channel c: model.getIOChannels()) {
DataTransferChannel ch = (DataTransferChannel) c;
// I/O channel
for (ChannelMember out: ch.getOutputChannelMembers()) {
if (resource.equals(out.getResource().getResourceHierarchy())) {
if (out.getStateTransition().getMessageExpression() instanceof Term || out.getStateTransition().getMessageExpression() instanceof Variable) {
Set<ChannelMember> channelMembers = ioChannelsAndMembers.get(ch);
if (channelMembers == null) {
channelMembers = new HashSet<>();
ioChannelsAndMembers.put(ch, channelMembers);
}
channelMembers.add(out);
}
}
}
}
return ioChannelsAndMembers;
}
private static List<MethodDeclaration> getInputMethods(TypeDeclaration component, ResourceNode resource, DataTransferModel model) {
List<MethodDeclaration> inputs = new ArrayList<>();
for (Channel c: model.getIOChannels()) {
DataTransferChannel channel = (DataTransferChannel) c;
// I/O channel
for (ChannelMember out: channel.getOutputChannelMembers()) {
if (resource.getInSideResources().contains(out.getResource())) {
MethodDeclaration input = getInputMethod(component, out);
inputs.add(input);
}
}
}
return inputs;
}
private static List<MethodDeclaration> getInputMethods(TypeDeclaration component, ResourceHierarchy resource, DataTransferModel model) {
List<MethodDeclaration> inputs = new ArrayList<>();
for (Channel c: model.getIOChannels()) {
DataTransferChannel channel = (DataTransferChannel) c;
// I/O channel
for (ChannelMember out: channel.getOutputChannelMembers()) {
if (resource.equals(out.getResource().getResourceHierarchy())) {
MethodDeclaration input = getInputMethod(component, out);
inputs.add(input);
}
}
}
return inputs;
}
private static MethodDeclaration getInputMethod(TypeDeclaration component, ChannelMember cm) {
MethodDeclaration input = null;
if (cm.getStateTransition().getMessageExpression() instanceof Term) {
Term message = (Term) cm.getStateTransition().getMessageExpression();
input = getMethod(component, message.getSymbol().getImplName());
} else if (cm.getStateTransition().getMessageExpression() instanceof Variable) {
Variable message = (Variable) cm.getStateTransition().getMessageExpression();
input = getMethod(component, message.getName());
}
return input;
}
private static MethodDeclaration getMethod(TypeDeclaration component, String methodName) {
for (MethodDeclaration m: component.getMethods()) {
if (m.getName().equals(methodName)) return m;
}
return null;
}
}