package algorithms;
import java.util.ArrayList;
import java.util.HashSet;
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.ChannelGenerator;
import models.dataConstraintModel.ChannelMember;
import models.dataConstraintModel.DataConstraintModel;
import models.dataConstraintModel.IdentifierTemplate;
import models.dataFlowModel.DataFlowModel;
import models.dataFlowModel.DataflowChannelGenerator.IResourceStateAccessor;
import models.dataFlowModel.PushPullAttribute;
import models.dataFlowModel.PushPullValue;
import models.dataFlowModel.ResourceDependency;
import models.dataFlowModel.ResourceDependencyGraph;
import models.dataFlowModel.ResourceNode;
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");
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;
}
static public ArrayList<CompilationUnit> doGenerate(ResourceDependencyGraph graph, DataFlowModel model) {
ArrayList<CompilationUnit> codes = new ArrayList<>();
ArrayList<ResourceNode> resources = StoreResourceCheck(graph);
for (ResourceNode rn : resources) {
String resourceName = rn.getIdentifierTemplate().getResourceName().substring(0, 1).toUpperCase()
+ rn.getIdentifierTemplate().getResourceName().substring(1);
// Declare the field to refer each resource in the main type.
TypeDeclaration type = new TypeDeclaration(resourceName);
// type.addAnnotation(new Annotation("Component"));
type.addAnnotation(new Annotation("Path", "\"/" + rn.getIdentifierTemplate().getResourceName() + "\""));
// Declare a client field and update methods from other resources.
boolean bDeclareClientField = false;
for (Edge e : rn.getOutEdges()) {
ResourceDependency re = (ResourceDependency) e;
if (!bDeclareClientField && ((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) {
// Declare a client field to connect to the destination resource of push transfer.
type.addField(new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()"));
bDeclareClientField = true;
}
}
for (Edge e : rn.getInEdges()) {
ResourceDependency re = (ResourceDependency) e;
String srcResName = ((ResourceNode) re.getSource()).getIdentifierTemplate().getResourceName()
.substring(0, 1).toUpperCase()
+ ((ResourceNode) re.getSource()).getIdentifierTemplate().getResourceName().substring(1);
if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) {
if (!bDeclareClientField) {
// Declare a client field to connect to the source resource of pull transfer.
type.addField(new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()"));
bDeclareClientField = true;
}
} else {
// Declare an update method in the type of the destination resource.
ArrayList<VariableDeclaration> vars = new ArrayList<>();
String srcName = ((ResourceNode) re.getSource()).getIdentifierTemplate().getResourceName();
VariableDeclaration param = new VariableDeclaration(((ResourceNode) re.getSource()).getIdentifierTemplate().getResourceStateType(), srcName);
param.addAnnotation(new Annotation("FormParam", "\"" + srcName + "\""));
vars.add(param);
MethodDeclaration update = new MethodDeclaration("update" + srcResName, false, typeVoid, vars);
if (((StoreAttribute) rn.getAttribute()).isNeeded()) {
update.addAnnotation(new Annotation("POST"));
} else {
update.addAnnotation(new Annotation("PUT"));
}
if (rn.getInEdges().size() > 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 + "\""));
// Declare a field to cash the state of the source resource in the type of the destination resource.
type.addField(new FieldDeclaration(((ResourceNode) re.getSource()).getIdentifierTemplate().getResourceStateType(), srcName));
}
type.addMethod(update);
}
}
// Declare input methods in resources.
for (ChannelGenerator cg : model.getIOChannelGenerators()) {
for (ChannelMember cm : cg.getChannelMembers()) {
if (cm.getIdentifierTemplate().getResourceName().equals(type.getTypeName().toLowerCase())) {
Expression message = cm.getStateTransition().getMessageExpression();
if (message.getClass() == Term.class) {
ArrayList<VariableDeclaration> params = new ArrayList<>();
for (Variable var: message.getVariables().values()) {
String paramName = var.getName();
VariableDeclaration param = new VariableDeclaration(var.getType(), paramName);
param.addAnnotation(new Annotation("FormParam", "\"" + paramName + "\""));
params.add(param);
}
MethodDeclaration input = new MethodDeclaration(
((Term) cm.getStateTransition().getMessageExpression()).getSymbol().getImplName(),
false, typeVoid, params);
input.addAnnotation(new Annotation("PUT"));
type.addMethod(input);
}
}
}
}
// Declare the field to store the state in the type of each resource.
if (((StoreAttribute) rn.getAttribute()).isStored()) {
String initializer = "new " + rn.getIdentifierTemplate().getResourceStateType().getImplementationTypeName() + "()";
if (!rn.getIdentifierTemplate().getResourceStateType().getTypeName().contains("List"))
initializer = null;
type.addField(new FieldDeclaration(rn.getIdentifierTemplate().getResourceStateType(),
rn.getIdentifierTemplate().getResourceName(), initializer));
}
// Declare the getter method to obtain the state in the type of each resource.
MethodDeclaration getter = new MethodDeclaration("get" + type.getTypeName(), rn.getIdentifierTemplate().getResourceStateType());
getter.addAnnotation(new Annotation("Produces", "MediaType.APPLICATION_JSON"));
getter.addAnnotation(new Annotation("GET"));
type.addMethod(getter);
// Add compilation unit for each resource.
CompilationUnit cu = new CompilationUnit(type);
cu.addImport(new ImportDeclaration("java.util.*"));
cu.addImport(new ImportDeclaration("javax.ws.rs.*"));
cu.addImport(new ImportDeclaration("javax.ws.rs.client.*"));
cu.addImport(new ImportDeclaration("javax.ws.rs.core.*"));
codes.add(cu);
}
return codes;
}
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 (field.getType().equals(DataConstraintModel.typeList))
cons += " = new ArrayList<>()";
cons += ";";
codes.add(cons);
} else {
String cons = "\t" + "private " + field.getType().getInterfaceTypeName() + " "
+ field.getName() + " = new " + field.getType().getTypeName() + "(";
for (TypeDeclaration tree : codeTree) {
if (field.getType().getTypeName() == tree.getTypeName()) {
for (VariableDeclaration var : tree.getConstructors()) {
cons += var.getName() + ",";
}
if (!tree.getConstructors().isEmpty())
cons = cons.substring(0, cons.length() - 1);
break;
}
}
cons += ");";
codes.add(cons);
}
}
codes.add("");
if (type.getTypeName() != mainTypeName) {
if (!type.getConstructors().isEmpty()) {
String cons = "\t" + "public " + type.getTypeName() + "(";
for (VariableDeclaration constructor : type.getConstructors()) {
cons += constructor.getType().getTypeName() + " " + constructor.getName() + ",";
}
if (!type.getConstructors().isEmpty())
cons = cons.substring(0, cons.length() - 1);
cons += "){";
codes.add(cons);
for (FieldDeclaration field : type.getFields()) {
for (VariableDeclaration vari : type.getConstructors()) {
if (field.getType().getTypeName().equals(vari.getType().getTypeName())) {
codes.add("\t\t" + "this." + field.getName() + " = " + field.getName() + ";");
}
}
}
codes.add("\t" + "}");
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 private ArrayList<ResourceNode> StoreResourceCheck(ResourceDependencyGraph graph) {
ArrayList<ResourceNode> resources = new ArrayList<>();
for (Node n : graph.getNodes()) {
ResourceNode rn = (ResourceNode) n;
boolean flag = true;
for (Edge e : rn.getOutEdges()) {
ResourceDependency re = (ResourceDependency) e;
if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) {
flag = false;
}
}
for (Edge e : rn.getInEdges()) {
ResourceDependency re = (ResourceDependency) e;
if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) {
flag = false;
}
}
if (flag)
resources.add(rn);
}
trackNode(resources.get(0), resources);
return resources;
}
static private void trackNode(ResourceNode current, ArrayList<ResourceNode> resources) {
if (!resources.contains(current))
resources.add(current);
for (Edge e : current.getOutEdges()) {
ResourceDependency re = (ResourceDependency) e;
if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) {
trackNode((ResourceNode) re.getDestination(), resources);
}
}
for (Edge e : current.getInEdges()) {
ResourceDependency re = (ResourceDependency) e;
if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) {
trackNode((ResourceNode) re.getSource(), resources);
}
}
}
static public IResourceStateAccessor pushAccessor = new IResourceStateAccessor() {
@Override
public Expression getCurrentStateAccessorFor(IdentifierTemplate target, IdentifierTemplate from) {
if (target.equals(from)) {
return new Field(target.getResourceName(),
target.getResourceStateType() != null ? target.getResourceStateType()
: DataConstraintModel.typeInt);
}
return null;
}
@Override
public Expression getNextStateAccessorFor(IdentifierTemplate target, IdentifierTemplate from) {
return new Parameter(target.getResourceName(),
target.getResourceStateType() != null ? target.getResourceStateType()
: DataConstraintModel.typeInt);
}
};
static public IResourceStateAccessor pullAccessor = new IResourceStateAccessor() {
@Override
public Expression getCurrentStateAccessorFor(IdentifierTemplate target, IdentifierTemplate from) {
if (target.equals(from)) {
return new Field(target.getResourceName(),
target.getResourceStateType() != null ? target.getResourceStateType()
: DataConstraintModel.typeInt);
}
return null;
}
@Override
public Expression getNextStateAccessorFor(IdentifierTemplate target, IdentifierTemplate from) {
return new Parameter(target.getResourceName(),
target.getResourceStateType() != null ? target.getResourceStateType()
: DataConstraintModel.typeInt);
}
};
}