diff --git a/AlgebraicDataflowArchitectureModel/models/VotingSystem.model b/AlgebraicDataflowArchitectureModel/models/VotingSystem.model new file mode 100644 index 0000000..ff425db --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/VotingSystem.model @@ -0,0 +1,14 @@ +channel Signup { + out accounts(acDB:Map, signUp(aid:Str, name:Str)) = insert(acDB, aid, {"name": name, "vote": null}) +} + +channel Cast(aid:Str) { + out accounts.{aid}.vote(preV, cast(v:Str)) = v +} + +channel Collect { + for EachAccount(aid:Str) { + in accounts.{aid}.vote(preV:Str, collect(m)) = m.{aid} + } + out counts(preCnts:Json, collect(m)) = m +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java index 67195cd..bbb2c3f 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java @@ -9,6 +9,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.Stack; import algorithms.TypeInference; import code.ast.CodeUtil; @@ -506,7 +507,7 @@ String dstPath = null; if (filledEntry != null) { ResourcePath filledDstPath = filledEntry.getKey(); - dstPath = filledDstPath.toString().replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + dstPath = filledDstPath.toString().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); } else { dstPath = dstRes.getResourceHierarchy().toResourcePath(pathParams); } @@ -685,7 +686,7 @@ String dstPath = null; if (filledEntry != null) { ResourcePath filledDstPath = filledEntry.getKey(); - dstPath = filledDstPath.toString().replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + dstPath = filledDstPath.toString().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); } else { dstPath = dstRes.getResourceHierarchy().toResourcePath(pathParams); } @@ -808,12 +809,8 @@ getter = getGetterMethod(dstComponent, dstResourceName); } if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { - // generate a return statement. - Expression curExp = ch.deriveUpdateExpressionOf(out, JerseyCodeGenerator.pullAccessor).getKey(); // no pull data transfer is included. - Map>> resourcePaths = ch.fillOutsideResourcePaths(out, JerseyCodeGenerator.pullAccessor); + // The first time to fill the getter method's body. String[] sideEffects = new String[] {""}; - String curState = curExp.toImplementation(sideEffects); - getter.addStatement(sideEffects[0] + "return " + curState + ";"); // For each reference channel member, get the current state of the reference side resource by pull data transfer. for (ChannelMember rc: ch.getReferenceChannelMembers()) { ResourcePath refRes = rc.getResource(); @@ -836,36 +833,156 @@ getter.addFirstStatement(sideEffects[0] + refTypeName + " " + refResourceName + " = " + refExp + ";"); } } + Map.Entry>>, Term> resourcePathsAndMessage = ch.fillOutsideResourcePaths(out, JerseyCodeGenerator.pullAccessor, null); + Map>> resourcePaths = resourcePathsAndMessage.getKey(); + Term messageTerm = resourcePathsAndMessage.getValue(); for (Entry>> pathEnt: resourcePaths.entrySet()) { ChannelMember cm = pathEnt.getKey(); ResourcePath src2 = pathEnt.getValue().getKey(); // get outside src resource state by pull data transfer. if (cm.isOutside() || src2.getCommonPrefix(dst.getInSideResource(ch)) == null) { - Type srcResourceType = src2.getResourceStateType(); List pathParams = new ArrayList<>(); for (Expression pathExp: src2.getPathParams()) { pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); } + // generate a pull data transfer from a depending in/ref resource. + Type srcResourceType = src2.getResourceStateType(); String srcResName2 = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(src2.getResourceHierarchy())); String srcPath2 = src2.toString().replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); generatePullDataTransfer(getter, srcResName2, srcPath2, srcResourceType); - if (ancestorDstChannels.contains(chNode)) { - // For hierarchical channels (collecting pull transfer). - Expression selExp = ch.getSelectors().get(0).getExpression(); +// if (ancestorDstChannels.contains(chNode)) { +// // For hierarchical channels (collecting pull transfer). +// Expression selExp = ch.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 :ch.getInputChannelMembers()) { +// if (!cm2.isOutside()) { +// insideChMem = cm2; +// break; +// } +// } +// if (insideChMem == null) { +// for (ChannelMember cm2 :ch.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 = null; +// if (JerseyCodeGenerator.generatesComponent(insideResPath.getResourceHierarchy())) { +// Expression parentGetter = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(insideResPath, src.getPrimaryResourcePath()); +// Term valueGetter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); +// valueGetter.addChild(parentGetter); +// parent = valueGetter.toImplementation(new String[] {}); +// } else { +// parent = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(insideResPath, src.getPrimaryResourcePath()).toImplementation(new String[] {}); +// } +// if (insideResPath != null) { +// if (selType.equals(DataConstraintModel.typeInt)) { +// // make a for loop (for a list) for broadcasting. +// getter.addFirstStatement("for (int " + varName + " = 0; " + varName +" < " + parent + ".size(); " + varName + "++) {"); +// getter.addStatement("}"); +// } else if (selType.equals(DataConstraintModel.typeString)) { +// // make a for loop (for a map) for broadcasting. +// getter.addFirstStatement("for (String " + varName + ": " + parent + ".keySet()) {"); +// getter.addStatement("}"); +// } +// } +// } +// } + } + } + // Should take into account the channel hierarchy. + 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(); + // 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()); + 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 srcPath2 = src2.toString().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + generatePullDataTransfer(getter, srcResName2, srcPath2, srcResType2); + } 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 srcResourceType = src2.getResourceStateType(); + String srcResName2 = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(src2.getResourceHierarchy())); + String srcPath2 = src2.toString().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + generatePullDataTransfer(getter, srcResName2, srcPath2, srcResourceType); + } + } + } + // side effects within the loop + String curState = messageTerm.toImplementation(sideEffects); + getter.addStatement(sideEffects[0].replaceAll("\n", "")); + // 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 :ch.getInputChannelMembers()) { + for (ChannelMember cm2 :curChannel.getInputChannelMembers()) { if (!cm2.isOutside()) { insideChMem = cm2; break; } } if (insideChMem == null) { - for (ChannelMember cm2 :ch.getReferenceChannelMembers()) { + for (ChannelMember cm2 :curChannel.getReferenceChannelMembers()) { if (!cm2.isOutside()) { insideChMem = cm2; break; @@ -888,18 +1005,36 @@ } if (insideResPath != null) { if (selType.equals(DataConstraintModel.typeInt)) { - // make a for loop (for a list) for broadcasting. + // make a for loop (for a list) for data collecting. getter.addFirstStatement("for (int " + varName + " = 0; " + varName +" < " + parent + ".size(); " + varName + "++) {"); - getter.addStatement("}"); } else if (selType.equals(DataConstraintModel.typeString)) { - // make a for loop (for a map) for broadcasting. + // make a for loop (for a map) for data collecting. getter.addFirstStatement("for (String " + varName + ": " + parent + ".keySet()) {"); - getter.addStatement("}"); } } } + // 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. + Expression curExp = ch.deriveUpdateExpressionOf(out, messageTerm, JerseyCodeGenerator.pullAccessor); + sideEffects = new String[] {""}; + String curState = curExp.toImplementation(sideEffects); + if (ch.getChildren() == null || ch.getChildren().size() == 0) { + getter.addStatement(sideEffects[0] + "return " + curState + ";"); + } else { + getter.addStatement("return " + curState + ";"); } } // get src resource state by pull data transfer. diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferChannel.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferChannel.java index a8d1916..66b6d58 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferChannel.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferChannel.java @@ -278,12 +278,12 @@ */ public Map>> fillOutsideResourcePaths(ChannelMember targetMember, IResourceStateAccessor stateAccessor) throws ParameterizedIdentifierIsFutureWork, ResolvingMultipleDefinitionIsFutureWork, InvalidMessage, UnificationFailed, ValueUndefined { - return fillOutsideResourcePaths(targetMember, stateAccessor, null); + if (!getOutputChannelMembers().contains(targetMember)) return null; + return fillOutsideResourcePaths(targetMember, stateAccessor, null).getKey(); } - public Map>> fillOutsideResourcePaths(ChannelMember targetMember, IResourceStateAccessor stateAccessor, Map inputResourceToStateAccessor) + public Map.Entry>>, Term> fillOutsideResourcePaths(ChannelMember targetMember, IResourceStateAccessor stateAccessor, Map inputResourceToStateAccessor) throws ParameterizedIdentifierIsFutureWork, ResolvingMultipleDefinitionIsFutureWork, InvalidMessage, UnificationFailed, ValueUndefined { - if (!getOutputChannelMembers().contains(targetMember)) return null; Map>> resourcePaths = new HashMap<>(); // Calculate unified message constraints from input and reference state transitions @@ -350,7 +350,7 @@ } } } - return resourcePaths; + return new AbstractMap.SimpleEntry<>(resourcePaths, unifiedMessage); } /**