diff --git a/AlgebraicDataflowArchitectureModel/src/generators/CodeGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/CodeGenerator.java index 3464845..481ac10 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/CodeGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/CodeGenerator.java @@ -324,29 +324,29 @@ } else { if (resourceHierarchy.getChildren() != null && resourceHierarchy.getChildren().size() == 1 && resourceHierarchy.getChildren().iterator().next().getNumParameters() > 0) { // list or map - if (!platformSpec.isMonolithic()) { - // For REST API - stateGetter.addStatement(langSpec.getReturnStatement(langSpec.getFieldAccessor(fieldOfResourceState)) + langSpec.getStatementDelimiter()); // return value; - } else { +// if (!platformSpec.isMonolithic()) { +// // For REST API +// stateGetter.addStatement(langSpec.getReturnStatement(langSpec.getFieldAccessor(fieldOfResourceState)) + langSpec.getStatementDelimiter()); // return value; +// } else { String implTypeName = resStateType.getImplementationTypeName(); // copy the current state to be returned as a 'value' List parameters = new ArrayList<>(); parameters.add(langSpec.getFieldAccessor(fieldOfResourceState)); stateGetter.addStatement(langSpec.getReturnStatement(langSpec.getConstructorInvocation(implTypeName, parameters)) + langSpec.getStatementDelimiter()); // return new Resource(value); - } +// } } else { if (resourceHierarchy.getChildren() == null || resourceHierarchy.getChildren().size() == 0) { - // a leaf resource - if (!platformSpec.isMonolithic()) { - // For REST API - stateGetter.addStatement(langSpec.getReturnStatement(langSpec.getFieldAccessor(fieldOfResourceState)) + langSpec.getStatementDelimiter()); // return value; - } else { +// // a leaf resource +// if (!platformSpec.isMonolithic()) { +// // For REST API +// stateGetter.addStatement(langSpec.getReturnStatement(langSpec.getFieldAccessor(fieldOfResourceState)) + langSpec.getStatementDelimiter()); // return value; +// } else { String implTypeName = resStateType.getImplementationTypeName(); // copy the current state to be returned as a 'value' List parameters = new ArrayList<>(); parameters.add(langSpec.getFieldAccessor(fieldOfResourceState)); stateGetter.addStatement(langSpec.getReturnStatement(langSpec.getConstructorInvocation(implTypeName, parameters)) + langSpec.getStatementDelimiter()); // return new Resource(value); - } +// } } else { Term composer = null; Term composerSub = new Constant(DataConstraintModel.nil); diff --git a/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java b/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java index fbb3ef2..0c6c682 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java @@ -277,6 +277,7 @@ } private void addUpdateStatementWithConstructorInvocationToMethod(MethodDeclaration method, Expression exp, ResourceHierarchy resource, ResourceHierarchy descendantRes, TypeDeclaration descendantComponent, ILanguageSpecific langSpec) { + // Replace each json term in exp with the corresponding constructor invocation. Type replacedJsonType = descendantRes.getResourceStateType(); String replacingClassName = getComponentName(descendantRes, langSpec); Type descendantType = new Type(replacingClassName, replacingClassName); @@ -292,10 +293,11 @@ List params = new ArrayList<>(); if (childConstructor != null) { for (VariableDeclaration var: childConstructor.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(); + Expression param = jsonMember.reduce(); // Reduce {"name": "foo", age: 25}.name => "foo" if (param != null) { if (param instanceof Term) { if (((Term) param).getType() == null) { @@ -1165,7 +1167,7 @@ } } else { // for REST API - parent = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(insideResPath.getResourceHierarchy())); + parent = langSpec.toVariableName(getComponentName(insideResPath.getResourceHierarchy(), langSpec)); } if (selType.equals(DataConstraintModel.typeInt)) { // make a for loop (for a list) for data collecting. @@ -1179,7 +1181,7 @@ && platformSpec.isDifferentTreesAsDifferentServices()) { // for REST API Type parentResType = insideResPath.getResourceStateType(); - String parentResName = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(insideResPath.getResourceHierarchy())); + String parentResName = langSpec.toVariableName(getComponentName(insideResPath.getResourceHierarchy(), langSpec)); String parentResPath = insideResPath.toString().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); generatePullDataTransfer(stateGetter, parentResName, parentResPath, parentResType, true, platformSpec, langSpec); } @@ -1817,7 +1819,7 @@ } } else { // for REST API - parent = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(insideResPath.getResourceHierarchy())); + parent = langSpec.toVariableName(getComponentName(insideResPath.getResourceHierarchy(), langSpec)); } if (selType.equals(DataConstraintModel.typeInt)) { // make a for loop (for a list) for broadcasting. @@ -1833,7 +1835,7 @@ && platformSpec.isDifferentTreesAsDifferentServices()) { // for REST API Type parentResType = insideResPath.getResourceStateType(); - String parentResName = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(insideResPath.getResourceHierarchy())); + String parentResName = langSpec.toVariableName(getComponentName(insideResPath.getResourceHierarchy(), langSpec)); String parentResPath = insideResPath.toString().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); generatePullDataTransfer(update, parentResName, parentResPath, parentResType, true, platformSpec, langSpec); } @@ -2123,7 +2125,7 @@ if (resourceNode.getInSideResources().contains(out.getResource())) { Expression message = out.getStateTransition().getMessageExpression(); MethodDeclaration input = null; - MethodDeclaration inputAccessor = null; + MethodDeclaration mainInputAccessor = null; MethodDeclaration rootInputAccessor = null; if (message instanceof Term) { // Declare an input method in this component. @@ -2240,14 +2242,14 @@ // Declare the accessor in the main component to call the input method. (for monolithic application) if (platformSpec.hasMain()) { String messageSymbol = ((Term) message).getSymbol().getImplName(); - inputAccessor = getMethod(mainComponent, messageSymbol); - if (inputAccessor == null) { - inputAccessor = langSpec.newMethodDeclaration(messageSymbol, false, null, mainInputParams); - mainComponent.addMethod(inputAccessor); + mainInputAccessor = getMethod(mainComponent, messageSymbol); + if (mainInputAccessor == null) { + mainInputAccessor = langSpec.newMethodDeclaration(messageSymbol, false, null, mainInputParams); + mainComponent.addMethod(mainInputAccessor); } else { // Add type to a parameter without type. - if (inputAccessor.getParameters() != null) { - for (VariableDeclaration param: inputAccessor.getParameters()) { + if (mainInputAccessor.getParameters() != null) { + for (VariableDeclaration param: mainInputAccessor.getParameters()) { if (param.getType() == null) { for (VariableDeclaration p: mainInputParams) { if (param.getName().equals(p.getName()) && p.getType() != null) { @@ -2266,8 +2268,12 @@ // If out is the receiver of the input event. priorMemberForInputChannel.put(ch, out); String messageSymbol = ((Term) message).getSymbol().getImplName(); - declareInputAccessorInTheRootResource(messageSymbol, rootInputParams, out, resourcePath, - rootComponent, platformSpec, langSpec); + rootInputAccessor = declareInputAccessorInTheRootResource(messageSymbol, rootInputParams, out, resourcePath, + rootComponent, platformSpec, langSpec); + if (input == null) { + input = rootInputAccessor; + rootInputAccessor = null; + } } } } else if (message instanceof Variable) { @@ -2344,14 +2350,14 @@ // Declare the accessor in the main component to call the input method. (for monolithic application) if (platformSpec.hasMain()) { String messageSymbol = ((Variable) message).getName(); - inputAccessor = getMethod(mainComponent, messageSymbol, mainInputParams); - if (inputAccessor == null) { + mainInputAccessor = getMethod(mainComponent, messageSymbol, mainInputParams); + if (mainInputAccessor == null) { if (mainInputParams.size() == 0) { - inputAccessor = langSpec.newMethodDeclaration(messageSymbol, null); + mainInputAccessor = langSpec.newMethodDeclaration(messageSymbol, null); } else { - inputAccessor = langSpec.newMethodDeclaration(messageSymbol, false, null, mainInputParams); + mainInputAccessor = langSpec.newMethodDeclaration(messageSymbol, false, null, mainInputParams); } - mainComponent.addMethod(inputAccessor); + mainComponent.addMethod(mainInputAccessor); } } @@ -2368,24 +2374,32 @@ resourcePath = ""; } String messageSymbol = ((Variable) message).getName(); - declareInputAccessorInTheRootResource(messageSymbol, rootInputParams, out, resourcePath, - rootComponent, platformSpec, langSpec); + rootInputAccessor = declareInputAccessorInTheRootResource(messageSymbol, rootInputParams, out, resourcePath, + rootComponent, platformSpec, langSpec); + if (input == null) { + input = rootInputAccessor; + rootInputAccessor = null; + } } } } // Add an invocation to the accessor method. - if (inputAccessor != null) { - for (ChannelMember rc: ((DataTransferChannel) 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 (!out.getResource().equals(ref)) { - String refVarName = ref.getLeafResourceName(); - Expression refGetter = getPullAccessor(platformSpec).getDirectStateAccessorFor(ref, null); - String[] sideEffects = new String[] {""}; - String refExp = refGetter.toImplementation(sideEffects); - String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); - inputAccessor.addFirstStatement(sideEffects[0] + refTypeName + " " + refVarName + " = " + refExp + ";"); + if (mainInputAccessor != null) { + if (platformSpec.hasMain()) { + // For an application with a main component, the reference resource is accessed from the main component. + for (ChannelMember rc: ((DataTransferChannel) ch).getReferenceChannelMembers()) { + // For each reference channel member, get the current state of the reference resource by pull data transfer. + ResourcePath ref = rc.getResource(); + if (!out.getResource().equals(ref)) { + String refVarName = ref.getLeafResourceName(); + Expression refGetter = getPullAccessor(platformSpec).getDirectStateAccessorFor(ref, null); + String[] sideEffects = new String[] {""}; + String refExp = refGetter.toImplementation(sideEffects); + String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); + mainInputAccessor.addFirstStatement(sideEffects[0] + langSpec.getVariableDeclaration(refTypeName, refVarName) + + langSpec.getAssignment() + refExp + langSpec.getStatementDelimiter()); + } } } Expression resExp = getPullAccessor(platformSpec).getDirectStateAccessorFor(out.getResource(), null); @@ -2427,12 +2441,42 @@ } } } - inputAccessor.addStatement(langSpec.getMethodInvocation(resourceAccess, input.getName(), args) + langSpec.getStatementDelimiter()); + mainInputAccessor.addStatement(langSpec.getMethodInvocation(resourceAccess, input.getName(), args) + langSpec.getStatementDelimiter()); } if (input != null) { // Add a statement to update the state field to the input method. try { + if (!platformSpec.hasMain()) { + // For an application with no main component, the reference resource is accessed from each resource. + for (ChannelMember rc: ((DataTransferChannel) 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 (!out.getResource().equals(ref)) { + String refResourceName = ref.getLeafResourceName(); + Type refResourceType = ref.getResourceStateType(); + String[] sideEffects = new String[] {""}; + if (!platformSpec.isMonolithic() && rc.isOutside()) { + List pathParams = new ArrayList<>(); + for (Expression pathExp: ref.getPathParams()) { + pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); + } + generatePullDataTransfer(input, refResourceName, ref.getResourceHierarchy().toResourcePath(pathParams), refResourceType, true, platformSpec, langSpec); + } else { + ResourcePath dstRes = out.getResource(); + if (!generatesComponent(dstRes.getResourceHierarchy())) { + dstRes = dstRes.getParent(); + } + Expression refGetter = getPullAccessor(platformSpec).getDirectStateAccessorFor(ref, dstRes); + String refExp = refGetter.toImplementation(sideEffects); + String refTypeName = refResourceType.getInterfaceTypeName(); + input.addFirstStatement(sideEffects[0] + langSpec.getVariableDeclaration(refTypeName, refResourceName) + + langSpec.getAssignment() + refExp + langSpec.getStatementDelimiter()); + } + } + } + } + Expression updateExp = ((DataTransferChannel) ch).deriveUpdateExpressionOf(out, getRefAccessor(platformSpec)).getKey(); // Replace Json constructor with a constructor of a descendant resource. ResourceHierarchy outRes = out.getResource().getResourceHierarchy(); @@ -2500,6 +2544,37 @@ } } } + if (rootInputAccessor != null) { + // In the root resource + // The expression of the receiver (resource) of the input method. + Expression resExp = getPullAccessor(platformSpec).getDirectStateAccessorFor(out.getResource(), out.getResource().getRoot()); + List args = new ArrayList<>(); + if (resExp instanceof Term) { + // to access the parent + if (((Term) resExp).getChildren().size() > 1 && ((Term) resExp).getChild(1) instanceof Variable) { + args.add(((Variable)((Term) resExp).getChild(1)).getName()); + } + 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.add(selVar.getName()); + } + } + // Values of message parameters. + if (message instanceof Term) { + for (Variable mesVar: message.getVariables().values()) { + args.add(mesVar.getName()); + } + } + rootInputAccessor.addStatement(langSpec.getMethodInvocation(resourceAccess, input.getName(), args) + langSpec.getStatementDelimiter()); + if (input != null && input.getThrows() != null && ((RestApiSpecific) platformSpec).hasJsonException(input)) { + ((RestApiSpecific) platformSpec).addJsonException(rootInputAccessor); + } + } } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage | UnificationFailed | ValueUndefined e) { e.printStackTrace(); @@ -2633,7 +2708,7 @@ } } else { // for REST API - parent = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(insideResPath.getResourceHierarchy())); + parent = langSpec.toVariableName(getComponentName(insideResPath.getResourceHierarchy(), langSpec)); } if (selType.equals(DataConstraintModel.typeInt)) { // make a for loop (for a list) for broadcasting. @@ -2649,7 +2724,7 @@ && platformSpec.isDifferentTreesAsDifferentServices()) { // for REST API Type parentResType = insideResPath.getResourceStateType(); - String parentResName = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(insideResPath.getResourceHierarchy())); + String parentResName = langSpec.toVariableName(getComponentName(insideResPath.getResourceHierarchy(), langSpec)); String parentResPath = insideResPath.toString().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); generatePullDataTransfer(input, parentResName, parentResPath, parentResType, true, platformSpec, langSpec); } @@ -3050,7 +3125,7 @@ rootComponent.addMethod(updateAccessor); } - protected void declareInputAccessorInTheRootResource(String inputMethodName, + protected MethodDeclaration declareInputAccessorInTheRootResource(String inputMethodName, ArrayList rootInputParams, ChannelMember cm, String resourcePath, TypeDeclaration rootComponent, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { MethodDeclaration rootInputAccessor; @@ -3070,6 +3145,7 @@ } } rootComponent.addMethod(rootInputAccessor); + return rootInputAccessor; } protected String getGetterResourcePathAndPathParams(ResourcePath resPath, List pathParams, diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java index d626b70..73bc20b 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java @@ -1247,6 +1247,7 @@ } 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 subTerms = ((Term) exp).getSubTerms(Term.class); Iterator> termEntItr = subTerms.entrySet().iterator(); @@ -1261,10 +1262,11 @@ String delimiter = ""; if (descendantConstructor != null) { 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(); + Expression param = jsonMember.reduce(); // Reduce {"name": "foo", age: 25}.name => "foo" if (param != null) { if (param instanceof Term) { if (((Term) param).getType() == null) { diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java index da1c8fe..f9b5fa0 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java @@ -1251,6 +1251,9 @@ 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 + ";"; } @@ -1262,6 +1265,9 @@ 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); @@ -1344,6 +1350,7 @@ } 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 subTerms = ((Term) exp).getSubTerms(Term.class); Iterator> termEntItr = subTerms.entrySet().iterator(); @@ -1358,10 +1365,11 @@ 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(); + Expression param = jsonMember.reduce(); // Reduce {"name": "foo", age: 25}.name => "foo" if (param != null) { if (param instanceof Term) { if (((Term) param).getType() == null) { diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JerseySpecific.java b/AlgebraicDataflowArchitectureModel/src/generators/JerseySpecific.java index e4ee223..e500208 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/JerseySpecific.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/JerseySpecific.java @@ -147,4 +147,10 @@ public void addJsonException(MethodDeclaration method) { method.addThrow("JsonProcessingException"); } + + @Override + public boolean hasJsonException(MethodDeclaration method) { + if (method.getThrows() == null || method.getThrows().getExceptions() == null) return false; + return method.getThrows().getExceptions().contains("JsonProcessingException"); + } } diff --git a/AlgebraicDataflowArchitectureModel/src/generators/RestApiSpecific.java b/AlgebraicDataflowArchitectureModel/src/generators/RestApiSpecific.java index 7a495e9..2fc154b 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/RestApiSpecific.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/RestApiSpecific.java @@ -60,6 +60,8 @@ abstract public String getHttpMethodCallWithResponseStatement(String baseURL, String resourceName, String httpMethod, String responseTypeName); abstract public void addJsonException(MethodDeclaration method); + + abstract public boolean hasJsonException(MethodDeclaration method); public String getBaseURL() { return "http://localhost:8080";