diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/VotingSystem/Account.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/VotingSystem/Account.java new file mode 100644 index 0000000..fce3635 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/VotingSystem/Account.java @@ -0,0 +1,19 @@ +import java.util.*; + +public class Account { + private String vote; + public Map getValue() { + Map temp_nil0 = new HashMap<>(); + temp_nil0.put("vote",this.getVote()); + return temp_nil0; + } + public String getVote() { + return this.vote; + } + public void cast(String aid, String v) { + this.vote = v; + } + public Account(String vote) { + this.vote = vote; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/VotingSystem/Accounts.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/VotingSystem/Accounts.java new file mode 100644 index 0000000..90e5808 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/VotingSystem/Accounts.java @@ -0,0 +1,42 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/accounts") +@Component +public class Accounts { + private Map value = new HashMap<>(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getValue() { + return new HashMap<>(value); + } + public Account getAccount(String aid) { + return this.value.get(aid); + } + @Path("/{aid}") + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getAccountValue(@PathParam("aid") String aid) { + return getAccount(aid).getValue(); + } + @Path("/{aid}/vote") + @Produces(MediaType.APPLICATION_JSON) + @GET + public String getVoteValue(@PathParam("aid") String aid) { + return getAccount(aid).getVote(); + } + @POST + public void signUp(@FormParam("name") String name, @FormParam("aid") String aid) { + this.value.put(aid,new Account(null)); + } + @Path("/{aid}/vote") + @PUT + public void cast(@PathParam("aid") String aid, @FormParam("v") String v) { + getAccount(aid).cast(aid, v); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/VotingSystem/Counts.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/VotingSystem/Counts.java new file mode 100644 index 0000000..a60f29e --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/VotingSystem/Counts.java @@ -0,0 +1,27 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/counts") +@Component +public class Counts { + private Map value = new HashMap<>(); + private Client client = ClientBuilder.newClient(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getValue() { + Map v0 = new HashMap<>(); + for (String aid: accounts.getValue().keySet()) { + String vote = client.target("http://localhost:8080").path("/accounts."+aid+".vote").request().get(String.class); + v0.put(aid,vote); + } + return v0; + } + public Counts(Map counts) { + this.counts = counts; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/Account.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/Account.java new file mode 100644 index 0000000..7dec970 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/Account.java @@ -0,0 +1,19 @@ +import java.util.*; + +public class Account { + private String vote; + public Map getValue() { + Map temp_nil1 = new HashMap<>(); + temp_nil1.put("vote",this.getVote()); + return temp_nil1; + } + public String getVote() { + return this.vote; + } + public void cast(String aid, String v) { + this.vote = v; + } + public Account(String vote) { + this.vote = vote; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/Accounts.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/Accounts.java new file mode 100644 index 0000000..d0b8bb3 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/Accounts.java @@ -0,0 +1,14 @@ +import java.util.*; + +public class Accounts { + private Map value = new HashMap<>(); + public Map getValue() { + return new HashMap<>(value); + } + public Account getAccount(String aid) { + return this.value.get(aid); + } + public void signUp(String name, String aid) { + this.value.put(aid,new Account(null)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/Counts.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/Counts.java new file mode 100644 index 0000000..a0d5b9f --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/Counts.java @@ -0,0 +1,18 @@ +import java.util.*; + +public class Counts { + private Map value = new HashMap<>(); + private Account account; + private Accounts accounts; + public Map getValue() { + Map v0 = new HashMap<>(); + for (String aid: this.accounts.getValue().keySet()) { + String vote = this.accounts.getAccount(aid).getVote(); + v0.put(aid,vote); + } + return v0; + } + public Counts(Accounts accounts) { + this.accounts = accounts; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/VotingSystem.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/VotingSystem.java new file mode 100644 index 0000000..50ae847 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/VotingSystem.java @@ -0,0 +1,28 @@ +import java.util.*; + +public class VotingSystem { + private Counts counts; + private Accounts accounts; + public VotingSystem() { + counts = new Counts(); + accounts = new Accounts(); + } + public Map getCounts() { + return this.counts.getValue(); + } + public Map getAccount(String aid) { + return this.accounts.getAccount(aid).getValue(); + } + public Map getAccounts() { + return this.accounts.getValue(); + } + public void signUp(String name, String aid) { + this.accounts.signUp(name, aid); + } + public String getVote(String aid) { + return this.accounts.getAccount(aid).getVote(); + } + public void cast(String aid, String v) { + this.accounts.getAccount(aid).cast(aid, v); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java b/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java index ddc0eb2..31fbea9 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java @@ -669,7 +669,8 @@ MethodDeclaration stateGetter = langSpec.newMethodDeclaration(getterOfResourceState, resStateType); component.addMethod(stateGetter); - if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { + boolean hasDescendantIn = hasDescendantInput(resourceNode); + if (((StoreAttribute) resourceNode.getAttribute()).isStored() && !hasDescendantIn) { fillStateGetterMethod(stateGetter, resourceNode.getResourceHierarchy(), resStateType, langSpec); } else { // invocations to other getter methods when at least one incoming data-flow edges is PULL-style. @@ -722,7 +723,8 @@ ancestorComponent.addMethod(stateGetter); } - if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { + boolean hasDescendantIn = hasDescendantInput(resourceNode); + if (((StoreAttribute) resourceNode.getAttribute()).isStored() && !hasDescendantIn) { fillDescendantGetterMethod(stateGetter, resourceNode.getResourceHierarchy(), childNode.getResourceHierarchy(), ancestorNode.getResourceHierarchy(), ancestorComponent, langSpec); } else { addOtherGetterInvocationsToStateGatter(stateGetter, resourceNode, langSpec); @@ -731,55 +733,209 @@ return stateGetter; } - private void addOtherGetterInvocationsToStateGatter(MethodDeclaration stateGetter, ResourceNode resourceNode, - ILanguageSpecific langSpec) { - boolean isContainedPush = false; - DataTransferChannel ch = null; - HashMap inputResourceToStateAccessor = new HashMap<>(); - for (Edge chToRes: resourceNode.getInEdges()) { - DataTransferChannel ch2 = ((ChannelNode) chToRes.getSource()).getChannel(); - for (Edge resToCh: chToRes.getSource().getInEdges()) { - DataFlowEdge dIn = (DataFlowEdge) resToCh; - ChannelMember in = null; - for (ChannelMember cm: ch2.getInputChannelMembers()) { - if (((ResourceNode) dIn.getSource()).getOutSideResources().contains(cm.getResource())) { - in = cm; - break; - } - } - if (((PushPullAttribute) dIn.getAttribute()).getSelectedOption() == PushPullValue.PUSH) { - // PUSH transfer - isContainedPush = true; - inputResourceToStateAccessor.put(in, getPushAccessor()); - } else { - // PULL transfer - inputResourceToStateAccessor.put(in, getPullAccessor()); - ch = ((ChannelNode) resToCh.getDestination()).getChannel(); // pull containing input side channel is always one. + private boolean hasDescendantInput(ResourceNode resourceNode) { + boolean hasDescendantIn = false; + outer: for (Edge chToRes: resourceNode.getInEdges()) { + ChannelNode chNode = (ChannelNode) chToRes.getSource(); + Set descendantChannels = chNode.getDescendants(); + for (ChannelNode descendantCh: descendantChannels) { + if (descendantCh.getIndegree() > 0) { + hasDescendantIn = true; + break outer; } } } - // for reference channel members. - for (ChannelMember c: ch.getReferenceChannelMembers()) { - inputResourceToStateAccessor.put(c, getPullAccessor()); // by pull data transfer - } - - // generate a return statement. + return hasDescendantIn; + } + + private void addOtherGetterInvocationsToStateGatter(MethodDeclaration stateGetter, ResourceNode resourceNode, + ILanguageSpecific langSpec) { try { - for (ChannelMember out: ch.getOutputChannelMembers()) { - if (resourceNode.getInSideResources().contains(out.getResource())) { - String[] sideEffects = new String[] {""}; - if (!isContainedPush) { - // All incoming edges are in PULL-style. - String curState = ch.deriveUpdateExpressionOf(out, getPullAccessor()).getKey().toImplementation(sideEffects); - stateGetter.addStatement(sideEffects[0] + langSpec.getReturnStatement(curState) + langSpec.getStatementDelimiter()); - } else { - // At least one incoming edge is in PUSH-style. - String curState = ch.deriveUpdateExpressionOf(out, getPullAccessor(), inputResourceToStateAccessor).getKey().toImplementation(sideEffects); - stateGetter.addStatement(sideEffects[0] + langSpec.getReturnStatement(curState) + langSpec.getStatementDelimiter()); + // Data transfer on the same channel hierarchy. + boolean isContainedPush = false; + DataTransferChannel ch = null; + DataTransferChannel ch2 = null; + HashMap inputResourceToStateAccessor = new HashMap<>(); + for (Edge chToRes: resourceNode.getInEdges()) { + ch2 = ((ChannelNode) chToRes.getSource()).getChannel(); + for (Edge resToCh: chToRes.getSource().getInEdges()) { + DataFlowEdge dIn = (DataFlowEdge) resToCh; + ChannelMember in = null; + for (ChannelMember cm: ch2.getInputChannelMembers()) { + if (((ResourceNode) dIn.getSource()).getOutSideResources().contains(cm.getResource())) { + in = cm; + break; + } } + if (((PushPullAttribute) dIn.getAttribute()).getSelectedOption() == PushPullValue.PUSH) { + // PUSH transfer + isContainedPush = true; + inputResourceToStateAccessor.put(in, getPushAccessor()); + } else { + // PULL transfer + inputResourceToStateAccessor.put(in, getPullAccessor()); + ch = ((ChannelNode) resToCh.getDestination()).getChannel(); // pull containing input side channel is at most one. + } + } + } + if (ch == null) { + ch = ch2; + } + ChannelMember out = null; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (resourceNode.getInSideResources().contains(cm.getResource())) { + out = cm; break; } } + + // for reference channel members. + for (ChannelMember c: ch.getReferenceChannelMembers()) { + inputResourceToStateAccessor.put(c, getPullAccessor()); // by pull data transfer + } + + // Construct the base message. + Map.Entry>>, Term> resourcePathsAndMessage; + if (!isContainedPush) { + // All incoming edges are in PULL-style. + resourcePathsAndMessage = ch.fillOutsideResourcePaths(out, JavaCodeGenerator.pullAccessor, null); + } else { + // At least one incoming edge is in PUSH-style. + resourcePathsAndMessage = ch.fillOutsideResourcePaths(out, JavaCodeGenerator.pullAccessor, inputResourceToStateAccessor); + } + Map>> resourcePaths = resourcePathsAndMessage.getKey(); + Term messageTerm = resourcePathsAndMessage.getValue(); + // Data transfer from the descendant channel hierarchies. + Stack> channelItrStack = new Stack<>(); + DataTransferChannel curChannel = ch; + if (curChannel.getChildren() != null && curChannel.getChildren().size() > 0) { + // retrieve descendant channels recursively. + Iterator chItr = curChannel.getChildren().iterator(); + do { + if (!chItr.hasNext()) { + chItr = channelItrStack.pop(); + } else { + curChannel = (DataTransferChannel) chItr.next(); + // generate pull data transfers. + Set chMems = new HashSet<>(curChannel.getInputChannelMembers()); + chMems.addAll(curChannel.getReferenceChannelMembers()); + for (ChannelMember cm2: chMems) { + if (resourcePaths == null || !resourcePaths.keySet().contains(cm2)) { + // not a depending channel member. + ResourcePath src2 = cm2.getResource(); + Type srcResType2 = src2.getResourceStateType(); + String srcResName2 = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(src2.getResourceHierarchy())); + String srcGetter = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(src2, resourceNode.getInSideResource(curChannel)).toImplementation(new String[] {}); + stateGetter.addStatement(srcResType2.getInterfaceTypeName() + " " + srcResName2 + " = " + srcGetter + ";"); + } else { + // a depending channel member. + ResourcePath src2 = resourcePaths.get(cm2).getKey(); + // get outside src2 resource state by pull data transfer. + if (cm2.isOutside() || src2.getCommonPrefix(resourceNode.getInSideResource(curChannel)) == null) { + // generate a pull data transfer from a depending in/ref resource. + Type srcResType2 = src2.getResourceStateType(); + String srcResName2 = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(src2.getResourceHierarchy())); + String dependingGetter = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(src2, resourceNode.getInSideResource(curChannel)).toImplementation(new String[] {}); + stateGetter.addStatement(srcResType2.getInterfaceTypeName() + " " + srcResName2 + " = " + dependingGetter + ";"); + } + } + } + // collect the message constraints by a descendant channel. + List varsForSideEffects = new ArrayList<>(); + int v = 0; + resourcePathsAndMessage = curChannel.fillOutsideResourcePaths(out, JavaCodeGenerator.pullAccessor, null); + if (resourcePathsAndMessage != null) { + resourcePaths = resourcePathsAndMessage.getKey(); + Term messageTermSub = resourcePathsAndMessage.getValue(); + for (Entry fieldEnt: ((Term) messageTermSub).getSubTerms(Field.class).entrySet()) { + Position pos = fieldEnt.getKey(); + Field field = fieldEnt.getValue(); + Variable var = new Variable(field.getSymbol().getName(), field.getType()); + ((Term) messageTermSub).replaceSubTerm(pos, var); + } + for (Map.Entry subTermEnt: messageTermSub.getSubTerms(Term.class).entrySet()) { + Term subTerm = subTermEnt.getValue(); + if (!(subTerm instanceof Constant) && subTerm.getSymbol().isImplWithSideEffect()) { + Variable var = new Variable("v" + v, subTerm.getType()); + varsForSideEffects.add(var); + v++; + // Add a side effect statement within the loop + Position pos = new Position(); + pos.addHeadOrder(0); + subTerm.replaceSubTerm(pos, var); + String[] sideEffects = new String[] {""}; + String curState = messageTermSub.toImplementation(sideEffects); + stateGetter.addStatement(sideEffects[0].replaceAll("\n", "")); + // Cancel the side effects in the return value. + pos = subTermEnt.getKey(); + messageTermSub.replaceSubTerm(pos, var); + } + } + if (messageTerm == null) { + messageTerm = messageTermSub; + } else { + messageTerm = (Term) messageTerm.unify(messageTermSub); + } + if (messageTerm == null) { + throw new UnificationFailed(); + } + } + // enclosed by a for loop + Expression selExp = curChannel.getSelectors().get(0).getExpression(); + Type selType = null; + String varName = null; + if (selExp instanceof Variable) { + selType = ((Variable) selExp).getType(); + varName = ((Variable) selExp).getName(); + ChannelMember insideChMem = null; + for (ChannelMember cm2 :curChannel.getInputChannelMembers()) { + if (!cm2.isOutside()) { + insideChMem = cm2; + break; + } + } + if (insideChMem == null) { + for (ChannelMember cm2 :curChannel.getReferenceChannelMembers()) { + if (!cm2.isOutside()) { + insideChMem = cm2; + break; + } + } + } + ResourcePath insideResPath = insideChMem.getResource(); + while (insideResPath.getParent() != null && (insideResPath.getLastParam() == null || !insideResPath.getLastParam().equals(selExp))) { + insideResPath = insideResPath.getParent(); + } + insideResPath = insideResPath.getParent(); + String parent = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(insideResPath, resourceNode.getInSideResource(curChannel)).toImplementation(new String[] {}); + if (insideResPath != null) { + if (selType.equals(DataConstraintModel.typeInt)) { + // make a for loop (for a list) for data collecting. + stateGetter.addFirstStatement("for (int " + varName + " = 0; " + varName +" < " + parent + ".size(); " + varName + "++) {"); + } else if (selType.equals(DataConstraintModel.typeString)) { + // make a for loop (for a map) for data collecting. + stateGetter.addFirstStatement("for (String " + varName + ": " + parent + ".keySet()) {"); + } + } + } + // initialize the variables to hold side effects within the loop + for (Variable var: varsForSideEffects) { + stateGetter.addFirstStatement(var.getType().getInterfaceTypeName() + " " + var.getName() + " = new " + var.getType().getImplementationTypeName() + "();"); + } + // end of the loop + stateGetter.addStatement("}"); + if (curChannel.getChildren() != null && curChannel.getChildren().size() > 0) { + channelItrStack.push(chItr); + chItr = curChannel.getChildren().iterator(); + } + } + } while (!channelItrStack.isEmpty()); + } + + // generate a return statement. + String[] sideEffects = new String[] {""}; + String curState = ch.deriveUpdateExpressionOf(out, messageTerm, getPullAccessor()).toImplementation(sideEffects); + stateGetter.addStatement(sideEffects[0] + langSpec.getReturnStatement(curState) + langSpec.getStatementDelimiter()); } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage | UnificationFailed | ValueUndefined e) { e.printStackTrace(); diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java index 5503479..58eaa0f 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java @@ -8,6 +8,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.Stack; import java.util.Map.Entry; import code.ast.CompilationUnit; @@ -634,6 +635,9 @@ getter = getGetterMethod(dstComponent, dstResourceName); } if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { + // The first time to fill the getter method's body. + // Data transfer on the same channel hierarchy. + String[] sideEffects = new String[] {""}; boolean isContainedPush = false; Map inputResourceToStateAccessor = new HashMap<>(); for (Edge chToRes2: dst.getInEdges()) { @@ -659,18 +663,148 @@ for (ChannelMember c: ch.getReferenceChannelMembers()) { inputResourceToStateAccessor.put(c, JavaCodeGenerator.pullAccessor); // by pull data transfer } - String[] sideEffects = new String[] {""}; - // generate a return statement. - // An input resource is outside. + Map.Entry>>, Term> resourcePathsAndMessage; + + // Construct the base message. if (!isContainedPush) { - // All incoming edges are in PULL style. - String curState = ch.deriveUpdateExpressionOf(out, JavaCodeGenerator.pullAccessor).getKey().toImplementation(sideEffects); - getter.addStatement(sideEffects[0] + "return " + curState + ";"); + // All incoming edges are in PULL-style. + resourcePathsAndMessage = ch.fillOutsideResourcePaths(out, JavaCodeGenerator.pullAccessor, null); } else { - // At least one incoming edge is in PUSH style. - String curState = ch.deriveUpdateExpressionOf(out, JavaCodeGenerator.pullAccessor, inputResourceToStateAccessor).getKey().toImplementation(sideEffects); - getter.addStatement(sideEffects[0] + "return " + curState + ";"); + // At least one incoming edge is in PUSH-style. + resourcePathsAndMessage = ch.fillOutsideResourcePaths(out, JavaCodeGenerator.pullAccessor, inputResourceToStateAccessor); } + Map>> resourcePaths = resourcePathsAndMessage.getKey(); + Term messageTerm = resourcePathsAndMessage.getValue(); + // Data transfer from the descendant channel hierarchies. + Stack> channelItrStack = new Stack<>(); + DataTransferChannel curChannel = ch; + if (curChannel.getChildren() != null && curChannel.getChildren().size() > 0) { + // retrieve descendant channels recursively. + Iterator chItr = curChannel.getChildren().iterator(); + do { + if (!chItr.hasNext()) { + chItr = channelItrStack.pop(); + } else { + curChannel = (DataTransferChannel) chItr.next(); + // generate pull data transfers. + Set chMems = new HashSet<>(curChannel.getInputChannelMembers()); + chMems.addAll(curChannel.getReferenceChannelMembers()); + for (ChannelMember cm2: chMems) { + if (resourcePaths == null || !resourcePaths.keySet().contains(cm2)) { + // not a depending channel member. + ResourcePath src2 = cm2.getResource(); + Type srcResType2 = src2.getResourceStateType(); + String srcResName2 = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(src2.getResourceHierarchy())); + String srcGetter = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(src2, dst.getInSideResource(curChannel)).toImplementation(new String[] {}); + getter.addStatement(srcResType2.getInterfaceTypeName() + " " + srcResName2 + " = " + srcGetter + ";"); + } else { + // a depending channel member. + ResourcePath src2 = resourcePaths.get(cm2).getKey(); + // get outside src2 resource state by pull data transfer. + if (cm2.isOutside() || src2.getCommonPrefix(dst.getInSideResource(curChannel)) == null) { + // generate a pull data transfer from a depending in/ref resource. + Type srcResType2 = src2.getResourceStateType(); + String srcResName2 = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(src2.getResourceHierarchy())); + String dependingGetter = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(src2, dst.getInSideResource(curChannel)).toImplementation(new String[] {}); + getter.addStatement(srcResType2.getInterfaceTypeName() + " " + srcResName2 + " = " + dependingGetter + ";"); + } + } + } + // collect the message constraints by a descendant channel. + List varsForSideEffects = new ArrayList<>(); + int v = 0; + resourcePathsAndMessage = curChannel.fillOutsideResourcePaths(out, JavaCodeGenerator.pullAccessor, null); + if (resourcePathsAndMessage != null) { + resourcePaths = resourcePathsAndMessage.getKey(); + Term messageTermSub = resourcePathsAndMessage.getValue(); + for (Entry fieldEnt: ((Term) messageTermSub).getSubTerms(Field.class).entrySet()) { + Position pos = fieldEnt.getKey(); + Field field = fieldEnt.getValue(); + Variable var = new Variable(field.getSymbol().getName(), field.getType()); + ((Term) messageTermSub).replaceSubTerm(pos, var); + } + for (Map.Entry subTermEnt: messageTermSub.getSubTerms(Term.class).entrySet()) { + Term subTerm = subTermEnt.getValue(); + if (!(subTerm instanceof Constant) && subTerm.getSymbol().isImplWithSideEffect()) { + Variable var = new Variable("v" + v, subTerm.getType()); + varsForSideEffects.add(var); + v++; + // Add a side effect statement within the loop + Position pos = new Position(); + pos.addHeadOrder(0); + subTerm.replaceSubTerm(pos, var); + sideEffects = new String[] {""}; + String curState = messageTermSub.toImplementation(sideEffects); + getter.addStatement(sideEffects[0].replaceAll("\n", "")); + // Cancel the side effects in the return value. + pos = subTermEnt.getKey(); + messageTermSub.replaceSubTerm(pos, var); + } + } + if (messageTerm == null) { + messageTerm = messageTermSub; + } else { + messageTerm = (Term) messageTerm.unify(messageTermSub); + } + if (messageTerm == null) { + throw new UnificationFailed(); + } + } + // enclosed by a for loop + Expression selExp = curChannel.getSelectors().get(0).getExpression(); + Type selType = null; + String varName = null; + if (selExp instanceof Variable) { + selType = ((Variable) selExp).getType(); + varName = ((Variable) selExp).getName(); + ChannelMember insideChMem = null; + for (ChannelMember cm2 :curChannel.getInputChannelMembers()) { + if (!cm2.isOutside()) { + insideChMem = cm2; + break; + } + } + if (insideChMem == null) { + for (ChannelMember cm2 :curChannel.getReferenceChannelMembers()) { + if (!cm2.isOutside()) { + insideChMem = cm2; + break; + } + } + } + ResourcePath insideResPath = insideChMem.getResource(); + while (insideResPath.getParent() != null && (insideResPath.getLastParam() == null || !insideResPath.getLastParam().equals(selExp))) { + insideResPath = insideResPath.getParent(); + } + insideResPath = insideResPath.getParent(); + String parent = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(insideResPath, src.getPrimaryResourcePath()).toImplementation(new String[] {}); + if (insideResPath != null) { + if (selType.equals(DataConstraintModel.typeInt)) { + // make a for loop (for a list) for data collecting. + getter.addFirstStatement("for (int " + varName + " = 0; " + varName +" < " + parent + ".size(); " + varName + "++) {"); + } else if (selType.equals(DataConstraintModel.typeString)) { + // make a for loop (for a map) for data collecting. + getter.addFirstStatement("for (String " + varName + ": " + parent + ".keySet()) {"); + } + } + } + // initialize the variables to hold side effects within the loop. + for (Variable var: varsForSideEffects) { + getter.addFirstStatement(var.getType().getInterfaceTypeName() + " " + var.getName() + " = new " + var.getType().getImplementationTypeName() + "();"); + } + // end of the loop + getter.addStatement("}"); + if (curChannel.getChildren() != null && curChannel.getChildren().size() > 0) { + channelItrStack.push(chItr); + chItr = curChannel.getChildren().iterator(); + } + } + } while (!channelItrStack.isEmpty()); + } + // generate a return statement. + sideEffects = new String[] {""}; + String curState = ch.deriveUpdateExpressionOf(out, messageTerm, JavaCodeGenerator.pullAccessor).toImplementation(sideEffects); + getter.addStatement(sideEffects[0] + "return " + curState + ";"); } if (outsideInputResource) { // Update fields to refer to outside resources. diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java index fbfd81e..11ba6a0 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java @@ -821,6 +821,7 @@ if (rc.isOutside()) { List pathParams = new ArrayList<>(); for (Expression pathExp: refRes.getPathParams()) { + sideEffects = new String[] {""}; pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); } generatePullDataTransfer(getter, refResourceName, refRes.getResourceHierarchy().toResourcePath(pathParams), refResourceType); @@ -830,6 +831,7 @@ dstRes = dstRes.getParent(); } Expression refGetter = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(refRes, dstRes); + sideEffects = new String[] {""}; String refExp = refGetter.toImplementation(sideEffects); String refTypeName = refResourceType.getInterfaceTypeName(); getter.addFirstStatement(sideEffects[0] + refTypeName + " " + refResourceName + " = " + refExp + ";"); @@ -845,6 +847,7 @@ if (cm.isOutside() || src2.getCommonPrefix(dst.getInSideResource(ch)) == null) { List pathParams = new ArrayList<>(); for (Expression pathExp: src2.getPathParams()) { + sideEffects = new String[] {""}; pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); } // generate a pull data transfer from a depending in/ref resource. @@ -865,33 +868,6 @@ chItr = channelItrStack.pop(); } else { curChannel = (DataTransferChannel) chItr.next(); - // collect the message constraints by a descendant channel. - List varsForSideEffects = new ArrayList<>(); - int v = 0; - resourcePathsAndMessage = curChannel.fillOutsideResourcePaths(out, JerseyCodeGenerator.pullAccessor, null); - if (resourcePathsAndMessage != null) { - resourcePaths = resourcePathsAndMessage.getKey(); - Term messageTermSub = resourcePathsAndMessage.getValue(); - for (Map.Entry subTermEnt: messageTermSub.getSubTerms(Term.class).entrySet()) { - Term subTerm = subTermEnt.getValue(); - if (!(subTerm instanceof Constant) && subTerm.getSymbol().isImplWithSideEffect()) { - Variable var = new Variable("v" + v, subTerm.getType()); - Position pos = new Position(); - pos.addHeadOrder(0); - subTerm.replaceSubTerm(pos, var); - varsForSideEffects.add(var); - v++; - } - } - if (messageTerm == null) { - messageTerm = messageTermSub; - } else { - messageTerm = (Term) messageTerm.unify(messageTermSub); - } - if (messageTerm == null) { - throw new UnificationFailed(); - } - } // generate pull data transfers. Set chMems = new HashSet<>(curChannel.getInputChannelMembers()); chMems.addAll(curChannel.getReferenceChannelMembers()); @@ -909,16 +885,47 @@ // get outside src2 resource state by pull data transfer. if (cm2.isOutside() || src2.getCommonPrefix(dst.getInSideResource(curChannel)) == null) { // generate a pull data transfer from a depending in/ref resource. - Type srcResourceType = src2.getResourceStateType(); + Type srcResType2 = src2.getResourceStateType(); String srcResName2 = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(src2.getResourceHierarchy())); String srcPath2 = src2.toString().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); - generatePullDataTransfer(getter, srcResName2, srcPath2, srcResourceType); + generatePullDataTransfer(getter, srcResName2, srcPath2, srcResType2); } } } - // side effects within the loop - String curState = messageTerm.toImplementation(sideEffects); - getter.addStatement(sideEffects[0].replaceAll("\n", "")); + // collect the message constraints by a descendant channel. + List varsForSideEffects = new ArrayList<>(); + int v = 0; + resourcePathsAndMessage = curChannel.fillOutsideResourcePaths(out, JerseyCodeGenerator.pullAccessor, null); + if (resourcePathsAndMessage != null) { + resourcePaths = resourcePathsAndMessage.getKey(); + Term messageTermSub = resourcePathsAndMessage.getValue(); + for (Map.Entry subTermEnt: messageTermSub.getSubTerms(Term.class).entrySet()) { + Term subTerm = subTermEnt.getValue(); + if (!(subTerm instanceof Constant) && subTerm.getSymbol().isImplWithSideEffect()) { + Variable var = new Variable("v" + v, subTerm.getType()); + varsForSideEffects.add(var); + v++; + // Add a side effect statement within the loop + Position pos = new Position(); + pos.addHeadOrder(0); + subTerm.replaceSubTerm(pos, var); + sideEffects = new String[] {""}; + String curState = messageTermSub.toImplementation(sideEffects); + getter.addStatement(sideEffects[0].replaceAll("\n", "")); + // Cancel the side effects in the return value. + pos = subTermEnt.getKey(); + messageTermSub.replaceSubTerm(pos, var); + } + } + if (messageTerm == null) { + messageTerm = messageTermSub; + } else { + messageTerm = (Term) messageTerm.unify(messageTermSub); + } + if (messageTerm == null) { + throw new UnificationFailed(); + } + } // enclosed by a for loop Expression selExp = curChannel.getSelectors().get(0).getExpression(); Type selType = null; @@ -971,7 +978,6 @@ } // end of the loop getter.addStatement("}"); - if (curChannel.getChildren() != null && curChannel.getChildren().size() > 0) { channelItrStack.push(chItr); chItr = curChannel.getChildren().iterator(); diff --git a/AlgebraicDataflowArchitectureModel/src/tests/JAXRSCodeGeneratorTest.java b/AlgebraicDataflowArchitectureModel/src/tests/JAXRSCodeGeneratorTest.java index e958d72..8f64cd8 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/JAXRSCodeGeneratorTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/JAXRSCodeGeneratorTest.java @@ -61,6 +61,7 @@ testOnlineBattleGame2(); testPOS(); testSimpleTwitter(); + testVotingSystem(); testWeatherObservationSystem(); } @@ -1125,6 +1126,85 @@ e.printStackTrace(); } } + + private void testVotingSystem() { + try { + ArrayList generatedCode = generateCode("models/VotingSystem.model", null); + Map, // class annotations + Entry, // field name to type + Map, // class annotations + Entry, // arg types + Integer>>>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Counts", Map.entry(Set.of("@Path(\"/counts\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "Map"), + Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of(), + 6)))), + Map.entry("Counts", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("Map"), + 1)))))))); + exprectedStructure.put("Account", Map.entry(Set.of(), + Map.entry(Map.ofEntries(Map.entry("vote", "String")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of(), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getVote", Map.entry(Set.of(), + Map.entry("String", + Map.entry(List.of(), + 1)))), + Map.entry("cast", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))), + Map.entry("Account", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String"), + 1)))))))); + exprectedStructure.put("Accounts", Map.entry(Set.of("@Path(\"/accounts\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "Map")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getAccount", Map.entry(Set.of(), + Map.entry("Account", + Map.entry(List.of("String"), + 1)))), + Map.entry("getAccountValue", Map.entry(Set.of("@Path(\"/{aid}\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of("String"), + 1)))), + Map.entry("getVoteValue", Map.entry(Set.of("@Path(\"/{aid}/vote\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("String", + Map.entry(List.of("String"), + 1)))), + Map.entry("signUp", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))), + Map.entry("cast", Map.entry(Set.of("@Path(\"/{aid}/vote\")","@PUT"), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } private void testWeatherObservationSystem() { try { diff --git a/AlgebraicDataflowArchitectureModel/src/tests/JavaCodeGeneratorTest.java b/AlgebraicDataflowArchitectureModel/src/tests/JavaCodeGeneratorTest.java index 7dd659f..d9e266f 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/JavaCodeGeneratorTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/JavaCodeGeneratorTest.java @@ -58,6 +58,7 @@ testOnlineBattleGame2(); // Two methods with the same signature are generated. testPOS(); testSimpleTwitter(); + testVotingSystem(); testWeatherObservationSystem(); } @@ -1574,6 +1575,83 @@ } } + private void testVotingSystem() { + try { + ArrayList generatedCode = generateCode("models/VotingSystem.model", null); + Map, // field name to type + Map, // arg types + Integer>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Main", Map.entry(Map.ofEntries(Map.entry("accounts", "Accounts"), + Map.entry("counts", "Counts")), + Map.ofEntries(Map.entry("Main", Map.entry("void", + Map.entry(List.of(), + 2))), + Map.entry("getAccounts", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("signUp", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("getVote", Map.entry("String", + Map.entry(List.of("String"), + 1))), + Map.entry("cast", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("getCounts", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getAccount", Map.entry("Map", + Map.entry(List.of("String"), + 1)))))); + exprectedStructure.put("Accounts", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getAccount", Map.entry("Account", + Map.entry(List.of("String"), + 1))), + Map.entry("signUp", Map.entry("void", + Map.entry(List.of("String","String"), + 1)))))); + exprectedStructure.put("Counts", Map.entry(Map.ofEntries(Map.entry("value", "Map"), + Map.entry("account", "Account"), + Map.entry("accounts", "Accounts")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 6))), + Map.entry("Counts", Map.entry("void", + Map.entry(List.of("Accounts"), + 1)))))); + exprectedStructure.put("Account", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getVote", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("cast", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("Account", Map.entry("void", + Map.entry(List.of("String"), + 1)))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + private void testWeatherObservationSystem() { try { // check PULL-first