diff --git a/AlgebraicDataflowArchitectureModel/src/algorithms/JerseyCodeGenerator.java b/AlgebraicDataflowArchitectureModel/src/algorithms/JerseyCodeGenerator.java index bc6363c..def1f43 100644 --- a/AlgebraicDataflowArchitectureModel/src/algorithms/JerseyCodeGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/algorithms/JerseyCodeGenerator.java @@ -70,7 +70,7 @@ // 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("Component")); type.addAnnotation(new Annotation("Path", "\"/" + rn.getIdentifierTemplate().getResourceName() + "\"")); // Declare a client field and update methods from other resources. @@ -98,11 +98,12 @@ // Declare an update method in the type of the destination resource. ArrayList vars = new ArrayList<>(); String srcName = ((ResourceNode) re.getSource()).getIdentifierTemplate().getResourceName(); - VariableDeclaration param = new VariableDeclaration(((ResourceNode) re.getSource()).getIdentifierTemplate().getResourceStateType(), srcName); + 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); - if (((StoreAttribute) rn.getAttribute()).isNeeded()) { + if (((StoreAttribute) rn.getAttribute()).isStored()) { update.addAnnotation(new Annotation("POST")); } else { update.addAnnotation(new Annotation("PUT")); @@ -138,7 +139,11 @@ MethodDeclaration input = new MethodDeclaration( ((Term) cm.getStateTransition().getMessageExpression()).getSymbol().getImplName(), false, typeVoid, params); - input.addAnnotation(new Annotation("PUT")); + if (((StoreAttribute) rn.getAttribute()).isNeeded()) { + input.addAnnotation(new Annotation("POST")); + } else { + input.addAnnotation(new Annotation("PUT")); + } type.addMethod(input); } } @@ -166,6 +171,10 @@ 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")); + cu.addImport(new ImportDeclaration("com.fasterxml.jackson.core.JsonProcessingException")); codes.add(cu); } diff --git a/AlgebraicDataflowArchitectureModel/src/algorithms/JerseyMethodBodyGenerator.java b/AlgebraicDataflowArchitectureModel/src/algorithms/JerseyMethodBodyGenerator.java index ac931f1..86ee9d9 100644 --- a/AlgebraicDataflowArchitectureModel/src/algorithms/JerseyMethodBodyGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/algorithms/JerseyMethodBodyGenerator.java @@ -12,6 +12,7 @@ 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.Expression; @@ -85,6 +86,40 @@ update.addFirstStatement(cashStatement); } } + // to convert a json param to a tuple 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 = compType.getInterfaceTypeName(); + mapTypeName = mapTypeName.replace("Map.Entry", "Map"); + mapTypeName = "Map 0) update.addFirstStatement(paramConverter); + } MethodDeclaration getter = getGetterMethod(dstType); if (((StoreAttribute) dst.getAttribute()).isStored()) { // returns the state stored in a field. @@ -94,7 +129,7 @@ } // src side (for a chain of update method invocations) String httpMethod = null; - if (((StoreAttribute) dst.getAttribute()).isNeeded()) { + if (((StoreAttribute) dst.getAttribute()).isStored()) { httpMethod = "post"; } else { httpMethod = "put"; @@ -112,6 +147,7 @@ } else { srcUpdate.addStatement("result = " + getHttpMethodCallStatement(baseURL, dstResourceName, srcResName, httpMethod)); } + srcUpdate.addThrow("JsonProcessingException"); } } MethodDeclaration srcInput = getInputMethod(srcType, src, model); @@ -127,6 +163,7 @@ } else { srcInput.addStatement("result = " + getHttpMethodCallStatement(baseURL, dstResourceName, srcResName, httpMethod)); } + srcInput.addThrow("JsonProcessingException"); } } else { // for pull (or push/pull) data transfer @@ -136,7 +173,40 @@ String curState = d.getChannelGenerator().deriveUpdateExpressionOf(out, JerseyCodeGenerator.pullAccessor).toImplementation(sideEffects); getter.addStatement(sideEffects[0] + "return " + curState + ";"); } - getter.addFirstStatement(getHttpMethodCallStatement(baseURL, srcResourceName, "get", src.getIdentifierTemplate().getResourceStateType())); + // get src side resource state by pull data transfer. + String varName = new String(srcResourceName); + Type srcResourceType = src.getIdentifierTemplate().getResourceStateType(); + String respTypeName = srcResourceType.getInterfaceTypeName(); + String respConverter = ""; + if (DataConstraintModel.typeList.isAncestorOf(srcResourceType) && srcResourceType != DataConstraintModel.typeList) { + Type compType = TypeInference.getListComponentType(srcResourceType); + if (DataConstraintModel.typeTuple.isAncestorOf(compType)) { + varName += "_json"; + String mapTypeName = compType.getInterfaceTypeName(); + mapTypeName = mapTypeName.replace("Map.Entry", "Map"); + mapTypeName = "Map"; + respConverter += srcResourceType.getInterfaceTypeName() + " " + srcResourceName + " = new " + srcResourceType.getImplementationTypeName() + "();\n"; + respConverter += "for (" + mapTypeName + " i: " + varName + ") {\n"; + respConverter += "\t" + srcResourceName + ".add(" + getCodeForConversionFromMapToTuple(compType, "i") + ");\n"; + respConverter += "}"; + getter.addThrow("JsonProcessingException"); + } + } else if (DataConstraintModel.typeTuple.isAncestorOf(srcResourceType)) { + varName += "_json"; + respConverter += "{\n"; + String mapTypeName = srcResourceType.getInterfaceTypeName(); + mapTypeName = mapTypeName.replace("Map.Entry", "Map"); + mapTypeName = "Map 0) { + getter.addFirstStatement(respConverter); + } + getter.addFirstStatement(respTypeName + " " + varName + " = " + getHttpMethodCallStatementWithResponse(baseURL, srcResourceName, "get", srcResourceType.getImplementationTypeName())); } } } @@ -182,16 +252,49 @@ return codes; } + private static String getCodeForConversionFromMapToTuple(Type tupleType, String mapVar) { + String decoded = "$x"; + List elementsTypes = TypeInference.getTupleComponentTypes(tupleType); + String elementBase = mapVar + ".entrySet().iterator().next()"; + if (elementsTypes.get(0) == DataConstraintModel.typeBoolean) { + decoded = decoded.replace("$x", "new AbstractMap.SimpleEntry<>(Boolean.parseBoolean(" + elementBase + ".getKey()), $x)"); + } else if (elementsTypes.get(0) == DataConstraintModel.typeInt) { + decoded = decoded.replace("$x", "new AbstractMap.SimpleEntry<>(Integer.parseInt(" + elementBase + ".getKey()), $x)"); + } else if (elementsTypes.get(0) == DataConstraintModel.typeLong) { + decoded = decoded.replace("$x", "new AbstractMap.SimpleEntry<>(Long.parseLong(" + elementBase + ".getKey()), $x)"); + } else if (elementsTypes.get(0) == DataConstraintModel.typeFloat) { + decoded = decoded.replace("$x", "new AbstractMap.SimpleEntry<>(Float.parseFloat(" + elementBase + ".getKey()), $x)"); + } else if (elementsTypes.get(0) == DataConstraintModel.typeDouble) { + decoded = decoded.replace("$x", "new AbstractMap.SimpleEntry<>(Double.parseDouble(" + elementBase + ".getKey()), $x)"); + } else if (elementsTypes.get(0) == DataConstraintModel.typeString) { + decoded = decoded.replace("$x", "new AbstractMap.SimpleEntry<>(" + elementBase + ", $x)"); + } else { + // Future work. + } + elementBase += ".getValue()"; + for (Type elmType: elementsTypes.subList(1, elementsTypes.size() - 1)) { + decoded = decoded.replace("$x", "new AbstractMap.SimpleEntry<>(" + elementBase + ".getKey(), $x)"); + elementBase += ".getValue()"; + } + decoded = decoded.replace("$x", elementBase); + return decoded; + } + private static String getHttpMethodParamsStatement(String callerResourceName, Type paramType, String paramName) { if (DataConstraintModel.typeList.isAncestorOf(paramType)) { - String compType = "Integer"; - String interfaceType = paramType.getInterfaceTypeName(); - if (interfaceType.contains("<")) { - compType = interfaceType.substring(interfaceType.indexOf("<") + 1, interfaceType.lastIndexOf(">")); - } + Type compType = TypeInference.getListComponentType(paramType); String statements = "Form form = new Form();\n"; - statements += "for (" + compType + " i: " + paramName + ") {\n"; - statements += "\tform.param(\"" + paramName + "\", i.toString());\n"; + String wrapperType = DataConstraintModel.getWrapperType(compType); + if (wrapperType == null) { + statements += "for (" + compType.getInterfaceTypeName() + " i: " + paramName + ") {\n"; + } else { + statements += "for (" + wrapperType + " i: " + paramName + ") {\n"; + } + if (DataConstraintModel.typeTuple.isAncestorOf(compType) || DataConstraintModel.typeList.isAncestorOf(compType)) { + statements += "\tform.param(\"" + paramName + "\", new ObjectMapper().writeValueAsString(i));\n"; + } else { + statements += "\tform.param(\"" + paramName + "\", i.toString());\n"; + } statements += "}\n"; statements += "Entity
entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED);"; return statements; @@ -209,13 +312,12 @@ } } - private static String getHttpMethodCallStatement(String baseURL, String resourceName, String httpMethod, Type responseType) { - String responseTypeName = responseType.getImplementationTypeName(); - String responseShortTypeName = responseTypeName; - if (responseTypeName.contains("<")) { - responseShortTypeName = responseTypeName.substring(0, responseTypeName.indexOf("<")); + 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 responseType.getInterfaceTypeName() + " " + resourceName + " = client.target(\"" + baseURL + "\").path(\"/" + resourceName + "\").request()." + httpMethod + "(" + responseShortTypeName + ".class);"; + return "client.target(\"" + baseURL + "\").path(\"/" + resourceName + "\").request()." + httpMethod + "(" + responseShortTypeName + ".class);"; } private static MethodDeclaration getUpdateMethod(TypeDeclaration type, TypeDeclaration from) { diff --git a/AlgebraicDataflowArchitectureModel/src/algorithms/TypeInference.java b/AlgebraicDataflowArchitectureModel/src/algorithms/TypeInference.java index d035b02..8bf79c6 100644 --- a/AlgebraicDataflowArchitectureModel/src/algorithms/TypeInference.java +++ b/AlgebraicDataflowArchitectureModel/src/algorithms/TypeInference.java @@ -30,7 +30,31 @@ static private Map listComponentTypes = new HashMap<>(); static private Map, Type> tupleTypes = new HashMap<>(); static private Map> tupleComponentTypes = new HashMap<>(); + + public static Type getListType(Type compType) { + return listTypes.get(compType); + } + public static Type getListComponentType(Type listType) { + return listComponentTypes.get(listType); + } + + public static Collection getListTypes() { + return listTypes.values(); + } + + public static Type getTupleType(List compTypes) { + return tupleTypes.get(compTypes); + } + + public static List getTupleComponentTypes(Type tupleType) { + return tupleComponentTypes.get(tupleType); + } + + public static Collection getTupleTypes() { + return tupleTypes.values(); + } + static public void infer(ResourceDependencyGraph graph, DataFlowModel model) { Map> resources = new HashMap<>(); Map variables = new HashMap<>(); @@ -51,6 +75,10 @@ Map> updateFromTuple = new HashMap<>(); listComponentTypes.put(DataConstraintModel.typeList, null); + listComponentTypes.put(DataConstraintModel.typeListInt, DataConstraintModel.typeInt); + listComponentTypes.put(DataConstraintModel.typeListStr, DataConstraintModel.typeString); + listTypes.put(DataConstraintModel.typeInt, DataConstraintModel.typeListInt); + listTypes.put(DataConstraintModel.typeString, DataConstraintModel.typeListStr); tupleComponentTypes.put(DataConstraintModel.typeTuple, null); for (Node n: graph.getNodes()) { ResourceNode resource = (ResourceNode) n; @@ -753,27 +781,16 @@ private static String getImplementationTypeName(Type type) { if (type == null) return "Integer"; - return wrapperType(type.getImplementationTypeName()); + String wrapperType = DataConstraintModel.getWrapperType(type); + if (wrapperType != null) return wrapperType; + return type.getImplementationTypeName(); } private static String getInterfaceTypeName(Type type) { if (type == null) return "Integer"; - return wrapperType(type.getInterfaceTypeName()); - } - - private static String wrapperType(String implTypeName) { - if (implTypeName.equals(DataConstraintModel.typeInt.getImplementationTypeName())) { - implTypeName = "Integer"; - } else if (implTypeName.equals(DataConstraintModel.typeLong.getImplementationTypeName())) { - implTypeName = "Long"; - } else if (implTypeName.equals(DataConstraintModel.typeFloat.getImplementationTypeName())) { - implTypeName = "Float"; - } else if (implTypeName.equals(DataConstraintModel.typeDouble.getImplementationTypeName())) { - implTypeName = "Double"; - } else if (implTypeName.equals(DataConstraintModel.typeBoolean.getImplementationTypeName())) { - implTypeName = "Boolean"; - } - return implTypeName; + String wrapperType = DataConstraintModel.getWrapperType(type); + if (wrapperType != null) return wrapperType; + return type.getInterfaceTypeName(); } private static > Map getUpdateSet(Map> updateSets, U keySet) { diff --git a/AlgebraicDataflowArchitectureModel/src/code/ast/MethodDeclaration.java b/AlgebraicDataflowArchitectureModel/src/code/ast/MethodDeclaration.java index 6389e16..fc4e104 100644 --- a/AlgebraicDataflowArchitectureModel/src/code/ast/MethodDeclaration.java +++ b/AlgebraicDataflowArchitectureModel/src/code/ast/MethodDeclaration.java @@ -15,6 +15,7 @@ private List parameters = null; private Block body = null; private Map annotations = new HashMap<>(); + private Throws thrws = null; public MethodDeclaration(String methodName) { this(methodName, false); @@ -101,6 +102,13 @@ } body.addFirstStatement(statement); } + + public void addThrow(String exception) { + if (thrws == null) { + thrws = new Throws(); + } + thrws.addException(exception); + } @Override public Annotation getAnnotation(String name) { @@ -136,7 +144,11 @@ delimitar = ", "; } } - code += ") {\n"; + code += ") "; + if (thrws != null) { + code += thrws.toString() + " "; + } + code += "{\n"; if (body != null) { code += CodeUtil.insertTab(body.toString()); } diff --git a/AlgebraicDataflowArchitectureModel/src/code/ast/Throws.java b/AlgebraicDataflowArchitectureModel/src/code/ast/Throws.java new file mode 100644 index 0000000..a8b41e5 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/code/ast/Throws.java @@ -0,0 +1,29 @@ +package code.ast; + +import java.util.Set; +import java.util.HashSet; + +public class Throws extends ASTNode { + private Set exceptions = new HashSet<>(); + + public Throws() { + } + + public void addException(String exception) { + exceptions.add(exception); + } + + public Set getExceptions() { + return exceptions; + } + + public String toString() { + String code = "throws "; + String delimiter = ""; + for (String exception: exceptions) { + code += delimiter + exception; + delimiter = ", "; + } + return code; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/DataConstraintModel.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/DataConstraintModel.java index c372588..f0c6fc8 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/DataConstraintModel.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/DataConstraintModel.java @@ -22,6 +22,8 @@ public static final Type typeBoolean = new Type("Bool", "boolean"); public static final Type typeString = new Type("Str", "String"); public static final Type typeList = new Type("List", "ArrayList", "List"); + public static final Type typeListInt = new Type("List", "ArrayList<>", "List", typeList); + public static final Type typeListStr = new Type("List", "ArrayList<>", "List", typeList); public static final Type typeTuple = new Type("Tuple", "AbstractMap.SimpleEntry", "Map.Entry"); public static final Symbol add = new Symbol(Parser.ADD, 2, Symbol.Type.INFIX); public static final Symbol mul = new Symbol(Parser.MUL, 2, Symbol.Type.INFIX);; @@ -195,6 +197,25 @@ return symbols.get(name); } + public static String getWrapperType(Type type) { + if (type == typeInt) { + return "Integer"; + } else if (type == typeLong) { + return "Long"; + } else if (type == typeFloat) { + return "Float"; + } else if (type == typeDouble) { + return "Double"; + } else if (type == typeBoolean) { + return "Boolean"; + } + return null; + } + + public static boolean isListType(Type type) { + return typeList.isAncestorOf(type); + } + @Override public String toString() { String out = ""; @@ -206,8 +227,4 @@ } return out; } - - public static boolean isListType(Type type) { - return typeList.isAncestorOf(type); - } }