package generators;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Stack;
import algorithms.TypeInference;
import code.ast.CodeUtil;
import code.ast.CompilationUnit;
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.DataTransferModel;
import models.dataFlowModel.DataTransferChannel;
import models.dataFlowModel.PushPullAttribute;
import models.dataFlowModel.PushPullValue;
import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork;
import models.dataFlowModel.ChannelNode;
import models.dataFlowModel.DataFlowEdge;
import models.dataFlowModel.DataFlowGraph;
import models.dataFlowModel.ResourceNode;
import models.dataFlowModel.StoreAttribute;
import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor;
public class JerseyMethodBodyGenerator {
private static String baseURL = "http://localhost:8080";
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 {
Set<MethodDeclaration> chainedCalls = new HashSet<>();
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();
ChannelNode directDstChNode = (ChannelNode) resToCh.getDestination();
DataTransferChannel directDstCh = directDstChNode.getChannel();
// 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 from src:ResourceNode to dst:ResourceNode.
ResourceNode dst = (ResourceNode) chToRes.getDestination();
String srcResourceName = JerseyCodeGenerator.getComponentName(src.getResourceHierarchy());
String dstResourceName = JerseyCodeGenerator.getComponentName(dst.getResourceHierarchy());
TypeDeclaration srcComponent = componentMap.get(srcResourceName);
TypeDeclaration dstComponent = componentMap.get(dstResourceName);
// DataTransferChannel ch = ((ChannelNode) resToCh.getDestination()).getChannel();
ChannelNode chNode = (ChannelNode) chToRes.getSource();
DataTransferChannel ch = chNode.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;
ChannelMember in = null;
for (ChannelMember cm: directDstCh.getInputChannelMembers()) {
if (src.getOutSideResources().contains(cm.getResource())) {
in = cm;
if (cm.isOutside()) {
outsideInputResource = true; // Regarded as pull transfer.
break;
}
}
}
// Check if the output resource is outside of the channel scope.
boolean outsideOutputResource = out.isOutside();
// 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 ((pushPull.getSelectedOption() == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) {
// for push data transfer
MethodDeclaration update = null;
if (dstComponent == null) {
String dstParentResourceName = JerseyCodeGenerator.getComponentName(dst.getResourceHierarchy().getParent());
dstComponent = componentMap.get(dstParentResourceName);
update = getUpdateMethod(dstComponent, dstResourceName, srcResourceName);
} else {
update = getUpdateMethod(dstComponent, null, srcResourceName);
}
if (((StoreAttribute) dst.getAttribute()).isStored()) {
// update stored state of dst side resource (when every incoming edge is in push style)
Term unifiedMassage = null;
if (directDstCh != ch) {
unifiedMassage = directDstCh.fillOutsideResourcePaths(out, JerseyCodeGenerator.pushAccessor, null).getValue();
}
Expression updateExp = null;
if (ch.getReferenceChannelMembers().size() == 0) {
Term message = ch.fillOutsideResourcePaths(out, JerseyCodeGenerator.pushAccessor, null).getValue();
if (unifiedMassage == null) {
unifiedMassage = message;
} else {
unifiedMassage = (Term) unifiedMassage.unify(message);
}
updateExp = ch.deriveUpdateExpressionOf(out, unifiedMassage, JerseyCodeGenerator.pushAccessor);
} else {
// if there exists one or more reference channel member.
HashMap<ChannelMember, IResourceStateAccessor> inputResourceToStateAccessor = new HashMap<>();
for (ChannelMember c: ch.getInputChannelMembers()) {
inputResourceToStateAccessor.put(c, JerseyCodeGenerator.pushAccessor);
}
for (ChannelMember c: ch.getReferenceChannelMembers()) {
inputResourceToStateAccessor.put(c, JerseyCodeGenerator.refAccessor);
}
Term message = ch.fillOutsideResourcePaths(out, JerseyCodeGenerator.pushAccessor, inputResourceToStateAccessor).getValue();
if (unifiedMassage == null) {
unifiedMassage = message;
} else {
unifiedMassage = (Term) unifiedMassage.unify(message);
}
updateExp = ch.deriveUpdateExpressionOf(out, unifiedMassage, JerseyCodeGenerator.pushAccessor);
}
// 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.getChildren().iterator().next();
Set<ResourceHierarchy> children;
do {
if (JerseyCodeGenerator.generatesComponent(descendantRes)) break;
children = descendantRes.getChildren();
} while (children != null && children.size() == 1 && (descendantRes = children.iterator().next()) != null);
Type descendantStateType = descendantRes.getResourceStateType();
String descendantComponentName = JerseyCodeGenerator.getComponentName(descendantRes);
TypeDeclaration descendantComponent = componentMap.get(descendantComponentName);
if (DataConstraintModel.typeJson.isAncestorOf(descendantStateType)) {
replaceJsonTermWithConstructorInvocation(updateExp, descendantStateType, descendantComponentName, descendantComponent);
}
}
// Replace the type of the state field.
Type fieldType = JerseyCodeGenerator.getImplStateType(outRes);
if (updateExp instanceof Term) {
((Term) updateExp).setType(fieldType);
for (Map.Entry<Position, Variable> varEnt: ((Term) updateExp).getVariables().entrySet()) {
if (varEnt.getValue().getName().equals("value")) {
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 (JerseyCodeGenerator.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] + "this.value = " + newState + ";";
}
} else {
if (sideEffects[0] != null) {
updateStatement = sideEffects[0];
updateStatement = updateStatement.replace(".value", "." + JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(outRes)));
if (updateStatement.endsWith("\n")) {
updateStatement = updateStatement.substring(0, updateStatement.length() - 1);
}
}
if (DataConstraintModel.typeList.isAncestorOf(outRes.getParent().getResourceStateType())) {
Term selector = new Term(DataConstraintModel.set);
selector.addChild(new Field("value"));
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(outRes.getParent().getResourceStateType())) {
Term selector = new Term(DataConstraintModel.insert);
selector.addChild(new Field("value"));
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())) {
updateStatement += "this." + JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(outRes)) + " = " + newState + ";";
}
}
// add an update statement of the state of dst side resource.
if (numOfOutResourcesWithTheSameHierarchy == 1) {
update.addFirstStatement(updateStatement);
} else {
Term conditions = null;
int v = 1;
Map<ChannelMember, Entry<ResourcePath, Set<ChannelMember>>> resourcePaths = ch.fillOutsideResourcePaths(out, JerseyCodeGenerator.pushAccessor);
for (Expression pathParam: out.getResource().getPathParams()) {
if (pathParam instanceof Variable) {
String selfParamName = ((Variable) pathParam).getName();
Expression arg = null;
for (Selector selector: ch.getAllSelectors()) {
if (selector.getExpression() instanceof Variable) {
Variable selVar = (Variable) selector.getExpression();
if (selVar.getName().equals(selfParamName)) {
arg = selVar;
break;
}
}
}
if (arg == null) {
ResourcePath filledPath = resourcePaths.get(out).getKey();
arg = filledPath.getPathParams().get(v - 1);
}
Term condition = new Term(DataConstraintModel.eq, new Expression[] {
new Parameter("self" + (v > 1 ? v : ""), DataConstraintModel.typeString),
arg});
if (conditions == null) {
conditions = condition;
} else {
conditions = new Term(DataConstraintModel.and, new Expression[] {
conditions,
condition});
}
}
v++;
}
String ifStatement = "if (" + conditions.toImplementation(new String[] {""})+ ") {\n";
update.addFirstStatement(ifStatement + "\t" + updateStatement.replace("\n", "\n\t") + "\n}");
}
}
// Calculate in-degree (PUSH transfer) of the destination resource.
int inDegree = 0;
Set<Edge> inEdges = new HashSet<>();
inEdges.addAll(chNode.getInEdges());
for (ChannelNode ancestor: chNode.getAncestors()) {
inEdges.addAll(ancestor.getInEdges());
}
for (ChannelNode descendant: chNode.getDescendants()) {
inEdges.addAll(descendant.getInEdges());
}
for (Edge resToCh2: inEdges) {
DataFlowEdge df =(DataFlowEdge) resToCh2;
if (((PushPullAttribute) df.getAttribute()).getSelectedOption() == PushPullValue.PUSH) {
inDegree++;
}
}
if (inDegree > 1
|| (inDegree == 1 && directDstCh.getInputChannelMembers().iterator().next().getStateTransition().isRightPartial())) {
// update a cache of src side resource (when incoming edges are multiple)
String cacheStatement = "this." + JerseyCodeGenerator.toVariableName(srcResourceName) + " = " + JerseyCodeGenerator.toVariableName(srcResourceName) + ";";
if (update.getBody() == null || !update.getBody().getStatements().contains(cacheStatement)) {
update.addStatement(cacheStatement);
}
}
// For a post/put REST API.
if (outsideOutputResource
|| (in.getResource().getCommonPrefix(out.getResource()) == null && JerseyCodeGenerator.differentTreesAsDifferentServices)) {
// Inter-services
if (dst.getResourceHierarchy().getParent() != null) {
// If not a root resource.
TypeDeclaration rootComponent = componentMap.get(JerseyCodeGenerator.getComponentName(dst.getResourceHierarchy().getRoot()));
MethodDeclaration update2 = update;
update = getMethod(rootComponent, update2.getName()); // get the accessor to the update method.
// To make the accessor call the update method.
ResourcePath outResPath = new ResourcePath(out.getResource());
for (int i = 0; i < outResPath.getPathParams().size(); i++) {
Parameter pathParam = new Parameter(update.getParameters().get(i).getName());
outResPath.replacePathParam(i, pathParam, null);
}
Expression resExp = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(outResPath, outResPath.getRoot());
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[] {""});
int v = 0;
for (VariableDeclaration var: update2.getParameters()) {
if (v < out.getResource().getPathParams().size()) {
if (out.getResource().getPathParams().get(v) instanceof Variable) {
args += delimiter + ((Variable) out.getResource().getPathParams().get(v)).getName();
} else if (out.getResource().getPathParams().get(v) instanceof Term) {
args += delimiter + "v" + (v + 1);
}
} else {
args += delimiter + var.getName();
}
delimiter = ", ";
v++;
}
update.addStatement(resourceAccess + "." + update2.getName() + "(" + args + ");");
}
// to convert a json param to a tuple, pair or map object.
for (VariableDeclaration param: update.getParameters()) {
Type paramType = param.getType();
String paramName = param.getName();
String paramConverter = "";
if (DataConstraintModel.typeList.isAncestorOf(paramType) && paramType != DataConstraintModel.typeList) {
Type compType = TypeInference.getListComponentType(paramType);
if (DataConstraintModel.typeTuple.isAncestorOf(compType)) {
param.setType(DataConstraintModel.typeListStr);
param.setName(paramName + "_json");
paramConverter += paramType.getInterfaceTypeName() + " " + paramName + " = new " + paramType.getImplementationTypeName() + "();\n";
paramConverter += "for (String str: " + param.getName() + ") {\n";
String mapTypeName = convertFromEntryToMapType(compType);
paramConverter += "\t" + mapTypeName + " i = new ObjectMapper().readValue(str, HashMap.class);\n";
paramConverter += "\t" + paramName + ".add(" + getCodeForConversionFromMapToTuple(compType, "i") + ");\n";
paramConverter += "}";
update.addThrow("JsonProcessingException");
} else if (DataConstraintModel.typePair.isAncestorOf(compType)) {
param.setType(DataConstraintModel.typeListStr);
param.setName(paramName + "_json");
paramConverter += paramType.getInterfaceTypeName() + " " + paramName + " = new " + paramType.getImplementationTypeName() + "();\n";
paramConverter += "for (String str: " + param.getName() + ") {\n";
String mapTypeName = convertFromEntryToMapType(compType);
paramConverter += "\t" + mapTypeName + " i = new ObjectMapper().readValue(str, HashMap.class);\n";
paramConverter += "\t" + paramName + ".add(" + getCodeForConversionFromMapToPair(compType, "i") + ");\n";
paramConverter += "}";
update.addThrow("JsonProcessingException");
} else if (DataConstraintModel.typeMap.isAncestorOf(compType)) {
param.setType(DataConstraintModel.typeListStr);
// To do.
}
} else if (DataConstraintModel.typeTuple.isAncestorOf(paramType)) {
param.setType(DataConstraintModel.typeString);
param.setName(paramName + "_json");
paramConverter += paramType.getInterfaceTypeName() + " " + paramName + ";\n";
paramConverter += "{\n";
String mapTypeName = convertFromEntryToMapType(paramType);
paramConverter += "\t" + mapTypeName + " i = new ObjectMapper().readValue(" + paramName + "_json" + ", HashMap.class);\n";
paramConverter += "\t" + paramName + " = " + getCodeForConversionFromMapToTuple(paramType, "i") + ";\n";
paramConverter += "}";
update.addThrow("JsonProcessingException");
} else if (DataConstraintModel.typePair.isAncestorOf(paramType)) {
param.setType(DataConstraintModel.typeString);
param.setName(paramName + "_json");
paramConverter += paramType.getInterfaceTypeName() + " " + paramName + ";\n";
paramConverter += "{\n";
String mapTypeName = convertFromEntryToMapType(paramType);
paramConverter += "\t" + mapTypeName + " i = new ObjectMapper().readValue(" + paramName + "_json" + ", HashMap.class);\n";
paramConverter += "\t" + paramName + " = " + getCodeForConversionFromMapToPair(paramType, "i") + ";\n";
paramConverter += "}";
update.addThrow("JsonProcessingException");
} else if (DataConstraintModel.typeMap.isAncestorOf(paramType)) {
param.setType(DataConstraintModel.typeString);
param.setName(paramName + "_json");
paramConverter += paramType.getInterfaceTypeName() + " " + paramName + " = " + "new " + paramType.getImplementationTypeName() + "();\n";
paramConverter += "{\n";
String mapTypeName = convertFromEntryToMapType(paramType);
paramConverter += "\t" + mapTypeName + " i = new ObjectMapper().readValue(" + paramName + "_json" + ", HashMap.class);\n";
paramConverter += "\t" + getCodeForConversionFromMapToMap(paramType, "i", paramName) + "\n";
paramConverter += "}";
update.addThrow("JsonProcessingException");
}
if (paramConverter.length() > 0 && !update.getBody().getStatements().contains(paramConverter)) {
update.addFirstStatement(paramConverter);
}
}
}
if (((StoreAttribute) dst.getAttribute()).isStored()) {
// returns the state stored in a field.
MethodDeclaration getter = null;
if (JerseyCodeGenerator.generatesComponent(dst.getResourceHierarchy())) {
getter = getMethod(dstComponent, "getValue");
} else {
getter = getGetterMethod(dstComponent, dstResourceName);
}
if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) {
if (dst.getResourceHierarchy().getNumParameters() == 0) {
if (JerseyCodeGenerator.generatesComponent(dst.getResourceHierarchy())) {
// dst has a component.
getter.addStatement("return value;");
} else {
// dst has no component.
String dstResName = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(dst.getResourceHierarchy()));
getter.addStatement("return " + dstResName + ";");
}
} 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)
String httpMethod = null;
if (out.getStateTransition().isRightUnary()) {
httpMethod = "put";
} else {
httpMethod = "post";
}
String srcName = null;
if (srcComponent == null) {
String srcParentResourceName = JerseyCodeGenerator.getComponentName(src.getResourceHierarchy().getParent());
srcComponent = componentMap.get(srcParentResourceName);
srcName = srcResourceName;
}
// For caller update methods
for (MethodDeclaration srcUpdate: getUpdateMethods(srcComponent, srcName)) {
if (srcUpdate != null) {
List<Map.Entry<Type, Map.Entry<String, String>>> params = new ArrayList<>();
ResourcePath dstRes = out.getResource();
// Values of channel parameters.
for (Selector selector: ch.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.
String srcFieldName = "this.value";
if (!JerseyCodeGenerator.generatesComponent(src.getResourceHierarchy())) {
srcFieldName = JerseyCodeGenerator.toVariableName(srcResourceName);
}
params.add(new AbstractMap.SimpleEntry<>(src.getResourceStateType(),
new AbstractMap.SimpleEntry<>(JerseyCodeGenerator.toVariableName(srcResourceName), srcFieldName)));
// Get the value of reference member to call the update method.
Set<ResourcePath> referredSet = referredResources.get(srcUpdate);
if (ch.getReferenceChannelMembers().size() > 0) {
for (ChannelMember rc: 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 (referredSet == null) {
referredSet = new HashSet<>();
referredResources.put(srcUpdate, referredSet);
}
if (!dst.getInSideResources().contains(ref)) {
String refResourceName = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(ref.getResourceHierarchy()));
Type refResourceType = ref.getResourceStateType();
if (!referredSet.contains(ref)) {
referredSet.add(ref);
String[] sideEffects = new String[] {""};
if (rc.isOutside()) {
List<String> pathParams = new ArrayList<>();
for (Expression pathExp: ref.getPathParams()) {
pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \"");
}
generatePullDataTransfer(srcUpdate, refResourceName, ref.getResourceHierarchy().toResourcePath(pathParams), refResourceType);
} else {
ResourcePath srcRes = in.getResource();
if (!JerseyCodeGenerator.generatesComponent(srcRes.getResourceHierarchy())) {
srcRes = srcRes.getParent();
}
Expression refGetter = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(ref, srcRes);
String refExp = refGetter.toImplementation(sideEffects);
String refTypeName = ref.getResourceStateType().getInterfaceTypeName();
srcUpdate.addFirstStatement(sideEffects[0] + refTypeName + " " + refResourceName + " = " + refExp + ";");
}
}
// Value of a reference side resource.
params.add(new AbstractMap.SimpleEntry<>(refResourceType, new AbstractMap.SimpleEntry<>(refResourceName, refResourceName)));
}
}
}
if (outsideOutputResource || (in.getResource().getCommonPrefix(dstRes) == null && JerseyCodeGenerator.differentTreesAsDifferentServices)) {
// Inter-servces
String[] sideEffects = new String[] {""};
List<String> pathParams = new ArrayList<>();
for (Expression pathExp: dstRes.getPathParams()) {
pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \"");
}
String srcResName = JerseyCodeGenerator.toVariableName(srcResourceName);
if (inDegree <= 1) {
srcResName = null;
}
Map<ChannelMember, Entry<ResourcePath, Set<ChannelMember>>> filledPaths = ch.fillOutsideResourcePaths(out, JerseyCodeGenerator.pushAccessor);
String dstPath = null;
if (filledPaths != null && filledPaths.get(out) != null) {
ResourcePath filledDstPath = filledPaths.get(out).getKey();
dstPath = filledDstPath.toResourcePath().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\"");
} else {
dstPath = dstRes.getResourceHierarchy().toResourcePath(pathParams);
}
// Call the update method.
if (!chainedCalls.contains(srcUpdate)) {
// The first call to an update method in this method
srcUpdate.addStatement(getHttpMethodParamsStatement(srcComponent.getTypeName(), params, true));
srcUpdate.addStatement("String result = " + getHttpMethodCallStatement(baseURL, dstPath, srcResName, httpMethod));
chainedCalls.add(srcUpdate);
} else {
// After the second time of call to update methods in this method
srcUpdate.addStatement(getHttpMethodParamsStatement(srcComponent.getTypeName(), params, false));
srcUpdate.addStatement("result = " + getHttpMethodCallStatement(baseURL, dstPath, srcResName, httpMethod));
}
if (descendantDstChannels.contains(chNode)) {
// For hierarchical channels (broadcasting push transfer).
if (ch.getSelectors() != null && ch.getSelectors().size() > 0) {
Expression selExp = ch.getSelectors().get(0).getExpression();
Type selType = null;
String varName = null;
if (selExp instanceof Variable) {
selType = ((Variable) selExp).getType();
varName = ((Variable) selExp).getName();
ChannelMember insideChMem = null;
for (ChannelMember cm :ch.getInputChannelMembers()) {
if (!cm.isOutside()) {
insideChMem = cm;
break;
}
}
if (insideChMem == null) {
for (ChannelMember cm :ch.getReferenceChannelMembers()) {
if (!cm.isOutside()) {
insideChMem = cm;
break;
}
}
}
if (insideChMem == null) {
for (ChannelMember cm :ch.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();
String parent = null;
if (JerseyCodeGenerator.generatesComponent(insideResPath.getResourceHierarchy())) {
Expression getter = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(insideResPath, src.getPrimaryResourcePath());
Term valueGetter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD));
valueGetter.addChild(getter);
parent = valueGetter.toImplementation(new String[] {});
} else {
parent = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(insideResPath, src.getPrimaryResourcePath()).toImplementation(new String[] {});
}
if (insideResPath != null) {
if (selType.equals(DataConstraintModel.typeInt)) {
// make a for loop (for a list) for broadcasting.
srcUpdate.addFirstStatement("for (int " + varName + " = 0; " + varName +" < " + parent + ".size(); " + varName + "++) {");
srcUpdate.addStatement("}");
} else if (selType.equals(DataConstraintModel.typeString)) {
// make a for loop (for a map) for broadcasting.
srcUpdate.addFirstStatement("for (String " + varName + ": " + parent + ".keySet()) {");
srcUpdate.addStatement("}");
}
}
} else if (selExp instanceof Term) {
// not supported.
}
}
}
srcUpdate.addThrow("JsonProcessingException");
} else {
// Intra-service
String updateMethodName = null;
if (JerseyCodeGenerator.generatesComponent(dst.getResourceHierarchy())) {
updateMethodName = "updateFrom" + srcResourceName;
} else {
updateMethodName = "update" + dstResourceName + "From" + srcResourceName;
}
String callParams = "";
String delimiter = "";
// Values of path parameters.
for (Expression pathParam: dstRes.getPathParams()) {
if (pathParam instanceof Variable) {
Variable pathVar = (Variable) pathParam;
callParams += delimiter + pathVar.getName();
delimiter = ", ";
}
}
// Values of other parameters.
for (Map.Entry<Type, Map.Entry<String, String>> paramEnt: params) {
callParams += delimiter + paramEnt.getValue().getValue();
delimiter = ", ";
}
// Call the update method.
if (srcComponent != dstComponent) {
srcUpdate.addStatement("this." + JerseyCodeGenerator.toVariableName(dstResourceName) + "." + updateMethodName + "(" + callParams + ");");
} else {
srcUpdate.addStatement("this." + updateMethodName + "(" + callParams + ");");
}
if (update != null && update.getThrows() != null && update.getThrows().getExceptions().contains("JsonProcessingException")) {
srcUpdate.addThrow("JsonProcessingException");
}
}
}
}
// For caller input methods
for (MethodDeclaration srcInput: getInputMethods(srcComponent, src, model)) {
List<Map.Entry<Type, Map.Entry<String, String>>> params = new ArrayList<>();
ResourcePath dstRes = out.getResource();
// Values of channel parameters.
for (Selector selector: ch.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.
String srcFieldName = "this.value";
if (!JerseyCodeGenerator.generatesComponent(src.getResourceHierarchy())) {
srcFieldName = JerseyCodeGenerator.toVariableName(srcResourceName);
}
params.add(new AbstractMap.SimpleEntry<>(src.getResourceStateType(),
new AbstractMap.SimpleEntry<>(JerseyCodeGenerator.toVariableName(srcResourceName), srcFieldName)));
// Get the value of reference member to call the update method.
Set<ResourcePath> referredSet = referredResources.get(srcInput);
for (ChannelMember rc: 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 (referredSet == null) {
referredSet = new HashSet<>();
referredResources.put(srcInput, referredSet);
}
if (!dst.getInSideResources().contains(ref)) {
String refResourceName = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(ref.getResourceHierarchy()));
Type refResourceType = ref.getResourceStateType();
if (!referredSet.contains(ref)) {
referredSet.add(ref);
String[] sideEffects = new String[] {""};
if (rc.isOutside()) {
List<String> pathParams = new ArrayList<>();
for (Expression pathExp: ref.getPathParams()) {
pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \"");
}
generatePullDataTransfer(srcInput, refResourceName, ref.getResourceHierarchy().toResourcePath(pathParams), refResourceType);
} else {
ResourcePath srcRes = in.getResource();
if (!JerseyCodeGenerator.generatesComponent(srcRes.getResourceHierarchy())) {
srcRes = srcRes.getParent();
}
Expression refGetter = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(ref, srcRes);
String refExp = refGetter.toImplementation(sideEffects);
String refTypeName = ref.getResourceStateType().getInterfaceTypeName();
srcInput.addFirstStatement(sideEffects[0] + refTypeName + " " + refResourceName + " = " + refExp + ";");
}
}
// Value of a reference side resource.
params.add(new AbstractMap.SimpleEntry<>(refResourceType, new AbstractMap.SimpleEntry<>(refResourceName, refResourceName)));
}
}
if (outsideOutputResource || (in.getResource().getCommonPrefix(dstRes) == null && JerseyCodeGenerator.differentTreesAsDifferentServices)) {
// Inter-services
String[] sideEffects = new String[] {""};
List<String> pathParams = new ArrayList<>();
for (Expression pathExp: dstRes.getPathParams()) {
pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \"");
}
String srcResName = JerseyCodeGenerator.toVariableName(srcResourceName);
if (inDegree <= 1) {
srcResName = null;
}
Map<ChannelMember, Entry<ResourcePath, Set<ChannelMember>>> filledPaths = ch.fillOutsideResourcePaths(out, JerseyCodeGenerator.pushAccessor);
String dstPath = null;
if (filledPaths != null && filledPaths.get(out) != null) {
ResourcePath filledDstPath = filledPaths.get(out).getKey();
dstPath = filledDstPath.toResourcePath().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\"");
} else {
dstPath = dstRes.getResourceHierarchy().toResourcePath(pathParams);
}
// Call the update method.
if (!chainedCalls.contains(srcInput)) {
// First call to an update method in this method
srcInput.addStatement(getHttpMethodParamsStatement(srcComponent.getTypeName(), params, true));
srcInput.addStatement("String result = " + getHttpMethodCallStatement(baseURL, dstPath, srcResName, httpMethod));
chainedCalls.add(srcInput);
} else {
// After the second time of call to update methods in this method
srcInput.addStatement(getHttpMethodParamsStatement(srcComponent.getTypeName(), params, false));
srcInput.addStatement("result = " + getHttpMethodCallStatement(baseURL, dstPath, srcResName, httpMethod));
}
if (descendantDstChannels.contains(chNode)) {
// For hierarchical channels (broadcasting push transfer).
if (ch.getSelectors() != null && ch.getSelectors().size() > 0) {
Expression selExp = ch.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 cm :ch.getInputChannelMembers()) {
if (!cm.isOutside()) {
insideChMem = cm;
break;
}
}
if (insideChMem == null) {
for (ChannelMember cm :ch.getReferenceChannelMembers()) {
if (!cm.isOutside()) {
insideChMem = cm;
break;
}
}
}
if (insideChMem == null) {
for (ChannelMember cm :ch.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();
String parent = null;
if (JerseyCodeGenerator.generatesComponent(insideResPath.getResourceHierarchy())) {
Expression getter = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(insideResPath, src.getPrimaryResourcePath());
Term valueGetter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD));
valueGetter.addChild(getter);
parent = valueGetter.toImplementation(new String[] {});
} else {
parent = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(insideResPath, src.getPrimaryResourcePath()).toImplementation(new String[] {});
}
if (insideResPath != null) {
if (selType.equals(DataConstraintModel.typeInt)) {
// make a for loop (for a list) for broadcasting.
srcInput.addFirstStatement("for (int " + forVarName + " = 0; " + forVarName +" < " + parent + ".size(); " + forVarName + "++) {");
srcInput.addStatement("}");
} else if (selType.equals(DataConstraintModel.typeString)) {
// make a for loop (for a map) for broadcasting.
srcInput.addFirstStatement("for (String " + forVarName + ": " + parent + ".keySet()) {");
srcInput.addStatement("}");
}
}
} else if (selExp instanceof Term) {
// not supported.
}
}
}
srcInput.addThrow("JsonProcessingException");
} else {
// Intra-service
String updateMethodName = null;
if (JerseyCodeGenerator.generatesComponent(dst.getResourceHierarchy())) {
updateMethodName = "updateFrom" + srcResourceName;
} else {
updateMethodName = "update" + dstResourceName + "From" + srcResourceName;
}
String callParams = "";
String delimiter = "";
// Values of path parameters.
for (Expression pathParam: dstRes.getPathParams()) {
if (pathParam instanceof Variable) {
Variable pathVar = (Variable) pathParam;
callParams += delimiter + pathVar.getName();
delimiter = ", ";
}
}
// Values of other parameters.
for (Map.Entry<Type, Map.Entry<String, String>> paramEnt: params) {
callParams += delimiter + paramEnt.getValue().getValue();
delimiter = ", ";
}
// Call the update method.
if (srcComponent != dstComponent) {
srcInput.addStatement("this." + JerseyCodeGenerator.toVariableName(dstResourceName) + "." + updateMethodName + "(" + callParams + ");");
} else {
srcInput.addStatement("this." + updateMethodName + "(" + callParams + ");");
}
if (update != null && update.getThrows() != null && update.getThrows().getExceptions().contains("JsonProcessingException")) {
srcInput.addThrow("JsonProcessingException");
}
}
}
} else if ((pushPull.getSelectedOption() != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) {
// for pull (or push/pull) data transfer
if (dstComponent == null) {
String dstParentResourceName = JerseyCodeGenerator.getComponentName(dst.getResourceHierarchy().getParent());
dstComponent = componentMap.get(dstParentResourceName);
}
MethodDeclaration getter = null;
if (JerseyCodeGenerator.generatesComponent(dst.getResourceHierarchy())) {
getter = getMethod(dstComponent, "getValue");
} else {
getter = getGetterMethod(dstComponent, dstResourceName);
}
if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) {
// The first time to fill the getter method's body.
// Data transfer on the same channel hierarchy.
String[] sideEffects = new String[] {""};
// For each reference channel member, get the current state of the reference side resource by pull data transfer.
for (ChannelMember rc: ch.getReferenceChannelMembers()) {
ResourcePath refRes = rc.getResource();
String refResourceName = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(refRes.getResourceHierarchy()));
Type refResourceType = refRes.getResourceStateType();
if (rc.isOutside()) {
List<String> pathParams = new ArrayList<>();
for (Expression pathExp: refRes.getPathParams()) {
sideEffects = new String[] {""};
pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \"");
}
generatePullDataTransfer(getter, refResourceName, refRes.getResourceHierarchy().toResourcePath(pathParams), refResourceType);
} else {
ResourcePath dstRes = out.getResource();
if (!JerseyCodeGenerator.generatesComponent(dstRes.getResourceHierarchy())) {
dstRes = dstRes.getParent();
}
Expression refGetter = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(refRes, dstRes);
sideEffects = new String[] {""};
String refExp = refGetter.toImplementation(sideEffects);
String refTypeName = refResourceType.getInterfaceTypeName();
getter.addFirstStatement(sideEffects[0] + refTypeName + " " + refResourceName + " = " + refExp + ";");
}
}
// Construct the base message.
Map.Entry<Map<ChannelMember, Entry<ResourcePath, Set<ChannelMember>>>, Term> resourcePathsAndMessage = ch.fillOutsideResourcePaths(out, JerseyCodeGenerator.pullAccessor, null);
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 src2 = pathEnt.getValue().getKey();
// get outside src2 resource state by pull data transfer.
if (cm.isOutside() || src2.getCommonPrefix(dst.getInSideResource(ch)) == null) {
// Data transfer from an outside input resource is regarded as PULL transfer.
List<String> pathParams = new ArrayList<>();
for (Expression pathExp: src2.getPathParams()) {
sideEffects = new String[] {""};
pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \"");
}
// generate a pull data transfer from a depending in/ref resource.
Type srcResourceType = src2.getResourceStateType();
String srcResName2 = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(src2.getResourceHierarchy()));
String srcPath2 = src2.toResourcePath().replaceAll("\\{", "\"+").replaceAll("\\}", "+\"");
generatePullDataTransfer(getter, srcResName2, srcPath2, srcResourceType);
}
}
// 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.
// (For each descendant channel, data transfer from every input resource is regarded as PULL transfer.)
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 = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(src2.getResourceHierarchy()));
String srcPath2 = src2.toResourcePath().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\"");
generatePullDataTransfer(getter, srcResName2, srcPath2, srcResType2);
} 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(dst.getInSideResource(curChannel)) == null) {
// generate a pull data transfer from a depending in/ref resource.
Type srcResType2 = src2.getResourceStateType();
String srcResName2 = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(src2.getResourceHierarchy()));
String srcPath2 = src2.toResourcePath().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\"");
generatePullDataTransfer(getter, srcResName2, srcPath2, srcResType2);
}
}
}
// collect the message constraints by a descendant channel.
List<Variable> varsForSideEffects = new ArrayList<>();
int v = 0;
resourcePathsAndMessage = curChannel.fillOutsideResourcePaths(out, JerseyCodeGenerator.pullAccessor, null);
if (resourcePathsAndMessage != null) {
resourcePaths = resourcePathsAndMessage.getKey();
Term messageTermSub = resourcePathsAndMessage.getValue();
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);
sideEffects = new String[] {""};
String curState = messageTermSub.toImplementation(sideEffects);
getter.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
Expression selExp = curChannel.getSelectors().get(0).getExpression();
Type selType = null;
String varName = null;
if (selExp instanceof Variable) {
selType = ((Variable) selExp).getType();
varName = ((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();
String parent = null;
if (insideResPath != null) {
if (insideResPath.getCommonPrefix(dst.getInSideResource(ch)) != null) {
if (JerseyCodeGenerator.generatesComponent(insideResPath.getResourceHierarchy())) {
Expression parentGetter = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(insideResPath, dst.getInSideResource(ch));
Term valueGetter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD));
valueGetter.addChild(parentGetter);
parent = valueGetter.toImplementation(new String[] {});
} else {
parent = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(insideResPath, dst.getInSideResource(ch)).toImplementation(new String[] {});
}
} else {
parent = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(insideResPath.getResourceHierarchy()));
}
if (selType.equals(DataConstraintModel.typeInt)) {
// make a for loop (for a list) for data collecting.
getter.addFirstStatement("for (int " + varName + " = 0; " + varName +" < " + parent + ".size(); " + varName + "++) {");
} else if (selType.equals(DataConstraintModel.typeString)) {
// make a for loop (for a map) for data collecting.
getter.addFirstStatement("for (String " + varName + ": " + parent + ".keySet()) {");
}
if (insideResPath.getCommonPrefix(dst.getInSideResource(ch)) == null) {
Type parentResType = insideResPath.getResourceStateType();
String parentResName = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(insideResPath.getResourceHierarchy()));
String parentResPath = insideResPath.toString().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\"");
generatePullDataTransfer(getter, parentResName, parentResPath, parentResType);
}
}
}
// initialize the variables to hold side effects within the loop
for (Variable var: varsForSideEffects) {
getter.addFirstStatement(var.getType().getInterfaceTypeName() + " " + var.getName() + " = new " + var.getType().getImplementationTypeName() + "();");
}
// end of the loop
getter.addStatement("}");
if (curChannel.getChildren() != null && curChannel.getChildren().size() > 0) {
channelItrStack.push(chItr);
chItr = curChannel.getChildren().iterator();
}
}
} while (!channelItrStack.isEmpty());
}
// generate a return statement.
Expression curExp = ch.deriveUpdateExpressionOf(out, messageTerm, JerseyCodeGenerator.pullAccessor);
sideEffects = new String[] {""};
String curState = curExp.toImplementation(sideEffects);
if (ch.getChildren() == null || ch.getChildren().size() == 0) {
getter.addStatement(sideEffects[0] + "return " + curState + ";");
} else {
getter.addStatement("return " + curState + ";");
}
}
// get src resource state by pull data transfer.
if (src.getNumberOfParameters() == 0 && src.getOutSideResource(ch).getCommonPrefix(dst.getInSideResource(ch)) == null) {
Type srcResourceType = src.getResourceStateType();
List<String> pathParams = new ArrayList<>();
generatePullDataTransfer(getter, src.getResourceName(), src.getResourceHierarchy().toResourcePath(pathParams), srcResourceType);
}
}
}
}
}
}
}
// for source nodes
for (ResourceHierarchy resource: model.getResourceHierarchies()) {
String resourceName = JerseyCodeGenerator.getComponentName(resource);
TypeDeclaration component = componentMap.get(resourceName);
if (JavaCodeGenerator.generatesComponent(resource)) {
if (component != null) {
// state getter method
Type resourceType = JerseyCodeGenerator.getImplStateType(resource);
MethodDeclaration stateGetter = getMethod(component, "getValue");
if (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 = JerseyCodeGenerator.getComponentName(child);
String fieldName = JerseyCodeGenerator.toVariableName(childTypeName);
Term childGetter = null;
if (!JerseyCodeGenerator.generatesComponent(child)) {
// 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, JerseyCodeGenerator.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+ ";");
}
}
}
}
}
// (#4) descendant getter method (the implementation must be kept consistent with #3)
if (resource.getChildren().size() > 0) {
for (ResourceHierarchy child: resource.getChildren()) {
ResourceHierarchy parent = resource;
ResourceHierarchy descendant = child;
Set<ResourceHierarchy> children;
Expression selector;
int params = 0;
if (DataConstraintModel.typeList.isAncestorOf(parent.getResourceStateType())) {
selector = new Field("value");
params++;
} else if (DataConstraintModel.typeMap.isAncestorOf(parent.getResourceStateType())) {
selector = new Field("value");
params++;
} else {
String fieldName = JerseyCodeGenerator.getComponentName(descendant);
selector = new Field(JerseyCodeGenerator.toVariableName(fieldName));
}
do {
String methodName = JerseyCodeGenerator.getComponentName(descendant);
MethodDeclaration descendantGetter = null;
for (MethodDeclaration getter: getGetterMethods(component, methodName)) {
if ((getter.getParameters() == null && params == 0) || (getter.getParameters() != null && getter.getParameters().size() == params)) {
descendantGetter = getter;
break;
}
}
if (descendantGetter != null) {
if (DataConstraintModel.typeList.isAncestorOf(parent.getResourceStateType())) {
Term newSelector = new Term(DataConstraintModel.get);
newSelector.addChild(selector);
newSelector.addChild(new Variable(descendantGetter.getParameters().get(descendantGetter.getParameters().size() - 1).getName()));
newSelector.setType(descendantGetter.getReturnType());
selector = newSelector;
} else if (DataConstraintModel.typeMap.isAncestorOf(parent.getResourceStateType())) {
Term newSelector = new Term(DataConstraintModel.lookup);
newSelector.addChild(selector);
newSelector.addChild(new Variable(descendantGetter.getParameters().get(descendantGetter.getParameters().size() - 1).getName()));
newSelector.setType(descendantGetter.getReturnType());
selector = newSelector;
}
if (descendantGetter != null && (descendantGetter.getBody() == null || descendantGetter.getBody().getStatements().size() == 0)) {
String[] sideEffects = new String[] {null};
String returnValue = selector.toImplementation(sideEffects);
if (sideEffects[0] != null) descendantGetter.addStatement(sideEffects[0]);
descendantGetter.addStatement("return " + returnValue + ";");
}
}
if (JerseyCodeGenerator.generatesComponent(descendant)) {
// If the descendant generates a component.
break;
}
parent = descendant;
if (DataConstraintModel.typeList.isAncestorOf(parent.getResourceStateType())) {
params++;
} else if (DataConstraintModel.typeMap.isAncestorOf(parent.getResourceStateType())) {
params++;
}
children = descendant.getChildren();
} while (children != null && children.size() == 1 && (descendant = children.iterator().next()) != null);
}
}
}
}
// methods for input events
Map<DataTransferChannel, Set<ChannelMember>> ioChannelsAndMembers = getIOChannelsAndMembers(resource, model);
for (Map.Entry<DataTransferChannel, Set<ChannelMember>> entry: ioChannelsAndMembers.entrySet()) {
DataTransferChannel ch = entry.getKey();
Set<ChannelMember> outs = entry.getValue();
for (ChannelMember out: outs) {
MethodDeclaration input = null;
if (JerseyCodeGenerator.generatesComponent(resource)) {
// A component is generated for this resource.
input = getInputMethod(component, out, ch.getOutputChannelMembers().size());
} else {
// No component is generated for this resource.
ResourceHierarchy parent = resource.getParent();
if (parent != null) {
TypeDeclaration parentType = componentMap.get(JerseyCodeGenerator.getComponentName(parent));
input = getInputMethod(parentType, out, ch.getOutputChannelMembers().size());
}
}
if (input != null) {
// In each resource
Set<ResourcePath> referredSet = referredResources.get(input);
for (ChannelMember rc: 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 (referredSet == null) {
referredSet = new HashSet<>();
referredResources.put(input, referredSet);
}
if (!out.getResource().equals(ref)) {
String refResourceName = ref.getLeafResourceName();
Type refResourceType = ref.getResourceStateType();
if (!referredSet.contains(ref)) {
referredSet.add(ref);
String[] sideEffects = new String[] {""};
if (rc.isOutside()) {
List<String> pathParams = new ArrayList<>();
for (Expression pathExp: ref.getPathParams()) {
pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \"");
}
generatePullDataTransfer(input, refResourceName, ref.getResourceHierarchy().toResourcePath(pathParams), refResourceType);
} else {
ResourcePath dstRes = out.getResource();
if (!JerseyCodeGenerator.generatesComponent(dstRes.getResourceHierarchy())) {
dstRes = dstRes.getParent();
}
Expression refGetter = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(ref, dstRes);
String refExp = refGetter.toImplementation(sideEffects);
String refTypeName = refResourceType.getInterfaceTypeName();
input.addFirstStatement(sideEffects[0] + refTypeName + " " + refResourceName + " = " + refExp + ";");
}
}
}
}
Expression updateExp = ch.deriveUpdateExpressionOf(out, JerseyCodeGenerator.refAccessor).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.getChildren().iterator().next();
Set<ResourceHierarchy> children;
do {
if (JerseyCodeGenerator.generatesComponent(descendantRes)) break;
children = descendantRes.getChildren();
} while (children != null && children.size() == 1 && (descendantRes = children.iterator().next()) != null);
Type descendantStateType = descendantRes.getResourceStateType();
String descendantComponentName = JerseyCodeGenerator.getComponentName(descendantRes);
TypeDeclaration descendantComponent = componentMap.get(descendantComponentName);
if (DataConstraintModel.typeJson.isAncestorOf(descendantStateType)) {
replaceJsonTermWithConstructorInvocation(updateExp, descendantStateType, descendantComponentName, descendantComponent);
}
}
// Replace the type of the state field.
Type fieldType = JerseyCodeGenerator.getImplStateType(outRes);
if (updateExp instanceof Term) {
((Term) updateExp).setType(fieldType);
for (Map.Entry<Position, Variable> varEnt: ((Term) updateExp).getVariables().entrySet()) {
if (varEnt.getValue().getName().equals("value")) {
varEnt.getValue().setType(fieldType);
}
}
} else if (updateExp instanceof Variable) {
((Variable) updateExp).setType(fieldType);
}
// Add statements to the input method.
String[] sideEffects = new String[] {""};
String newState = updateExp.toImplementation(sideEffects);
if (JerseyCodeGenerator.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] + "this.value = " + newState + ";";
}
if (input.getBody() == null || !input.getBody().getStatements().contains(updateStatement)) {
input.addStatement(updateStatement);
}
} else {
String updateStatement = "";
if (sideEffects[0] != null) {
updateStatement = sideEffects[0];
updateStatement = updateStatement.replace(".value", "." + JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(resource)));
if (updateStatement.endsWith("\n")) {
updateStatement = updateStatement.substring(0, updateStatement.length() - 1);
}
}
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 += 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 += sideEffects2[0];
} else if (!(updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect())) {
updateStatement += "this." + JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(resource)) + " = " + newState + ";";
}
if (updateStatement != null && (input.getBody() == null || !input.getBody().getStatements().contains(updateStatement))) {
input.addStatement(updateStatement);
}
}
if (out.getResource().getParent() != null && out.getResource().getParent().getParent() != null) {
// In the root resource
Expression message = out.getStateTransition().getMessageExpression();
String inputAccessorName = input.getName();
if (message instanceof Term) {
inputAccessorName = ((Term) message).getSymbol().getImplName();
} else if (message instanceof Variable) {
inputAccessorName = ((Variable) message).getName();
}
MethodDeclaration inputAccessor = getMethod(componentMap.get(JerseyCodeGenerator.getComponentName(resource.getRoot())), inputAccessorName);
if (inputAccessor != null) {
ResourcePath outResPath = new ResourcePath(out.getResource());
for (int i = 0; i < outResPath.getPathParams().size(); i++) {
Parameter pathParam = new Parameter(inputAccessor.getParameters().get(i).getName());
outResPath.replacePathParam(i, pathParam, null);
}
// The expression of the receiver (resource) of the input method.
Expression resExp = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(outResPath, outResPath.getRoot());
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[] {""});
// Values of channel parameters.
for (Selector selector: ch.getAllSelectors()) {
if (selector.getExpression() instanceof Variable) {
Variable selVar = (Variable) selector.getExpression();
args += delimiter + selVar.getName();
delimiter = ", ";
}
}
// Values of message parameters.
if (message instanceof Term) {
for (Variable mesVar: message.getVariables().values()) {
args += delimiter + mesVar.getName();
delimiter = ", ";
}
}
inputAccessor.addStatement(resourceAccess + "." + input.getName() + "(" + args + ");");
if (input != null && input.getThrows() != null && input.getThrows().getExceptions().contains("JsonProcessingException")) {
inputAccessor.addThrow("JsonProcessingException");
}
}
}
}
}
}
}
} catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork
| InvalidMessage | UnificationFailed | ValueUndefined e1) {
e1.printStackTrace();
}
return codes;
}
private static void replaceJsonTermWithConstructorInvocation(Expression exp, Type replacedJsonType, String replacingClassName, TypeDeclaration descendantComponent) {
// Replace each json term in exp with the corresponding constructor invocation.
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)) {
String constructorInvocation = "new " + replacingClassName + "(";
MethodDeclaration descendantConstructor = getConstructor(descendantComponent);
if (descendantConstructor != null) {
String delimiter = "";
for (VariableDeclaration var: descendantConstructor.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());
}
}
constructorInvocation = constructorInvocation + delimiter + param.toImplementation(null);
} 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();
} 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);
}
}
}
}
private static void generatePullDataTransfer(MethodDeclaration methodBody, String fromResourceName, String fromResourcePath, Type fromResourceType) {
String varName = new String(fromResourceName);
String respTypeName = fromResourceType.getInterfaceTypeName();
String respImplTypeName = fromResourceType.getImplementationTypeName();
String respConverter = "";
if (DataConstraintModel.typeList.isAncestorOf(fromResourceType) && fromResourceType != DataConstraintModel.typeList) {
Type compType = TypeInference.getListComponentType(fromResourceType);
if (DataConstraintModel.typeTuple.isAncestorOf(compType)) {
varName += "_json";
String mapTypeName = convertFromEntryToMapType(compType);
respTypeName = "List<" + mapTypeName + ">";
respConverter += fromResourceType.getInterfaceTypeName() + " " + fromResourceName + " = new " + fromResourceType.getImplementationTypeName() + "();\n";
respConverter += "for (" + mapTypeName + " i: " + varName + ") {\n";
respConverter += "\t" + fromResourceName + ".add(" + getCodeForConversionFromMapToTuple(compType, "i") + ");\n";
respConverter += "}";
methodBody.addThrow("JsonProcessingException");
} else if (DataConstraintModel.typeMap.isAncestorOf(compType)) {
// To do.
}
} else if (DataConstraintModel.typeTuple.isAncestorOf(fromResourceType)) {
varName += "_json";
respTypeName = convertFromEntryToMapType(fromResourceType);
respConverter += fromResourceType.getInterfaceTypeName() + " " + fromResourceName + " = " + getCodeForConversionFromMapToTuple(fromResourceType, varName) + ";";
respImplTypeName = "HashMap";
} else if (DataConstraintModel.typePair.isAncestorOf(fromResourceType)) {
varName += "_json";
respTypeName = convertFromEntryToMapType(fromResourceType);
respConverter += fromResourceType.getInterfaceTypeName() + " " + fromResourceName + " = " + getCodeForConversionFromMapToPair(fromResourceType, varName) + ";";
respImplTypeName = "HashMap";
} else if (DataConstraintModel.typeMap.isAncestorOf(fromResourceType)) {
varName += "_json";
respTypeName = convertFromEntryToMapType(fromResourceType);
respConverter += fromResourceType.getInterfaceTypeName() + " " + fromResourceName + " = new " + fromResourceType.getImplementationTypeName() + "();\n";
respConverter += getCodeForConversionFromMapToMap(fromResourceType, varName, fromResourceName);
respImplTypeName = "HashMap";
}
if (respConverter.length() > 0) {
methodBody.addFirstStatement(respConverter);
}
methodBody.addFirstStatement(respTypeName + " " + varName + " = " + getHttpMethodCallStatementWithResponse(baseURL, fromResourcePath, "get", respImplTypeName));
}
private static String convertFromEntryToMapType(Type type) {
String mapTypeName = null;
if (DataConstraintModel.typePair.isAncestorOf(type)) {
Type compType = TypeInference.getPairComponentType(type);
String wrapperType = DataConstraintModel.getWrapperType(compType);
if (wrapperType != null) {
mapTypeName = "Map<String, " + wrapperType + ">";
} else {
mapTypeName = "Map<String, " + compType.getInterfaceTypeName() + ">";
}
} else if (DataConstraintModel.typeMap.isAncestorOf(type)) {
List<Type> compTypes = TypeInference.getMapComponentTypes(type);
String wrapperType = DataConstraintModel.getWrapperType(compTypes.get(1));
if (wrapperType != null) {
mapTypeName = "Map<String, " + wrapperType + ">";
} else {
mapTypeName = "Map<String, " + compTypes.get(1).getInterfaceTypeName() + ">";
}
} else {
mapTypeName = type.getInterfaceTypeName();
mapTypeName = mapTypeName.replace("Map.Entry", "Map");
for (int idx = mapTypeName.indexOf("<", 0); idx >= 0; idx = mapTypeName.indexOf("<", idx + 1)) {
int to = mapTypeName.indexOf(",", idx);
if (to > idx) {
mapTypeName = mapTypeName.substring(0, idx + 1) + "String" + mapTypeName.substring(to); // All elements except for the last one have the string type.
}
}
}
return mapTypeName;
}
private static String getCodeForConversionFromMapToTuple(Type tupleType, String mapVar) {
String decoded = "$x";
List<Type> elementsTypes = TypeInference.getTupleComponentTypes(tupleType);
String elementBase = mapVar;
for (Type elmType: elementsTypes.subList(0, elementsTypes.size() - 1)) {
elementBase += ".entrySet().iterator().next()";
if (elmType == DataConstraintModel.typeBoolean
|| elmType == DataConstraintModel.typeInt
|| elmType == DataConstraintModel.typeLong
|| elmType == DataConstraintModel.typeFloat
|| elmType == DataConstraintModel.typeDouble) {
String elmVal = new JavaSpecific().getStringToValueExp(elmType.getImplementationTypeName(), elementBase + ".getKey()");
decoded = decoded.replace("$x", "new AbstractMap.SimpleEntry<>(" + elmVal + ", $x)");
} else if (elmType == DataConstraintModel.typeString) {
decoded = decoded.replace("$x", "new AbstractMap.SimpleEntry<>(" + elementBase + ".getKey(), $x)");
} else {
// To do.
}
elementBase += ".getValue()";
}
decoded = decoded.replace("$x", elementBase);
return decoded;
}
private static String getCodeForConversionFromMapToPair(Type pairType, String mapVar) {
String decoded = "$x";
decoded = decoded.replace("$x", "new Pair<>(" + mapVar + ".get(\"left\"), $x)");
decoded = decoded.replace("$x", mapVar + ".get(\"right\")");
return decoded;
}
private static String getCodeForConversionFromMapToMap(Type mapType, String mapVal, String mapVar) {
List<Type> elementsTypes = TypeInference.getMapComponentTypes(mapType);
Type keyType = elementsTypes.get(0);
Type valType = elementsTypes.get(1);
String keyVal = null;
String decoded = "";
if (keyType == DataConstraintModel.typeBoolean
|| keyType == DataConstraintModel.typeInt
|| keyType == DataConstraintModel.typeLong
|| keyType == DataConstraintModel.typeFloat
|| keyType == DataConstraintModel.typeDouble) {
decoded += "for (String k: " + mapVal + ".keySet()) {\n";
decoded += "\t" + mapVar + ".put(";
keyVal = new JavaSpecific().getStringToValueExp(keyType.getImplementationTypeName(), "k");
decoded += keyVal + ", " + mapVal + ".get(" + keyVal + ")" + ");\n";
decoded += "}";
} else if (keyType == DataConstraintModel.typeString) {
decoded += mapVar + " = " + mapVal + ";";
}
return decoded;
}
private static String getHttpMethodParamsStatement(String callerResourceName, List<Map.Entry<Type, Map.Entry<String, String>>> params, boolean isFirstCall) {
String statements = "";
if (isFirstCall) {
statements += "Form ";
}
statements += "form = new Form();\n";
for (Map.Entry<Type, Map.Entry<String, String>> param: params) {
Type paramType = param.getKey();
String paramName = param.getValue().getKey();
String value = param.getValue().getValue();
if (DataConstraintModel.typeList.isAncestorOf(paramType)) {
Type compType = TypeInference.getListComponentType(paramType);
String wrapperType = DataConstraintModel.getWrapperType(compType);
if (wrapperType == null) {
statements += "for (" + compType.getInterfaceTypeName() + " i: " + value + ") {\n";
} else {
statements += "for (" + wrapperType + " i: " + value + ") {\n";
}
if (DataConstraintModel.typeTuple.isAncestorOf(compType) || DataConstraintModel.typePair.isAncestorOf(paramType) || DataConstraintModel.typeList.isAncestorOf(compType) || DataConstraintModel.typeMap.isAncestorOf(paramType)) {
statements += "\tform.param(\"" + paramName + "\", new ObjectMapper().writeValueAsString(i));\n"; // typeTuple: {"1.0":2.0}, typePair: {"left": 1.0, "right":2.0}
} else {
statements += "\tform.param(\"" + paramName + "\", i.toString());\n";
}
statements += "}\n";
// return "Entity<String> entity = Entity.entity(" + paramName + ".toString(), MediaType.APPLICATION_JSON);";
} else if (DataConstraintModel.typeTuple.isAncestorOf(paramType) || DataConstraintModel.typePair.isAncestorOf(paramType) || DataConstraintModel.typeMap.isAncestorOf(paramType)) {
// typeTuple: {"1.0":2.0}, typePair: {"left": 1.0, "right":2.0}
statements += "form.param(\"" + paramName + "\", new ObjectMapper().writeValueAsString(" + value + "));\n";
} else {
statements += "form.param(\"" + paramName + "\", " + new JavaSpecific().getValueToStringExp(paramType.getImplementationTypeName(), value) + ");\n";
}
}
if (isFirstCall) {
statements += "Entity<Form> ";
}
statements += "entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED);";
return statements;
}
private static String getHttpMethodCallStatement(String baseURL, String resourceName, String srcResName, String httpMethod) {
if (srcResName == null) {
return "client.target(\"" + baseURL + "\").path(\"/" + resourceName + "\").request()." + httpMethod + "(entity, String.class);";
} else {
// For each source resource, a child resource is defined in the destination resource so that its state can be updated separately.
return "client.target(\"" + baseURL + "\").path(\"/" + resourceName + "/" + srcResName + "\").request()." + httpMethod + "(entity, String.class);";
}
}
private static String getHttpMethodCallStatementWithResponse(String baseURL, String resourceName, String httpMethod, String respImplName) {
String responseShortTypeName = respImplName;
if (respImplName.contains("<")) {
responseShortTypeName = respImplName.substring(0, respImplName.indexOf("<"));
}
return "client.target(\"" + baseURL + "\").path(\"/" + resourceName + "\").request()." + httpMethod + "(" + responseShortTypeName + ".class);";
}
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 dstResName, String srcResName) {
for (MethodDeclaration m: component.getMethods()) {
if (dstResName == null) {
if (m.getName().equals("updateFrom" + srcResName)) return m;
} else {
if (m.getName().equals("update" + dstResName + "From" + srcResName)) return m;
}
}
return null;
}
private static List<MethodDeclaration> getUpdateMethods(TypeDeclaration component, String resName) {
List<MethodDeclaration> updates = new ArrayList<>();
for (MethodDeclaration m: component.getMethods()) {
if (resName == null) {
if (m.getName().startsWith("updateFrom")) {
updates.add(m);
}
} else {
if (m.getName().startsWith("update" + resName + "From")) {
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 List<MethodDeclaration> getGetterMethods(TypeDeclaration component, String resourceName) {
List<MethodDeclaration> getters = new ArrayList<>();
for (MethodDeclaration m: component.getMethods()) {
if (m.getName().equals("get" + resourceName)) {
getters.add(m);
}
}
return getters;
}
private static Map<DataTransferChannel, Set<ChannelMember>> getIOChannelsAndMembers(ResourceHierarchy resource, DataTransferModel model) {
Map<DataTransferChannel, Set<ChannelMember>> ioChannelsAndMembers = new HashMap<>();
for (Channel c: model.getInputChannels()) {
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.getInputChannels()) {
DataTransferChannel channel = (DataTransferChannel) c;
// I/O channel
for (ChannelMember out: channel.getOutputChannelMembers()) {
if (resource.getInSideResources().contains(out.getResource())) {
MethodDeclaration input = getInputMethod(component, out, channel.getOutputChannelMembers().size());
inputs.add(input);
}
}
}
return inputs;
}
private static MethodDeclaration getInputMethod(TypeDeclaration component, ChannelMember cm, int outNumber) {
String inputMethodName = null;
if (cm.getStateTransition().getMessageExpression() instanceof Term) {
Term message = (Term) cm.getStateTransition().getMessageExpression();
inputMethodName = message.getSymbol().getImplName();
} else if (cm.getStateTransition().getMessageExpression() instanceof Variable) {
Variable message = (Variable) cm.getStateTransition().getMessageExpression();
inputMethodName = message.getName();
}
if (outNumber > 1) {
inputMethodName += "For" + JerseyCodeGenerator.getComponentName(cm.getResource().getResourceHierarchy());
}
MethodDeclaration input = getMethod(component, inputMethodName);
return input;
}
private static MethodDeclaration getMethod(TypeDeclaration component, String methodName) {
for (MethodDeclaration m: component.getMethods()) {
if (m.getName().equals(methodName)) return m;
}
return null;
}
}