package generators;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import code.ast.Annotation;
import code.ast.Block;
import code.ast.CompilationUnit;
import code.ast.FieldDeclaration;
import code.ast.ImportDeclaration;
import code.ast.MethodDeclaration;
import code.ast.TypeDeclaration;
import code.ast.VariableDeclaration;
import models.Edge;
import models.Node;
import models.algebra.Expression;
import models.algebra.Field;
import models.algebra.Parameter;
import models.algebra.Symbol;
import models.algebra.Term;
import models.algebra.Type;
import models.algebra.Variable;
import models.dataConstraintModel.Channel;
import models.dataConstraintModel.ChannelMember;
import models.dataConstraintModel.DataConstraintModel;
import models.dataConstraintModel.ResourceHierarchy;
import models.dataConstraintModel.ResourcePath;
import models.dataConstraintModel.Selector;
import models.dataFlowModel.DataTransferModel;
import models.dataFlowModel.DataTransferChannel;
import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor;
import models.dataFlowModel.PushPullAttribute;
import models.dataFlowModel.PushPullValue;
import models.dataFlowModel.DataFlowEdge;
import models.dataFlowModel.DataFlowGraph;
import models.dataFlowModel.ResourceNode;
import models.dataFlowModel.ChannelNode;
import models.dataFlowModel.StoreAttribute;
/**
* Generator for Jersey prototypes
*
* @author Nitta
*
*/
public class JerseyCodeGenerator {
public static final Type typeVoid = new Type("Void", "void");
public static final Type typeClient = new Type("Client", "Client");
public static boolean differentTreesAsDifferentServices = true;
private static String defaultMainTypeName = "Main";
static String mainTypeName = defaultMainTypeName;
public static String getMainTypeName() {
return mainTypeName;
}
public static void setMainTypeName(String mainTypeName) {
JerseyCodeGenerator.mainTypeName = mainTypeName;
}
public static void resetMainTypeName() {
JerseyCodeGenerator.mainTypeName = defaultMainTypeName;
}
public static String getComponentName(ResourceHierarchy res) {
String name = res.getResourceName();
if (res.getNumParameters() > 0) {
if (name.length() > 3 && name.endsWith("ies")) {
name = name.substring(0, name.length() - 3) + "y";
} else if (name.length() > 1 && name.endsWith("s")) {
name = name.substring(0, name.length() - 1);
} else {
name += "Element";
}
}
return name.substring(0, 1).toUpperCase() + name.substring(1);
}
public static String toVariableName(String name) {
return name.substring(0, 1).toLowerCase() + name.substring(1);
}
public static Type getImplStateType(ResourceHierarchy res) {
Set<ResourceHierarchy> children = res.getChildren();
if (children == null || children.size() == 0) {
// leaf resource.
return res.getResourceStateType();
} else {
ResourceHierarchy child = children.iterator().next();
if (children.size() == 1 && child.getNumParameters() > 0) {
// map or list.
if (DataConstraintModel.typeList.isAncestorOf(res.getResourceStateType())) {
// list.
if (generatesComponent(child)) {
return new Type("List", "ArrayList<>", "List<" + getComponentName(child) + ">", DataConstraintModel.typeList);
} else {
return new Type("List", "ArrayList<>", "List<" + getImplStateType(child).getImplementationTypeName() + ">", DataConstraintModel.typeList);
}
} else if (DataConstraintModel.typeMap.isAncestorOf(res.getResourceStateType())) {
// map.
if (generatesComponent(child)) {
return new Type("Map", "HashMap<>", "Map<String, " + getComponentName(child) + ">", DataConstraintModel.typeMap);
} else {
return new Type("Map", "HashMap<>", "Map<String, " + getImplStateType(child).getImplementationTypeName() + ">", DataConstraintModel.typeMap);
}
}
return null;
} else {
// class
return res.getResourceStateType();
}
}
}
public static boolean generatesComponent(ResourceHierarchy res) {
return res.getParent() == null || !(res.getChildren() == null || res.getChildren().size() == 0);
// Type resType = res.getResourceStateType();
// return DataConstraintModel.typeJson.isAncestorOf(resType) || DataConstraintModel.typeList.isAncestorOf(resType) || DataConstraintModel.typeMap.isAncestorOf(resType);
}
static public ArrayList<CompilationUnit> doGenerate(DataFlowGraph graph, DataTransferModel model) {
ArrayList<CompilationUnit> codes = new ArrayList<>();
// ArrayList<ResourceNode> resources = StoreResourceCheck(graph);
Collection<ResourceNode> resources = graph.getResourceNodes();
Map<ResourceHierarchy, TypeDeclaration> resourceComponents = new HashMap<>();
Map<ResourceHierarchy, MethodDeclaration> resourceConstructors = new HashMap<>();
List<Map.Entry<ResourceHierarchy, MethodDeclaration>> getters = new ArrayList<>();
List<Map.Entry<ResourceHierarchy, MethodDeclaration>> inputs = new ArrayList<>();
List<Map.Entry<ResourceHierarchy, FieldDeclaration>> fields = new ArrayList<>();
Map<ResourceHierarchy, MethodDeclaration> getterAccessors = new HashMap<>();
Map<ResourceHierarchy, MethodDeclaration> inputAccessors = new HashMap<>();
List<Map.Entry<ResourceHierarchy, VariableDeclaration>> constructorParams = new ArrayList<>();
// For each resource node.
for (Node n : resources) {
ResourceNode rn = (ResourceNode) n;
TypeDeclaration component = null;
if (generatesComponent(rn.getResourceHierarchy())) {
String resourceName = getComponentName(rn.getResourceHierarchy());
component = resourceComponents.get(rn.getResourceHierarchy());
if (component == null) {
// Add compilation unit for each resource.
component = new TypeDeclaration(resourceName);
if (rn.getResourceHierarchy().getParent() == null) {
// For a root node.
component.addAnnotation(new Annotation("Component"));
component.addAnnotation(new Annotation("Path", "\"/" + rn.getResourceName() + "\""));
}
resourceComponents.put(rn.getResourceHierarchy(), component);
CompilationUnit cu = new CompilationUnit(component);
cu.addImport(new ImportDeclaration("java.util.*"));
if (rn.getResourceHierarchy().getParent() == null) {
// For a root node.
cu.addImport(new ImportDeclaration("javax.ws.rs.*"));
cu.addImport(new ImportDeclaration("javax.ws.rs.client.*"));
cu.addImport(new ImportDeclaration("javax.ws.rs.core.*"));
cu.addImport(new ImportDeclaration("org.springframework.stereotype.Component"));
cu.addImport(new ImportDeclaration("com.fasterxml.jackson.databind.ObjectMapper"));
cu.addImport(new ImportDeclaration("com.fasterxml.jackson.core.JsonProcessingException"));
}
codes.add(cu);
// Declare a client field and update methods from other resources.
boolean bDeclareClientField = false;
for (Edge resToCh: rn.getOutEdges()) {
DataFlowEdge re = (DataFlowEdge) resToCh;
DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel();
// Check if the input resource is outside of the channel scope.
boolean outsideInputResource = false;
for (ChannelMember cm: ch.getInputChannelMembers()) {
if (cm.getResource().getResourceHierarchy().equals(rn.getResourceHierarchy()) && cm.isOutside()) {
outsideInputResource = true; // Regarded as pull transfer.
break;
}
}
if (!bDeclareClientField && ((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) {
for (ChannelMember cm: ch.getOutputChannelMembers()) {
ResourcePath dstRes = cm.getResource();
String dstResName = null;
if (generatesComponent(dstRes.getResourceHierarchy())) {
dstResName = getComponentName(dstRes.getResourceHierarchy());
} else {
dstResName = getComponentName(dstRes.getResourceHierarchy().getParent());
}
if (rn.getOutSideResource().getCommonPrefix(dstRes) == null && differentTreesAsDifferentServices) {
// Inter-service
if (bDeclareClientField) {
// Declare a client field to connect to the destination resource of push transfer.
component.addField(new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()"));
bDeclareClientField = true;
}
} else {
// Inner-service
// Declare a field to directly refer to the destination resource of push transfer.
component.addField(new FieldDeclaration(new Type(dstResName, dstResName), toVariableName(dstResName)));
}
}
}
}
for (Edge chToRes : rn.getInEdges()) {
for (Edge resToCh: chToRes.getSource().getInEdges()) {
DataFlowEdge re = (DataFlowEdge) resToCh;
ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource();
DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel();
// Check if the input and output resources are outside of the channel scope.
boolean outsideInputResource = false;
for (ChannelMember cm: ch.getInputChannelMembers()) {
if (cm.getResource().getResourceHierarchy().equals(srcRes.getResourceHierarchy()) && cm.isOutside()) {
outsideInputResource = true; // Regarded as pull transfer.
break;
}
}
boolean outsideOutputResource = false;
for (ChannelMember cm: ch.getOutputChannelMembers()) {
if (cm.getResource().getResourceHierarchy().equals(rn.getOutSideResource().getResourceHierarchy()) && cm.isOutside()) {
outsideOutputResource = true; // Regarded as push transfer.
break;
}
}
String srcResName = null;
if (generatesComponent(srcRes.getResourceHierarchy())) {
srcResName = getComponentName(srcRes.getResourceHierarchy());
} else {
srcResName = getComponentName(srcRes.getResourceHierarchy().getParent());
}
if ((((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) {
if (rn.getOutSideResource().getCommonPrefix(srcRes) == null && differentTreesAsDifferentServices) {
// Inter-service
if (!bDeclareClientField) {
// Declare a client field to connect to the source resource of pull transfer.
component.addField(new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()"));
bDeclareClientField = true;
}
} else {
// Inner-service
// Declare a field to directly refer to the source resource of pull transfer.
component.addField(new FieldDeclaration(new Type(srcResName, srcResName), toVariableName(srcResName)));
}
} else {
// Declare an update method in the type of the destination resource.
ArrayList<VariableDeclaration> vars = new ArrayList<>();
String srcName = srcRes.getResourceName();
Type srcType = srcRes.getResourceStateType();
VariableDeclaration param = new VariableDeclaration(srcType, srcName);
param.addAnnotation(new Annotation("FormParam", "\"" + srcName + "\""));
vars.add(param);
for (ResourcePath refRes: ((ChannelNode) re.getDestination()).getChannel().getReferenceResources()) {
if (!refRes.equals(rn.getOutSideResource())) {
param = new VariableDeclaration(refRes.getResourceStateType(), refRes.getResourceName());
param.addAnnotation(new Annotation("FormParam", "\"" + refRes.getResourceName() + "\""));
vars.add(param);
}
}
MethodDeclaration update = new MethodDeclaration("update" + srcResName, false, typeVoid, vars);
for (ChannelMember cm: ((ChannelNode) re.getDestination()).getChannel().getOutputChannelMembers()) {
if (rn.getInSideResources().contains(cm.getResource())) {
if (cm.getStateTransition().isRightUnary()) {
update.addAnnotation(new Annotation("PUT"));
} else {
update.addAnnotation(new Annotation("POST"));
}
}
}
if (re.getDestination().getIndegree() > 1
|| (re.getDestination().getIndegree() == 1 && ch.getInputChannelMembers().iterator().next().getStateTransition().isRightPartial())) {
// Declare a field to cache the state of the source resource in the type of the destination resource.
ResourcePath cacheRes = ((ResourceNode) re.getSource()).getOutSideResource();
component.addField(new FieldDeclaration(cacheRes.getResourceStateType(), srcName, getInitializer(cacheRes)));
if (re.getDestination().getIndegree() > 1) {
// For each source resource, a child resource is defined in the destination resource so that its state can be updated separately.
update.addAnnotation(new Annotation("Path", "\"/" + srcName + "\""));
}
}
component.addMethod(update);
}
}
}
}
// // Declare a client field to connect to the source resource of reference transfer.
// if (!bDeclareClientField) {
// for (ChannelGenerator cg : model.getChannelGenerators()) {
// DataflowChannelGenerator dcg = ((DataflowChannelGenerator) cg);
// for (ChannelMember cm : dcg.getOutputChannelMembers()) {
// if (cm.getIdentifierTemplate().getResourceName().equals(type.getTypeName().toLowerCase())) {
// if (dcg.getReferenceChannelMembers().size() > 0) {
// // If there exists one or more reference channel member.
// type.addField(new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()"));
// bDeclareClientField = true;
// break;
// }
// }
// }
// if (bDeclareClientField) break;
// }
// }
// Declare the field to store the state in the type of each resource.
if (((StoreAttribute) rn.getAttribute()).isStored()) {
ResourcePath res = rn.getOutSideResource();
Set<ResourceHierarchy> children = rn.getResourceHierarchy().getChildren();
if (children == null || children.size() == 0) {
// leaf resource.
Type fieldType = getImplStateType(res.getResourceHierarchy());
component.addField(new FieldDeclaration(fieldType, "value", getInitializer(res)));
constructorParams.add(new AbstractMap.SimpleEntry<>(rn.getResourceHierarchy(), new VariableDeclaration(fieldType, toVariableName(resourceName))));
} else {
ResourceHierarchy child = children.iterator().next();
if (children.size() == 1 && child.getNumParameters() > 0) {
// map or list.
component.addField(new FieldDeclaration(getImplStateType(res.getResourceHierarchy()), "value", getInitializer(res)));
} else {
// class
for (ResourceHierarchy c: children) {
String childTypeName = getComponentName(c);
Type childType = null;
if (generatesComponent(c)) {
// The child has a component.
childType = new Type(childTypeName, childTypeName);
String fieldName = toVariableName(childTypeName);
component.addField(new FieldDeclaration(childType, fieldName, getInitializer(res)));
}
}
}
}
}
// Declare the getter methods to obtain the children resources.
for (ResourceNode child: rn.getChildren()) {
if (generatesComponent(child.getResourceHierarchy())) {
// The child generates a component.
List<VariableDeclaration> pathParams = new ArrayList<>();
int v = 1;
for (Selector pathParam: child.getSelectors()) {
if (pathParam.getExpression() instanceof Variable) {
Variable var = (Variable) pathParam.getExpression();
pathParams.add(new VariableDeclaration(var.getType(), var.getName()));
} else if (pathParam.getExpression() instanceof Term) {
Term var = (Term) pathParam.getExpression();
pathParams.add(new VariableDeclaration(var.getType(), "v" + v));
}
v++;
}
String childCompName = getComponentName(child.getResourceHierarchy());
Type childType = new Type(childCompName, childCompName);
MethodDeclaration childGetter = null;
if (pathParams.size() == 0) {
childGetter = new MethodDeclaration("get" + childCompName, childType);
} else {
childGetter = new MethodDeclaration("get" + childCompName, false, childType, pathParams);
}
component.addMethod(childGetter);
}
}
}
// Declare the state field and reference fields in the parent component.
if (component == null) {
// Declare reference fields for push/pull data transfer.
boolean bDeclareClientField = false;
boolean noPullTransfer = true;
for (Edge resToCh : rn.getOutEdges()) {
DataFlowEdge re = (DataFlowEdge) resToCh;
DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel();
for (Edge chToRes: re.getDestination().getOutEdges()) {
ResourcePath dstRes = ((ResourceNode) chToRes.getDestination()).getOutSideResource();
boolean outsideOutputResource = false;
for (ChannelMember cm: ch.getOutputChannelMembers()) {
if (cm.getResource().getResourceHierarchy().equals(dstRes.getResourceHierarchy()) && cm.isOutside()) {
outsideOutputResource = true; // Regarded as push transfer.
break;
}
}
if (outsideOutputResource) {
// Declare a field in the parent component to refer to the destination resource of push transfer.
String dstResName = null;
if (generatesComponent(dstRes.getResourceHierarchy())) {
dstResName = getComponentName(dstRes.getResourceHierarchy());
} else {
dstResName = getComponentName(dstRes.getResourceHierarchy().getParent());
}
if (rn.getOutSideResource().getCommonPrefix(dstRes) == null && differentTreesAsDifferentServices) {
// Inter-service
if (!bDeclareClientField) {
// Declare a client field to connect to the destination resource of push transfer.
FieldDeclaration clientField = new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()");
fields.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), clientField));
bDeclareClientField = true;
}
} else {
// Inner-service
// Declare a field to directly refer to the destination resource of push transfer.
FieldDeclaration refFieldForPush = new FieldDeclaration(new Type(dstResName, dstResName), toVariableName(dstResName));
fields.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), refFieldForPush));
}
}
}
}
for (Edge chToRes : rn.getInEdges()) {
for (Edge resToCh: chToRes.getSource().getInEdges()) {
DataFlowEdge re = (DataFlowEdge) resToCh;
ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource();
DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel();
boolean outsideInputResource = false;
for (ChannelMember cm: ch.getInputChannelMembers()) {
if (cm.getResource().getResourceHierarchy().equals(srcRes.getResourceHierarchy()) && cm.isOutside()) {
outsideInputResource = true; // Regarded as pull transfer.
break;
}
}
if (outsideInputResource) {
// Declare a field in the parent component to refer to the source resource of pull transfer.
String srcResName = null;
if (generatesComponent(srcRes.getResourceHierarchy())) {
srcResName = getComponentName(srcRes.getResourceHierarchy());
} else {
srcResName = getComponentName(srcRes.getResourceHierarchy().getParent());
}
if (rn.getOutSideResource().getCommonPrefix(srcRes) == null && differentTreesAsDifferentServices) {
// Inter-service
if (!bDeclareClientField) {
// Declare a client field to connect to the source resource of pull transfer.
FieldDeclaration clientField = new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()");
fields.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), clientField));
bDeclareClientField = true;
}
} else {
// Inner-service
// Declare a field to directly refer to the source resource of pull transfer.
FieldDeclaration refFieldForPull = new FieldDeclaration(new Type(srcResName, srcResName), toVariableName(srcResName));
fields.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), refFieldForPull));
noPullTransfer = false;
}
}
}
}
// Declare the state field in the parent component.
ResourceHierarchy res = rn.getOutSideResource().getResourceHierarchy();
if (((StoreAttribute) rn.getAttribute()).isStored() && noPullTransfer && res.getNumParameters() == 0) {
String resName = getComponentName(res);
FieldDeclaration stateField = new FieldDeclaration(res.getResourceStateType(), toVariableName(resName));
fields.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), stateField));
constructorParams.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), new VariableDeclaration(res.getResourceStateType(), toVariableName(resName))));
}
}
// Declare the getter method to obtain the resource state in the component of each resource.
if (component != null) {
// A component is created for this resource.
MethodDeclaration stateGetter = new MethodDeclaration("getValue", getImplStateType(rn.getResourceHierarchy()));
if (rn.getResourceHierarchy().getParent() == null) {
// Since this getter is also an accessor.
stateGetter.addAnnotation(new Annotation("Produces", "MediaType.APPLICATION_JSON"));
stateGetter.addAnnotation(new Annotation("GET"));
}
component.addMethod(stateGetter);
} else {
// No component is created for this resource.
List<VariableDeclaration> pathParams = new ArrayList<>();
int v = 1;
for (Selector pathParam: rn.getSelectors()) {
if (pathParam.getExpression() instanceof Variable) {
Variable var = (Variable) pathParam.getExpression();
pathParams.add(new VariableDeclaration(var.getType(), var.getName()));
} else if (pathParam.getExpression() instanceof Term) {
Term var = (Term) pathParam.getExpression();
pathParams.add(new VariableDeclaration(var.getType(), "v" + v));
}
v++;
}
String resCompName = getComponentName(rn.getResourceHierarchy());
Type resType = getImplStateType(rn.getResourceHierarchy());
MethodDeclaration stateGetter = null;
if (pathParams.size() == 0) {
stateGetter = new MethodDeclaration("get" + resCompName, resType);
} else {
stateGetter = new MethodDeclaration("get" + resCompName, false, resType, pathParams);
}
getters.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), stateGetter));
}
// Declare the getter accessor in the root resource.
if (rn.getResourceHierarchy().getParent() != null) {
// For a non-root resource
MethodDeclaration getterAccessor = null;
List<VariableDeclaration> mainGetterParams = new ArrayList<>();
String resourcePath = getGetterResourcePathAndPathParams(rn.getOutSideResource(), mainGetterParams);
if (resourcePath.indexOf('/') > 0) {
resourcePath = "\"" + resourcePath.substring(resourcePath.indexOf('/')) + "\"";
} else {
resourcePath = "\"" + resourcePath + "\"";
}
if (mainGetterParams.size() > 0) {
getterAccessor = new MethodDeclaration("get" + getComponentName(rn.getResourceHierarchy()) + "Value",
false,
getImplStateType(rn.getResourceHierarchy()),
mainGetterParams);
} else {
getterAccessor = new MethodDeclaration("get" + getComponentName(rn.getResourceHierarchy()) + "Value",
getImplStateType(rn.getResourceHierarchy()));
}
getterAccessor.setBody(new Block());
Expression getState = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(rn.getOutSideResource(), rn.getOutSideResource().getRoot());
getterAccessor.getBody().addStatement("return " + getState.toImplementation(new String[] {null}) + ";");
getterAccessor.addAnnotation(new Annotation("Produces", "MediaType.APPLICATION_JSON"));
getterAccessor.addAnnotation(new Annotation("GET"));
if (rn.getAllSelectors().size() > 0) {
getterAccessor.addAnnotation(new Annotation("Path", resourcePath));
}
getterAccessors.put(rn.getResourceHierarchy(), getterAccessor);
}
// Declare the input method in each resource and the root resource.
for (Channel ch : model.getIOChannels()) {
for (ChannelMember cm : ((DataTransferChannel) ch).getOutputChannelMembers()) {
if (rn.getInSideResources().contains(cm.getResource())) {
Expression message = cm.getStateTransition().getMessageExpression();
if (message instanceof Term) {
// In each resource.
ArrayList<VariableDeclaration> resInputParams = new ArrayList<>();
ArrayList<VariableDeclaration> rootInputParams = new ArrayList<>();
String resourcePath = getInputMethodResourcePathaAndPathParams(cm.getResource(), resInputParams, rootInputParams);
if (resourcePath.indexOf('/') > 0) {
resourcePath = "\"" + resourcePath.substring(resourcePath.indexOf('/')) + "\"";
} else {
resourcePath = "\"" + resourcePath + "\"";
}
for (Variable var: message.getVariables().values()) {
String paramName = var.getName();
VariableDeclaration param = new VariableDeclaration(var.getType(), paramName);
resInputParams.add(param);
param = new VariableDeclaration(var.getType(), paramName);
param.addAnnotation(new Annotation("FormParam", "\"" + paramName + "\""));
rootInputParams.add(param);
}
if (rn.getResourceHierarchy().getParent() != null && rn.getResourceHierarchy().getParent().getParent() != null) {
MethodDeclaration input = new MethodDeclaration(((Term) message).getSymbol().getImplName(),
false, typeVoid, resInputParams);
if (component != null) {
// A component is created for this resource.
component.addMethod(input);
} else {
// No component is created for this resource.
inputs.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), input));
}
}
// For the root resource.
String str = ((Term) message).getSymbol().getImplName();
MethodDeclaration inputAccessor = new MethodDeclaration(str, false, typeVoid, rootInputParams);
if (cm.getStateTransition().isRightUnary()) {
inputAccessor.addAnnotation(new Annotation("PUT"));
} else {
inputAccessor.addAnnotation(new Annotation("POST"));
}
if (ch.getAllSelectors().size() > 0) {
inputAccessor.addAnnotation(new Annotation("Path", resourcePath));
}
inputAccessors.put(rn.getResourceHierarchy(), inputAccessor);
} else if (message instanceof Variable) {
// In each resource.
ArrayList<VariableDeclaration> resInputParams = new ArrayList<>();
int v = 1;
if (cm.getResource().getLastParam() != null) {
Expression pathParam = cm.getResource().getLastParam();
if (pathParam instanceof Variable) {
Variable var = (Variable) pathParam;
String paramName = var.getName();
VariableDeclaration param = new VariableDeclaration(var.getType(), paramName);
resInputParams.add(param);
} else if (pathParam instanceof Term) {
Term var = (Term) pathParam;
String paramName = "v" + v;
VariableDeclaration param = new VariableDeclaration(var.getType(), paramName);
resInputParams.add(param);
}
v++;
}
if (cm.getResource().getResourceHierarchy().getParent() != null && cm.getResource().getResourceHierarchy().getParent().getParent() != null) {
MethodDeclaration input = new MethodDeclaration(((Variable) message).getName(),
false, typeVoid, null);
if (component != null) {
// A component is created for this resource.
component.addMethod(input);
} else {
// No component is created for this resource.
inputs.add(new AbstractMap.SimpleEntry<>(cm.getResource().getParent().getResourceHierarchy(), input));
}
}
// For the root resource.
ArrayList<VariableDeclaration> rootInputParams = new ArrayList<>();
String resourcePath = getGetterResourcePathAndPathParams(cm.getResource(), rootInputParams);
if (resourcePath.indexOf('/') > 0) {
resourcePath = "\"" + resourcePath.substring(resourcePath.indexOf('/')) + "\"";
} else {
resourcePath = "\"" + resourcePath + "\"";
}
String str = ((Variable) message).getName();
MethodDeclaration inputAccessor = new MethodDeclaration(str, false, typeVoid, rootInputParams);
if (cm.getStateTransition().isRightUnary()) {
inputAccessor.addAnnotation(new Annotation("PUT"));
} else {
inputAccessor.addAnnotation(new Annotation("POST"));
}
if (ch.getAllSelectors().size() > 0) {
inputAccessor.addAnnotation(new Annotation("Path", resourcePath));
}
inputAccessors.put(rn.getResourceHierarchy(), inputAccessor);
}
}
}
}
}
// Add leaf getter methods to the parent components.
for (Map.Entry<ResourceHierarchy, MethodDeclaration> entry: getters) {
resourceComponents.get(entry.getKey()).addMethod(entry.getValue());
}
// Add leaf input methods to the parent components.
for (Map.Entry<ResourceHierarchy, MethodDeclaration> entry: inputs) {
resourceComponents.get(entry.getKey()).addMethod(entry.getValue());
}
// Add leaf reference fields to the parent components.
for (Map.Entry<ResourceHierarchy, FieldDeclaration> entry: fields) {
ResourceHierarchy resource = entry.getKey();
FieldDeclaration field = entry.getValue();
TypeDeclaration component = resourceComponents.get(resource);
boolean existsField = false;
for (FieldDeclaration fld: component.getFields()) {
if (fld.getName().equals(field.getName())) {
existsField = true;
break;
}
}
if (!existsField) {
component.addField(field);
if (field.getType().equals(typeClient)) {
for (CompilationUnit cu: codes) {
if (cu.types().contains(component)) {
cu.addImport(new ImportDeclaration("javax.ws.rs.client.*"));
break;
}
}
}
}
}
// Add constructor parameters to the ancestor components.
for (ResourceNode root: graph.getRootResourceNodes()) {
addConstructorParameters(root.getResourceHierarchy(), resourceComponents, resourceConstructors, constructorParams);
}
// Add accessors.
for (ResourceHierarchy rootRes: model.getResourceHierarchies()) {
if (rootRes.getParent() == null) {
// root resource
TypeDeclaration rootComponent = resourceComponents.get(rootRes);
// Add getter accessors.
for (ResourceHierarchy res: getterAccessors.keySet()) {
if (rootRes.isAncestorOf(res)) {
rootComponent.addMethod(getterAccessors.get(res));
}
}
// Add input accessors.
for (ResourceHierarchy res: inputAccessors.keySet()) {
if (rootRes.isAncestorOf(res)) {
rootComponent.addMethod(inputAccessors.get(res));
}
}
}
}
// Declare the Pair class.
boolean isCreatedPair = false;
for(Node n : resources) {
ResourceNode rn = (ResourceNode) n;
if(isCreatedPair) continue;
if(model.getType("Pair").isAncestorOf(rn.getResourceStateType())) {
TypeDeclaration type = new TypeDeclaration("Pair<T>");
type.addField(new FieldDeclaration(new Type("Double", "T"), "left"));
type.addField(new FieldDeclaration(new Type("Double", "T"), "right"));
MethodDeclaration constructor = new MethodDeclaration("Pair", true);
constructor.addParameter(new VariableDeclaration(new Type("Double", "T"), "left"));
constructor.addParameter(new VariableDeclaration(new Type("Double", "T"), "right"));
Block block = new Block();
block.addStatement("this.left = left;");
block.addStatement("this.right = right;");
constructor.setBody(block);
type.addMethod(constructor);
for(FieldDeclaration field : type.getFields()) {
MethodDeclaration getter = new MethodDeclaration(
"get" + field.getName().substring(0,1).toUpperCase() + field.getName().substring(1),
new Type("Double","T"));
getter.setBody(new Block());
getter.getBody().addStatement("return " + field.getName() + ";");
type.addMethod(getter);
}
// MethodDeclaration toStr = new MethodDeclaration("toString", false, DataConstraintModel.typeString, null);
// block = new Block();
// block.addStatement("return \"{\\\"\" + left + \"\\\":\\\"\" + right + \"\\\"}\";");
// toStr.setBody(block);
// type.addMethod(toStr);
CompilationUnit cu = new CompilationUnit(type);
cu.addImport(new ImportDeclaration("java.util.*"));
codes.add(cu);
isCreatedPair = true;
}
}
return codes;
}
private static List<VariableDeclaration> addConstructorParameters(ResourceHierarchy resource,
Map<ResourceHierarchy, TypeDeclaration> resourceComponents,
Map<ResourceHierarchy, MethodDeclaration> resourceConstructors,
List<Map.Entry<ResourceHierarchy, VariableDeclaration>> constructorParams) {
List<VariableDeclaration> params = new ArrayList<>();
for (ResourceHierarchy child: resource.getChildren()) {
params.addAll(addConstructorParameters(child, resourceComponents, resourceConstructors, constructorParams));
}
for (Map.Entry<ResourceHierarchy, VariableDeclaration> paramEnt: constructorParams) {
if (paramEnt.getKey().equals(resource)) {
params.add(paramEnt.getValue());
}
}
if (params.size() > 0) {
MethodDeclaration constructor = resourceConstructors.get(resource);
if (constructor == null) {
if (resourceComponents.get(resource) != null) {
String resourceName = getComponentName(resource);
constructor = new MethodDeclaration(resourceName, true);
Block body = new Block();
constructor.setBody(body);
resourceComponents.get(resource).addMethod(constructor);
resourceConstructors.put(resource, constructor);
}
}
if (constructor != null) {
for (VariableDeclaration param: params) {
constructor.addParameter(param);
constructor.getBody().addStatement("this." + toVariableName(param.getName()) + " = " + toVariableName(param.getName()) + ";");
}
}
}
if (resource.getNumParameters() > 0) params.clear();
return params;
}
private static String getGetterResourcePathAndPathParams(ResourcePath resPath, List<VariableDeclaration> pathParams) {
int v = 1;
List<String> params = new ArrayList<>();
for (Expression pathParam: resPath.getPathParams()) {
if (pathParam instanceof Variable) {
Variable var = (Variable) pathParam;
String paramName = var.getName();
params.add("{" + paramName + "}");
VariableDeclaration param = new VariableDeclaration(var.getType(), paramName);
param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\""));
pathParams.add(param);
} else if (pathParam instanceof Term) {
Term var = (Term) pathParam;
String paramName = "v" + v;
params.add("{" + paramName + "}");
VariableDeclaration param = new VariableDeclaration(var.getType(), paramName);
param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\""));
pathParams.add(param);
}
v++;
}
return resPath.getResourceHierarchy().toResourcePath(params);
}
private static String getInputMethodResourcePathaAndPathParams(ResourcePath resPath, ArrayList<VariableDeclaration> resInputParams,
ArrayList<VariableDeclaration> rootInputParams) {
int v = 1;
List<String> params = new ArrayList<>();
if (resPath.getLastParam() != null) {
Expression pathParam = resPath.getLastParam();
if (pathParam instanceof Variable) {
Variable var = (Variable) pathParam;
String paramName = var.getName();
params.add("{" + paramName + "}");
VariableDeclaration param = new VariableDeclaration(var.getType(), paramName);
resInputParams.add(param);
param = new VariableDeclaration(var.getType(), paramName);
param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\""));
rootInputParams.add(param);
} else if (pathParam instanceof Term) {
Term var = (Term) pathParam;
String paramName = "v" + v;
params.add("{" + paramName + "}");
VariableDeclaration param = new VariableDeclaration(var.getType(), paramName);
resInputParams.add(param);
param = new VariableDeclaration(var.getType(), paramName);
param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\""));
rootInputParams.add(param);
}
v++;
}
if (resPath.getParent() != null) {
for (Expression pathParam: resPath.getParent().getPathParams()) {
if (pathParam instanceof Variable) {
Variable var = (Variable) pathParam;
String paramName = var.getName();
params.add("{" + paramName + "}");
VariableDeclaration param = new VariableDeclaration(var.getType(), paramName);
param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\""));
rootInputParams.add(param);
} else if (pathParam instanceof Term) {
Term var = (Term) pathParam;
String paramName = "v" + v;
params.add("{" + paramName + "}");
VariableDeclaration param = new VariableDeclaration(var.getType(), paramName);
param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\""));
rootInputParams.add(param);
}
v++;
}
}
return resPath.getResourceHierarchy().toResourcePath(params);
}
private static String getInitializer(ResourcePath resId) {
Type stateType = resId.getResourceStateType();
String initializer = null;
if (resId.getResourceHierarchy().getInitialValue() != null) {
initializer = resId.getResourceHierarchy().getInitialValue().toImplementation(new String[] {""});
} else {
if (DataConstraintModel.typeList.isAncestorOf(stateType)) {
initializer = "new " + resId.getResourceStateType().getImplementationTypeName() + "()";
} else if (DataConstraintModel.typeMap.isAncestorOf(stateType)) {
initializer = "new " + resId.getResourceStateType().getImplementationTypeName() + "()";
}
}
return initializer;
}
static public ArrayList<String> getCodes(ArrayList<TypeDeclaration> codeTree) {
ArrayList<String> codes = new ArrayList<>();
for (TypeDeclaration type : codeTree) {
codes.add("public class " + type.getTypeName() + "{");
for (FieldDeclaration field : type.getFields()) {
if (type.getTypeName() != mainTypeName) {
String cons = "\t" + "private " + field.getType().getInterfaceTypeName() + " "
+ field.getName();
if (DataConstraintModel.isListType(field.getType()))
cons += " = new " + field.getType().getImplementationTypeName() + "()";
cons += ";";
codes.add(cons);
} else {
String cons = "\t" + "private " + field.getType().getInterfaceTypeName() + " "
+ field.getName() + " = new " + field.getType().getTypeName() + "(";
cons += ");";
codes.add(cons);
}
}
codes.add("");
for (MethodDeclaration method : type.getMethods()) {
String varstr = "\t" + "public " + method.getReturnType().getInterfaceTypeName() + " "
+ method.getName() + "(";
if (method.getParameters() != null) {
for (VariableDeclaration var : method.getParameters()) {
varstr += var.getType().getInterfaceTypeName() + " " + var.getName() + ",";
}
if (!method.getParameters().isEmpty())
varstr = varstr.substring(0, varstr.length() - 1);
}
if (method.getBody() != null) {
for (String str : method.getBody().getStatements()) {
codes.add("\t\t" + str + ";");
}
}
codes.add(varstr + ")" + "{");
codes.add("\t" + "}");
codes.add("");
}
codes.add("}");
codes.add("");
}
return codes;
}
static public IResourceStateAccessor pushAccessor = new IResourceStateAccessor() {
@Override
public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) {
ResourcePath targetRes = target.getResource();
ResourcePath fromRes = from.getResource();
if (targetRes.equals(fromRes)) {
return new Field("value",
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
// use the cached value as the current state
return new Field(targetRes.getResourceName(),
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
@Override
public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) {
ResourcePath targetRes = target.getResource();
return new Parameter(targetRes.getResourceName(),
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
@Override
public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) {
if (fromRes != null && targetRes.equals(fromRes)) {
return new Field("value",
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
return null;
}
};
static public IResourceStateAccessor pullAccessor = new IResourceStateAccessor() {
@Override
public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) {
ResourcePath targetRes = target.getResource();
if (from != null && !target.isOutside()) {
ResourcePath fromRes = from.getResource();
if (targetRes.getCommonPrefix(fromRes) != null) {
return getDirectStateAccessorFor(targetRes, fromRes);
}
}
// for reference channel member
return new Parameter(targetRes.getResourceName(),
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
@Override
public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) {
ResourcePath targetRes = target.getResource();
if (from != null && !target.isOutside()) {
ResourcePath fromRes = from.getResource();
if (targetRes.getCommonPrefix(fromRes) != null) {
return getDirectStateAccessorFor(targetRes, fromRes);
}
}
return new Parameter(targetRes.getResourceName(),
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
@Override
public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) {
if (fromRes != null && !fromRes.getResourceHierarchy().isAncestorOf(targetRes.getResourceHierarchy())) {
if (targetRes.equals(fromRes)) {
return new Field("value",
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
// for reference channel member
return new Parameter(targetRes.getResourceName(),
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
} else {
// access from an ancestor or outside of the hierarchy
Stack<ResourcePath> pathStack = new Stack<>();
ResourcePath curPath = targetRes;
do {
if (fromRes != null && curPath.equals(fromRes)) break;
pathStack.push(curPath);
curPath = curPath.getParent();
} while (curPath != null);
// iterate from the `from' resource
Term getter = null;
int v = 1;
while (!pathStack.empty()) {
curPath = pathStack.pop();
String typeName = getComponentName(curPath.getResourceHierarchy());
if (getter == null && fromRes == null) {
// root resource
String fieldName = toVariableName(typeName);
getter = new Field(fieldName, new Type(typeName, typeName));
} else {
Term newGetter = new Term(new Symbol("get" + typeName, -1, Symbol.Type.METHOD));
newGetter.addChild(getter);
if (curPath.getResourceHierarchy().getNumParameters() > 0) {
Variable var = null;
Expression param = curPath.getLastParam();
if (param instanceof Variable) {
var = (Variable) param;
} else if (param instanceof Term) {
var = new Variable("v" + v, ((Term) param).getType());
}
if (var != null) {
newGetter.addChild(var);
newGetter.getSymbol().setArity(2);
}
v++;
}
getter = newGetter;
}
}
if (generatesComponent(targetRes.getResourceHierarchy())) {
Term newGetter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD));
newGetter.addChild(getter);
getter = newGetter;
}
return getter;
}
}
};
static public IResourceStateAccessor refAccessor = new IResourceStateAccessor() {
@Override
public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) {
ResourcePath targetRes = target.getResource();
ResourcePath fromRes = from.getResource();
if (targetRes.equals(fromRes)) {
return new Field("value",
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
// for reference channel member
return new Parameter(targetRes.getResourceName(),
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
@Override
public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) {
ResourcePath targetRes = target.getResource();
return new Parameter(targetRes.getResourceName(),
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
@Override
public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) {
if (fromRes != null && targetRes.equals(fromRes)) {
return new Field("value",
targetRes.getResourceStateType() != null ? targetRes.getResourceStateType()
: DataConstraintModel.typeInt);
}
return null;
}
};
}