package algorithms; import java.util.ArrayList; import java.util.HashSet; import java.util.Set; 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); Set<Node> resources = graph.getNodes(); for (Node n : resources) { ResourceNode rn = (ResourceNode) n; 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(); Type srcType = ((ResourceNode) re.getSource()).getIdentifierTemplate().getResourceStateType(); VariableDeclaration param = new VariableDeclaration(srcType, srcName); param.addAnnotation(new Annotation("FormParam", "\"" + srcName + "\"")); vars.add(param); MethodDeclaration update = new MethodDeclaration("update" + srcResName, false, typeVoid, vars); for (ChannelMember cm: re.getChannelGenerator().getOutputChannelMembers()) { if (cm.getIdentifierTemplate() == rn.getIdentifierTemplate()) { if (cm.getStateTransition().isRightUnary()) { update.addAnnotation(new Annotation("PUT")); } else { update.addAnnotation(new Annotation("POST")); } } } 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. String cashInitializer = null; Type cashType = ((ResourceNode) re.getSource()).getIdentifierTemplate().getResourceStateType(); if (DataConstraintModel.typeList.isAncestorOf(cashType) || DataConstraintModel.typeTuple.isAncestorOf(cashType)) { cashInitializer = "new " + cashType.getImplementationTypeName() + "()"; } type.addField(new FieldDeclaration(cashType, srcName, cashInitializer)); } 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); if (cm.getStateTransition().isRightUnary()) { input.addAnnotation(new Annotation("PUT")); } else { input.addAnnotation(new Annotation("POST")); } 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() + "()"; Type stateType = rn.getIdentifierTemplate().getResourceStateType(); if (!DataConstraintModel.typeList.isAncestorOf(stateType) && !DataConstraintModel.typeTuple.isAncestorOf(stateType)) initializer = null; type.addField(new FieldDeclaration(stateType, 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.*")); 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); } 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 (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() + "("; 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 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); } // for reference channel member return new Parameter(target.getResourceName(), target.getResourceStateType() != null ? target.getResourceStateType() : DataConstraintModel.typeInt); } @Override public Expression getNextStateAccessorFor(IdentifierTemplate target, IdentifierTemplate from) { return new Parameter(target.getResourceName(), target.getResourceStateType() != null ? target.getResourceStateType() : DataConstraintModel.typeInt); } }; }