diff --git a/AlgebraicDataflowArchitectureModel/models/Accounts.model b/AlgebraicDataflowArchitectureModel/models/Accounts.model new file mode 100644 index 0000000..81093de --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/Accounts.model @@ -0,0 +1,7 @@ +channel CIO1 { + out accounts(l:List, signup(name:Str)) == cons(name, l) +} + +channel CIO2(uid:Int) { + out accounts.{uid}(n:Str, changeName(name)) == name +} diff --git a/AlgebraicDataflowArchitectureModel/models/Clock.dtram b/AlgebraicDataflowArchitectureModel/models/Clock.dtram new file mode 100644 index 0000000..939c325 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/Clock.dtram @@ -0,0 +1,27 @@ +model { +channel CIO1 { + out min(m: Int, tick) == mod(m + 1, 60) +} +channel HourUpdate { + in hour(h: Int, update(h2)) == h2 + out hour_ang(h_ang: Double, update(h2)) == h2 / 6 * PI +} +channel MinUpdate { + in min(m, update(m2)) == m2 + out min_ang(m_ang: Double, update(m2)) == m2 / 30 * PI +} +channel Clock { + in min(m, update(m2)) == m2 + out hour(h, update(m2)) == if(eq(m2, 0), mod(h + 1, 24), h) +} +} +geometry { + node c HourUpdate:520,340,30,30 + node c MinUpdate:520,100,30,30 + node c Clock:280,220,30,30 + node r min_ang:670,100,80,30 + node r min:250,100,80,30 + node r hour:270,340,80,30 + node r hour_ang:680,340,80,30 + node ioc CIO1:100,100,30,30 +} diff --git a/AlgebraicDataflowArchitectureModel/models/Clock.model b/AlgebraicDataflowArchitectureModel/models/Clock.model new file mode 100644 index 0000000..0cabf0e --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/Clock.model @@ -0,0 +1,15 @@ +channel CIO1 { + out min(m: Int, tick) == mod(m + 1, 60) +} +channel HourUpdate { + in hour(h: Int, update(h2)) == h2 + out hour_ang(h_ang: Double, update(h2)) == h2 / 6 * PI +} +channel MinUpdate { + in min(m, update(m2)) == m2 + out min_ang(m_ang: Double, update(m2)) == m2 / 30 * PI +} +channel Clock { + in min(m, update(m2)) == m2 + out hour(h, update(m2)) == if(eq(m2, 0), mod(h + 1, 24), h) +} diff --git a/AlgebraicDataflowArchitectureModel/models/CustomerOffice.model b/AlgebraicDataflowArchitectureModel/models/CustomerOffice.model index f140bff..b3ddeb5 100644 --- a/AlgebraicDataflowArchitectureModel/models/CustomerOffice.model +++ b/AlgebraicDataflowArchitectureModel/models/CustomerOffice.model @@ -1,33 +1,21 @@ -channel C_CustomerAOff_In { - out customerA_off(c:String, setOff(x)) == x - out customerA_off(c, e) == c +channel CIO_AddCustomer { + out customers(db:Map, addCustomer(uid:Str, off:Str)) == insert(db, uid, {"off": off}) } -channel C_CustomerBOff_In { - out customerB_off(c:String, setOff(x)) == x - out customerB_off(c, e) == c +channel CIO_AddCampany { + out companies(db:Map, addCampany(cid:Str, add:Str)) == insert(db, cid, {"add": add}) } -channel C_CompanyC1Add_In { - out companyC1_add(a:String, setAdd(y)) == y - out companyC1_add(a, e) == a +channel CIO_SetCustomerOff(uid:Str) { + out customers.{uid}.off(cid:Str, setOff(cid2)) == cid2 } -channel C_CompanyC2Add_In { - out companyC2_add(a:String, setAdd(y)) == y - out companyC2_add(a, e) == a +channel CIO_SetCompanyAdd(cid:Str) { + out companies.{cid}.add(a1:Str, setAdd(a2)) == a2 } -channel CA { - in customerA_off(c, sync(z, u, v)) == z - in companyC1_add(a1, sync(z, u, v)) == u - in companyC2_add(a2, sync(z, u, v)) == v - out customerA_add(a3:String, sync(z, u, v)) == if(eq(z, C1), u, if(eq(z, C2), v, null)) -} - -channel CB { - in customerB_off(c, sync(z, u, v)) == z - in companyC1_add(a1, sync(z, u, v)) == u - in companyC2_add(a2, sync(z, u, v)) == v - out customerB_add(a3:String, sync(z, u, v)) == if(eq(z, C1), u, if(eq(z, C2), v, null)) +channel C(uid:Str) { + in customers.{uid}.off(cid, sync(cid2, add2)) == cid2 + in companies.{cid2}.add(a1, sync(cid2, add2)) == add2 + out customers.{uid}.add(a3:Str, sync(cid2, add2)) == add2 } diff --git a/AlgebraicDataflowArchitectureModel/models/Triangle.model b/AlgebraicDataflowArchitectureModel/models/Triangle.model new file mode 100644 index 0000000..ebfe6b4 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/Triangle.model @@ -0,0 +1,13 @@ +channel cio1 { + out base(x: Double, setBase(x2)) == x2 +} + +channel cio2 { + out height(y: Double, setHeight(y2)) == y2 +} + +channel triangle { + in base(x, update(x2, y2)) == x2 + in height(y, update(x2, y2)) == y2 + out hypothenuse(z: Double, update(x2, y2)) == sqrt(x2 * x2 + y2 * y2) +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/models/Triangle2.model b/AlgebraicDataflowArchitectureModel/models/Triangle2.model new file mode 100644 index 0000000..82c59bd --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/Triangle2.model @@ -0,0 +1,13 @@ +channel cio1 { + out base(x: Double, setBase(x2)) == x2 +} + +channel cio2 { + out height(y: Double, setHeight(y2)) == y2 +} + +channel triangle { + in base(x, update: Tuple) == fst(update) + in height(y, update) == snd(update) + out hypothenuse(z: Double, update) == sqrt(fst(update) * fst(update) + snd(update) * snd(update)) +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/models/Twitter.model b/AlgebraicDataflowArchitectureModel/models/Twitter.model index 57de3b3..649abac 100644 --- a/AlgebraicDataflowArchitectureModel/models/Twitter.model +++ b/AlgebraicDataflowArchitectureModel/models/Twitter.model @@ -1,47 +1,16 @@ -channel a_Tweet { - out a_tweets(l:List, a_tweet(t:Str, time:Long)) == cons(tuple(time, t), l) +channel CIO1(myId:Str) { + out accounts(ac:List, signup(name:Str)) == cons(tuple(name, nil, nil), t1) } -channel a_Follow { - out a_following(f:List, a_follow(u:Int)) == cons(u, f) +channel CIO2(id:Str) { + out accounts.{id}.tweets(t1:List, tweet(text:Str, time:Long)) == cons(tuple(time, text), t1) } -channel a_Home { - in a_tweets(la1, update_a(f2, la2, lb2, lc2)) == la2 - in b_tweets(lb1, update_a(f2, la2, lb2, lc2)) == lb2 - in c_tweets(lc1, update_a(f2, la2, lb2, lc2)) == lc2 - in a_following(f, update_a(f2, la2, lb2, lc2)) == f2 - out a_timeline(t:List, update_a(f2, la2, lb2, lc2)) == merge(la2, if(contains(f2,2), merge(lb2, if(contains(f2,3), lc2, nil)), if(contains(f2,3), lc2, nil))) -} - -channel b_Tweet { - out b_tweets(l:List, b_tweet(t:Str, time:Long)) == cons(tuple(time, t), l) -} - -channel b_Follow { - out b_following(f:List, b_follow(u:Int)) == cons(u, f) -} - -channel b_Home { - in a_tweets(la1, update_b(f2, la2, lb2, lc2)) == la2 - in b_tweets(lb1, update_b(f2, la2, lb2, lc2)) == lb2 - in c_tweets(lc1, update_b(f2, la2, lb2, lc2)) == lc2 - in b_following(f, update_b(f2, la2, lb2, lc2)) == f2 - out b_timeline(t:List, update_b(f2, la2, lb2, lc2)) == merge(lb2, if(contains(f2,1), merge(la2, if(contains(f2,3), lc2, nil)), if(contains(f2,3), lc2, nil))) -} - -channel c_Tweet { - out c_tweets(l:List, c_tweet(t:Str, time:Long)) == cons(tuple(time, t), l) -} - -channel c_Follow { - out c_following(f:List, c_follow(u:Int)) == cons(u, f) -} - -channel c_Home { - in a_tweets(la1, update_c(f2, la2, lb2, lc2)) == la2 - in b_tweets(lb1, update_c(f2, la2, lb2, lc2)) == lb2 - in c_tweets(lc1, update_c(f2, la2, lb2, lc2)) == lc2 - in c_following(f, update_c(f2, la2, lb2, lc2)) == f2 - out c_timeline(t:List, update_c(f2, la2, lb2, lc2)) == merge(lc2, if(contains(f2,1), merge(la2, if(contains(f2,2), lb2, nil)), if(contains(f2,2), lb2, nil))) -} +channel C(myId:Str; m:Json) { + in accounts.{myId}.tweets(t1:List, m) == m.myTweets + sub C2(no:Int; flw:Json := m.followees.{no}) { + in accounts.{myId}.followees.{no}(id:Str, flw) == flw.id + in accounts.{flw.id}.tweets(t2:List, flw) == flw.tweets + } + out accounts.{myId}.timeline(l:List, m) == merge(m.myTweets, m.followees) +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/src/algorithms/DataTransferModelAnalyzer.java b/AlgebraicDataflowArchitectureModel/src/algorithms/DataTransferModelAnalyzer.java index 836227a..69fe530 100644 --- a/AlgebraicDataflowArchitectureModel/src/algorithms/DataTransferModelAnalyzer.java +++ b/AlgebraicDataflowArchitectureModel/src/algorithms/DataTransferModelAnalyzer.java @@ -28,37 +28,43 @@ channels.addAll(model.getChannels()); for (Channel channel: channels) { for (ChannelMember member: ((DataTransferChannel) channel).getOutputChannelMembers()) { - boolean flag = !member.getStateTransition().isRightUnary(); // The state does not need to be stored if the state transition function is right unary. - for (Node node : graph.getNodes()) { - if (((ResourceNode) node).getResource().equals(member.getResource())) { - setStoreAttribute(flag, (ResourceNode) node); + boolean toBeStored = !member.getStateTransition().isRightUnary(); // The state does not need to be stored if the state transition function is right unary. + for (Node node : graph.getResourceNodes()) { + if (((ResourceNode) node).getInSideResources().contains(member.getResource())) { + setStoreAttribute((ResourceNode) node, toBeStored); } } } } - for (Node node : graph.getNodes()) { + for (Node node: graph.getResourceNodes()) { HashSet inChannels = new HashSet<>(); - for(Edge pre : ((ResourceNode) node).getInEdges()) { - inChannels.add(((DataFlowEdge) pre).getChannel()); + for(Edge inEdge: ((ResourceNode) node).getInEdges()) { + if (inEdge instanceof DataFlowEdge) { + DataFlowEdge dfEdge = (DataFlowEdge) inEdge; + if (dfEdge.isChannelToResource()) { + inChannels.add(((ChannelNode) dfEdge.getSource()).getChannel()); + } + } } if ((inChannels.size() > 1)) { - setStoreAttribute(true, (ResourceNode) node); + // If the resource has multiple input channels, then the state of the resource needs to be stored. + setStoreAttribute((ResourceNode) node, true); } else if (((ResourceNode) node).getAttribute() == null) { - setStoreAttribute(false, (ResourceNode) node); + setStoreAttribute((ResourceNode) node, false); } } return graph; } - static private void setStoreAttribute(boolean flag, ResourceNode node) { + static private void setStoreAttribute(ResourceNode node, boolean toBeStored) { NodeAttribute attr = node.getAttribute(); StoreAttribute store; if (attr != null && attr instanceof NodeAttribute) { store = (StoreAttribute) attr; - store.setNeeded(store.isNeeded() || flag); + store.setNeeded(store.isNeeded() || toBeStored); } else { store = new StoreAttribute(); - store.setNeeded(flag); + store.setNeeded(toBeStored); node.setAttribute(store); } } @@ -69,17 +75,17 @@ * @return annotated data flow graph */ static public DataFlowGraph annotateWithSelectableDataTransferAttiribute(DataFlowGraph graph) { - List nodes = new ArrayList<>(graph.getNodes()); + List resNodes = new ArrayList<>(graph.getResourceNodes()); // set push only attributes - for (Node n: graph.getNodes()) { - if (nodes.contains(n) && ((StoreAttribute) ((ResourceNode) n).getAttribute()).isNeeded()) { - nodes.remove(n); - trackEdges(n, nodes); + for (Node resNode: graph.getResourceNodes()) { + if (resNodes.contains(resNode) && ((StoreAttribute) ((ResourceNode) resNode).getAttribute()).isNeeded()) { + resNodes.remove(resNode); + trackEdges(resNode, resNodes); } } // set push/pull attributes to the remaining edges for (Edge e : graph.getEdges()) { - if (((DataFlowEdge) e).getAttribute() == null) { + if (!((DataFlowEdge) e).isChannelToResource() && ((DataFlowEdge) e).getAttribute() == null) { PushPullAttribute ppat = new PushPullAttribute(); ppat.addOption(PushPullValue.PUSHorPULL); ppat.addOption(PushPullValue.PUSH); @@ -90,16 +96,19 @@ return graph; } - static private void trackEdges(Node n, List nodes) { + static private void trackEdges(Node resNode, List resNodes) { // recursively set push only attributes to input side edges - for (Edge e : ((ResourceNode) n).getInEdges()) { - PushPullAttribute ppat = new PushPullAttribute(); - ppat.addOption(PushPullValue.PUSH); - ((DataFlowEdge) e).setAttribute(ppat); - Node n2 = e.getSource(); - if (nodes.contains(n2)) { - nodes.remove(n2); - trackEdges(n2, nodes); + for (Edge chToRes : ((ResourceNode) resNode).getInEdges()) { + Node chNode = chToRes.getSource(); + for (Edge resToCh : ((ChannelNode) chNode).getInEdges()) { + PushPullAttribute ppat = new PushPullAttribute(); + ppat.addOption(PushPullValue.PUSH); + ((DataFlowEdge) resToCh).setAttribute(ppat); + Node resNode2 = resToCh.getSource(); + if (resNodes.contains(resNode2)) { + resNodes.remove(resNode2); + trackEdges(resNode2, resNodes); + } } } } diff --git a/AlgebraicDataflowArchitectureModel/src/algorithms/TypeInference.java b/AlgebraicDataflowArchitectureModel/src/algorithms/TypeInference.java index d29070d..21220f2 100644 --- a/AlgebraicDataflowArchitectureModel/src/algorithms/TypeInference.java +++ b/AlgebraicDataflowArchitectureModel/src/algorithms/TypeInference.java @@ -13,6 +13,7 @@ import javax.xml.crypto.Data; import models.Node; +import models.algebra.Constant; import models.algebra.Expression; import models.algebra.Position; import models.algebra.Symbol; @@ -22,7 +23,10 @@ import models.dataConstraintModel.Channel; import models.dataConstraintModel.ChannelMember; import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.JsonType; +import models.dataConstraintModel.ResourceHierarchy; import models.dataConstraintModel.ResourcePath; +import models.dataConstraintModel.Selector; import models.dataConstraintModel.StateTransition; import models.dataFlowModel.DataTransferModel; import models.dataFlowModel.ResourceNode; @@ -42,6 +46,8 @@ static private Map pairComponentTypes = new HashMap<>(); static private Map, Type> mapTypes = new HashMap<>(); static private Map> mapComponentTypes = new HashMap<>(); + static private Map, Type> jsonTypes = new HashMap<>(); + static private Map> jsonMemberTypes = new HashMap<>(); public static Type getListType(Type compType) { return listTypes.get(compType); @@ -83,29 +89,46 @@ return mapComponentTypes.get(mapType); } + public static Type getJsonType(Map memberTypes) { + return jsonTypes.get(memberTypes); + } + + public static Map getJsonMemberTypes(Type jsonType) { + return jsonMemberTypes.get(jsonType); + } + static public void infer(DataTransferModel model) { - Map> resources = new HashMap<>(); + Map> resources = new HashMap<>(); + Map> resourcePathParams = new HashMap<>(); Map variables = new HashMap<>(); Map, Type>>> messages = new HashMap<>(); Map consOrSet = new HashMap<>(); Map tuple = new HashMap<>(); Map pair = new HashMap<>(); Map map = new HashMap<>(); + Map json = new HashMap<>(); + + // Maps from the objectId of each expression to its belonging group that has the same type as the expression Map> expToResource = new HashMap<>(); + Map> expToPathParams = new HashMap<>(); Map> expToVariable = new HashMap<>(); Map> expToMessage = new HashMap<>(); Map>> expToConsOrSet = new HashMap<>(); Map>> expToTuple = new HashMap<>(); Map>> expToPair = new HashMap<>(); Map>> expToMap = new HashMap<>(); + Map>> expToJson = new HashMap<>(); + // Maps from the objectId of each group to the set of updated expressions. Map> updateFromResource = new HashMap<>(); + Set updateFromResourceOwnership = new HashSet<>(); Map> updateFromVariable = new HashMap<>(); Map> updateFromMessage = new HashMap<>(); Map> updateFromConsOrSet = new HashMap<>(); Map> updateFromTuple = new HashMap<>(); Map> updateFromPair = new HashMap<>(); Map> updateFromMap = new HashMap<>(); + Map> updateFromJson = new HashMap<>(); listComponentTypes.put(DataConstraintModel.typeList, null); listComponentTypes.put(DataConstraintModel.typeListInt, DataConstraintModel.typeInt); @@ -128,25 +151,27 @@ for (Channel c : channels) { for (ChannelMember cm : c.getChannelMembers()) { StateTransition st = cm.getStateTransition(); - ResourcePath id = cm.getResource(); + ResourceHierarchy res = cm.getResource().getResourceHierarchy(); // 1.1 Group expressions by resources. - List sameResource = resources.get(id); - if (sameResource == null) { - sameResource = new ArrayList<>(); - resources.put(id, sameResource); + List identicalResources = resources.get(res); + if (identicalResources == null) { + identicalResources = new ArrayList<>(); + resources.put(res, identicalResources); } - sameResource.add(st.getCurStateExpression()); - if (st.getNextStateExpression() != null) sameResource.add(st.getNextStateExpression()); - expToResource.put(System.identityHashCode(st.getCurStateExpression()), sameResource); - if (st.getNextStateExpression() != null) expToResource.put(System.identityHashCode(st.getNextStateExpression()), sameResource); - Map updatedExps = getUpdateSet(updateFromResource, sameResource); - Type resType = id.getResourceStateType(); + identicalResources.add(st.getCurStateExpression()); + expToResource.put(System.identityHashCode(st.getCurStateExpression()), identicalResources); + if (st.getNextStateExpression() != null) { + identicalResources.add(st.getNextStateExpression()); + expToResource.put(System.identityHashCode(st.getNextStateExpression()), identicalResources); + } + Map updatedExps = getUpdateSet(updateFromResource, identicalResources); + Type resType = res.getResourceStateType(); Expression exp = st.getCurStateExpression(); Type expType = getExpTypeIfUpdatable(resType, exp); if (expType != null) { - id.setResourceStateType(expType); - for (Expression resExp : sameResource) { + res.setResourceStateType(expType); + for (Expression resExp : identicalResources) { if (resExp != exp) { if (resExp instanceof Variable && compareTypes(((Variable) resExp).getType(), expType)) { ((Variable) resExp).setType(expType); @@ -168,13 +193,13 @@ updatedExps.put(System.identityHashCode(exp), exp); } } - resType = id.getResourceStateType(); + resType = res.getResourceStateType(); exp = st.getNextStateExpression(); if (exp != null) { expType = getExpTypeIfUpdatable(resType, exp); if (expType != null) { - id.setResourceStateType(expType); - for (Expression resExp : sameResource) { + res.setResourceStateType(expType); + for (Expression resExp : identicalResources) { if (resExp != exp) { if (resExp instanceof Variable && compareTypes(((Variable) resExp).getType(), expType)) { ((Variable) resExp).setType(expType); @@ -204,8 +229,22 @@ List allVariables = new ArrayList<>(); allVariables.addAll(st.getCurStateExpression().getVariables().values()); allVariables.addAll(st.getMessageExpression().getVariables().values()); - if (st.getNextStateExpression() != null) + if (st.getNextStateExpression() != null) { allVariables.addAll(st.getNextStateExpression().getVariables().values()); + } + for (Selector s: c.getSelectors()) { // add channel selectors + if (s.getExpression() instanceof Variable) { + allVariables.add((Variable) s.getExpression()); + } + } + ResourcePath resPath = cm.getResource(); + for (Expression param: resPath.getPathParams()) { // add path parameters + if (param instanceof Variable) { + allVariables.add((Variable) param); + } else if (param instanceof Term) { + allVariables.addAll(((Term) param).getVariables().values()); + } + } for (Variable var : allVariables) { List sameVariable = locals.get(var.getName()); if (sameVariable == null) { @@ -343,7 +382,7 @@ if (symbol.equals(DataConstraintModel.cons) || symbol.equals(DataConstraintModel.set)) { // If the root symbol of the term is cons or set. List consExps = new ArrayList<>(); - consExps.add(t); + consExps.add(t); // list term updateExpressionBelonging(expToConsOrSet, t, consExps); if (symbol.equals(DataConstraintModel.cons)) { // If the root symbol of the term is cons. @@ -354,10 +393,10 @@ } else { // If the root symbol of the term is set. Expression e = t.getChildren().get(2); - consExps.add(e); + consExps.add(e); // list component updateExpressionBelonging(expToConsOrSet, e, consExps); e = t.getChildren().get(0); - consExps.add(e); + consExps.add(e); // list argument updateExpressionBelonging(expToConsOrSet, e, consExps); } Type newType = getExpTypeIfUpdatable(t.getType(), consExps.get(2)); @@ -410,9 +449,9 @@ // If the root symbol of the term is head or get. List consExps = new ArrayList<>(); Expression e = t.getChildren().get(0); - consExps.add(e); + consExps.add(e); // list argument updateExpressionBelonging(expToConsOrSet, e, consExps); - consExps.add(t); + consExps.add(t); // list's component updateExpressionBelonging(expToConsOrSet, t, consExps); consExps.add(null); Type listType = listTypes.get(t.getType()); @@ -446,11 +485,11 @@ } else if (symbol.equals(DataConstraintModel.tail)) { // If the root symbol of the term is tail. List consExps = new ArrayList<>(); - consExps.add(t); + consExps.add(t); // list term updateExpressionBelonging(expToConsOrSet, t, consExps); - consExps.add(null); + consExps.add(null); // list's component Expression e = t.getChildren().get(0); - consExps.add(e); + consExps.add(e); // list argument updateExpressionBelonging(expToConsOrSet, e, consExps); Type newType = getExpTypeIfUpdatable(t.getType(), consExps.get(2)); if (newType != null) { @@ -482,26 +521,27 @@ } else if (symbol.equals(DataConstraintModel.tuple)) { // If the root symbol of the term is tuple. List tupleExps = new ArrayList<>(); - List argsTypeList = new ArrayList<>(); - tupleExps.add(t); + List newArgTypesList = new ArrayList<>(); + tupleExps.add(t); // tuple term updateExpressionBelonging(expToTuple, t, tupleExps); for (Expression e : t.getChildren()) { - tupleExps.add(e); + tupleExps.add(e); // tuple's component updateExpressionBelonging(expToTuple, e, tupleExps); if (e instanceof Variable) { - argsTypeList.add(((Variable) e).getType()); + newArgTypesList.add(((Variable) e).getType()); } else if (e instanceof Term) { - argsTypeList.add(((Term) e).getType()); + newArgTypesList.add(((Term) e).getType()); } else { - argsTypeList.add(null); + newArgTypesList.add(null); } } if (t.getType() == DataConstraintModel.typeTuple) { - Type newTupleType = tupleTypes.get(argsTypeList); + Type newTupleType = tupleTypes.get(newArgTypesList); if (newTupleType == null) { // Create new tuple type; - newTupleType = createNewTupleType(argsTypeList, DataConstraintModel.typeTuple); + newTupleType = createNewTupleType(newArgTypesList, DataConstraintModel.typeTuple); } + // Update the type of the tuple term and record the updated expression. t.setType(newTupleType); Map updateExps = getUpdateSet(updateFromTuple, tupleExps); updateExps.put(System.identityHashCode(t), t); @@ -510,22 +550,22 @@ } else if (symbol.equals(DataConstraintModel.pair)) { // If the root symbol of the term is pair. List pairExps = new ArrayList<>(); - pairExps.add(t); + pairExps.add(t); // pair updateExpressionBelonging(expToPair, t, pairExps); if (t.getType() == DataConstraintModel.typePair) { for (Expression e : t.getChildren()) { - pairExps.add(e); + pairExps.add(e); // left/right updateExpressionBelonging(expToPair, e, pairExps); - Type argType = null; + Type newArgType = null; if (e instanceof Variable) { - argType = (((Variable) e).getType()); + newArgType = (((Variable) e).getType()); } else if (e instanceof Term) { - argType = (((Term) e).getType()); + newArgType = (((Term) e).getType()); } - if (argType != null) { - Type newPairType = pairTypes.get(argType); + if (newArgType != null) { + Type newPairType = pairTypes.get(newArgType); if (newPairType != null) { t.setType(newPairType); Map updateExps = getUpdateSet(updateFromPair, pairExps); @@ -540,11 +580,11 @@ // If the root symbol of the term is fst. List tupleExps = new ArrayList<>(); Expression arg = t.getChildren().get(0); - tupleExps.add(arg); + tupleExps.add(arg); // tuple argument updateExpressionBelonging(expToTuple, arg, tupleExps); - tupleExps.add(t); + tupleExps.add(t); // first component updateExpressionBelonging(expToTuple, t, tupleExps); - tupleExps.add(null); + tupleExps.add(null); // second component Type argType = null; if (arg instanceof Variable) { argType = ((Variable) arg).getType(); @@ -553,16 +593,17 @@ } Type newTupleType = DataConstraintModel.typeTuple; if (argType == DataConstraintModel.typeTuple && t.getType() != null) { - List compTypeList = new ArrayList<>(); - compTypeList.add(t.getType()); - compTypeList.add(null); - newTupleType = tupleTypes.get(compTypeList); + List newCompTypeList = new ArrayList<>(); + newCompTypeList.add(t.getType()); + newCompTypeList.add(null); + newTupleType = tupleTypes.get(newCompTypeList); if (newTupleType == null) { // Create new tuple type; - newTupleType = createNewTupleType(compTypeList, DataConstraintModel.typeTuple); + newTupleType = createNewTupleType(newCompTypeList, DataConstraintModel.typeTuple); } } if (argType != newTupleType && newTupleType != null) { + // Update the type of the tuple argument and record the updated expression. if (arg instanceof Variable) { ((Variable) arg).setType(newTupleType); argType = newTupleType; @@ -578,10 +619,10 @@ // If the root symbol of the term is snd. List tupleExps = new ArrayList<>(); Expression arg = t.getChildren().get(0); - tupleExps.add(arg); + tupleExps.add(arg); // tuple argument updateExpressionBelonging(expToTuple, arg, tupleExps); - tupleExps.add(null); - tupleExps.add(t); + tupleExps.add(null); // first component + tupleExps.add(t); // second component updateExpressionBelonging(expToTuple, t, tupleExps); Type argType = null; if (arg instanceof Variable) { @@ -591,27 +632,28 @@ } Type newTupleType = DataConstraintModel.typeTuple; if (argType == DataConstraintModel.typeTuple && t.getType() != null) { - List compTypeList = new ArrayList<>(); - compTypeList.add(null); + List newCompTypeList = new ArrayList<>(); + newCompTypeList.add(null); if (DataConstraintModel.typeTuple.isAncestorOf(t.getType())) { List sndTypes = tupleComponentTypes.get(t.getType()); if (sndTypes != null) { for (Type t2: sndTypes) { - compTypeList.add(t2); + newCompTypeList.add(t2); } } else { - compTypeList.add(t.getType()); + newCompTypeList.add(t.getType()); } } else { - compTypeList.add(t.getType()); + newCompTypeList.add(t.getType()); } - newTupleType = tupleTypes.get(compTypeList); + newTupleType = tupleTypes.get(newCompTypeList); if (newTupleType == null) { // Create new tuple type; - newTupleType = createNewTupleType(compTypeList, DataConstraintModel.typeTuple); + newTupleType = createNewTupleType(newCompTypeList, DataConstraintModel.typeTuple); } } if (argType != newTupleType && newTupleType != null) { + // Update the type of the tuple argument and record the updated expression. if (arg instanceof Variable) { ((Variable) arg).setType(newTupleType); argType = newTupleType; @@ -627,11 +669,11 @@ // If the root symbol of the term is left. List pairExps = new ArrayList<>(); Expression arg = t.getChildren().get(0); - pairExps.add(arg); + pairExps.add(arg); // pair updateExpressionBelonging(expToPair, arg, pairExps); - pairExps.add(t); + pairExps.add(t); // left updateExpressionBelonging(expToPair, t, pairExps); - pairExps.add(null); + pairExps.add(null); // right Type argType = null; if (arg instanceof Variable) { argType = ((Variable) arg).getType(); @@ -640,13 +682,13 @@ } Type newPairType = DataConstraintModel.typePair; if (argType == DataConstraintModel.typePair && t.getType() != null) { - List compTypeList = new ArrayList<>(); - compTypeList.add(t.getType()); - compTypeList.add(null); - newPairType = pairTypes.get(compTypeList); + List newCompTypeList = new ArrayList<>(); + newCompTypeList.add(t.getType()); + newCompTypeList.add(null); + newPairType = pairTypes.get(newCompTypeList); if (newPairType == null) { // Create new tuple type; - newPairType = createNewTupleType(compTypeList, DataConstraintModel.typePair); + newPairType = createNewTupleType(newCompTypeList, DataConstraintModel.typePair); } } if (argType != newPairType && newPairType != null) { @@ -665,10 +707,10 @@ // If the root symbol of the term is right. List pairExps = new ArrayList<>(); Expression arg = t.getChildren().get(0); - pairExps.add(arg); + pairExps.add(arg); // pair updateExpressionBelonging(expToPair, arg, pairExps); - pairExps.add(null); - pairExps.add(t); + pairExps.add(null); // left + pairExps.add(t); // right updateExpressionBelonging(expToPair, t, pairExps); Type argType = null; if (arg instanceof Variable) { @@ -678,13 +720,13 @@ } Type newPairType = DataConstraintModel.typePair; if (argType == DataConstraintModel.typePair && t.getType() != null) { - List compTypeList = new ArrayList<>(); - compTypeList.add(null); - compTypeList.add(t.getType()); - newPairType = pairTypes.get(compTypeList); + List newCompTypeList = new ArrayList<>(); + newCompTypeList.add(null); + newCompTypeList.add(t.getType()); + newPairType = pairTypes.get(newCompTypeList); if (newPairType == null) { // Create new tuple type; - newPairType = createNewTupleType(compTypeList, DataConstraintModel.typePair); + newPairType = createNewTupleType(newCompTypeList, DataConstraintModel.typePair); } } if (argType != newPairType && newPairType != null) { @@ -716,21 +758,22 @@ } else if (arg1 instanceof Term) { arg1Type = ((Term) arg1).getType(); } - List compTypeList = new ArrayList<>(); + List newCompTypeList = new ArrayList<>(); if (arg2 instanceof Variable) { - compTypeList.add(((Variable) arg2).getType()); + newCompTypeList.add(((Variable) arg2).getType()); } else if (arg2 instanceof Term) { - compTypeList.add(((Term) arg2).getType()); + newCompTypeList.add(((Term) arg2).getType()); } else { - compTypeList.add(null); + newCompTypeList.add(null); } - compTypeList.add(t.getType()); + newCompTypeList.add(t.getType()); if (arg1Type == DataConstraintModel.typeMap || arg1Type == null) { - Type newMapType = mapTypes.get(compTypeList); + Type newMapType = mapTypes.get(newCompTypeList); if (newMapType == null) { // Create new tuple type; - newMapType = createNewMapType(compTypeList, DataConstraintModel.typeMap); + newMapType = createNewMapType(newCompTypeList, DataConstraintModel.typeMap); } + // Update the type of the map argument and record the updated expression. if (arg1 instanceof Variable) { ((Variable) arg1).setType(newMapType); arg1Type = newMapType; @@ -757,33 +800,186 @@ mapExps.add(arg0); updateExpressionBelonging(expToMap, arg0, mapExps); Type termType = t.getType(); - List compTypeList = new ArrayList<>(); + List newCompTypeList = new ArrayList<>(); if (arg1 instanceof Variable) { - compTypeList.add(((Variable) arg1).getType()); + newCompTypeList.add(((Variable) arg1).getType()); } else if (arg1 instanceof Term) { - compTypeList.add(((Term) arg1).getType()); + newCompTypeList.add(((Term) arg1).getType()); } else { - compTypeList.add(null); + newCompTypeList.add(null); } if (arg2 instanceof Variable) { - compTypeList.add(((Variable) arg2).getType()); + newCompTypeList.add(((Variable) arg2).getType()); } else if (arg2 instanceof Term) { - compTypeList.add(((Term) arg2).getType()); + newCompTypeList.add(((Term) arg2).getType()); } else { - compTypeList.add(null); + newCompTypeList.add(null); } if (termType == DataConstraintModel.typeMap || termType == null) { - Type newMapType = mapTypes.get(compTypeList); + Type newMapType = mapTypes.get(newCompTypeList); if (newMapType == null) { // Create new tuple type; - newMapType = createNewMapType(compTypeList, DataConstraintModel.typeMap); + newMapType = createNewMapType(newCompTypeList, DataConstraintModel.typeMap); } + // Update the type of the map term and record the updated expression. t.setType(newMapType); termType = newMapType; Map updateExps = getUpdateSet(updateFromMap, mapExps); updateExps.put(System.identityHashCode(t), t); } map.put(System.identityHashCode(mapExps), termType); + } else if (symbol.equals(DataConstraintModel.addMember)) { + // If the root symbol of the term is addMember (addMember(json, key, value)). + List dotExps = new ArrayList<>(); + Expression jsonArg = t.getChildren().get(0); + Expression keyArg = t.getChildren().get(1); + Expression valueArg = t.getChildren().get(2); + dotExps.add(t); // json + updateExpressionBelonging(expToJson, t, dotExps); + dotExps.add(keyArg); // key + updateExpressionBelonging(expToJson, keyArg, dotExps); + dotExps.add(valueArg); // value + updateExpressionBelonging(expToJson, valueArg, dotExps); + dotExps.add(jsonArg); // json + updateExpressionBelonging(expToJson, jsonArg, dotExps); + Type jsonType = t.getType(); + Type valueType = null; + if (valueArg instanceof Variable) { + valueType = ((Variable) valueArg).getType(); + } else if (valueArg instanceof Term) { + valueType = ((Term) valueArg).getType(); + } + String keyName = null; + if (keyArg instanceof Constant) { + keyName = ((Constant) keyArg).getSymbol().getName(); + } + Type jsonArgType = null; + if (jsonArg instanceof Variable) { + jsonArgType = ((Variable) jsonArg).getType(); + } else if (jsonArg instanceof Term) { + jsonArgType = ((Term) jsonArg).getType(); + } + Type newJsonType = DataConstraintModel.typeJson; + if (jsonType == DataConstraintModel.typeJson && jsonArgType != null && keyName != null) { + Map newMemberTypes = new HashMap<>(((JsonType) jsonArgType).getMemberTypes()); + newMemberTypes.put(keyName, valueType); + newJsonType = jsonTypes.get(newMemberTypes); + if (newJsonType == null) { + // Create new json type; + newJsonType = createNewJsonType(newMemberTypes, DataConstraintModel.typeJson); + } + } + if (jsonType != newJsonType && newJsonType != null) { + // Update the type of the json term and record the updated expression. + t.setType(newJsonType); + jsonType = newJsonType; + Map updateExps = getUpdateSet(updateFromJson, dotExps); + updateExps.put(System.identityHashCode(t), t); + } + json.put(System.identityHashCode(dotExps), jsonType); + } else if (symbol.equals(DataConstraintModel.dot)) { + // If the root symbol of the term is dot (json.property). + List dotExps = new ArrayList<>(); + Expression jsonArg = t.getChildren().get(0); + Expression keyArg = t.getChildren().get(1); + dotExps.add(jsonArg); // json + updateExpressionBelonging(expToJson, jsonArg, dotExps); + dotExps.add(keyArg); // key + updateExpressionBelonging(expToJson, keyArg, dotExps); + dotExps.add(t); // value + updateExpressionBelonging(expToJson, t, dotExps); + dotExps.add(null); // json + Type jsonType = null; + if (jsonArg instanceof Variable) { + jsonType = ((Variable) jsonArg).getType(); + } else if (jsonArg instanceof Term) { + jsonType = ((Term) jsonArg).getType(); + } + String keyName = null; + if (keyArg instanceof Constant) { + keyName = ((Constant) keyArg).getSymbol().getName(); + } + Type newJsonType = DataConstraintModel.typeJson; + if (jsonType == DataConstraintModel.typeJson && t.getType() != null && keyName != null) { + Map newMemberTypes = new HashMap<>(); + newMemberTypes.put(keyName, t.getType()); + newJsonType = jsonTypes.get(newMemberTypes); + if (newJsonType == null) { + // Create new json type; + newJsonType = createNewJsonType(newMemberTypes, DataConstraintModel.typeJson); + } + } + if (jsonType != newJsonType && newJsonType != null) { + // Update the type of the json argument and record the updated expression. + if (jsonArg instanceof Variable) { + ((Variable) jsonArg).setType(newJsonType); + jsonType = newJsonType; + } else if (jsonArg instanceof Term) { + ((Term) jsonArg).setType(newJsonType); + jsonType = newJsonType; + } + Map updateExps = getUpdateSet(updateFromJson, dotExps); + updateExps.put(System.identityHashCode(jsonArg), jsonArg); + } + json.put(System.identityHashCode(dotExps), jsonType); + } else if (symbol.equals(DataConstraintModel.dotParam)) { + // If the root symbol of the term is dot (json.{param}). + List dotExps = new ArrayList<>(); + Expression jsonArg = t.getChildren().get(0); + Expression keyArg = t.getChildren().get(1); + dotExps.add(jsonArg); // json (list/map) + updateExpressionBelonging(expToJson, jsonArg, dotExps); + dotExps.add(null); // key + dotExps.add(t); // value + updateExpressionBelonging(expToJson, t, dotExps); + dotExps.add(null); // json + Type jsonType = null; + if (jsonArg instanceof Variable) { + jsonType = ((Variable) jsonArg).getType(); + } else if (jsonArg instanceof Term) { + jsonType = ((Term) jsonArg).getType(); + } + Type keyType = null; + if (keyArg instanceof Variable) { + keyType = ((Variable) keyArg).getType(); + } else if (keyArg instanceof Term) { + keyType = ((Term) keyArg).getType(); + } + Type newJsonType = null; + if (keyType == DataConstraintModel.typeInt) { + newJsonType = DataConstraintModel.typeList; + } else if (keyType == DataConstraintModel.typeString) { + newJsonType = DataConstraintModel.typeMap; + } + if (t.getType() != null) { + if ((jsonType == DataConstraintModel.typeList)) { + newJsonType = listTypes.get(t.getType()); + if (newJsonType == null) { + // Create new list type; + newJsonType = createNewListType(t.getType(), DataConstraintModel.typeList); + } + } else if (jsonType == DataConstraintModel.typeMap) { + List keyValueTypes = Arrays.asList(new Type[] {DataConstraintModel.typeString, t.getType()}); + newJsonType = mapTypes.get(keyValueTypes); + if (newJsonType == null) { + // Create new map type; + newJsonType = createNewMapType(keyValueTypes, DataConstraintModel.typeMap); + } + } + } + if (jsonType != newJsonType && newJsonType != null) { + // Update the type of the json argument and record the updated expression. + if (jsonArg instanceof Variable) { + ((Variable) jsonArg).setType(newJsonType); + jsonType = newJsonType; + } else if (jsonArg instanceof Term) { + ((Term) jsonArg).setType(newJsonType); + jsonType = newJsonType; + } + Map updateExps = getUpdateSet(updateFromJson, dotExps); + updateExps.put(System.identityHashCode(jsonArg), jsonArg); + } + json.put(System.identityHashCode(dotExps), jsonType); } else if (symbol.equals(DataConstraintModel.cond)) { // If the root symbol of the term is if function. Expression c1 = t.getChild(1); @@ -924,12 +1120,57 @@ variables.put(System.identityHashCode(consExps), condType); } } + + // 1.5 Extract constraints on path parameters and resources. + ResourcePath rPath = cm.getResource(); + while (rPath != null) { + Expression param = rPath.getLastParam(); + if (param != null) { + ResourceHierarchy parent = rPath.getResourceHierarchy().getParent(); + if (parent != null) { + List pathParams = resourcePathParams.get(parent); + if (pathParams == null) { + pathParams = new ArrayList<>(); + resourcePathParams.put(parent, pathParams); + } + pathParams.add(param); + expToPathParams.put(System.identityHashCode(param), pathParams); + Type parentType = parent.getResourceStateType(); + Type paramType = null; + if (param instanceof Variable) { + paramType = ((Variable) param).getType(); + } else if (param instanceof Term) { + paramType = ((Term) param).getType(); + } + if (paramType != null && parentType == null) { + if (paramType.equals(DataConstraintModel.typeString)) { + parentType = DataConstraintModel.typeMap; + } else if (paramType.equals(DataConstraintModel.typeInt)) { + parentType = DataConstraintModel.typeList; + } + if (parentType != null) { + parent.setResourceStateType(parentType); + updateFromResourceOwnership.add(parent); + } + } + } + } + rPath = rPath.getParent(); + } + } + } + + // 1.6 Extract constraints on resource hierarchies. + for (ResourceHierarchy res: model.getResourceHierarchies()) { + if (res.getResourceStateType() != null) { + updateFromResourceOwnership.add(res); } } // 2. Propagate type information. while (updateFromResource.size() > 0 || updateFromVariable.size() > 0 || updateFromMessage.size() > 0 - || updateFromConsOrSet.size() > 0 || updateFromTuple.size() > 0 || updateFromPair.size() > 0 || updateFromMap.size() > 0) { + || updateFromConsOrSet.size() > 0 || updateFromTuple.size() > 0 || updateFromPair.size() > 0 + || updateFromMap.size() > 0 || updateFromJson.size() > 0 || updateFromResourceOwnership.size() > 0) { if (updateFromResource.size() > 0) { Set resourceKeys = updateFromResource.keySet(); Integer resourceKey = resourceKeys.iterator().next(); @@ -943,6 +1184,8 @@ updateTupleTypes(resExp, tuple, expToTuple, updateFromTuple); updatePairTypes(resExp, pair, expToPair, updateFromPair); updateMapTypes(resExp, map, expToMap, updateFromMap); + updateJsonTypes(resExp, json, expToJson, updateFromJson); + updateResourcePathParamsTypes(resExp, resourcePathParams, updateFromResourceOwnership); } } if (updateFromVariable.size() > 0) { @@ -952,13 +1195,15 @@ updateFromVariable.remove(variableKey); for (int i : variableValue.keySet()) { Expression var = variableValue.get(i); - updateResourceTypes(var, resources, expToResource, updateFromResource); + updateResourceTypes(var, resources, expToResource, updateFromResource, updateFromResourceOwnership); updateVaribleTypes(var, variables, expToVariable, updateFromVariable); updateMessageTypes(var, messages, expToMessage, updateFromMessage); updateConsOrSetTypes(var, consOrSet, expToConsOrSet, updateFromConsOrSet); updateTupleTypes(var, tuple, expToTuple, updateFromTuple); updatePairTypes(var, pair, expToPair, updateFromPair); updateMapTypes(var, map, expToMap, updateFromMap); + updateJsonTypes(var, json, expToJson, updateFromJson); + updateResourcePathParamsTypes(var, resourcePathParams, updateFromResourceOwnership); } } if (updateFromMessage.size() > 0) { @@ -968,12 +1213,14 @@ updateFromMessage.remove(messageKey); for (int i : messageValue.keySet()) { Expression mesExp = messageValue.get(i); - updateResourceTypes(mesExp, resources, expToResource, updateFromResource); + updateResourceTypes(mesExp, resources, expToResource, updateFromResource, updateFromResourceOwnership); updateVaribleTypes(mesExp, variables, expToVariable, updateFromVariable); updateConsOrSetTypes(mesExp, consOrSet, expToConsOrSet, updateFromConsOrSet); updateTupleTypes(mesExp, tuple, expToTuple, updateFromTuple); updatePairTypes(mesExp, pair, expToPair, updateFromPair); updateMapTypes(mesExp, map, expToMap, updateFromMap); + updateJsonTypes(mesExp, json, expToJson, updateFromJson); + updateResourcePathParamsTypes(mesExp, resourcePathParams, updateFromResourceOwnership); } } if (updateFromConsOrSet.size() > 0) { @@ -983,13 +1230,14 @@ updateFromConsOrSet.remove(consKey); for (int i : consValue.keySet()) { Expression consExp = consValue.get(i); - updateResourceTypes(consExp, resources, expToResource, updateFromResource); + updateResourceTypes(consExp, resources, expToResource, updateFromResource, updateFromResourceOwnership); updateVaribleTypes(consExp, variables, expToVariable, updateFromVariable); updateMessageTypes(consExp, messages, expToMessage, updateFromMessage); updateConsOrSetTypes(consExp, consOrSet, expToConsOrSet, updateFromConsOrSet); updateTupleTypes(consExp, tuple, expToTuple, updateFromTuple); updatePairTypes(consExp, pair, expToPair, updateFromPair); updateMapTypes(consExp, map, expToMap, updateFromMap); + updateJsonTypes(consExp, json, expToJson, updateFromJson); } } if (updateFromTuple.size() > 0) { @@ -999,13 +1247,14 @@ updateFromTuple.remove(tupleKey); for (int i : tupleValue.keySet()) { Expression tupleExp = tupleValue.get(i); - updateResourceTypes(tupleExp, resources, expToResource, updateFromResource); + updateResourceTypes(tupleExp, resources, expToResource, updateFromResource, updateFromResourceOwnership); updateVaribleTypes(tupleExp, variables, expToVariable, updateFromVariable); updateMessageTypes(tupleExp, messages, expToMessage, updateFromMessage); updateConsOrSetTypes(tupleExp, consOrSet, expToConsOrSet, updateFromConsOrSet); updateTupleTypes(tupleExp, tuple, expToTuple, updateFromTuple); updatePairTypes(tupleExp, pair, expToPair, updateFromPair); updateMapTypes(tupleExp, map, expToMap, updateFromMap); + updateJsonTypes(tupleExp, json, expToJson, updateFromJson); } } if (updateFromPair.size() > 0) { @@ -1015,13 +1264,14 @@ updateFromPair.remove(pairKey); for (int i : pairValue.keySet()) { Expression pairExp = pairValue.get(i); - updateResourceTypes(pairExp, resources, expToResource, updateFromResource); + updateResourceTypes(pairExp, resources, expToResource, updateFromResource, updateFromResourceOwnership); updateVaribleTypes(pairExp, variables, expToVariable, updateFromVariable); updateMessageTypes(pairExp, messages, expToMessage, updateFromMessage); updateConsOrSetTypes(pairExp, consOrSet, expToConsOrSet, updateFromConsOrSet); updateTupleTypes(pairExp, tuple, expToTuple, updateFromTuple); updatePairTypes(pairExp, pair, expToPair, updateFromPair); updateMapTypes(pairExp, map, expToMap, updateFromMap); + updateJsonTypes(pairExp, json, expToJson, updateFromJson); } } if (updateFromMap.size() > 0) { @@ -1031,15 +1281,38 @@ updateFromMap.remove(mapKey); for (int i : mapValue.keySet()) { Expression mapExp = mapValue.get(i); - updateResourceTypes(mapExp, resources, expToResource, updateFromResource); + updateResourceTypes(mapExp, resources, expToResource, updateFromResource, updateFromResourceOwnership); updateVaribleTypes(mapExp, variables, expToVariable, updateFromVariable); updateMessageTypes(mapExp, messages, expToMessage, updateFromMessage); updateConsOrSetTypes(mapExp, consOrSet, expToConsOrSet, updateFromConsOrSet); updateTupleTypes(mapExp, tuple, expToTuple, updateFromTuple); updatePairTypes(mapExp, pair, expToPair, updateFromPair); updateMapTypes(mapExp, map, expToMap, updateFromMap); + updateJsonTypes(mapExp, json, expToJson, updateFromJson); } } + if (updateFromJson.size() > 0) { + Set jsonKeys = updateFromJson.keySet(); + Integer jsonKey = jsonKeys.iterator().next(); + Map jsonValue = updateFromJson.get(jsonKey); + updateFromJson.remove(jsonKey); + for (int i : jsonValue.keySet()) { + Expression jsonExp = jsonValue.get(i); + updateResourceTypes(jsonExp, resources, expToResource, updateFromResource, updateFromResourceOwnership); + updateVaribleTypes(jsonExp, variables, expToVariable, updateFromVariable); + updateMessageTypes(jsonExp, messages, expToMessage, updateFromMessage); + updateConsOrSetTypes(jsonExp, consOrSet, expToConsOrSet, updateFromConsOrSet); + updateTupleTypes(jsonExp, tuple, expToTuple, updateFromTuple); + updatePairTypes(jsonExp, pair, expToPair, updateFromPair); + updateMapTypes(jsonExp, map, expToMap, updateFromMap); + updateJsonTypes(jsonExp, json, expToJson, updateFromJson); + } + } + if (updateFromResourceOwnership.size() > 0) { + ResourceHierarchy res = updateFromResourceOwnership.iterator().next(); + updateFromResourceOwnership.remove(res); + updateResourceOwnershipTypes(res, resources, expToResource, updateFromResource, updateFromResourceOwnership); + } } } @@ -1056,18 +1329,20 @@ } } - private static void updateResourceTypes(Expression exp, Map> resources, - Map> expToResource, Map> updateFromResource) { - List sameResource = expToResource.get(System.identityHashCode(exp)); - if (sameResource == null) return; - for (ResourcePath id : resources.keySet()) { - if (resources.get(id) == sameResource) { - Type resType = id.getResourceStateType(); + private static void updateResourceTypes(Expression exp, Map> resources, Map> expToResource, + Map> updateFromResource, Set updateFromResourceOwnership) { + List identicalResources = expToResource.get(System.identityHashCode(exp)); + if (identicalResources == null) return; + for (ResourceHierarchy res: resources.keySet()) { + if (resources.get(res) == identicalResources) { + Type resType = res.getResourceStateType(); Type newResType = getExpTypeIfUpdatable(resType, exp); if (newResType != null) { - id.setResourceStateType(newResType); - Map updateExps = getUpdateSet(updateFromResource, sameResource); - for (Expression resExp : sameResource) { + res.setResourceStateType(newResType); + updateFromResourceOwnership.add(res); // To update parent and children resources + // Update identical resources + Map updateExps = getUpdateSet(updateFromResource, identicalResources); + for (Expression resExp : identicalResources) { if (resExp != exp) { if (resExp instanceof Variable) { ((Variable) resExp).setType(newResType); @@ -1082,6 +1357,145 @@ } } } + + private static void updateResourcePathParamsTypes(Expression exp, Map> resourcePathParams, Set updateFromResourceOwnership) { + for (ResourceHierarchy parent: resourcePathParams.keySet()) { + List pathParams = resourcePathParams.get(parent); + if (pathParams.contains(exp)) { + Type parentType = parent.getResourceStateType(); + Type paramType = null; + if (exp instanceof Variable) { + paramType = ((Variable) exp).getType(); + } else if (exp instanceof Term) { + paramType = ((Term) exp).getType(); + } + if (paramType != null && parentType == null) { + if (paramType.equals(DataConstraintModel.typeString)) { + parentType = DataConstraintModel.typeMap; + } else if (paramType.equals(DataConstraintModel.typeInt)) { + parentType = DataConstraintModel.typeList; + } + if (parentType != null) { + parent.setResourceStateType(parentType); + updateFromResourceOwnership.add(parent); + } + } + } + } + } + + private static void updateResourceOwnershipTypes(ResourceHierarchy res, Map> resources, + Map> expToResource, Map> updateFromResource, Set updateFromResourceOwnership) { + for (ResourceHierarchy parent: resources.keySet()) { + Type resType = res.getResourceStateType(); + Set children = parent.getChildren(); + if (res.equals(parent)) { + // Propagate an update of a parent resource type to its children' types. + if (DataConstraintModel.typeList.isAncestorOf(resType)) { + Type newElementType = listComponentTypes.get(resType); + if (newElementType != null && children != null && children.size() == 1) { + ResourceHierarchy element = children.iterator().next(); + Type elementType = element.getResourceStateType(); + if (compareTypes(elementType, newElementType)) { + element.setResourceStateType(newElementType); + updateFromResourceOwnership.add(element); + } + } + } else if (DataConstraintModel.typeMap.isAncestorOf(resType)) { + List newComponentTypes = mapComponentTypes.get(resType); + if (newComponentTypes != null && newComponentTypes.size() == 2 && children != null && children.size() == 1) { + ResourceHierarchy value = children.iterator().next(); + Type valueType = value.getResourceStateType(); + if (compareTypes(valueType, newComponentTypes.get(1))) { + value.setResourceStateType(newComponentTypes.get(1)); + updateFromResourceOwnership.add(value); + } + } + } else if (DataConstraintModel.typeJson.isAncestorOf(resType)) { + Map newMemberTypes = jsonMemberTypes.get(resType); + if (newMemberTypes != null && newMemberTypes.size() > 0 && children != null && children.size() > 0) { + for (ResourceHierarchy chlid: children) { + String key = chlid.getResourceName(); + Type memberType = chlid.getResourceStateType(); + if (compareTypes(memberType, newMemberTypes.get(key))) { + chlid.setResourceStateType(newMemberTypes.get(key)); + updateFromResourceOwnership.add(chlid); + } + } + } + } + } + if (children.contains(res)) { + // Propagate an update of a child resource type to its parent's type. + Type parentType = parent.getResourceStateType(); + if (parentType != null && DataConstraintModel.typeList.isAncestorOf(parentType)) { + Type oldElementType = listComponentTypes.get(parentType); + if (compareTypes(oldElementType, resType)) { + Type newListType = listTypes.get(resType); + if (newListType == null) { + newListType = createNewListType(resType, parentType); + } + if (newListType != null) { + parent.setResourceStateType(newListType); + updateFromResourceOwnership.add(parent); + } + } + } else if (parentType != null && DataConstraintModel.typeMap.isAncestorOf(parentType)) { + List oldComponentTypes = mapComponentTypes.get(parentType); + if (compareTypes(oldComponentTypes.get(1), resType)) { + List newComponentTypes = Arrays.asList(new Type[] {DataConstraintModel.typeString, resType}); + Type newMapType = mapTypes.get(newComponentTypes); + if (newMapType == null) { + newMapType = createNewMapType(newComponentTypes, parentType); + } + if (newMapType != null) { + parent.setResourceStateType(newMapType); + updateFromResourceOwnership.add(parent); + } + } + } else if (parentType != null && DataConstraintModel.typeJson.isAncestorOf(parentType)) { + Map oldMemberTypes = jsonMemberTypes.get(parentType); + String key= res.getResourceName(); + if (oldMemberTypes == null || compareTypes(oldMemberTypes.get(key), resType)) { + Map newMemberTypes = new HashMap<>(); + if (oldMemberTypes != null) newMemberTypes.putAll(oldMemberTypes); + newMemberTypes.put(key, resType); + Type newJsonType = jsonTypes.get(newMemberTypes); + if (newJsonType == null) { + newJsonType = createNewJsonType(newMemberTypes, parentType); + } + if (newJsonType != null) { + parent.setResourceStateType(newJsonType); + updateFromResourceOwnership.add(parent); + } + } + } + } + } + // Propagate an update of the resource to its expressions. + List identicalResources = resources.get(res); + if (identicalResources != null) { + Type newResType = res.getResourceStateType(); + for (Expression resExp: identicalResources) { + Type resType = null; + if (resExp instanceof Variable) { + resType = ((Variable) resExp).getType(); + } else if (resExp instanceof Term) { + resType = ((Term) resExp).getType(); + } + if (resType == null || compareTypes(resType, newResType)) { + Map updateExps = getUpdateSet(updateFromResource, identicalResources); + if (resExp instanceof Variable) { + ((Variable) resExp).setType(newResType); + updateExps.put(System.identityHashCode(resExp), resExp); + } else if (resExp instanceof Term) { + ((Term) resExp).setType(newResType); + updateExps.put(System.identityHashCode(resExp), resExp); + } + } + } + } + } private static void updateVaribleTypes(Expression exp, Map variables, Map> expToVariable, Map> updateFromVariable) { @@ -1167,6 +1581,7 @@ int idx = consOrSetComponentGroup.indexOf(exp); switch (idx) { case 0: + // exp is a list itself. if (!(exp instanceof Term)) break; Type listType = consOrSet.get(System.identityHashCode(consOrSetComponentGroup)); Type expType = getExpTypeIfUpdatable(listType, exp); @@ -1191,6 +1606,7 @@ } break; case 1: + // exp is a list's component. listType = consOrSet.get(System.identityHashCode(consOrSetComponentGroup)); Type compType = listComponentTypes.get(listType); Type newCompType = getExpTypeIfUpdatable(compType, exp); @@ -1216,6 +1632,7 @@ } break; case 2: + // exp is a list itself. listType = consOrSet.get(System.identityHashCode(consOrSetComponentGroup)); expType = getExpTypeIfUpdatable(listType, exp); if (expType != null) { @@ -1245,6 +1662,7 @@ for (List tupleComponentGroup: tupleComponentGroups) { int idx = tupleComponentGroup.indexOf(exp); if (idx == 0) { + // exp is a tuple itself. Type tupleType = tuple.get(System.identityHashCode(tupleComponentGroup)); Type newTupleType = getExpTypeIfUpdatable(tupleType, exp); if (newTupleType != null) { @@ -1312,6 +1730,7 @@ } } } else { + // exp is a tuple's component. Type tupleType = tuple.get(System.identityHashCode(tupleComponentGroup)); List componentTypes = tupleComponentTypes.get(tupleType); boolean updated = false; @@ -1382,6 +1801,7 @@ for (List pairComponentGroup: pairComponentGroups) { int idx = pairComponentGroup.indexOf(exp); if (idx == 0) { + // exp is a pair itself. Type pairType = pair.get(System.identityHashCode(pairComponentGroup)); Type newPairType = getExpTypeIfUpdatable(pairType, exp); if (newPairType != null) { @@ -1405,6 +1825,7 @@ } } } else { + // exp is a pair's component. Type pairType = pair.get(System.identityHashCode(pairComponentGroup)); Type compType = pairComponentTypes.get(pairType); Type newCompType = getExpTypeIfUpdatable(compType, exp); @@ -1435,6 +1856,7 @@ for (List mapComponentGroup: mapComponentGroups) { int idx = mapComponentGroup.indexOf(exp); if (idx == 0 || idx == 3) { + // exp is a map itself. Type mapType = map.get(System.identityHashCode(mapComponentGroup)); Type newMapType = getExpTypeIfUpdatable(mapType, exp); if (newMapType != null) { @@ -1478,6 +1900,7 @@ } } } else { + // exp is a map's key or value. Type mapType = map.get(System.identityHashCode(mapComponentGroup)); List componentTypes = mapComponentTypes.get(mapType); Type compType = componentTypes.get(idx - 1); @@ -1516,6 +1939,116 @@ } } + + private static void updateJsonTypes(Expression exp, Map json, + Map>> expToJson, Map> updateFromJson) { + Set> jsonMemberGroups = expToJson.get(System.identityHashCode(exp)); + if (jsonMemberGroups == null) return; + for (List jsonMemberGroup: jsonMemberGroups) { + int idx = jsonMemberGroup.indexOf(exp); + if (idx == 3) { + // exp is a json argument (0:t = addMember(3:json, 1:key, 2:value)). + Type jsonType = json.get(System.identityHashCode(jsonMemberGroup)); + Map memberTypes = new HashMap<>(jsonMemberTypes.get(jsonType)); + Map argMemberTypes = new HashMap<>(memberTypes); + String keyName = null; + Type valueType = null; + if (jsonMemberGroup.get(1) instanceof Constant) { + keyName = ((Constant) jsonMemberGroup.get(1)).getSymbol().getName(); + valueType = ((Constant) jsonMemberGroup.get(1)).getType(); + argMemberTypes.remove(keyName); + } + Type jsonArgType = jsonTypes.get(argMemberTypes); + Type newJsonArgType = getExpTypeIfUpdatable(jsonArgType, exp); + if (newJsonArgType != null && keyName != null) { + // Propagate an update of a json arg's type to its container's (json's) type. + argMemberTypes = ((JsonType) newJsonArgType).getMemberTypes(); + argMemberTypes.put(keyName, valueType); + memberTypes.putAll(argMemberTypes); + Type newJsonType = jsonTypes.get(memberTypes); + if (newJsonType == null) { + // Create new json type. + newJsonType = createNewJsonType(memberTypes, jsonType); + } + // Update the type of the json term and record the updated expression. + Map updateExps = getUpdateSet(updateFromJson, jsonMemberGroup); + Expression jsonExp = jsonMemberGroup.get(0); + if (jsonExp instanceof Variable) { + ((Variable) jsonExp).setType(newJsonType); + updateExps.put(System.identityHashCode(jsonExp), jsonExp); + } else if (jsonExp instanceof Term) { + ((Term) jsonExp).setType(newJsonType); + updateExps.put(System.identityHashCode(jsonExp), jsonExp); + } + json.put(System.identityHashCode(jsonMemberGroup), newJsonType); + } + } else if (idx == 2) { + // exp is a value (0:t = addMember(3:json, 1:key, 2:value) or 2:value = dot(0:list/map, 1:key)). + Type jsonType = json.get(System.identityHashCode(jsonMemberGroup)); + Type newJsonType = null; + if (exp instanceof Term && ((Term) exp).getSymbol().equals(DataConstraintModel.dotParam)) { + if (DataConstraintModel.typeList.isAncestorOf(jsonType)) { + Type elementType = listComponentTypes.get(jsonType); + Type newElementType = getExpTypeIfUpdatable(elementType, exp); + if (newElementType != null) { + // Propagate an update of a member's type to its container's (json's) type. + newJsonType = listTypes.get(newElementType); + if (newJsonType == null) { + // Create new json type. + newJsonType = createNewListType(newElementType, jsonType); + } + } + } else if (DataConstraintModel.typeMap.isAncestorOf(jsonType)) { + List keyValueTypes = mapComponentTypes.get(jsonType); + Type newValueType = getExpTypeIfUpdatable(keyValueTypes.get(1), exp); + if (newValueType != null) { + // Propagate an update of a member's type to its container's (json's) type. + List newKeyValueTypes = Arrays.asList(new Type[] {DataConstraintModel.typeString, newValueType}); + newJsonType = mapTypes.get(newKeyValueTypes); + if (newJsonType == null) { + // Create new json type. + newJsonType = createNewMapType(newKeyValueTypes, jsonType); + } + } + } + } else { + Map memberTypes = jsonMemberTypes.get(jsonType); + String keyName = null; + if (jsonMemberGroup.get(1) instanceof Constant) { + keyName = ((Constant) jsonMemberGroup.get(1)).getSymbol().getName(); + } + if (memberTypes != null) { + Type memberType = memberTypes.get(keyName); + Type newMemberType = getExpTypeIfUpdatable(memberType, exp); + if (newMemberType != null && keyName != null) { + // Propagate an update of a member's type to its container's (json's) type. + Map newMemberTypes = new HashMap<>(memberTypes); + newMemberTypes.put(keyName, newMemberType); + newJsonType = jsonTypes.get(newMemberTypes); + if (newJsonType == null) { + // Create new json type. + newJsonType = createNewJsonType(newMemberTypes, jsonType); + } + } + } + } + if (newJsonType != null) { + // Update the type of the json term and record the updated expression. + Map updateExps = getUpdateSet(updateFromJson, jsonMemberGroup); + Expression jsonExp = jsonMemberGroup.get(0); + if (jsonExp instanceof Variable) { + ((Variable) jsonExp).setType(newJsonType); + updateExps.put(System.identityHashCode(jsonExp), jsonExp); + } else if (jsonExp instanceof Term) { + ((Term) jsonExp).setType(newJsonType); + updateExps.put(System.identityHashCode(jsonExp), jsonExp); + } + json.put(System.identityHashCode(jsonMemberGroup), newJsonType); + } + } + } + } + private static Type createNewListType(Type compType, Type parentType) { String compTypeName = getInterfaceTypeName(compType); List childrenTypes = getChildrenTypes(parentType, listComponentTypes.keySet()); @@ -1598,9 +2131,39 @@ return newMapType; } - private static List getChildrenTypes(Type parentType, Set componentTypes) { + private static JsonType createNewJsonType(Map memberTypes, Type parentJsonType) { + String implTypeName = "HashMap<>"; + String interfaceTypeName = "Map"; + List childrenTypes = getChildrenTypes(parentJsonType, jsonMemberTypes.keySet()); + JsonType newJsonType = new JsonType("Json", implTypeName, interfaceTypeName, parentJsonType); + for (String key: memberTypes.keySet()) { + newJsonType.addMemberType(key, memberTypes.get(key)); + } + jsonTypes.put(memberTypes, newJsonType); + jsonMemberTypes.put(newJsonType, memberTypes); + for (Type childType : childrenTypes) { + if (compareTypes(childType, newJsonType)) { + if (newJsonType.getParentTypes().contains(parentJsonType)) { + newJsonType.replaceParentType(parentJsonType, childType); + } else { + newJsonType.addParentType(childType); + } + } else if (compareTypes(newJsonType, childType)) { + childType.replaceParentType(parentJsonType, newJsonType); + } + } + return newJsonType; + } + + /** + * Get children types of a given type from given set of types. + * @param parentType a type + * @param allTypes set of types + * @return list of the children types + */ + private static List getChildrenTypes(Type parentType, Set allTypes) { List childrenTypes = new ArrayList<>(); - for (Type childType : componentTypes) { + for (Type childType : allTypes) { if (childType.getParentTypes().contains(parentType)) { childrenTypes.add(childType); } @@ -1626,8 +2189,7 @@ return type.getInterfaceTypeName(); } - private static > Map getUpdateSet( - Map> updateSets, U keySet) { + private static > Map getUpdateSet(Map> updateSets, U keySet) { Map updatedExps = updateSets.get(System.identityHashCode(keySet)); if (updatedExps == null) { updatedExps = new HashMap<>(); @@ -1683,6 +2245,7 @@ for (int i = 0; i < originalCompTypes.size(); i++) { if (originalCompTypes.get(i) != null) { if (DataConstraintModel.typeTuple.isAncestorOf(originalCompTypes.get(i))) { + // Unfold the nested tuple's types. Type tupleType = originalCompTypes.remove(i); for (Type t: tupleComponentTypes.get(tupleType)) { originalCompTypes.add(t); @@ -1690,6 +2253,7 @@ } if (newCompTypes.size() - 1 < i) return false; if (newCompTypes.get(i) != null && DataConstraintModel.typeTuple.isAncestorOf(newCompTypes.get(i))) { + // Unfold the nested tuple's types. Type tupleType = newCompTypes.remove(i); for (Type t: tupleComponentTypes.get(tupleType)) { newCompTypes.add(t); @@ -1707,6 +2271,19 @@ if (originalCompType != null && (newCompType == null || !originalCompType.isAncestorOf(newCompType))) return false; return true; } + if (DataConstraintModel.typeJson.isAncestorOf(originalType) && DataConstraintModel.typeJson.isAncestorOf(newType)) { + Map originalMemberTypes = jsonMemberTypes.get(originalType); + Map newMemberTypes = jsonMemberTypes.get(newType); + if (originalMemberTypes == null) return true; + if (originalMemberTypes.keySet().size() < newMemberTypes.keySet().size() + && newMemberTypes.keySet().containsAll(originalMemberTypes.keySet())) return true; + if (originalMemberTypes.keySet().size() > newMemberTypes.keySet().size()) return false; + if (!newMemberTypes.keySet().containsAll(originalMemberTypes.keySet())) return false; + for (String key: originalMemberTypes.keySet()) { + if (!originalMemberTypes.get(key).isAncestorOf(newMemberTypes.get(key))) return false; + } + return true; + } } return false; } diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/JavaPrototypeGenerateAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/JavaPrototypeGenerateAction.java index 83acc69..4083dce 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/actions/JavaPrototypeGenerateAction.java +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/JavaPrototypeGenerateAction.java @@ -13,9 +13,12 @@ import algorithms.*; import application.editor.Editor; import code.ast.*; +import generators.CodeGeneratorFromDataFlowGraph; import generators.DataTransferMethodAnalyzer; import generators.JavaCodeGenerator; import generators.JavaMethodBodyGenerator; +import generators.JavaSpecific; +import models.dataConstraintModel.ResourceHierarchy; import models.dataConstraintModel.ResourcePath; import models.dataFlowModel.DataTransferModel; import models.dataFlowModel.ModelExtension; @@ -45,8 +48,8 @@ if (fileName == null) fileName = "Main"; String mainTypeName = fileName.split("\\.")[0]; boolean exist = false; - for (ResourcePath id: model.getResourcePaths()) { - String resourceName = id.getResourceName().substring(0, 1).toUpperCase() + id.getResourceName().substring(1); + for (ResourceHierarchy res: model.getResourceHierarchies()) { + String resourceName = res.getResourceName().substring(0, 1).toUpperCase() + res.getResourceName().substring(1); if (mainTypeName.equals(resourceName)) { exist = true; } @@ -57,6 +60,7 @@ JavaCodeGenerator.resetMainTypeName(); // use the default main type's name. } editor.setCodes(JavaMethodBodyGenerator.doGenerate(graph, model, JavaCodeGenerator.doGenerate(graph, model))); +// editor.setCodes(new CodeGeneratorFromDataFlowGraph().generateCode(model, graph, new JavaSpecific())); ModelExtension.recoverModel(model); for (CompilationUnit file : editor.getCodes()) { System.out.println(file); diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/JerseyPrototypeGenerateAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/JerseyPrototypeGenerateAction.java index 766c384..dbf75fd 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/actions/JerseyPrototypeGenerateAction.java +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/JerseyPrototypeGenerateAction.java @@ -17,6 +17,7 @@ import generators.JerseyCodeGenerator; import generators.JerseyMethodBodyGenerator; import models.algebra.Type; +import models.dataConstraintModel.ResourceHierarchy; import models.dataConstraintModel.ResourcePath; import models.dataFlowModel.DataTransferModel; import models.dataFlowModel.ModelExtension; @@ -46,8 +47,8 @@ if (fileName == null) fileName = "Main"; String mainTypeName = fileName.split("\\.")[0]; boolean exist = false; - for (ResourcePath id: model.getResourcePaths()) { - String resourceName = id.getResourceName().substring(0, 1).toUpperCase() + id.getResourceName().substring(1); + for (ResourceHierarchy res: model.getResourceHierarchies()) { + String resourceName = res.getResourceName().substring(0, 1).toUpperCase() + res.getResourceName().substring(1); if (mainTypeName.equals(resourceName)) { exist = true; } diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/NewResourceAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/NewResourceAction.java index 50b1512..b01fc46 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/actions/NewResourceAction.java +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/NewResourceAction.java @@ -22,7 +22,7 @@ public void actionPerformed(ActionEvent e) { String resName = JOptionPane.showInputDialog("Resourece Name:"); if (resName == null) return; - editor.addResourcePath(new ResourcePath(resName, 0)); + editor.addResourcePath(null, resName); } } diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/DataTransferModelingCellEditor.java b/AlgebraicDataflowArchitectureModel/src/application/editor/DataTransferModelingCellEditor.java index b4d81a6..7ded3d9 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/editor/DataTransferModelingCellEditor.java +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/DataTransferModelingCellEditor.java @@ -30,7 +30,9 @@ import models.visualModel.FormulaChannel; import parser.Parser; import parser.Parser.TokenStream; +import parser.exceptions.ExpectedColon; import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.WrongJsonExpression; public class DataTransferModelingCellEditor implements mxICellEditor { public int DEFAULT_MIN_WIDTH = 70; @@ -123,7 +125,7 @@ Expression exp = parser.parseTerm(stream, editor.getModel()); ((FormulaChannel) ch).setFormula(formula); ((FormulaChannel) ch).setFormulaTerm(exp); - } catch (ExpectedRightBracket e) { + } catch (ExpectedRightBracket | WrongJsonExpression | ExpectedColon e) { e.printStackTrace(); } } diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/Editor.java b/AlgebraicDataflowArchitectureModel/src/application/editor/Editor.java index b285ce0..25604c4 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/editor/Editor.java +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/Editor.java @@ -6,7 +6,10 @@ import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; +import java.util.AbstractMap; +import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; @@ -32,15 +35,26 @@ import algorithms.Validation; import application.layouts.*; import code.ast.CompilationUnit; +import generators.JavaCodeGenerator; import models.Edge; import models.EdgeAttribute; import models.Node; +import models.algebra.Expression; +import models.algebra.InvalidMessage; +import models.algebra.ParameterizedIdentifierIsFutureWork; +import models.algebra.UnificationFailed; +import models.algebra.ValueUndefined; import models.dataConstraintModel.Channel; import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.ResourceHierarchy; import models.dataConstraintModel.ResourcePath; +import models.dataConstraintModel.Selector; import models.dataFlowModel.DataTransferModel; import models.dataFlowModel.DataTransferChannel; import models.dataFlowModel.PushPullAttribute; +import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork; +import models.dataFlowModel.ChannelNode; import models.dataFlowModel.DataFlowEdge; import models.dataFlowModel.DataFlowGraph; import models.dataFlowModel.ResourceNode; @@ -50,10 +64,11 @@ import parser.exceptions.ExpectedAssignment; import parser.exceptions.ExpectedChannel; import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; import parser.exceptions.ExpectedEquals; import parser.exceptions.ExpectedFormulaChannel; import parser.exceptions.ExpectedGeometry; -import parser.exceptions.ExpectedInOrOutOrRefKeyword; +import parser.exceptions.ExpectedInOrOutOrRefOrSubKeyword; import parser.exceptions.ExpectedIoChannel; import parser.exceptions.ExpectedLeftCurlyBracket; import parser.exceptions.ExpectedModel; @@ -62,8 +77,11 @@ import parser.exceptions.ExpectedResource; import parser.exceptions.ExpectedRightBracket; import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; import parser.exceptions.WrongLHSExpression; import parser.exceptions.WrongRHSExpression; +import parser.exceptions.ExpectedRightCurlyBracket; +import parser.exceptions.WrongPathExpression; import parser.ParserDTRAM; public class Editor { @@ -79,6 +97,8 @@ protected String curFileName = null; protected String curFilePath = null; protected ArrayList codes = null; + + private boolean bReflectingArchitectureModel = false; public Editor(mxGraphComponent graphComponent) { this.graphComponent = graphComponent; @@ -105,6 +125,7 @@ public DataFlowGraph getDataFlowGraph() { if (dataFlowGraph == null) { analyzeDataTransferModel(getModel()); + updateEdgeAttiributes(dataFlowGraph); } return dataFlowGraph; } @@ -112,7 +133,6 @@ public DataFlowGraph analyzeDataTransferModel(DataTransferModel model) { DataFlowGraph flowGraph = DataTransferModelAnalyzer.createDataFlowGraphWithStateStoringAttribute(model); dataFlowGraph = DataTransferModelAnalyzer.annotateWithSelectableDataTransferAttiribute(flowGraph); - updateEdgeAttiributes(dataFlowGraph); return dataFlowGraph; } @@ -175,17 +195,26 @@ } else { ParserDTRAM parserDTRAM = new ParserDTRAM(new BufferedReader(new FileReader(file))); try { + // Parse the .dtram file. model = parserDTRAM.doParseModel(); - graph = constructGraph(model); + + // Analyze the model. + if (!Validation.checkUpdateConflict(model)) return null; + DataFlowGraph dataFlowGraph = analyzeDataTransferModel(model); + + // Visualize the model. + graph = constructGraph(model, dataFlowGraph); parserDTRAM.doParseGeometry(graph); + updateEdgeAttiributes(dataFlowGraph); + curFilePath = file.getAbsolutePath(); curFileName = file.getName(); - if (!Validation.checkUpdateConflict(model)) return null; - analyzeDataTransferModel(model); return model; - } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefKeyword + } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression - | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedModel | ExpectedGeometry | ExpectedNode | ExpectedResource | ExpectedFormulaChannel | ExpectedIoChannel e) { + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedModel + | ExpectedGeometry | ExpectedNode | ExpectedResource | ExpectedFormulaChannel | ExpectedIoChannel + | ExpectedRightCurlyBracket | WrongPathExpression | WrongJsonExpression | ExpectedColon e) { e.printStackTrace(); } } @@ -205,20 +234,23 @@ // Parse the .model file. model = parser.doParse(); - curFilePath = file.getAbsolutePath(); - curFileName = file.getName(); - // Analyze the model. if (!Validation.checkUpdateConflict(model)) return null; - graph = constructGraph(model); - analyzeDataTransferModel(model); + DataFlowGraph dataFlowGraph = analyzeDataTransferModel(model); + + // Visualize the model. + graph = constructGraph(model, dataFlowGraph); + updateEdgeAttiributes(dataFlowGraph); // Set DAG layout. - setDAGLayout(); +// setDAGLayout(); + + curFilePath = file.getAbsolutePath(); + curFileName = file.getName(); return model; - } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefKeyword + } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression - | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment e) { + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket | WrongPathExpression | WrongJsonExpression | ExpectedColon e) { e.printStackTrace(); } } catch (FileNotFoundException e) { @@ -322,10 +354,11 @@ /** * Construct a mxGraph from DataFlowModel and DataFlowModel * @param model - * @param dataFlowGraph + * @param dataFlowGraph * @return constructed mxGraph */ - public mxGraph constructGraph(DataTransferModel model) { + public mxGraph constructGraph(DataTransferModel model, DataFlowGraph dataFlowGraph) { + bReflectingArchitectureModel = true; ((mxGraphModel) graph.getModel()).clear(); Object parent = graph.getDefaultParent(); graph.getModel().beginUpdate(); @@ -343,66 +376,136 @@ Map resources = new HashMap<>(); // create channel vertices - for (Channel c: model.getChannels()) { - DataTransferChannel channelGen = (DataTransferChannel) c; - if (channelsIn.get(channelGen) == null || channelsOut.get(channelGen) == null) { - Object channel = graph.insertVertex(parent, null, channelGen.getChannelName(), 150, 20, 30, 30); // insert a channel as a vertex - mxCell port_in = new mxCell(null, geo1, "shape=ellipse;perimter=ellipsePerimeter"); - port_in.setVertex(true); - graph.addCell(port_in, channel); // insert the input port of a channel - mxCell port_out = new mxCell(null, geo2, "shape=ellipse;perimter=ellipsePerimeter"); - port_out.setVertex(true); - graph.addCell(port_out, channel); // insert the output port of a channel - channelsIn.put(channelGen, port_in); - channelsOut.put(channelGen, port_out); + for (ChannelNode c: dataFlowGraph.getRootChannelNodes()) { + DataTransferChannel channel = (DataTransferChannel) c.getChannel(); + if (channel.getInputResources().size() > 0) { + // Normal channel + if (channelsIn.get(channel) == null || channelsOut.get(channel) == null) { + if(channel.getSelectors().toString() == "[]") { + Object chCell = graph.insertVertex(parent, null, channel.getChannelName(), 150, 20, 30, 30); // insert a channel as a vertex + mxCell port_in = new mxCell(null, geo1, "shape=ellipse;perimter=ellipsePerimeter"); + port_in.setVertex(true); + graph.addCell(port_in, chCell); // insert the input port of a channel + mxCell port_out = new mxCell(null, geo2, "shape=ellipse;perimter=ellipsePerimeter"); + port_out.setVertex(true); + graph.addCell(port_out, chCell); // insert the output port of a channel + channelsIn.put(channel, port_in); + channelsOut.put(channel, port_out); + }else { + + for (Selector s: channel.getSelectors()) { + Expression exp = s.getExpression(); + String selName = exp.toString(); + String cName = channel.getChannelName(); + String channelName = cName + " (" + selName + ")"; + Object chCell = graph.insertVertex(parent, null, channelName, 150, 20, 60, 30); // insert a channel as a vertex + mxCell port_in = new mxCell(null, geo1, "shape=ellipse;perimter=ellipsePerimeter"); + port_in.setVertex(true); + graph.addCell(port_in, chCell); // insert the input port of a channel + mxCell port_out = new mxCell(null, geo2, "shape=ellipse;perimter=ellipsePerimeter"); + port_out.setVertex(true); + graph.addCell(port_out, chCell); // insert the output port of a channel + channelsIn.put(channel, port_in); + channelsOut.put(channel, port_out); + } + } + } + } else { + // I/O channel + if (channelsOut.get(channel) == null) { + if(channel.getSelectors().toString() == "[]") { + Object chCell = graph.insertVertex(parent, null, channel.getChannelName(), 150, 20, 30, 30); // insert a channel as a vertex + mxCell port_in = new mxCell(null, geo1, "shape=ellipse;perimter=ellipsePerimeter"); + port_in.setVertex(true); + graph.addCell(port_in, chCell); // insert the input port of a channel + mxCell port_out = new mxCell(null, geo2, "shape=ellipse;perimter=ellipsePerimeter"); + port_out.setVertex(true); + graph.addCell(port_out, chCell); // insert the output port of a channel + channelsIn.put(channel, port_in); + channelsOut.put(channel, port_out); + }else { + + for (Selector s: channel.getSelectors()) { + Expression exp = s.getExpression(); + String selName = exp.toString(); + String cName = channel.getChannelName(); + String channelName = cName + " (" + selName + ")"; + Object chCell = graph.insertVertex(parent, null, channelName, 150, 20, 60, 30); // insert a channel as a vertex + mxCell port_in = new mxCell(null, geo1, "shape=ellipse;perimter=ellipsePerimeter"); + port_in.setVertex(true); + graph.addCell(port_in, chCell); // insert the input port of a channel + mxCell port_out = new mxCell(null, geo2, "shape=ellipse;perimter=ellipsePerimeter"); + port_out.setVertex(true); + graph.addCell(port_out, chCell); // insert the output port of a channel + channelsIn.put(channel, port_in); + channelsOut.put(channel, port_out); + } + } + } } } // create resource vertices - for (ResourcePath res: model.getResourcePaths()) { + for (ResourceNode resNode: dataFlowGraph.getRootResourceNodes()) { + int w = 300; + int h = 200; + ResourcePath res = resNode.getOutSideResource(); Object resource = graph.insertVertex(parent, null, - res.getResourceName(), 20, 20, 80, 30, + res.getResourceName(), 20, 20, w, h, "shape=ellipse;perimeter=ellipsePerimeter"); // insert a resource as a vertex resources.put(res, resource); + getChildResource(resource, resNode, resources, w, h); } - + // add input, output and reference edges - for (Channel ch: model.getChannels()) { - DataTransferChannel channelGen = (DataTransferChannel) ch; - // input edge - for (ResourcePath srcRes: channelGen.getInputResources()) { - graph.insertEdge(parent, null, new SrcDstAttribute(srcRes, channelGen), resources.get(srcRes), channelsIn.get(channelGen), "movable=false"); - } - // output edge - for (ResourcePath dstRes: channelGen.getOutputResources()) { - graph.insertEdge(parent, null, new SrcDstAttribute(channelGen, dstRes), channelsOut.get(channelGen), resources.get(dstRes), "movable=false"); - } - // reference edges - for (ResourcePath refRes: channelGen.getReferenceResources()) { - graph.insertEdge(parent, null, null, resources.get(refRes), channelsIn.get(channelGen), "dashed=true;movable=false"); + for (Edge edge: dataFlowGraph.getEdges()) { + DataFlowEdge dfEdge = (DataFlowEdge) edge; + if (dfEdge.isChannelToResource()) { + // output edge + DataTransferChannel channel = ((ChannelNode) dfEdge.getSource()).getChannel(); + ResourcePath dstRes = ((ResourceNode) dfEdge.getDestination()).getOutSideResource(); + graph.insertEdge(parent, null, new SrcDstAttribute(channel, dstRes), channelsOut.get(channel), resources.get(dstRes), "movable=false"); + } else { + // input edge + ResourcePath srcRes = ((ResourceNode) dfEdge.getSource()).getOutSideResource(); + DataTransferChannel channel = ((ChannelNode) dfEdge.getDestination()).getChannel(); + graph.insertEdge(parent, null, new SrcDstAttribute(srcRes, channel), resources.get(srcRes), channelsIn.get(channel), "movable=false"); } } - - for (Channel ioChannelGen: model.getIOChannels()) { - if (channelsOut.get(ioChannelGen) == null) { - Object channel = graph.insertVertex(parent, null, ioChannelGen.getChannelName(), 150, 20, 30, 30); // insert an I/O channel as a vertex - mxCell port_out = new mxCell(null, geo2, "shape=ellipse;perimter=ellipsePerimeter"); - port_out.setVertex(true); - graph.addCell(port_out, channel); // insert the output port of a channel - channelsOut.put((DataTransferChannel) ioChannelGen, port_out); - for (ResourcePath outRes: ((DataTransferChannel) ioChannelGen).getOutputResources()) { - graph.insertEdge(parent, null, null, port_out, resources.get(outRes), "movable=false"); - } + + for (Channel ch: model.getChannels()) { + // reference edges + DataTransferChannel channel = (DataTransferChannel) ch; + for (ResourcePath refRes: channel.getReferenceResources()) { + graph.insertEdge(parent, null, null, resources.get(refRes), channelsIn.get(channel), "dashed=true;movable=false"); } } } finally { graph.getModel().endUpdate(); } - setTreeLayout(); - +// setTreeLayout(); + + bReflectingArchitectureModel = false; return graph; } + public void getChildResource(Object resource, ResourceNode resNode, Map resources, int w, int h) { + + for (ResourceNode c: resNode.getChildren()) { + w = w - 100; + if(w <= 0) { + w = 100; + } + h = (2 * w) / 3; + ResourcePath chRes = c.getOutSideResource(); + Object chResource = graph.insertVertex(resource, null, + chRes.getName(), 110, 90, w, h, + "shape=ellipse;perimeter=ellipsePerimeter"); // insert a resource as a vertex + resources.put(chRes, chResource); + getChildResource(chResource, c, resources, w, h); + } + } + public void setDAGLayout() { Object parent = graph.getDefaultParent(); graph.getModel().beginUpdate(); @@ -422,16 +525,18 @@ for (Edge e : dataFlowGraph.getEdges()) { if (e instanceof DataFlowEdge) { DataFlowEdge dataFlow = (DataFlowEdge) e; - DataTransferChannel channelGen = dataFlow.getChannel(); - ResourceNode srcRes = (ResourceNode) dataFlow.getSource(); - // input edge - for (Object edge: graph.getChildEdges(parent)) { - mxCell edgeCell = (mxCell) edge; - if (edgeCell.getValue() instanceof SrcDstAttribute) { - SrcDstAttribute edgeAttr = (SrcDstAttribute) edgeCell.getValue(); - if (edgeAttr.getSrouce() == srcRes.getResource() && edgeAttr.getDestination() == channelGen) { - edgeCell.setValue(dataFlow.getAttribute()); - break; + if (!dataFlow.isChannelToResource()) { + ResourceNode srcRes = (ResourceNode) dataFlow.getSource(); + DataTransferChannel channel = ((ChannelNode) dataFlow.getDestination()).getChannel(); + // input edge + for (Object edge: graph.getChildEdges(parent)) { + mxCell edgeCell = (mxCell) edge; + if (edgeCell.getValue() instanceof SrcDstAttribute) { + SrcDstAttribute edgeAttr = (SrcDstAttribute) edgeCell.getValue(); + if (srcRes.getOutSideResource().equals(edgeAttr.getSource()) && channel.equals(edgeAttr.getDestination())) { + edgeCell.setValue(dataFlow.getAttribute()); + break; + } } } } @@ -468,17 +573,39 @@ } } - public void addResourcePath(ResourcePath res) { - getModel().addResourcePath(res); + public ResourcePath addResourcePath(ResourcePath parentPath, String resName) { + ResourcePath resourcePath = null; + if (parentPath == null) { + resourcePath = new ResourcePath(resName); + getModel().addResourcePath(resourcePath); + } else { + if (resName.startsWith(Parser.LEFT_CURLY_BRACKET) && resName.endsWith(Parser.RIGHT_CURLY_BRACKET)) { + TokenStream stream = new Parser.TokenStream(); + Parser parser = new Parser(stream); + stream.addLine(resName.substring(1, resName.length() - 1)); + try { + Expression exp = parser.parseTerm(stream, getModel()); + resourcePath = new ResourcePath(parentPath, exp); + getModel().addResourcePath(resourcePath); + } catch (ExpectedRightBracket | WrongJsonExpression | ExpectedColon e) { + e.printStackTrace(); + return null; + } + } else { + resourcePath = new ResourcePath(parentPath, resName); + getModel().addResourcePath(resourcePath); + } + } resetDataFlowGraph(); graph.getModel().beginUpdate(); Object parent = graph.getDefaultParent(); try { - graph.insertVertex(parent, null, res.getResourceName(), 20, 20, 80, 30, + graph.insertVertex(parent, null, resName, 20, 20, 80, 30, "shape=ellipse;perimeter=ellipsePerimeter"); // insert a resource as a vertex } finally { graph.getModel().endUpdate(); } + return resourcePath; } public void addChannel(DataTransferChannel channelGen) { @@ -553,6 +680,7 @@ } public boolean connectEdge(mxCell edge, mxCell src, mxCell dst) { + if (bReflectingArchitectureModel) return false; DataTransferModel model = getModel(); Channel srcCh = model.getChannel((String) src.getValue()); if (srcCh == null) { @@ -648,11 +776,29 @@ } resetDataFlowGraph(); } catch (ExpectedRightBracket | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket - | ExpectedInOrOutOrRefKeyword | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression - | WrongLHSExpression | WrongRHSExpression | ExpectedAssignment e) { + | ExpectedInOrOutOrRefOrSubKeyword | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression + | WrongLHSExpression | WrongRHSExpression | ExpectedAssignment | ExpectedRightCurlyBracket | WrongPathExpression | WrongJsonExpression | ExpectedColon e) { e.printStackTrace(); } } + + public Set> getResourceDependencyForChannel(DataTransferChannel ch, DataTransferModel model, DataFlowGraph dataFlowGraph) { + Set> resourceDpendency = new HashSet<>(); + try { + Map>> dependency = ch.fillOutsideResourcePaths(ch.getOutputChannelMembers().iterator().next(), JavaCodeGenerator.pullAccessor); + for (ChannelMember srcMem: dependency.keySet()) { + ResourceNode srcNode = dataFlowGraph.getResourceNode(srcMem.getResource()); + for (ChannelMember dstMem: dependency.get(srcMem).getValue()) { + ResourceNode dstNode = dataFlowGraph.getResourceNode(dstMem.getResource()); + resourceDpendency.add(new AbstractMap.SimpleEntry<>(srcNode, dstNode)); + } + } + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage + | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + return resourceDpendency; + } private class SrcDstAttribute extends EdgeAttribute { private Object src; @@ -663,7 +809,7 @@ this.dst = dst; } - public Object getSrouce() { + public Object getSource() { return src; } diff --git a/AlgebraicDataflowArchitectureModel/src/generators/CodeGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/CodeGenerator.java new file mode 100644 index 0000000..72f2358 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/generators/CodeGenerator.java @@ -0,0 +1,653 @@ +package generators; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Stack; + +import code.ast.Block; +import code.ast.CompilationUnit; +import code.ast.FieldDeclaration; +import code.ast.MethodDeclaration; +import code.ast.TypeDeclaration; +import code.ast.VariableDeclaration; +import models.Edge; +import models.Node; +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Field; +import models.algebra.Parameter; +import models.algebra.Symbol; +import models.algebra.Term; +import models.algebra.Type; +import models.algebra.Variable; +import models.dataConstraintModel.Channel; +import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.ResourceHierarchy; +import models.dataConstraintModel.ResourcePath; +import models.dataConstraintModel.Selector; +import models.dataFlowModel.ChannelNode; +import models.dataFlowModel.DataFlowEdge; +import models.dataFlowModel.DataFlowGraph; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.PushPullAttribute; +import models.dataFlowModel.PushPullValue; +import models.dataFlowModel.ResourceNode; +import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor; + +/** + * Common generator for prototypes + * + * @author Nitta + * + */ +public abstract class CodeGenerator { + public static final String fieldOfResourceState = "value"; + public static final String getterPrefix = "get"; + public static final String getterOfResourceState = "getValue"; + public static final String updateMethodName = "update"; + private static String mainTypeName = null; + private static ILanguageSpecific langSpec = null; + + public static String getMainTypeName() { + return mainTypeName; + } + + public static void setMainTypeName(String mainTypeName) { + CodeGenerator.mainTypeName = mainTypeName; + } + + public static void resetMainTypeName() { + CodeGenerator.mainTypeName = null; + } + + public static String getComponentName(ResourceHierarchy res, ILanguageSpecific langSpec) { + String name = res.getResourceName(); + if (res.getNumParameters() > 0) { + if (name.length() > 3 && name.endsWith("ies")) { + name = name.substring(0, name.length() - 3) + "y"; + } else if (name.length() > 1 && name.endsWith("s")) { + name = name.substring(0, name.length() - 1); + } else { + name += "Element"; + } + } + return langSpec.toComponentName(name); + } + + public static Type getImplStateType(ResourceHierarchy res, ILanguageSpecific langSpec) { + Set children = res.getChildren(); + if (children == null || children.size() == 0) { + // leaf resource. + return res.getResourceStateType(); + } else { + ResourceHierarchy child = children.iterator().next(); + if (children.size() == 1 && child.getNumParameters() > 0) { + // map or list. + if (DataConstraintModel.typeList.isAncestorOf(res.getResourceStateType())) { + // list. + if (generatesComponent(child)) { + return langSpec.newListType(getComponentName(child, langSpec)); + } else { + return langSpec.newListType(getImplStateType(child, langSpec).getImplementationTypeName()); + } + } else if (DataConstraintModel.typeMap.isAncestorOf(res.getResourceStateType())) { + // map. + if (generatesComponent(child)) { + return langSpec.newMapType(DataConstraintModel.typeString, getComponentName(child, langSpec)); + } else { + return langSpec.newMapType(DataConstraintModel.typeString, getImplStateType(child, langSpec).getImplementationTypeName()); + } + } + return null; + } else { + // class + return res.getResourceStateType(); + } + } + } + + public static boolean generatesComponent(ResourceHierarchy res) { + return res.getParent() == null || !(res.getChildren() == null || res.getChildren().size() == 0); +// Type resType = res.getResourceStateType(); +// return DataConstraintModel.typeJson.isAncestorOf(resType) || DataConstraintModel.typeList.isAncestorOf(resType) || DataConstraintModel.typeMap.isAncestorOf(resType); + } + + /** + * Generate source codes in specified language from data-flow/control-flow graph. + * + * @param model architecture model + * @param flowGraph data-flow or control-flow graph + * @param langSpec specified language + * @return source codes + */ + public ArrayList generateCode(DataTransferModel model, DataFlowGraph flowGraph, ILanguageSpecific langSpec) { + CodeGenerator.langSpec = langSpec; + ArrayList codes = new ArrayList<>(); + + // Get the dependency among root nodes. + Map> dependedRootComponentGraph = getDependedRootComponentGraph(model); + + // Sort the all components. + ArrayList components = determineComponentOrder(flowGraph, dependedRootComponentGraph); + + // Add the main component. + if (mainTypeName == null) { + mainTypeName = langSpec.getMainComponentName(); + } + TypeDeclaration mainComponent = langSpec.newTypeDeclaration(mainTypeName); + MethodDeclaration mainConstructor = mainComponent.createConstructor(); + CompilationUnit mainCU = langSpec.newCompilationUnit(mainComponent); + codes.add(mainCU); + + // Generate the other components. + generateCodeFromFlowGraph(model, flowGraph, components, dependedRootComponentGraph, mainComponent, mainConstructor, codes, langSpec); + + return codes; + } + + public abstract void generateCodeFromFlowGraph(DataTransferModel model, DataFlowGraph flowGraph, ArrayList components, Map> dependedRootComponentGraph, + TypeDeclaration mainComponent, MethodDeclaration mainConstructor, ArrayList codes, ILanguageSpecific langSpec); + + private static Map> getDependedRootComponentGraph(DataTransferModel model) { + Map> dependedComponentGraph = new HashMap<>(); + for (Channel ch: model.getChannels()) { + DataTransferChannel dtCh = (DataTransferChannel) ch; + Set inRes = new HashSet<>(); + Set outRes = new HashSet<>(); + for (ChannelMember cm: dtCh.getChannelMembers()) { + if (cm.isOutside()) { + outRes.add(cm.getResource().getResourceHierarchy()); + } else { + inRes.add(cm.getResource().getResourceHierarchy()); + } + } + if (outRes.size() > 0 && inRes.size() > 0) { + for (ResourceHierarchy out: outRes) { + for (ResourceHierarchy in: inRes) { + Set dependings = dependedComponentGraph.get(out.getRoot()); + if (dependings == null) { + dependings = new HashSet<>(); + dependedComponentGraph.put(out.getRoot(), dependings); + } + dependings.add(in.getRoot()); + } + } + } + } + return dependedComponentGraph; + } + + private static ArrayList determineComponentOrder(DataFlowGraph graph, Map> dependedRootComponentGraph) { + ArrayList objects = new ArrayList<>(); + Set visited = new HashSet<>(); + Collection allNodes = graph.getResourceNodes(); + for (ResourceNode resNode: allNodes) { + topologicalSort(resNode, allNodes, dependedRootComponentGraph, visited, objects); + } + return objects; + } + + private static void topologicalSort(ResourceNode curResNode, Collection allNodes, Map> dependedRootComponentGraph, Set visited, List orderedList) { + if (visited.contains(curResNode)) return; + visited.add(curResNode); + // A caller is before the callee + + // For each incoming PUSH transfer. + for (Edge chToRes: curResNode.getInEdges()) { + for (Edge resToCh: chToRes.getSource().getInEdges()) { + if (!(resToCh instanceof DataFlowEdge) || ((PushPullAttribute)((DataFlowEdge) resToCh).getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { + topologicalSort((ResourceNode) resToCh.getSource(), allNodes, dependedRootComponentGraph, visited, orderedList); + } + } + } + // For each outgoing PULL transfer. + if (curResNode instanceof ResourceNode) { + for (Edge resToCh: curResNode.getOutEdges()) { + DataFlowEdge de = (DataFlowEdge) resToCh; + if (((PushPullAttribute) de.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { + for (Edge chToRes : resToCh.getDestination().getOutEdges()) { + topologicalSort((ResourceNode) chToRes.getDestination(), allNodes, dependedRootComponentGraph, visited, orderedList); + } + } + } + } + // For each depending root node. + if (curResNode instanceof ResourceNode && dependedRootComponentGraph.get(curResNode.getResourceHierarchy()) != null) { + for (ResourceHierarchy dependingRes: dependedRootComponentGraph.get(curResNode.getResourceHierarchy())) { + for (ResourceNode rootNode: allNodes) { + ResourceHierarchy rootRes = rootNode.getResourceHierarchy(); + if (rootRes.getParent() == null && rootRes.equals(dependingRes)) { + topologicalSort(rootNode, allNodes, dependedRootComponentGraph, visited, orderedList); + } + } + } + } + // For each reference resource. + ResourceNode cn = null; + if (curResNode instanceof ResourceNode) { + cn = (ResourceNode) curResNode; + } + if (cn != null) { + for (Node n: allNodes) { + ResourceNode resNode = null; + if (n instanceof ResourceNode) { + resNode = (ResourceNode) n; + } + if (resNode != null) { + for (Edge resToCh: resNode.getOutEdges()) { + ChannelNode chNode = (ChannelNode) resToCh.getDestination(); + for (ChannelMember m: chNode.getChannel().getReferenceChannelMembers()) { + if (m.getResource().equals(curResNode.getOutSideResource())) { + topologicalSort(resNode, allNodes, dependedRootComponentGraph, visited, orderedList); + } + } + } + } + } + } + orderedList.add(0, curResNode); + } + + protected void updateMainComponent(DataTransferModel model, TypeDeclaration mainType, MethodDeclaration mainConstructor, Node componentNode, + final List depends, ILanguageSpecific langSpec) { + // Declare the field to refer to each object in the main type. + ResourceNode resNode = null; + String nodeName = null; + if (componentNode instanceof ResourceNode) { + resNode = (ResourceNode) componentNode; + nodeName = resNode.getResourceName(); + } + String componentName = langSpec.toComponentName(nodeName); + // Declare a field to refer each object. + if (langSpec.declareField()) { + FieldDeclaration refField = langSpec.newFieldDeclaration(new Type(componentName, componentName), nodeName); + mainType.addField(refField); + } + // Add a statement to instantiate each object to the main constructor. + List parameters = new ArrayList<>(); + for (ResourceHierarchy res: depends) { + // For the callee objects (the destination resource of push transfer or the source resource of pull transfer). + parameters.add(res.getResourceName()); + } + // For the refs. + if (resNode != null) { + Set refs = new HashSet<>(); + for (Channel cg : model.getChannels()) { + DataTransferChannel ch = (DataTransferChannel) cg; + if (ch.getInputResources().contains(resNode.getOutSideResource())) { + for (ResourcePath resPath: ch.getReferenceResources()) { + if (!refs.contains(resPath) && !depends.contains(resPath.getResourceHierarchy())) { + refs.add(resPath); + String refResName = resPath.getResourceName(); + parameters.add(refResName); + } + } + } + } + } + + Block mainConstructorBody = mainConstructor.getBody(); + if (mainConstructorBody == null) { + mainConstructorBody = new Block(); + mainConstructor.setBody(mainConstructorBody); + } + mainConstructorBody.addStatement(langSpec.getFieldAccessor(nodeName) + langSpec.getAssignment() + langSpec.getConstructorInvocation(componentName, parameters) + langSpec.getStatementDelimiter()); + } + + protected ResourceHierarchy addReference(TypeDeclaration component, MethodDeclaration constructor, ResourceHierarchy dstRes, ILanguageSpecific langSpec) { + if (!generatesComponent(dstRes)) { + dstRes = dstRes.getParent(); + } + String dstComponentName = getComponentName(dstRes, langSpec); + if (dstComponentName != null) { + String dstNodeName = langSpec.toVariableName(dstComponentName); + if (langSpec.declareField()) { + // Declare a field to refer to another component. + component.addField(langSpec.newFieldDeclaration(new Type(dstComponentName, dstComponentName), dstNodeName)); + } + // Initialize the field to refer to another component. + constructor.addParameter(langSpec.newVariableDeclaration(new Type(dstComponentName, dstComponentName), dstNodeName)); + constructor.getBody().addStatement(langSpec.getFieldAccessor(dstNodeName) + langSpec.getAssignment() + dstNodeName + langSpec.getStatementDelimiter()); + } + return dstRes; + } + + protected void fillStateGetterMethod(MethodDeclaration stateGetter, ResourceHierarchy resourceHierarchy, Type resStateType, ILanguageSpecific langSpec) { + // returns the state field when all incoming data-flow edges are PUSH-style. + if (langSpec.isValueType(resStateType)) { + stateGetter.addStatement(langSpec.getReturnStatement(langSpec.getFieldAccessor(fieldOfResourceState)) + langSpec.getStatementDelimiter()); // return value; + } else { + if (resourceHierarchy.getChildren() != null && resourceHierarchy.getChildren().size() == 1 && resourceHierarchy.getChildren().iterator().next().getNumParameters() > 0) { + // list or map + 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 + 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); + composerSub.setType(DataConstraintModel.typeMap); + for (ResourceHierarchy child: resourceHierarchy.getChildren()) { + String childTypeName = getComponentName(child, langSpec); + String fieldName = langSpec.toVariableName(childTypeName); + Term childGetter = null; + if ((child.getChildren() == null || child.getChildren().size() == 0) && child.getNumParameters() == 0) { + // the child is not a class + childGetter = new Term(new Symbol("get" + childTypeName, 1, Symbol.Type.METHOD)); + childGetter.addChild(new Constant("this")); + } else { + // the child is a class + childGetter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); + childGetter.addChild(new Field(fieldName, getImplStateType(child, langSpec))); + } + composer = new Term(DataConstraintModel.insert); + composer.addChild(composerSub); + composer.addChild(new Constant(langSpec.getStringDelimiter() + fieldName + langSpec.getStringDelimiter(), DataConstraintModel.typeString)); // key + composer.addChild(childGetter); // value + composer.setType(DataConstraintModel.typeMap); + composerSub = composer; + } + composer.setType(stateGetter.getReturnType()); + String[] sideEffects = new String[] {null}; + String returnValue = composer.toImplementation(sideEffects); + if (sideEffects[0] != null) { + stateGetter.addStatement(sideEffects[0] + langSpec.getReturnStatement(returnValue) + langSpec.getStatementDelimiter()); + } else { + stateGetter.addStatement(langSpec.getReturnStatement(returnValue) + langSpec.getStatementDelimiter()); + } + } + } + } + } + + protected void fillChildGetterMethod(MethodDeclaration childGetter, ResourceHierarchy child, Type parentResourceType, ILanguageSpecific langSpec) { + if (DataConstraintModel.typeList.isAncestorOf(parentResourceType)) { + Term selector = new Term(DataConstraintModel.get); + selector.addChild(new Variable(langSpec.getFieldAccessor(fieldOfResourceState))); + selector.addChild(new Variable(childGetter.getParameters().get(childGetter.getParameters().size() - 1).getName())); + selector.setType(childGetter.getReturnType()); + String[] sideEffects = new String[] {null}; + String returnValue = selector.toImplementation(sideEffects); + if (sideEffects[0] != null) childGetter.addStatement(sideEffects[0]); + childGetter.addStatement(langSpec.getReturnStatement(returnValue) + langSpec.getStatementDelimiter()); + } else if (DataConstraintModel.typeMap.isAncestorOf(parentResourceType)) { + Term selector = new Term(DataConstraintModel.lookup); + selector.addChild(new Variable(langSpec.getFieldAccessor(fieldOfResourceState))); + selector.addChild(new Variable(childGetter.getParameters().get(childGetter.getParameters().size() - 1).getName())); + selector.setType(childGetter.getReturnType()); + String[] sideEffects = new String[] {null}; + String returnValue = selector.toImplementation(sideEffects); + if (sideEffects[0] != null) childGetter.addStatement(sideEffects[0]); + childGetter.addStatement(langSpec.getReturnStatement(returnValue) + langSpec.getStatementDelimiter()); + } else { + String fieldName = langSpec.toVariableName(getComponentName(child, langSpec)); + childGetter.addStatement(langSpec.getReturnStatement(langSpec.getFieldAccessor(fieldName)) + langSpec.getStatementDelimiter()); + } + } + + protected void declareAccessorInMainComponent(TypeDeclaration mainComponent, ResourceNode accessRes, MethodDeclaration stateGetter, ILanguageSpecific langSpec) { + List mainGetterParams = new ArrayList<>(); + int v = 1; + for (Selector selector: accessRes.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable var = (Variable) selector.getExpression(); + mainGetterParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + } else if (selector.getExpression() instanceof Term) { + Term var = (Term) selector.getExpression(); + mainGetterParams.add(new VariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + MethodDeclaration accessor = null; + if (mainGetterParams.size() == 0) { + accessor = langSpec.newMethodDeclaration(getterPrefix + getComponentName(accessRes.getResourceHierarchy(), langSpec), getImplStateType(accessRes.getResourceHierarchy(), langSpec)); + } else { + accessor = langSpec.newMethodDeclaration(getterPrefix + getComponentName(accessRes.getResourceHierarchy(), langSpec), false, getImplStateType(accessRes.getResourceHierarchy(), langSpec), mainGetterParams); + } + Block block = new Block(); + Expression getState = getPullAccessor().getDirectStateAccessorFor(accessRes.getOutSideResource(), null); + block.addStatement(langSpec.getReturnStatement(getState.toImplementation(new String[] {null})) + langSpec.getStatementDelimiter()); +// if (stateGetter.getParameters() == null || stateGetter.getParameters().size() == 0) { +// block.addStatement(langSpec.getReturnStatement(langSpec.getMethodInvocation(accessRes.getResourceName(), stateGetter.getName())) + langSpec.getStatementDelimiter()); +// } else { +// List resParams = new ArrayList<>(); +// for (VariableDeclaration var: stateGetter.getParameters()) { +// resParams.add(var.getName()); +// } +// block.addStatement(langSpec.getReturnStatement(langSpec.getMethodInvocation(accessRes.getResourceName(), stateGetter.getName(), resParams)) + langSpec.getStatementDelimiter()); +// } + accessor.setBody(block); + mainComponent.addMethod(accessor); + } + + protected void declareFieldsToReferenceResources(DataTransferModel model, ResourceNode resourceNode, TypeDeclaration component, MethodDeclaration constructor, + final List depends, ILanguageSpecific langSpec) { + Set refs = new HashSet<>(); + for (Channel ch : model.getChannels()) { + DataTransferChannel c = (DataTransferChannel) ch; + if (c.getInputResources().contains(resourceNode.getOutSideResource())) { + for (ResourcePath res: c.getReferenceResources()) { + if (!refs.contains(res) && !depends.contains(res.getResourceHierarchy())) { + refs.add(res); + String refResName = langSpec.toComponentName(res.getResourceName()); + component.addField(langSpec.newFieldDeclaration(new Type(refResName, refResName), res.getResourceName())); + constructor.addParameter(langSpec.newVariableDeclaration(new Type(refResName, refResName), res.getResourceName())); + constructor.getBody().addStatement(langSpec.getFieldAccessor(res.getResourceName()) + langSpec.getAssignment() + res.getResourceName() + langSpec.getStatementDelimiter()); + } + } + } + } + } + + protected MethodDeclaration getConstructor(TypeDeclaration component) { + for (MethodDeclaration m: component.getMethods()) { + if (m.isConstructor()) return m; + } + return null; + } + + protected MethodDeclaration getUpdateMethod(Edge inEdge, TypeDeclaration component, + Map>> dataFlowInform, ILanguageSpecific langSpec) { + List passedResoueces = dataFlowInform.get(inEdge).get(PushPullValue.PUSH); + String methodName = updateMethodName; + for (ResourceNode rn: passedResoueces) { + methodName += langSpec.toComponentName(rn.getResourceName()); + } + return getMethod(component, methodName); + } + + protected MethodDeclaration getInputMethod(ResourceNode resourceNode, DataTransferChannel ch, TypeDeclaration component) { + MethodDeclaration input = null; + for (ChannelMember out : ch.getOutputChannelMembers()) { + if (resourceNode.getInSideResources().contains(out.getResource())) { + Expression message = out.getStateTransition().getMessageExpression(); + if (message instanceof Term) { + input = getMethod(component, ((Term) message).getSymbol().getImplName()); + } else if (message instanceof Variable) { + // Declare an input method in this component. + input = getMethod(component, ((Variable) message).getName()); + } + break; + } + } + return input; + } + + protected MethodDeclaration getMethod(TypeDeclaration component, String methodName) { + for (MethodDeclaration m: component.getMethods()) { + if (m.getName().equals(methodName)) return m; + } + return null; + } + + protected IResourceStateAccessor getPushAccessor() { + return new IResourceStateAccessor() { + @Override + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes= target.getResource(); + ResourcePath fromRes= from.getResource(); + if (targetRes.equals(fromRes)) { + return new Field(fieldOfResourceState, + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + return new Parameter(targetRes.getResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targerRes= target.getResource(); + return new Parameter(targerRes.getResourceName(), + targerRes.getResourceStateType() != null ? targerRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { + if (fromRes != null && targetRes.equals(fromRes)) { + return new Field(fieldOfResourceState, + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + return new Parameter(targetRes.getResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + }; + } + + protected IResourceStateAccessor getPullAccessor() { + return new IResourceStateAccessor() { + @Override + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes= target.getResource(); + if (from != null) { + ResourcePath fromRes= from.getResource(); + if (!target.isOutside()) { + return getDirectStateAccessorFor(targetRes, fromRes); + } + Term getter = null; + String targetComponentName = getComponentName(targetRes.getResourceHierarchy(), langSpec); + if (generatesComponent(targetRes.getResourceHierarchy())) { + getter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); + getter.addChild(new Field(langSpec.toVariableName(targetComponentName), targetRes.getResourceStateType())); + } else { + String parentName = langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy().getParent(), langSpec)); + Type parentType = targetRes.getResourceHierarchy().getParent().getResourceStateType(); + getter = new Term(new Symbol(getterPrefix + targetComponentName, 1, Symbol.Type.METHOD)); + getter.addChild(new Field(parentName, parentType)); + } + return getter; + } else { + return getDirectStateAccessorFor(targetRes, null); + } + } + + @Override + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes= target.getResource(); + if (from != null) { + ResourcePath fromRes= from.getResource(); + if (!target.isOutside()) { + return getDirectStateAccessorFor(targetRes, fromRes); + } + Term getter = null; + String targetComponentName = getComponentName(targetRes.getResourceHierarchy(), langSpec); + if (generatesComponent(targetRes.getResourceHierarchy())) { + getter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); + getter.addChild(new Field(langSpec.toVariableName(targetComponentName), targetRes.getResourceStateType())); + } else { + String parentName = langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy().getParent(), langSpec)); + Type parentType = targetRes.getResourceHierarchy().getParent().getResourceStateType(); + getter = new Term(new Symbol(getterPrefix + targetComponentName, 1, Symbol.Type.METHOD)); + getter.addChild(new Field(parentName, parentType)); + } + return getter; + } else { + return getDirectStateAccessorFor(targetRes, null); + } + } + + @Override + public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { + if (fromRes != null) { + if (targetRes.equals(fromRes)) { + return new Field(fieldOfResourceState, + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + Term getter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); + getter.addChild(new Field(targetRes.getResourceName(), targetRes.getResourceStateType())); + return getter; + } else { + // access from the outside of the hierarchy + Stack pathStack = new Stack<>(); + ResourcePath curPath = targetRes; + do { + pathStack.push(curPath); + curPath = curPath.getParent(); + } while (curPath != null); + // iterate from the root resource + Term getter = null; + int v = 1; + while (!pathStack.empty()) { + curPath = pathStack.pop(); + String typeName = getComponentName(curPath.getResourceHierarchy(), langSpec); + if (getter == null) { + // root resource + String fieldName = langSpec.toVariableName(typeName); + getter = new Field(fieldName, new Type(typeName, typeName)); + } else { + Term newGetter = new Term(new Symbol(getterPrefix + typeName, -1, Symbol.Type.METHOD)); + newGetter.addChild(getter); + if (curPath.getResourceHierarchy().getNumParameters() > 0) { + Variable var = null; + Expression param = curPath.getLastParam(); + if (param instanceof Variable) { + var = (Variable) param; + } else if (param instanceof Term) { + var = new Variable("v" + v, ((Term) param).getType()); + } + if (var != null) { + newGetter.addChild(var); + newGetter.getSymbol().setArity(2); + } + v++; + } + getter = newGetter; + } + } + + if (generatesComponent(targetRes.getResourceHierarchy())) { + Term newGetter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); + newGetter.addChild(getter); + getter = newGetter; + } + return getter; + } + } + }; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java b/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java new file mode 100644 index 0000000..cf3f036 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java @@ -0,0 +1,1215 @@ +package generators; + +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.AbstractMap.SimpleEntry; + +import code.ast.Block; +import code.ast.CompilationUnit; +import code.ast.FieldDeclaration; +import code.ast.MethodDeclaration; +import code.ast.TypeDeclaration; +import code.ast.VariableDeclaration; +import models.Edge; +import models.Node; +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Field; +import models.algebra.InvalidMessage; +import models.algebra.ParameterizedIdentifierIsFutureWork; +import models.algebra.Position; +import models.algebra.Term; +import models.algebra.Type; +import models.algebra.UnificationFailed; +import models.algebra.ValueUndefined; +import models.algebra.Variable; +import models.dataConstraintModel.Channel; +import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.JsonAccessor; +import models.dataConstraintModel.ResourceHierarchy; +import models.dataConstraintModel.ResourcePath; +import models.dataConstraintModel.Selector; +import models.dataFlowModel.DataFlowEdge; +import models.dataFlowModel.DataFlowGraph; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.PushPullAttribute; +import models.dataFlowModel.PushPullValue; +import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork; +import models.dataFlowModel.ResourceNode; +import models.dataFlowModel.ChannelNode; +import models.dataFlowModel.StoreAttribute; +import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor; + +public class CodeGeneratorFromDataFlowGraph extends CodeGenerator { + + public void generateCodeFromFlowGraph(DataTransferModel model, DataFlowGraph flowGraph, ArrayList components, Map> dependedRootComponentGraph, + TypeDeclaration mainComponent, MethodDeclaration mainConstructor, ArrayList codes, ILanguageSpecific langSpec) { + Map resourceComponents = new HashMap<>(); + Map resourceConstructors = new HashMap<>(); + List> constructorParams = new ArrayList<>(); + List> constructorStatements = new ArrayList<>(); + Map> updateStatements = new HashMap<>(); + + // For each components (1st pass). + for (Node componentNode: components) { + ResourceNode resourceNode = (ResourceNode) componentNode; + TypeDeclaration component = null; + if (generatesComponent(resourceNode.getResourceHierarchy())) { + // A component will be generated for this resource. + String resourceName = getComponentName(resourceNode.getResourceHierarchy(), langSpec); + component = resourceComponents.get(resourceNode.getResourceHierarchy()); + List depends = new ArrayList<>(); + if (component == null) { + // Add compilation unit for this component. + component = langSpec.newTypeDeclaration(resourceName); + resourceComponents.put(resourceNode.getResourceHierarchy(), component); + CompilationUnit cu = langSpec.newCompilationUnit(component); + codes.add(cu); + + // Declare the constructor and the fields to refer to other resources. + MethodDeclaration constructor = declareConstructorAndFieldsToReferToResources(resourceNode, component, dependedRootComponentGraph, depends, langSpec); + + if (resourceNode.getResourceHierarchy().getParent() == null) { + // For each root resource + // Update the main component for this component. + updateMainComponent(model, mainComponent, mainConstructor, componentNode, depends, langSpec); + } + + // Declare the fields to refer to reference resources. + declareFieldsToReferenceResources(model, resourceNode, component, constructor, depends, langSpec); + + if (constructor.getParameters() == null) { + component.removeMethod(constructor); + } else { + resourceConstructors.put(resourceNode.getResourceHierarchy(), constructor); + } + } + } + } + + // For each components (2nd pass). + for (Node componentNode: components) { + // Declare this resource. + ResourceNode resourceNode = (ResourceNode) componentNode; + Type resStateType = getImplStateType(resourceNode.getResourceHierarchy(), langSpec); + TypeDeclaration component = null; + TypeDeclaration parentComponent = null; + if (generatesComponent(resourceNode.getResourceHierarchy())) { + String resourceName = getComponentName(resourceNode.getResourceHierarchy(), langSpec); + component = resourceComponents.get(resourceNode.getResourceHierarchy()); + + // Declare the field in this resource to store the state. + if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { + declareStateField(resourceNode, resourceName, component, resStateType, constructorParams, langSpec); + } + + // Declare the getter methods in this resource to obtain the children resources. + declareChildGetterMethod(resourceNode, component, langSpec); + } + if (resourceNode.getResourceHierarchy().getParent() != null) { + parentComponent = resourceComponents.get(resourceNode.getResourceHierarchy().getParent()); + } + + // Declare cache fields and update methods in this resource. + Map.Entry, Map>> initStatementsAndUpdateUpdates = declareCacheFieldsAndUpdateMethods(resourceNode, component, parentComponent, langSpec); + if (component == null) { + // Constructor statements were not added to any component because no component had been generated. + for (String statement: initStatementsAndUpdateUpdates.getKey()) { + constructorStatements.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy().getParent(), statement)); + } + } + for (Map.Entry> entry: initStatementsAndUpdateUpdates.getValue().entrySet()) { + updateStatements.put(entry.getKey(), entry.getValue()); + } + + // Declare the state field and reference fields in the parent component. + if (component == null) { + declareFieldsInParentComponent(resourceNode, parentComponent, constructorParams, langSpec); + } + + // Declare the getter method in this resource to obtain the state. + MethodDeclaration stateGetter = declareStateGetterMethod(resourceNode, component, parentComponent, resStateType, langSpec); + + // Declare the accessor method in the main component to call the getter method. + declareAccessorInMainComponent(mainComponent, resourceNode, stateGetter, langSpec); + + // Declare input methods in this component and the main component. + Map.Entry, Map>> initStatementsAndInputUpdates = declareInputMethodsInThisAndMainComponents(resourceNode, component, parentComponent, mainComponent, model, langSpec); + if (component == null) { + // Constructor statements were not added to any component because no component had been generated. + for (String statement: initStatementsAndInputUpdates.getKey()) { + constructorStatements.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy().getParent(), statement)); + } + } + for (Map.Entry> entry: initStatementsAndInputUpdates.getValue().entrySet()) { + updateStatements.put(entry.getKey(), entry.getValue()); + } + } + + // Add constructor parameters to the ancestor components. + for (ResourceNode root: flowGraph.getRootResourceNodes()) { + addConstructorParameters(root.getResourceHierarchy(), resourceComponents, resourceConstructors, constructorParams, langSpec); + } + + // Add constructor statements. + for (Map.Entry entry: constructorStatements) { + resourceConstructors.get(entry.getKey()).addStatement(entry.getValue()); + } + + // Add update statements. + for (MethodDeclaration method: updateStatements.keySet()) { + Expression updateExp = updateStatements.get(method).getKey(); + ResourceHierarchy childRes = updateStatements.get(method).getValue(); + TypeDeclaration childComponent = resourceComponents.get(childRes); + addUpdateStatementWithConstructorInvocationToMethod(method, updateExp, childRes, childComponent, langSpec); + } + } + + private static List addConstructorParameters(ResourceHierarchy resource, Map resourceComponents, + Map resourceConstructors, List> constructorParams, ILanguageSpecific langSpec) { + List params = new ArrayList<>(); + for (ResourceHierarchy child: resource.getChildren()) { + params.addAll(addConstructorParameters(child, resourceComponents, resourceConstructors, constructorParams, langSpec)); + } + for (Entry paramEnt: constructorParams) { + if (paramEnt.getKey().equals(resource)) { + params.add(paramEnt.getValue()); + } + } + if (params.size() > 0) { + MethodDeclaration constructor = resourceConstructors.get(resource); + if (constructor == null) { + if (resourceComponents.get(resource) != null) { + String resourceName = getComponentName(resource, langSpec); + constructor = new MethodDeclaration(resourceName, true); + Block body = new Block(); + constructor.setBody(body); + resourceComponents.get(resource).addMethod(constructor); + resourceConstructors.put(resource, constructor); + } + } + if (constructor != null) { + for (VariableDeclaration param: params) { + boolean existsParam = false; + if (constructor.getParameters() != null) { + for (VariableDeclaration constParam: constructor.getParameters()) { + if (constParam.getName().equals(param.getName())) { + existsParam = true; + break; + } + } + } + if (!existsParam) { + constructor.addParameter(param); + constructor.getBody().addStatement(langSpec.getFieldAccessor(langSpec.toVariableName(param.getName())) + langSpec.getAssignment() + langSpec.toVariableName(param.getName()) + langSpec.getStatementDelimiter()); + } + } + } + } + if (resource.getNumParameters() > 0) params.clear(); + return params; + } + + private void addUpdateStatementWithConstructorInvocationToMethod(MethodDeclaration method, Expression exp, ResourceHierarchy childRes, TypeDeclaration childComponent, ILanguageSpecific langSpec) { + Type replacedJsonType = childRes.getResourceStateType(); + String replacingClassName = getComponentName(childRes, langSpec); + Map subTerms = ((Term) exp).getSubTerms(Term.class); + Iterator> termEntItr = subTerms.entrySet().iterator(); + while (termEntItr.hasNext()) { + Entry termEnt = termEntItr.next(); + Term jsonTerm = termEnt.getValue(); + if (jsonTerm.getType().equals(replacedJsonType)) { + MethodDeclaration childConstructor = getConstructor(childComponent); + List params = new ArrayList<>(); + for (VariableDeclaration var: childConstructor.getParameters()) { + JsonAccessor jsonMember = new JsonAccessor(DataConstraintModel.dot); + jsonMember.addChild(jsonTerm); + jsonMember.addChild(new Constant("\"" + var.getName() + "\"")); + Expression param = jsonMember.reduce(); + if (param != null) { + params.add(param.toImplementation(new String[] {""})); + } else { + params.add(var.getName()); + } + } + ((Term) exp).replaceSubTerm(termEnt.getKey(), new Constant(langSpec.getConstructorInvocation(replacingClassName, params))); + subTerms = ((Term) exp).getSubTerms(Term.class); + termEntItr = subTerms.entrySet().iterator(); + } + } + String[] sideEffects = new String[] {""}; + String newState = exp.toImplementation(sideEffects); + String updateStatement; + if (exp instanceof Term && ((Term) exp).getSymbol().isImplWithSideEffect()) { + updateStatement = sideEffects[0]; + } else { + updateStatement = sideEffects[0] + langSpec.getFieldAccessor(fieldOfResourceState) + langSpec.getAssignment() + newState + langSpec.getStatementDelimiter(); + } + method.addFirstStatement(updateStatement); + } + + private MethodDeclaration declareConstructorAndFieldsToReferToResources(ResourceNode resourceNode, TypeDeclaration component, Map> dependedRootComponentGraph, + List depends, ILanguageSpecific langSpec) { + // Declare a constructor in each component. + MethodDeclaration constructor = component.createConstructor(); + Block block = new Block(); + constructor.setBody(block); + + // Declare fields in each component. (for data-flow graph) + for (Edge resToCh: resourceNode.getOutEdges()) { + DataTransferChannel ch = ((ChannelNode) resToCh.getDestination()).getChannel(); + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(resourceNode.getResourceHierarchy()) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + // Check if the output resource is outside of the channel scope. + boolean outsideOutputResource = false; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(resourceNode.getOutSideResource().getResourceHierarchy()) && cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + if (((PushPullAttribute) ((DataFlowEdge) resToCh).getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) { + for (Edge chToRes: resToCh.getDestination().getOutEdges()) { + // for PUSH transfer + if (chToRes.getDestination() instanceof ResourceNode) { + ResourceHierarchy dstRes = addReference(component, constructor, ((ResourceNode) chToRes.getDestination()).getOutSideResource().getResourceHierarchy(), langSpec); + if (outsideOutputResource) { + if (dstRes != null && dstRes.getParent() != null) { + // Reference to root resource. + addReference(component, constructor, dstRes.getRoot(), langSpec); + } + } + if (!depends.contains(dstRes)) depends.add(dstRes); + } + } + } + } + for (Edge chToRes: resourceNode.getInEdges()) { + for (Edge resToCh: chToRes.getSource().getInEdges()) { + ResourceHierarchy srcRes = ((ResourceNode) resToCh.getSource()).getOutSideResource().getResourceHierarchy(); + DataTransferChannel ch = ((ChannelNode) resToCh.getDestination()).getChannel(); + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(srcRes) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + // Check if the output resource is outside of the channel scope. + boolean outsideOutputResource = false; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(resourceNode.getOutSideResource().getResourceHierarchy()) && cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + if ((((PushPullAttribute) ((DataFlowEdge) resToCh).getAttribute()).getOptions().get(0) != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { + // for PULL transfer + srcRes = addReference(component, constructor, ((ResourceNode) resToCh.getSource()).getOutSideResource().getResourceHierarchy(), langSpec); + if (outsideInputResource) { + if (srcRes != null & srcRes.getParent() != null) { + // Reference to root resource. + addReference(component, constructor, srcRes.getRoot(), langSpec); + } + } + if (!depends.contains(srcRes)) depends.add(srcRes); + } + } + } + // Declare a field to refer to outside resources. + if (resourceNode.getParent() == null) { + for (ResourceHierarchy dependedRes: dependedRootComponentGraph.keySet()) { + for (ResourceHierarchy dependingRes: dependedRootComponentGraph.get(dependedRes)) { + if (resourceNode.getResourceHierarchy().equals(dependingRes)) { + // Declare a field to refer to outside resources. + depends.add(dependedRes); + addReference(component, constructor, dependedRes, langSpec); + } + } + } + } + return constructor; + } + + private void declareStateField(ResourceNode resourceNode, String resourceName, TypeDeclaration component, Type resStateType, List> constructorParams, ILanguageSpecific langSpec) { + Set children = resourceNode.getResourceHierarchy().getChildren(); + if (children == null || children.size() == 0) { + // leaf resource. + FieldDeclaration stateField = langSpec.newFieldDeclaration(resStateType, fieldOfResourceState, langSpec.getFieldInitializer(resStateType, resourceNode.getResourceHierarchy().getInitialValue())); + component.addField(stateField); + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy(), langSpec.newVariableDeclaration(resStateType, langSpec.toVariableName(resourceName)))); + } else { + ResourceHierarchy child = children.iterator().next(); + if (children.size() == 1 && child.getNumParameters() > 0) { + // map or list. + FieldDeclaration stateField = langSpec.newFieldDeclaration(resStateType, fieldOfResourceState, langSpec.getFieldInitializer(resStateType, resourceNode.getResourceHierarchy().getInitialValue())); + component.addField(stateField); + } else { + // class + for (ResourceHierarchy c: children) { + String childTypeName = getComponentName(c, langSpec); + Type childType = null; + if (generatesComponent(c)) { + // The child has a component. + childType = new Type(childTypeName, childTypeName); + String fieldName = langSpec.toVariableName(childTypeName); + FieldDeclaration stateField = langSpec.newFieldDeclaration(childType, fieldName, langSpec.getFieldInitializer(resStateType, resourceNode.getResourceHierarchy().getInitialValue())); + component.addField(stateField); + } + } + } + } + } + + private void declareFieldsInParentComponent(ResourceNode resourceNode, TypeDeclaration parentComponent, List> constructorParams, ILanguageSpecific langSpec) { + // Declare reference fields for push/pull data transfer. + boolean noPullTransfer = true; + for (Edge resToCh : resourceNode.getOutEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + for (Edge chToRes: re.getDestination().getOutEdges()) { + ResourcePath dstRes = ((ResourceNode) chToRes.getDestination()).getOutSideResource(); + // Check if the destination resource is outside of the channel scope. + boolean outsideOutputResource = false; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(dstRes.getResourceHierarchy()) && cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + if (outsideOutputResource) { + // Declare a field in the parent component to refer to the destination resource of push transfer. + String dstResName = null; + if (!generatesComponent(dstRes.getResourceHierarchy())) { + dstRes = dstRes.getParent(); + } + dstResName = getComponentName(dstRes.getResourceHierarchy(), langSpec); + FieldDeclaration refFieldForPush = langSpec.newFieldDeclaration(new Type(dstResName, dstResName), langSpec.toVariableName(dstResName)); + parentComponent.addField(refFieldForPush); + if (dstRes.getParent() != null) { + // Reference to root resource. + String dstRootResName = getComponentName(dstRes.getRoot().getResourceHierarchy(), langSpec); + Type dstRootResType = new Type(dstRootResName, dstRootResName); + dstRootResName = langSpec.toVariableName(dstRootResName); + FieldDeclaration refRootFieldForPush = langSpec.newFieldDeclaration(dstRootResType, dstRootResName); + boolean existsField = false; + for (FieldDeclaration field: parentComponent.getFields()) { + if (dstRootResName.equals(field.getName())) { + existsField = true; + break; + } + } + if (!existsField) { + parentComponent.addField(refRootFieldForPush); + } + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), langSpec.newVariableDeclaration(dstRootResType, dstRootResName))); + } + } + } + } + for (Edge chToRes : resourceNode.getInEdges()) { + for (Edge resToCh: chToRes.getSource().getInEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(); + DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + // Check if the source resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(srcRes.getResourceHierarchy()) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + if (outsideInputResource) { + // Declare a field in the parent component to refer to the source resource of pull transfer. + String srcResName = null; + if (!generatesComponent(srcRes.getResourceHierarchy())) { + srcRes = srcRes.getParent(); + } + srcResName = getComponentName(srcRes.getResourceHierarchy(), langSpec); + FieldDeclaration refFieldForPull = langSpec.newFieldDeclaration(new Type(srcResName, srcResName), langSpec.toVariableName(srcResName)); + parentComponent.addField(refFieldForPull); + if (srcRes.getParent() != null) { + // Reference to root resource. + String srcRootResName = getComponentName(srcRes.getRoot().getResourceHierarchy(), langSpec); + Type srcRootResType = new Type(srcRootResName, srcRootResName); + srcRootResName = langSpec.toVariableName(srcRootResName); + FieldDeclaration refRootFieldForPull = langSpec.newFieldDeclaration(srcRootResType, srcRootResName); + boolean existsField = false; + for (FieldDeclaration field: parentComponent.getFields()) { + if (srcRootResName.equals(field.getName())) { + existsField = true; + break; + } + } + if (!existsField) { + parentComponent.addField(refRootFieldForPull); + } + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), langSpec.newVariableDeclaration(srcRootResType, srcRootResName))); + } + noPullTransfer = false; + } + } + } + // Declare the state field in the parent component. + ResourceHierarchy res = resourceNode.getOutSideResource().getResourceHierarchy(); + if (((StoreAttribute) resourceNode.getAttribute()).isStored() && noPullTransfer && res.getNumParameters() == 0) { + String resName = getComponentName(res, langSpec); + FieldDeclaration stateField = langSpec.newFieldDeclaration(res.getResourceStateType(), langSpec.toVariableName(resName)); + parentComponent.addField(stateField); + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), langSpec.newVariableDeclaration(res.getResourceStateType(), langSpec.toVariableName(resName)))); + } + } + + private MethodDeclaration declareStateGetterMethod(ResourceNode resourceNode, TypeDeclaration component, TypeDeclaration parentComponent, Type resStateType, ILanguageSpecific langSpec) { + // Declare the getter method of the resource state. + MethodDeclaration stateGetter = null; + if (component != null) { + // A component is created for this resource. + stateGetter = langSpec.newMethodDeclaration(getterOfResourceState, resStateType); + component.addMethod(stateGetter); + } else { + // No component is created for this resource. + List getterParams = new ArrayList<>(); + int v = 1; + for (Selector param: resourceNode.getSelectors()) { + if (param.getExpression() instanceof Variable) { + Variable var = (Variable) param.getExpression(); + getterParams.add(new VariableDeclaration(var.getType(), var.getName())); + } else if (param.getExpression() instanceof Term) { + Term var = (Term) param.getExpression(); + getterParams.add(new VariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + String resCompName = getComponentName(resourceNode.getResourceHierarchy(), langSpec); + if (getterParams.size() == 0) { + stateGetter = new MethodDeclaration(getterPrefix + resCompName, resStateType); + } else { + stateGetter = new MethodDeclaration(getterPrefix + resCompName, false, resStateType, getterParams); + } + if (parentComponent != null) { + parentComponent.addMethod(stateGetter); + } + } + + if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { + if (component != null) { + // A component is created for this resource. + fillStateGetterMethod(stateGetter, resourceNode.getResourceHierarchy(), resStateType, langSpec); + } else { + // No component is created for this resource. + fillChildGetterMethod(stateGetter, resourceNode.getResourceHierarchy(), resourceNode.getResourceHierarchy().getParent().getResourceStateType(), langSpec); + } + } else { + // invocations to other getter methods when at least one incoming data-flow edges is PULL-style. + 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 (cm.getResource().equals(((ResourceNode) dIn.getSource()).getOutSideResource())) { + in = cm; + break; + } + } + if (((PushPullAttribute) dIn.getAttribute()).getOptions().get(0) == 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. + } + } + } + // for reference channel members. + for (ChannelMember c: ch.getReferenceChannelMembers()) { + inputResourceToStateAccessor.put(c, getPullAccessor()); // by pull data transfer + } + + // generate a return statement. + 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()).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).toImplementation(sideEffects); + stateGetter.addStatement(sideEffects[0] + langSpec.getReturnStatement(curState) + langSpec.getStatementDelimiter()); + } + break; + } + } + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork + | InvalidMessage | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + } + + return stateGetter; + } + + private void declareChildGetterMethod(ResourceNode resourceNode, TypeDeclaration component, ILanguageSpecific langSpec) { + // Declare the getter methods in this resource to obtain the children resources. + for (ResourceNode child: resourceNode.getChildren()) { + if (generatesComponent(child.getResourceHierarchy())) { + // A component for the child is generated. + List params = new ArrayList<>(); + int v = 1; + for (Selector param: child.getSelectors()) { + if (param.getExpression() instanceof Variable) { + Variable var = (Variable) param.getExpression(); + params.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + } else if (param.getExpression() instanceof Term) { + Term var = (Term) param.getExpression(); + params.add(langSpec.newVariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + String childCompName = getComponentName(child.getResourceHierarchy(), langSpec); + Type childType = new Type(childCompName, childCompName); + MethodDeclaration childGetter = null; + if (params.size() == 0) { + childGetter = langSpec.newMethodDeclaration(getterPrefix + childCompName, childType); + } else { + childGetter = langSpec.newMethodDeclaration(getterPrefix + childCompName, false, childType, params); + } + + fillChildGetterMethod(childGetter, child.getResourceHierarchy(), resourceNode.getResourceStateType(), langSpec); + component.addMethod(childGetter); + } + } + } + + private Map.Entry, Map>> declareCacheFieldsAndUpdateMethods(ResourceNode resourceNode, TypeDeclaration component, TypeDeclaration parentComponent, ILanguageSpecific langSpec) { + // Declare cash fields and update methods in the component. + String resComponentName = langSpec.toComponentName(resourceNode.getResourceName()); + List constructorStatements = new ArrayList<>(); + Map> updateStatements = new HashMap<>(); + for (Edge chToRes: resourceNode.getInEdges()) { + for (Edge resToCh: chToRes.getSource().getInEdges()) { + DataTransferChannel ch = ((ChannelNode) resToCh.getDestination()).getChannel(); + DataFlowEdge re = (DataFlowEdge) resToCh; + ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(); + String srcResName = srcRes.getResourceName(); + String srcResComponentName = langSpec.toComponentName(srcResName); + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(srcRes.getResourceHierarchy()) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) { + // for push data transfer + + // Declare an update method in the type of the destination resource. + ArrayList vars = new ArrayList<>(); + vars.add(langSpec.newVariableDeclaration(srcRes.getResourceStateType(), srcRes.getResourceName())); + // For the refs. + for (ResourcePath ref: ch.getReferenceResources()) { + if (!resourceNode.getInSideResources().contains(ref)) { + vars.add(langSpec.newVariableDeclaration(ref.getResourceStateType(), ref.getResourceName())); + } + } + MethodDeclaration update = langSpec.newMethodDeclaration(updateMethodName + srcResComponentName, false, null, vars); + if (component != null) { + component.addMethod(update); + } else if (parentComponent != null) { + parentComponent.addMethod(update); + } + + // Add a statement to update the state field + if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { + try { + for (ChannelMember out: ch.getOutputChannelMembers()) { + if (resourceNode.getInSideResources().contains(out.getResource())) { + Expression updateExp = ch.deriveUpdateExpressionOf(out, getPushAccessor()); + // Replace Json constructor with a constructor of the child resource. + ResourceHierarchy outRes = out.getResource().getResourceHierarchy(); + if (outRes.getChildren().size() == 1 && outRes.getChildren().iterator().next().getNumParameters() > 0) { + ResourceHierarchy childRes = outRes.getChildren().iterator().next(); + Type childStateType = childRes.getResourceStateType(); + if (DataConstraintModel.typeJson.isAncestorOf(childStateType)) { + updateStatements.put(update, new AbstractMap.SimpleEntry<>(updateExp, childRes)); + break; + } + } + // Add statements to the input method. + String[] sideEffects = new String[] {""}; + String curState = updateExp.toImplementation(sideEffects); + String updateStatement; + if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { + updateStatement = sideEffects[0]; + } else { + updateStatement = sideEffects[0] + langSpec.getFieldAccessor(fieldOfResourceState) + langSpec.getAssignment() + curState + langSpec.getStatementDelimiter(); // this.value = ... + } + if (update.getBody() == null || !update.getBody().getStatements().contains(updateStatement)) { + update.addFirstStatement(updateStatement); + } + break; + } + } + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork + | InvalidMessage | UnificationFailed | ValueUndefined e1) { + e1.printStackTrace(); + } + } + + // Declare the field to cache the state of the source resource in the type of the destination resource. + if (resToCh.getDestination().getIndegree() > 1) { + // If incoming edges are multiple + if (langSpec.declareField()) { + // Declare the cache field. + FieldDeclaration cacheField = langSpec.newFieldDeclaration( + srcRes.getResourceStateType(), + srcRes.getResourceName(), + langSpec.getFieldInitializer(srcRes.getResourceStateType(), srcRes.getResourceHierarchy().getInitialValue())); + if (component != null) { + component.addField(cacheField); + } else if (parentComponent != null){ + parentComponent.addField(cacheField); + } + + } + // Update the cache field. + String cashStatement = langSpec.getFieldAccessor(langSpec.toVariableName(srcResName)) + langSpec.getAssignment() + langSpec.toVariableName(srcResName) + langSpec.getStatementDelimiter(); + if (update.getBody() == null || !update.getBody().getStatements().contains(cashStatement)) { + update.addFirstStatement(cashStatement); + } + } + + Set outsideInputMembers = new HashSet<>(); + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.isOutside()) { + outsideInputMembers.add(cm); + } + } + if (outsideInputMembers.size() > 0) { + Map>> resourcePaths = null; + for (ChannelMember out: ch.getOutputChannelMembers()) { + if (resourceNode.getInSideResources().contains(out.getResource())) { + try { + resourcePaths = ch.fillOutsideResourcePaths(out, getPullAccessor()); + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork + | InvalidMessage | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + break; + } + } + if (resourcePaths != null && resourcePaths.size() > 0) { + for (ChannelMember outsideMember: outsideInputMembers) { + for (ChannelMember dependingMember: resourcePaths.get(outsideMember).getValue()) { + if (dependingMember.getResource().equals(srcRes)) { + // An outside input resource path depends on srcRes. + ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey(); + String outsideResName = langSpec.toVariableName(getComponentName(outsidePath.getResourceHierarchy(), langSpec)); + Expression outsideExp = getPullAccessor().getDirectStateAccessorFor(outsidePath, null); + if (generatesComponent(outsidePath.getResourceHierarchy())) { + outsideExp = ((Term) outsideExp).getChild(0); + } + Expression nextExp = dependingMember.getStateTransition().getNextStateExpression(); + if (nextExp != null && outsideExp instanceof Term) { + if (nextExp instanceof Variable) { + outsideExp = ((Term) outsideExp).substitute((Variable) nextExp, new Field(langSpec.toVariableName(getComponentName(dependingMember.getResource().getResourceHierarchy(), langSpec)))); + } else { + // ToDo. + } + } + String[] sideEffects = new String[] {""}; + String outsideAccessor = outsideExp.toImplementation(sideEffects); + String updateReference = langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter(); + update.addStatement(updateReference); // Update the reference field. + // Update constructor. + if (component != null) { + MethodDeclaration constructor = getConstructor(component); + constructor.addStatement(updateReference); // Initialize the reference field. + } else if (parentComponent != null){ + constructorStatements.add(updateReference); + } + } + } + } + } + } + + // Add an invocation to another update method (for a chain of update method invocations). + for (Edge resToCh2: resourceNode.getOutEdges()) { + DataFlowEdge dOut = (DataFlowEdge) resToCh2; + DataTransferChannel ch2 = ((ChannelNode) resToCh2.getDestination()).getChannel(); + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource2 = false; + Set outsideInputMembers2 = new HashSet<>(); + for (ChannelMember cm: ch2.getInputChannelMembers()) { + if (cm.isOutside()) { + outsideInputMembers2.add(cm); + if (cm.getResource().getResourceHierarchy().equals(resourceNode.getResourceHierarchy())) { + outsideInputResource2 = true; // Regarded as pull transfer. + } + } + } + if (((PushPullAttribute) dOut.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource2) { + for (Edge chToRes2: resToCh2.getDestination().getOutEdges()) { + // PUSH transfer + ChannelMember in = null; + for (ChannelMember cm: ch2.getInputChannelMembers()) { + if (cm.getResource().equals(resourceNode.getOutSideResource())) { + in = cm; + break; + } + } + Map> referredResources = new HashMap<>(); + List params = new ArrayList<>(); + params.add(langSpec.getFieldAccessor(fieldOfResourceState)); + Set referredSet = referredResources.get(update); + for (ChannelMember rc: ch2.getReferenceChannelMembers()) { + // to get the value of reference member. + ResourcePath ref = rc.getResource(); + if (referredSet == null) { + referredSet = new HashSet<>(); + referredResources.put(update, referredSet); + } + if (!resourceNode.getInSideResources().contains(ref)) { + String refVarName = ref.getResourceName(); + if (!referredSet.contains(ref)) { + referredSet.add(ref); + Expression refGetter = getPullAccessor().getCurrentStateAccessorFor(rc, in); + String[] sideEffects = new String[] {""}; + String refExp = refGetter.toImplementation(sideEffects); + String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); + update.addFirstStatement(sideEffects[0] + langSpec.getVariableDeclaration(refTypeName, refVarName) + langSpec.getAssignment() + refExp + langSpec.getStatementDelimiter()); + } + params.add(refVarName); + } + } + update.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(((ResourceNode) chToRes2.getDestination()).getResourceName()), + updateMethodName + resComponentName, + params) + langSpec.getStatementDelimiter()); // this.dst.updateSrc(value, refParams); + } + } + if (outsideInputMembers2.size() > 0) { + if (!generatesComponent(resourceNode.getResourceHierarchy())) { + // srcRes2 does not have a component. + ResourcePath srcRes2 = resourceNode.getOutSideResource(); + for (ChannelMember out: ch2.getOutputChannelMembers()) { + if (!generatesComponent(out.getResource().getResourceHierarchy())) { + // Also dstRes2 does not have a component. + ResourcePath dstRes2 = out.getResource(); + if (srcRes2.getParent().equals(dstRes2.getParent())) { + Map>> resourcePaths = null; + try { + resourcePaths = ch2.fillOutsideResourcePaths(out, getPullAccessor()); + if (resourcePaths != null && resourcePaths.size() > 0) { + for (ChannelMember outsideMember: outsideInputMembers2) { + for (ChannelMember dependingMember: resourcePaths.get(outsideMember).getValue()) { + if (dependingMember.getResource().equals(srcRes2)) { + // An outside input resource path depends on srcRes. + ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey(); + if (!generatesComponent(outsidePath.getResourceHierarchy())) { + outsidePath = outsidePath.getParent(); + } + String outsideResName = langSpec.toVariableName(getComponentName(outsidePath.getResourceHierarchy(), langSpec)); + Expression outsideExp = getPullAccessor().getDirectStateAccessorFor(outsidePath, null); + if (generatesComponent(outsidePath.getResourceHierarchy())) { + outsideExp = ((Term) outsideExp).getChild(0); + } + Expression nextExp = dependingMember.getStateTransition().getNextStateExpression(); + if (nextExp != null && outsideExp instanceof Term) { + if (nextExp instanceof Variable) { + outsideExp = ((Term) outsideExp).substitute((Variable) nextExp, new Field(langSpec.toVariableName(getComponentName(dependingMember.getResource().getResourceHierarchy(), langSpec)))); + } else { + // ToDo. + } + } + String[] sideEffects = new String[] {""}; + String outsideAccessor = outsideExp.toImplementation(sideEffects); + String updateReference = langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter(); + update.addStatement(updateReference); // Update the reference field. + // Update constructor. + if (component != null) { + MethodDeclaration constructor = getConstructor(component); + constructor.addStatement(updateReference); // Initialize the reference field. + } else if (parentComponent != null) { + constructorStatements.add(updateReference); + } + } + } + } + } + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork + | InvalidMessage | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + } + } + } + } + } + } + } + } + } + return new AbstractMap.SimpleEntry<>(constructorStatements, updateStatements); + } + + private Map.Entry, Map>> declareInputMethodsInThisAndMainComponents(ResourceNode resourceNode, TypeDeclaration component, + TypeDeclaration parentComponent, TypeDeclaration mainComponent, DataTransferModel model, ILanguageSpecific langSpec) { + // Declare input methods. + String resName = resourceNode.getResourceName(); + String resComponentName = langSpec.toComponentName(resName); + List constructorStatements = new ArrayList<>(); + Map> inputStatements = new HashMap<>(); + for (Channel ch : model.getIOChannels()) { + for (ChannelMember out : ((DataTransferChannel) ch).getOutputChannelMembers()) { + if (resourceNode.getInSideResources().contains(out.getResource())) { + Expression message = out.getStateTransition().getMessageExpression(); + MethodDeclaration input = null; + MethodDeclaration inputAccessor = null; + if (message instanceof Term) { + // Declare an input method in this component. + ArrayList resInputParams = new ArrayList<>(); + ArrayList mainInputParams = new ArrayList<>(); + int v = 1; + if (out.getResource().getLastParam() != null) { + Expression param = out.getResource().getLastParam(); + if (param instanceof Variable) { + Variable var = (Variable) param; + resInputParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + } else if (param instanceof Term) { + Term var = (Term) param; + resInputParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v)); + mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + if (out.getResource().getParent() != null) { + for (Expression param: out.getResource().getParent().getPathParams()) { + if (param instanceof Variable) { + Variable var = (Variable) param; + mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + } else if (param instanceof Term) { + Term var = (Term) param; + mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + } + for (Variable var: message.getVariables().values()) { + resInputParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + } + input = langSpec.newMethodDeclaration(((Term) message).getSymbol().getImplName(), false, null, resInputParams); + if (component != null) { + // A component is created for this resource. + component.addMethod(input); + } else if (parentComponent != null) { + // No component is created for this resource. + parentComponent.addMethod(input); + } + + // Declare the accessor in the main component to call the input method. + String str = ((Term) message).getSymbol().getImplName(); + inputAccessor = getMethod(mainComponent, str); + if (inputAccessor == null) { + inputAccessor = langSpec.newMethodDeclaration(str, false, null, mainInputParams); + mainComponent.addMethod(inputAccessor); + } else { + // Add type to a parameter without type. + if (inputAccessor.getParameters() != null) { + for (VariableDeclaration param: inputAccessor.getParameters()) { + if (param.getType() == null) { + for (VariableDeclaration p: mainInputParams) { + if (param.getName().equals(p.getName()) && p.getType() != null) { + param.setType(p.getType()); + } + } + } + } + } + } + } else if (message instanceof Variable) { + // Declare an input method in this component. + ArrayList resInputParams = new ArrayList<>(); + ArrayList mainInputParams = new ArrayList<>(); + int v = 1; + if (out.getResource().getLastParam() != null) { + Expression param = out.getResource().getLastParam(); + if (param instanceof Variable) { + Variable var = (Variable) param; + resInputParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + } else if (param instanceof Term) { + Term var = (Term) param; + resInputParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v)); + mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + if (out.getResource().getParent() != null) { + for (Expression param: out.getResource().getParent().getPathParams()) { + if (param instanceof Variable) { + Variable var = (Variable) param; + mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + } else if (param instanceof Term) { + Term var = (Term) param; + mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + } + if (resInputParams.size() == 0) { + input = langSpec.newMethodDeclaration(((Variable) message).getName(), null); + } else { + input = langSpec.newMethodDeclaration(((Variable) message).getName(), false, null, resInputParams); + } + if (component != null) { + // A component is created for this resource. + component.addMethod(input); + } else if (parentComponent != null) { + // No component is created for this resource. + parentComponent.addMethod(input); + } + + // Declare the accessor in the main component to call the input method. + String str = ((Variable) message).getName(); + inputAccessor = getMethod(mainComponent, str); + if (inputAccessor == null) { + if (mainInputParams.size() == 0) { + inputAccessor = langSpec.newMethodDeclaration(str, null); + } else { + inputAccessor = langSpec.newMethodDeclaration(str, false, null, mainInputParams); + } + mainComponent.addMethod(inputAccessor); + } + } + + // Add an invocation to the accessor method. + if (inputAccessor != null) { + Expression resExp = getPullAccessor().getDirectStateAccessorFor(out.getResource(), null); + 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[] {null}); + if (message instanceof Term) { + for (Variable var: message.getVariables().values()) { + args.add(var.getName()); + } + } + inputAccessor.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 { + Expression updateExp = ((DataTransferChannel) ch).deriveUpdateExpressionOf(out, getPullAccessor()); + // Replace Json constructor with a constructor of the child resource. + ResourceHierarchy outRes = out.getResource().getResourceHierarchy(); + if (outRes.getChildren().size() == 1 && outRes.getChildren().iterator().next().getNumParameters() > 0) { + ResourceHierarchy childRes = outRes.getChildren().iterator().next(); + Type childStateType = childRes.getResourceStateType(); + if (DataConstraintModel.typeJson.isAncestorOf(childStateType)) { + inputStatements.put(input, new AbstractMap.SimpleEntry<>(updateExp, childRes)); + updateExp = null; + } + } + // Add statements to the input method. + if (updateExp != null) { + String[] sideEffects = new String[] {""}; + String newState = updateExp.toImplementation(sideEffects); + ResourceHierarchy resource = resourceNode.getResourceHierarchy(); + if (generatesComponent(resource)) { + String updateStatement; + if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { + updateStatement = sideEffects[0]; + } else { + updateStatement = sideEffects[0] + langSpec.getFieldAccessor(fieldOfResourceState) + langSpec.getAssignment() + newState + langSpec.getStatementDelimiter(); + } + input.addFirstStatement(updateStatement); + } else { + String updateStatement = null; + if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { + // ToDo. + updateStatement = sideEffects[0]; + } else { + if (DataConstraintModel.typeList.isAncestorOf(resourceNode.getParent().getResourceStateType())) { + Term selector = new Term(DataConstraintModel.set); + selector.addChild(new Constant(langSpec.getFieldAccessor(fieldOfResourceState))); + selector.addChild(new Variable(input.getParameters().get(input.getParameters().size() - 2).getName())); + selector.addChild(new Constant(newState)); + String[] sideEffects2 = new String[] {""}; + String newList = selector.toImplementation(sideEffects2); + updateStatement = sideEffects[0] + sideEffects2[0]; + } else if (DataConstraintModel.typeMap.isAncestorOf(resourceNode.getParent().getResourceStateType())) { + Term selector = new Term(DataConstraintModel.insert); + selector.addChild(new Constant(langSpec.getFieldAccessor(fieldOfResourceState))); + selector.addChild(new Variable(input.getParameters().get(input.getParameters().size() - 2).getName())); + selector.addChild(new Constant(newState)); + String[] sideEffects2 = new String[] {""}; + String newMap = selector.toImplementation(sideEffects2); + updateStatement = sideEffects[0] + sideEffects2[0]; + } else { + String resourceName = langSpec.toVariableName(getComponentName(resource, langSpec)); + updateStatement = sideEffects[0] + langSpec.getFieldAccessor(resourceName) + langSpec.getAssignment() + newState + langSpec.getStatementDelimiter(); + } + if (updateStatement != null) { + input.addFirstStatement(updateStatement); + } + } + } + } + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork + | InvalidMessage | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + + // Add an invocation to an update method (for a chain of update method invocations). + for (Edge resToCh: resourceNode.getOutEdges()) { + DataFlowEdge dOut = (DataFlowEdge) resToCh; + DataTransferChannel ch2 = ((ChannelNode) resToCh.getDestination()).getChannel(); + boolean outsideInputResource2 = false; + Set outsideInputMembers2 = new HashSet<>(); + for (ChannelMember cm: ch2.getInputChannelMembers()) { + if (cm.isOutside()) { + outsideInputMembers2.add(cm); + if (cm.getResource().getResourceHierarchy().equals(resourceNode.getResourceHierarchy())) { + outsideInputResource2 = true; // Regarded as pull transfer. + } + } + } + if (((PushPullAttribute) dOut.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource2) { + for (Edge chToRes: resToCh.getDestination().getOutEdges()) { + // PUSH transfer + ChannelMember in = null; + for (ChannelMember cm: ch2.getInputChannelMembers()) { + if (cm.getResource().equals(resourceNode.getOutSideResource())) { + in = cm; + break; + } + } + Map> referredResources = new HashMap<>(); + List params = new ArrayList<>(); + params.add(langSpec.getFieldAccessor(fieldOfResourceState)); + Set referredSet = referredResources.get(input); + for (ChannelMember rc: ch2.getReferenceChannelMembers()) { + // to get the value of reference member. + ResourcePath ref = rc.getResource(); + if (referredSet == null) { + referredSet = new HashSet<>(); + referredResources.put(input, referredSet); + } + if (!ref.equals(resourceNode.getOutSideResource())) { + String refVarName = ref.getResourceName(); + if (!referredSet.contains(ref)) { + referredSet.add(ref); + Expression refGetter = getPullAccessor().getCurrentStateAccessorFor(rc, in); + String[] sideEffects = new String[] {""}; + String refExp = refGetter.toImplementation(sideEffects); + String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); + input.addFirstStatement(sideEffects[0] + langSpec.getVariableDeclaration(refTypeName, refVarName) + langSpec.getAssignment() + refExp + langSpec.getStatementDelimiter()); + } + params.add(refVarName); + } + } + input.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(((ResourceNode) chToRes.getDestination()).getResourceName()), + updateMethodName + resComponentName, + params) + langSpec.getStatementDelimiter()); // this.dst.updateSrc(value, refParams); + } + } + if (outsideInputMembers2.size() > 0) { + if (!generatesComponent(resourceNode.getResourceHierarchy())) { + ResourcePath srcRes2 = resourceNode.getOutSideResource(); + for (ChannelMember out2: ch2.getOutputChannelMembers()) { + if (!generatesComponent(out2.getResource().getResourceHierarchy())) { + ResourcePath dstRes2 = out2.getResource(); + if (srcRes2.getParent().equals(dstRes2.getParent())) { + Map>> resourcePaths = null; + try { + resourcePaths = ch2.fillOutsideResourcePaths(out2, getPullAccessor()); + if (resourcePaths != null && resourcePaths.size() > 0) { + for (ChannelMember outsideMember: outsideInputMembers2) { + for (ChannelMember dependingMember: resourcePaths.get(outsideMember).getValue()) { + if (dependingMember.getResource().equals(srcRes2)) { + // An outside input resource path depends on srcRes. + ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey(); + if (!generatesComponent(outsidePath.getResourceHierarchy())) { + outsidePath = outsidePath.getParent(); + } + String outsideResName = langSpec.toVariableName(getComponentName(outsidePath.getResourceHierarchy(), langSpec)); + Expression outsideExp = getPullAccessor().getDirectStateAccessorFor(outsidePath, null); + if (generatesComponent(outsidePath.getResourceHierarchy())) { + outsideExp = ((Term) outsideExp).getChild(0); + } + Expression nextExp = dependingMember.getStateTransition().getNextStateExpression(); + if (nextExp != null && outsideExp instanceof Term) { + if (nextExp instanceof Variable) { + outsideExp = ((Term) outsideExp).substitute((Variable) nextExp, new Field(langSpec.toVariableName(getComponentName(dependingMember.getResource().getResourceHierarchy(), langSpec)))); + } else { + // ToDo. + } + } + String[] sideEffects = new String[] {""}; + String outsideAccessor = outsideExp.toImplementation(sideEffects); + input.addStatement(langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter()); // change the reference field. + // Update constructor. + String initializingStatement = langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter(); + if (component != null) { + MethodDeclaration constructor = getConstructor(component); + constructor.addStatement(initializingStatement); // initialize the reference field. + } else { + constructorStatements.add(initializingStatement); // initialize the reference field. + } + } + } + } + } + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork + | InvalidMessage | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + } + } + } + } + } + } + } + } + } + } + return new AbstractMap.SimpleEntry<>(constructorStatements, inputStatements); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/generators/DataTransferMethodAnalyzer.java b/AlgebraicDataflowArchitectureModel/src/generators/DataTransferMethodAnalyzer.java index 8e68103..518ec7f 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/DataTransferMethodAnalyzer.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/DataTransferMethodAnalyzer.java @@ -15,7 +15,7 @@ * */ public class DataTransferMethodAnalyzer { - static private HashSet reachableNodes = new HashSet<>(); + static private HashSet reachableNodes = new HashSet<>(); /** * Determine whether each resource state is stored or not depending on selected data transfer methods. @@ -24,7 +24,7 @@ */ static public void decideToStoreResourceStates(DataFlowGraph graph) { reachableNodes.clear(); - for (Node n : graph.getNodes()) { + for (ResourceNode n: graph.getResourceNodes()) { ResourceNode resource = (ResourceNode) n; trackNode(resource); } @@ -35,11 +35,13 @@ return; reachableNodes.add(resource); boolean flag = true; - for (Edge e : resource.getInEdges()) { - if (((PushPullAttribute) e.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { - // Traverse pull edges only. - trackNode((ResourceNode) e.getSource()); - flag = false; + for (Edge chToRes: resource.getInEdges()) { + for (Edge resToCh: chToRes.getSource().getInEdges()) { + if (((PushPullAttribute) resToCh.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { + // Traverse pull edges only. + trackNode((ResourceNode) resToCh.getSource()); + flag = false; + } } } ((StoreAttribute) resource.getAttribute()).setStored(flag); diff --git a/AlgebraicDataflowArchitectureModel/src/generators/ILanguageSpecific.java b/AlgebraicDataflowArchitectureModel/src/generators/ILanguageSpecific.java new file mode 100644 index 0000000..7d62432 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/generators/ILanguageSpecific.java @@ -0,0 +1,45 @@ +package generators; + +import java.util.ArrayList; +import java.util.List; + +import code.ast.CompilationUnit; +import code.ast.FieldDeclaration; +import code.ast.MethodDeclaration; +import code.ast.TypeDeclaration; +import code.ast.VariableDeclaration; +import models.algebra.Expression; +import models.algebra.Type; +import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor; + +public interface ILanguageSpecific { + CompilationUnit newCompilationUnit(TypeDeclaration component); + TypeDeclaration newTypeDeclaration(String typeName); + VariableDeclaration newVariableDeclaration(Type type, String varName); + MethodDeclaration newMethodDeclaration(String methodName, Type returnType); + MethodDeclaration newMethodDeclaration(String methodName, boolean isConstructor, Type returnType, List parameters); + FieldDeclaration newFieldDeclaration(Type fieldType, String fieldName); + FieldDeclaration newFieldDeclaration(Type fieldType, String fieldName, String fieldInitializer); + Type newListType(String compTypeName); + Type newMapType(Type keyType, String compTypeName); + Type newTupleType(List compTypes); + String getVariableDeclaration(String typeName, String varName); + String getFieldInitializer(Type type, Expression initialValue); + boolean declareField(); + String getFieldAccessor(String fieldName); + String getMethodInvocation(String methodName); + String getMethodInvocation(String receivertName, String methodName); + String getMethodInvocation(String receivertName, String methodName, List parameters); + String getConstructorInvocation(String componentName, List parameters); + String getReturnStatement(String returnValue); + String toComponentName(String name); + String toVariableName(String name); + String getMainComponentName(); + String getTupleGet(String tupleExp, int idx, int length); + String getDecomposedTuple(String tupleExp, VariableDeclaration tupleVar, List vars); + String getAssignment(); + String getStatementDelimiter(); + String getStringDelimiter(); + boolean isValueType(Type type); + boolean isVoidType(Type type); +} diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JavaCodeGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/JavaCodeGenerator.java index 314e81a..b383fb1 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/JavaCodeGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/JavaCodeGenerator.java @@ -1,11 +1,15 @@ package generators; +import java.util.AbstractMap; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.Set; - +import java.util.Stack; import code.ast.Block; import code.ast.CompilationUnit; @@ -26,12 +30,15 @@ import models.dataConstraintModel.Channel; import models.dataConstraintModel.ChannelMember; import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.ResourceHierarchy; import models.dataConstraintModel.ResourcePath; +import models.dataConstraintModel.Selector; import models.dataFlowModel.DataTransferModel; import models.dataFlowModel.DataTransferChannel; import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor; import models.dataFlowModel.PushPullAttribute; import models.dataFlowModel.PushPullValue; +import models.dataFlowModel.ChannelNode; import models.dataFlowModel.DataFlowEdge; import models.dataFlowModel.DataFlowGraph; import models.dataFlowModel.ResourceNode; @@ -60,165 +67,573 @@ JavaCodeGenerator.mainTypeName = defaultMainTypeName; } + public static String getComponentName(ResourceHierarchy res) { + String name = res.getResourceName(); + if (res.getNumParameters() > 0) { + if (name.length() > 3 && name.endsWith("ies")) { + name = name.substring(0, name.length() - 3) + "y"; + } else if (name.length() > 1 && name.endsWith("s")) { + name = name.substring(0, name.length() - 1); + } else { + name += "Element"; + } + } + return name.substring(0, 1).toUpperCase() + name.substring(1); + } + + public static String toVariableName(String name) { + return name.substring(0, 1).toLowerCase() + name.substring(1); + } + + public static Type getImplStateType(ResourceHierarchy res) { + Set children = res.getChildren(); + if (children == null || children.size() == 0) { + // leaf resource. + return res.getResourceStateType(); + } else { + ResourceHierarchy child = children.iterator().next(); + if (children.size() == 1 && child.getNumParameters() > 0) { + // map or list. + if (DataConstraintModel.typeList.isAncestorOf(res.getResourceStateType())) { + // list. + if (generatesComponent(child)) { + return new Type("List", "ArrayList<>", "List<" + getComponentName(child) + ">", DataConstraintModel.typeList); + } else { + return new Type("List", "ArrayList<>", "List<" + getImplStateType(child).getImplementationTypeName() + ">", DataConstraintModel.typeList); + } + } else if (DataConstraintModel.typeMap.isAncestorOf(res.getResourceStateType())) { + // map. + if (generatesComponent(child)) { + return new Type("Map", "HashMap<>", "Map", DataConstraintModel.typeMap); + } else { + return new Type("Map", "HashMap<>", "Map", DataConstraintModel.typeMap); + } + } + return null; + } else { + // class + return res.getResourceStateType(); + } + } + } + + public static boolean generatesComponent(ResourceHierarchy res) { + return res.getParent() == null || !(res.getChildren() == null || res.getChildren().size() == 0); +// Type resType = res.getResourceStateType(); +// return DataConstraintModel.typeJson.isAncestorOf(resType) || DataConstraintModel.typeList.isAncestorOf(resType) || DataConstraintModel.typeMap.isAncestorOf(resType); + } + static public ArrayList doGenerate(DataFlowGraph graph, DataTransferModel model) { ArrayList codes = new ArrayList<>(); - ArrayList resources = determineResourceOrder(graph); + Map resourceComponents = new HashMap<>(); + Map resourceConstructors = new HashMap<>(); + List> getters = new ArrayList<>(); + List> inputs = new ArrayList<>(); + List> fields = new ArrayList<>(); + List> constructorParams = new ArrayList<>(); - TypeDeclaration mainType = new TypeDeclaration(mainTypeName); - CompilationUnit mainCU = new CompilationUnit(mainType); + Map> dependedRootComponentGraph = getDependedRootComponentGraph(model); + ArrayList resources = determineResourceOrder(graph, dependedRootComponentGraph); + TypeDeclaration mainComponent = new TypeDeclaration(mainTypeName); + CompilationUnit mainCU = new CompilationUnit(mainComponent); mainCU.addImport(new ImportDeclaration("java.util.*")); codes.add(mainCU); // Declare the constructor of the main type. MethodDeclaration mainConstructor = new MethodDeclaration(mainTypeName, true); - mainType.addMethod(mainConstructor); + mainComponent.addMethod(mainConstructor); - // For each resource. + // For each resource node. for (ResourceNode rn: resources) { - boolean f = false; - String resourceName = rn.getResource().getResourceName().substring(0, 1).toUpperCase() - + rn.getResource().getResourceName().substring(1); - TypeDeclaration type = new TypeDeclaration(resourceName); - - // Declare the field to refer to each resource in the main type. - String fieldInitializer = "new " + resourceName + "("; - Set depends = new HashSet<>(); - for (Edge e : rn.getOutEdges()) { - DataFlowEdge re = (DataFlowEdge) e; - ResourcePath dstRes = ((ResourceNode) re.getDestination()).getResource(); - String resName = dstRes.getResourceName().substring(0, 1).toUpperCase() + dstRes.getResourceName().substring(1); - if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { - depends.add(dstRes); - fieldInitializer += resName.toLowerCase() + ","; - f = true; - } - } - for (Edge e : rn.getInEdges()) { - DataFlowEdge re = (DataFlowEdge) e; - ResourcePath srcRes = ((ResourceNode) re.getSource()).getResource(); - String resName = srcRes.getResourceName().substring(0, 1).toUpperCase() + srcRes.getResourceName().substring(1); - if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { - depends.add(srcRes); - fieldInitializer += resName.toLowerCase() + ","; - f = true; - } else { - if (rn.getIndegree() > 1) { - // Declare a field to cash the state of the source resource in the type of the destination resource. - ResourcePath cashResId = ((ResourceNode) re.getSource()).getResource(); - type.addField(new FieldDeclaration( - cashResId.getResourceStateType(), ((ResourceNode) re.getSource()).getResource().getResourceName(), getInitializer(cashResId))); + TypeDeclaration component = null; + if (generatesComponent(rn.getResourceHierarchy())) { + boolean f = false; + String resourceName = getComponentName(rn.getResourceHierarchy()); + + component = resourceComponents.get(rn.getResourceHierarchy()); + if (component == null) { + // Add compilation unit for each resource. + component = new TypeDeclaration(resourceName); + resourceComponents.put(rn.getResourceHierarchy(), component); + CompilationUnit cu = new CompilationUnit(component); + cu.addImport(new ImportDeclaration("java.util.*")); + codes.add(cu); + + // Declare the field to refer to each resource in the main type. + Set depends = new HashSet<>(); + Set refs = new HashSet<>(); + if (rn.getResourceHierarchy().getParent() == null) { + // For a root resource + String fieldInitializer = "new " + resourceName + "("; + for (Edge resToCh: rn.getOutEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { + for (Edge chToRes: re.getDestination().getOutEdges()) { + ResourcePath dstRes = ((ResourceNode) chToRes.getDestination()).getOutSideResource(); + String resName = getComponentName(dstRes.getResourceHierarchy()); + depends.add(dstRes.getResourceHierarchy()); + fieldInitializer += toVariableName(resName) + ","; + f = true; + } + } + } + for (Edge chToRes : rn.getInEdges()) { + for (Edge resToCh: chToRes.getSource().getInEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(); + String resName = getComponentName(srcRes.getResourceHierarchy()); + if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { + depends.add(srcRes.getResourceHierarchy()); + fieldInitializer += toVariableName(resName) + ","; + f = true; + } else { + if (re.getDestination().getIndegree() > 1) { + // Declare a field to cash the state of the source resource in the type of the destination resource. + ResourcePath cashRes = ((ResourceNode) re.getSource()).getOutSideResource(); + component.addField(new FieldDeclaration( + cashRes.getResourceStateType(), ((ResourceNode) re.getSource()).getOutSideResource().getResourceName(), getInitializer(cashRes))); + } + } + } + } + for (ResourceHierarchy dependedRes: dependedRootComponentGraph.keySet()) { + for (ResourceHierarchy dependingRes: dependedRootComponentGraph.get(dependedRes)) { + if (rn.getResourceHierarchy().equals(dependingRes)) { + // Declare a field to refer to outside resources. + depends.add(dependedRes); + String resName = getComponentName(dependedRes); + fieldInitializer += toVariableName(resName) + ","; + f = true; + } + } + } + for (Channel ch : model.getChannels()) { + DataTransferChannel c = (DataTransferChannel) ch; + if (c.getInputResources().contains(rn.getOutSideResource())) { + for (ResourcePath res: c.getReferenceResources()) { + if (!refs.contains(res) && !depends.contains(res.getResourceHierarchy())) { + refs.add(res); + String refResName = res.getResourceName(); + fieldInitializer += toVariableName(refResName) + ","; + f = true; + } + } + } + } + if (f) fieldInitializer = fieldInitializer.substring(0, fieldInitializer.length() - 1); + fieldInitializer += ")"; + FieldDeclaration field = new FieldDeclaration(new Type(resourceName, resourceName), rn.getResourceName()); + mainComponent.addField(field); + Block mainConstructorBody = mainConstructor.getBody(); + if (mainConstructorBody == null) { + mainConstructorBody = new Block(); + mainConstructor.setBody(mainConstructorBody); + } + mainConstructorBody.addStatement(rn.getResourceName() + " = " + fieldInitializer + ";"); + } + + // Declare a constructor, fields and update methods in the type of each resource. + MethodDeclaration constructor = new MethodDeclaration(resourceName, true); + Block block = new Block(); + depends = new HashSet<>(); + for (Edge resToCh : rn.getOutEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(rn.getResourceHierarchy()) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + // Check if the output resource is outside of the channel scope. + boolean outsideOutputResource = false; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(rn.getOutSideResource().getResourceHierarchy()) && cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) { + // Declare a field to refer to the destination resource of push transfer. + for (Edge chToRes: re.getDestination().getOutEdges()) { + ResourcePath dstRes = ((ResourceNode) chToRes.getDestination()).getOutSideResource(); + if (!generatesComponent(dstRes.getResourceHierarchy())) { + dstRes = dstRes.getParent(); + } + String dstResName = getComponentName(dstRes.getResourceHierarchy()); + depends.add(dstRes.getResourceHierarchy()); + component.addField(new FieldDeclaration(new Type(dstResName, dstResName), toVariableName(dstResName))); + constructor.addParameter(new VariableDeclaration(new Type(dstResName, dstResName), toVariableName(dstResName))); + block.addStatement("this." + toVariableName(dstResName) + " = " + toVariableName(dstResName) + ";"); + if (outsideOutputResource) { + if (dstRes.getParent() != null) { + // Reference to root resource. + ResourcePath dstRootRes = dstRes.getRoot(); + String dstRootResName = getComponentName(dstRootRes.getResourceHierarchy()); + component.addField(new FieldDeclaration(new Type(dstRootResName, dstRootResName), toVariableName(dstRootResName))); + constructor.addParameter(new VariableDeclaration(new Type(dstRootResName, dstRootResName), toVariableName(dstRootResName))); + block.addStatement("this." + toVariableName(dstRootResName) + " = " + toVariableName(dstRootResName) + ";"); + } + } + } + } + } + for (Edge chToRes : rn.getInEdges()) { + for (Edge resToCh: chToRes.getSource().getInEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(); + DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(srcRes.getResourceHierarchy()) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + // Check if the output resource is outside of the channel scope. + boolean outsideOutputResource = false; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(rn.getOutSideResource().getResourceHierarchy()) && cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + String srcResName = null; + ResourcePath srcRes2 = srcRes; + if (generatesComponent(srcRes.getResourceHierarchy())) { + srcResName = getComponentName(srcRes.getResourceHierarchy()); + } else { + srcRes2 = srcRes.getParent(); + srcResName = getComponentName(srcRes2.getResourceHierarchy()); + } + if ((((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { + // Declare a field to refer to the source resource of pull transfer. + depends.add(srcRes2.getResourceHierarchy()); + component.addField(new FieldDeclaration(new Type(srcResName, srcResName), toVariableName(srcResName))); + constructor.addParameter(new VariableDeclaration(new Type(srcResName, srcResName), toVariableName(srcResName))); + block.addStatement("this." + toVariableName(srcResName) + " = " + toVariableName(srcResName) + ";"); + if (outsideInputResource) { + if (srcRes2.getParent() != null) { + // Reference to root resource. + ResourcePath srcRootRes = srcRes2.getRoot(); + String srcRootResName = getComponentName(srcRootRes.getResourceHierarchy()); + component.addField(new FieldDeclaration(new Type(srcRootResName, srcRootResName), toVariableName(srcRootResName))); + constructor.addParameter(new VariableDeclaration(new Type(srcRootResName, srcRootResName), toVariableName(srcRootResName))); + block.addStatement("this." + toVariableName(srcRootResName) + " = " + toVariableName(srcRootResName) + ";"); + } + } + } else { + // Declare an update method in the type of the destination resource. + ArrayList vars = new ArrayList<>(); + vars.add(new VariableDeclaration(srcRes.getResourceStateType(), srcRes.getResourceName())); + DataTransferChannel c = ((ChannelNode) resToCh.getDestination()).getChannel(); + for (ResourcePath ref: c.getReferenceResources()) { + if (!ref.equals(rn.getOutSideResource())) { + vars.add(new VariableDeclaration(ref.getResourceStateType(), ref.getResourceName())); + } + } + component.addMethod(new MethodDeclaration("update" + srcResName, false, typeVoid, vars)); + } + } + } + // Declare a field to refer to outside resources. + if (rn.getParent() == null) { + for (ResourceHierarchy dependedRes: dependedRootComponentGraph.keySet()) { + for (ResourceHierarchy dependingRes: dependedRootComponentGraph.get(dependedRes)) { + if (rn.getResourceHierarchy().equals(dependingRes)) { + // Declare a field to refer to outside resources. + depends.add(dependedRes); + String resName = getComponentName(dependedRes); + component.addField(new FieldDeclaration(new Type(resName, resName), toVariableName(resName))); + constructor.addParameter(new VariableDeclaration(new Type(resName, resName), toVariableName(resName))); + block.addStatement("this." + toVariableName(resName) + " = " + toVariableName(resName) + ";"); + } + } + } + } + // Declare a field to refer to the reference resource. + refs = new HashSet<>(); + for (Channel ch : model.getChannels()) { + DataTransferChannel c = (DataTransferChannel) ch; + if (c.getInputResources().contains(rn.getOutSideResource())) { + for (ResourcePath res: c.getReferenceResources()) { + if (!refs.contains(res) && !depends.contains(res.getResourceHierarchy())) { + refs.add(res); + String refResName = getComponentName(res.getResourceHierarchy()); + component.addField(new FieldDeclaration(new Type(refResName, refResName), res.getResourceName())); + constructor.addParameter(new VariableDeclaration(new Type(refResName, refResName), res.getResourceName())); + block.addStatement("this." + res.getResourceName() + " = " + res.getResourceName() + ";"); + } + } + } + } + constructor.setBody(block); + if (constructor.getParameters() != null) { + component.addMethod(constructor); + resourceConstructors.put(rn.getResourceHierarchy(), constructor); } } - } - Set refs = new HashSet<>(); - for (Channel cg : model.getChannels()) { - DataTransferChannel c = (DataTransferChannel) cg; - if (c.getInputResources().contains(rn.getResource())) { - for (ResourcePath id: c.getReferenceResources()) { - if (!refs.contains(id) && !depends.contains(id)) { - refs.add(id); - String refResName = id.getResourceName(); - fieldInitializer += refResName.toLowerCase() + ","; - f = true; + + // Declare the field to store the state in the type of each resource. + if (((StoreAttribute) rn.getAttribute()).isStored()) { + ResourcePath res = rn.getOutSideResource(); + Set children = rn.getResourceHierarchy().getChildren(); + if (children == null || children.size() == 0) { + // leaf resource. + Type fieldType = getImplStateType(res.getResourceHierarchy()); + component.addField(new FieldDeclaration(fieldType, "value", getInitializer(res))); + constructorParams.add(new AbstractMap.SimpleEntry<>(rn.getResourceHierarchy(), new VariableDeclaration(fieldType, toVariableName(resourceName)))); + } else { + ResourceHierarchy child = children.iterator().next(); + if (children.size() == 1 && child.getNumParameters() > 0) { + // map or list. + component.addField(new FieldDeclaration(getImplStateType(res.getResourceHierarchy()), "value", getInitializer(res))); + } else { + // class + for (ResourceHierarchy c: children) { + String childTypeName = getComponentName(c); + Type childType = null; + if (generatesComponent(c)) { + // The child has a component. + childType = new Type(childTypeName, childTypeName); + String fieldName = toVariableName(childTypeName); + component.addField(new FieldDeclaration(childType, fieldName, getInitializer(res))); + } + } } } } - } - if (f) fieldInitializer = fieldInitializer.substring(0, fieldInitializer.length() - 1); - fieldInitializer += ")"; - FieldDeclaration field = new FieldDeclaration(new Type(resourceName, resourceName), rn.getResource().getResourceName()); - mainType.addField(field); - Block manConstructorBody = mainConstructor.getBody(); - if (manConstructorBody == null) { - manConstructorBody = new Block(); - mainConstructor.setBody(manConstructorBody); - } - manConstructorBody.addStatement(rn.getResource().getResourceName() + " = " + fieldInitializer + ";"); - - // Declare a constructor, fields and update methods in the type of each resource. - MethodDeclaration constructor = new MethodDeclaration(resourceName, true); - Block block = new Block(); - depends = new HashSet<>(); - for (Edge e : rn.getOutEdges()) { - DataFlowEdge re = (DataFlowEdge) e; - ResourcePath dstRes = ((ResourceNode) re.getDestination()).getResource(); - String dstResName = dstRes.getResourceName().substring(0, 1).toUpperCase() + dstRes.getResourceName().substring(1); - if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { - // Declare a field to refer to the destination resource of push transfer. - depends.add(dstRes); - type.addField(new FieldDeclaration(new Type(dstResName, dstResName), dstRes.getResourceName())); - constructor.addParameter(new VariableDeclaration(new Type(dstResName, dstResName), dstRes.getResourceName())); - block.addStatement("this." + dstResName.toLowerCase() + " = " + dstResName.toLowerCase() + ";"); - } - } - for (Edge e : rn.getInEdges()) { - DataFlowEdge re = (DataFlowEdge) e; - ResourcePath srcRes = ((ResourceNode) re.getSource()).getResource(); - String srcResName = srcRes.getResourceName().substring(0, 1).toUpperCase() + srcRes.getResourceName().substring(1); - if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { - // Declare a field to refer to the source resource of pull transfer. - depends.add(srcRes); - type.addField(new FieldDeclaration(new Type(srcResName, srcResName), srcRes.getResourceName())); - constructor.addParameter(new VariableDeclaration(new Type(srcResName, srcResName), srcRes.getResourceName())); - block.addStatement("this." + srcResName.toLowerCase() + " = " + srcResName.toLowerCase() + ";"); - } else { - // Declare an update method in the type of the destination resource. - ArrayList vars = new ArrayList<>(); - vars.add(new VariableDeclaration(srcRes.getResourceStateType(), srcRes.getResourceName())); - DataTransferChannel c = (DataTransferChannel) re.getChannel(); - for (ResourcePath ref: c.getReferenceResources()) { - if (ref != rn.getResource()) { - vars.add(new VariableDeclaration(ref.getResourceStateType(), ref.getResourceName())); - } - } - type.addMethod(new MethodDeclaration("update" + srcResName, false, typeVoid, vars)); - } - } - // Declare a field to refer to the reference resource. - refs = new HashSet<>(); - for (Channel cg : model.getChannels()) { - DataTransferChannel c = (DataTransferChannel) cg; - if (c.getInputResources().contains(rn.getResource())) { - for (ResourcePath id: c.getReferenceResources()) { - if (!refs.contains(id) && !depends.contains(id)) { - refs.add(id); - String refResName = id.getResourceName(); - refResName = refResName.substring(0, 1).toUpperCase() + refResName.substring(1); - type.addField(new FieldDeclaration(new Type(refResName, refResName), id.getResourceName())); - constructor.addParameter(new VariableDeclaration(new Type(refResName, refResName), id.getResourceName())); - block.addStatement("this." + id.getResourceName() + " = " + id.getResourceName() + ";"); - } - } - } - } - constructor.setBody(block); - if (constructor.getParameters() != null) - type.addMethod(constructor); - - // Declare input methods in resources and the main type. - for (Channel cg : model.getIOChannels()) { - for (ChannelMember cm : ((DataTransferChannel) cg).getOutputChannelMembers()) { - if (cm.getResource().equals(rn.getResource())) { - Expression message = cm.getStateTransition().getMessageExpression(); - if (message.getClass() == Term.class) { - ArrayList params = new ArrayList<>(); - for (Variable var: message.getVariables().values()) { + + // Declare the getter methods to obtain the children resources. + for (ResourceNode child: rn.getChildren()) { + if (generatesComponent(child.getResourceHierarchy())) { + // A component for the child is generated. + List params = new ArrayList<>(); + int v = 1; + for (Selector param: child.getSelectors()) { + if (param.getExpression() instanceof Variable) { + Variable var = (Variable) param.getExpression(); params.add(new VariableDeclaration(var.getType(), var.getName())); + } else if (param.getExpression() instanceof Term) { + Term var = (Term) param.getExpression(); + params.add(new VariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + String childCompName = getComponentName(child.getResourceHierarchy()); + Type childType = new Type(childCompName, childCompName); + MethodDeclaration childGetter = null; + if (params.size() == 0) { + childGetter = new MethodDeclaration("get" + childCompName, childType); + } else { + childGetter = new MethodDeclaration("get" + childCompName, false, childType, params); + } + component.addMethod(childGetter); + } + } + } + + // Declare the state field and reference fields in the parent component. + if (component == null) { + // Declare reference fields for push/pull data transfer. + boolean noPullTransfer = true; + for (Edge resToCh : rn.getOutEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + for (Edge chToRes: re.getDestination().getOutEdges()) { + ResourcePath dstRes = ((ResourceNode) chToRes.getDestination()).getOutSideResource(); + boolean outsideOutputResource = false; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(dstRes.getResourceHierarchy()) && cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + if (outsideOutputResource) { // This logic may be incorrect. (ToDo) + // Declare a field in the parent component to refer to the destination resource of push transfer. + if (!generatesComponent(dstRes.getResourceHierarchy())) { + dstRes = dstRes.getParent(); + } + String dstResName = getComponentName(dstRes.getResourceHierarchy()); + FieldDeclaration refFieldForPush = new FieldDeclaration(new Type(dstResName, dstResName), toVariableName(dstResName)); + fields.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), refFieldForPush)); + if (dstRes.getParent() != null) { + // Reference to root resource. + ResourcePath dstRootRes = dstRes.getRoot(); + String dstRootResName = getComponentName(dstRootRes.getResourceHierarchy()); + Type dstRootResType = new Type(dstRootResName, dstRootResName); + FieldDeclaration refRootFieldForPush = new FieldDeclaration(dstRootResType, toVariableName(dstRootResName)); + fields.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), refRootFieldForPush)); + constructorParams.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), new VariableDeclaration(dstRootResType, toVariableName(dstRootResName)))); + } + } + } + } + for (Edge chToRes : rn.getInEdges()) { + for (Edge resToCh: chToRes.getSource().getInEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(); + DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(srcRes.getResourceHierarchy()) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + if (outsideInputResource) { // This logic may be incorrect. (ToDo) + // Declare a field in the parent component to refer to the source resource of pull transfer. + if (!generatesComponent(srcRes.getResourceHierarchy())) { + srcRes = srcRes.getParent(); + } + String srcResName = getComponentName(srcRes.getResourceHierarchy()); + FieldDeclaration refFieldForPull = new FieldDeclaration(new Type(srcResName, srcResName), toVariableName(srcResName)); + fields.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), refFieldForPull)); + if (srcRes.getParent() != null) { + // Reference to root resource. + ResourcePath srcRootRes = srcRes.getRoot(); + String srcRootResName = getComponentName(srcRootRes.getResourceHierarchy()); + Type srcRootResType = new Type(srcRootResName, srcRootResName); + FieldDeclaration refRootFieldForPull = new FieldDeclaration(srcRootResType, toVariableName(srcRootResName)); + fields.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), refRootFieldForPull)); + constructorParams.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), new VariableDeclaration(srcRootResType, toVariableName(srcRootResName)))); + } + noPullTransfer = false; + } + } + } + // Declare the state field in the parent component. + ResourceHierarchy res = rn.getOutSideResource().getResourceHierarchy(); + if (((StoreAttribute) rn.getAttribute()).isStored() && noPullTransfer && res.getNumParameters() == 0) { + String resName = getComponentName(res); + FieldDeclaration stateField = new FieldDeclaration(res.getResourceStateType(), toVariableName(resName)); + fields.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), stateField)); + constructorParams.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), new VariableDeclaration(res.getResourceStateType(), toVariableName(resName)))); + } + } + + // Declare the getter method in this component to obtain the resource state. + if (component != null) { + // A component is created for this resource. + MethodDeclaration stateGetter = new MethodDeclaration("getValue", getImplStateType(rn.getResourceHierarchy())); + component.addMethod(stateGetter); + } else { + // No component is created for this resource. + List getterParams = new ArrayList<>(); + int v = 1; + for (Selector param: rn.getSelectors()) { + if (param.getExpression() instanceof Variable) { + Variable var = (Variable) param.getExpression(); + getterParams.add(new VariableDeclaration(var.getType(), var.getName())); + } else if (param.getExpression() instanceof Term) { + Term var = (Term) param.getExpression(); + getterParams.add(new VariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + String resCompName = getComponentName(rn.getResourceHierarchy()); + Type resType = getImplStateType(rn.getResourceHierarchy()); + MethodDeclaration stateGetter = null; + if (getterParams.size() == 0) { + stateGetter = new MethodDeclaration("get" + resCompName, resType); + } else { + stateGetter = new MethodDeclaration("get" + resCompName, false, resType, getterParams); + } + getters.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), stateGetter)); + } + + // Declare the accessor method in the main type to call the getter method. + MethodDeclaration accessor = null; + List mainGetterParams = new ArrayList<>(); + int v = 1; + for (Selector param: rn.getAllSelectors()) { + if (param.getExpression() instanceof Variable) { + Variable var = (Variable) param.getExpression(); + mainGetterParams.add(new VariableDeclaration(var.getType(), var.getName())); + } else if (param.getExpression() instanceof Term) { + Term var = (Term) param.getExpression(); + mainGetterParams.add(new VariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + if (mainGetterParams.size() > 0) { + accessor = new MethodDeclaration("get" + getComponentName(rn.getResourceHierarchy()), + false, + getImplStateType(rn.getResourceHierarchy()), + mainGetterParams); + } else { + accessor = new MethodDeclaration("get" + getComponentName(rn.getResourceHierarchy()), + getImplStateType(rn.getResourceHierarchy())); + } + accessor.setBody(new Block()); + Expression getState = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(rn.getOutSideResource(), null); + accessor.getBody().addStatement("return " + getState.toImplementation(new String[] {null}) + ";"); + mainComponent.addMethod(accessor); + + // Declare the input method in this component and the main component. + for (Channel ch : model.getIOChannels()) { + for (ChannelMember cm : ((DataTransferChannel) ch).getOutputChannelMembers()) { + if (rn.getInSideResources().contains(cm.getResource())) { + Expression message = cm.getStateTransition().getMessageExpression(); + if (message instanceof Term) { + // In each resource. + ArrayList resInputParams = new ArrayList<>(); + ArrayList mainInputParams = new ArrayList<>(); + v = 1; + if (cm.getResource().getLastParam() != null) { + Expression param = cm.getResource().getLastParam(); + if (param instanceof Variable) { + Variable var = (Variable) param; + resInputParams.add(new VariableDeclaration(var.getType(), var.getName())); + mainInputParams.add(new VariableDeclaration(var.getType(), var.getName())); + } else if (param instanceof Term) { + Term var = (Term) param; + resInputParams.add(new VariableDeclaration(var.getType(), "v" + v)); + mainInputParams.add(new VariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + if (cm.getResource().getParent() != null) { + for (Expression param: cm.getResource().getParent().getPathParams()) { + if (param instanceof Variable) { + Variable var = (Variable) param; + mainInputParams.add(new VariableDeclaration(var.getType(), var.getName())); + } else if (param instanceof Term) { + Term var = (Term) param; + mainInputParams.add(new VariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + } + for (Variable var: message.getVariables().values()) { + resInputParams.add(new VariableDeclaration(var.getType(), var.getName())); + mainInputParams.add(new VariableDeclaration(var.getType(), var.getName())); } MethodDeclaration input = new MethodDeclaration( - ((Term) cm.getStateTransition().getMessageExpression()).getSymbol().getImplName(), - false, typeVoid, params); - type.addMethod(input); + ((Term) cm.getStateTransition().getMessageExpression()).getSymbol().getImplName(), + false, typeVoid, resInputParams); + if (component != null) { + // A component is created for this resource. + component.addMethod(input); + } else { + // No component is created for this resource. + inputs.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), input)); + } + + // In the main type. String str = ((Term) cm.getStateTransition().getMessageExpression()).getSymbol().getImplName(); - input = getMethod(mainType, str); + input = getMethod(mainComponent, str, mainInputParams); if (input == null) { - input = new MethodDeclaration(str, false, typeVoid, params); - mainType.addMethod(input); + input = new MethodDeclaration(str, false, typeVoid, mainInputParams); + mainComponent.addMethod(input); } else { // Add type to a parameter without type. for (VariableDeclaration param: input.getParameters()) { if (param.getType() == null) { - for (VariableDeclaration p: params) { + for (VariableDeclaration p: mainInputParams) { if (param.getName().equals(p.getName()) && p.getType() != null) { param.setType(p.getType()); } @@ -226,43 +641,105 @@ } } } - } else if (message.getClass() == Variable.class) { - MethodDeclaration input = new MethodDeclaration( - ((Variable) cm.getStateTransition().getMessageExpression()).getName(), - false, typeVoid, null); - type.addMethod(input); + } else if (message instanceof Variable) { + // In each resource. + ArrayList resInputParams = new ArrayList<>(); + ArrayList mainInputParams = new ArrayList<>(); + v = 1; + if (cm.getResource().getLastParam() != null) { + Expression param = cm.getResource().getLastParam(); + if (param instanceof Variable) { + Variable var = (Variable) param; + resInputParams.add(new VariableDeclaration(var.getType(), var.getName())); + mainInputParams.add(new VariableDeclaration(var.getType(), var.getName())); + } else if (param instanceof Term) { + Term var = (Term) param; + resInputParams.add(new VariableDeclaration(var.getType(), "v" + v)); + mainInputParams.add(new VariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + if (cm.getResource().getParent() != null) { + for (Expression param: cm.getResource().getParent().getPathParams()) { + if (param instanceof Variable) { + Variable var = (Variable) param; + mainInputParams.add(new VariableDeclaration(var.getType(), var.getName())); + } else if (param instanceof Term) { + Term var = (Term) param; + mainInputParams.add(new VariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + } + MethodDeclaration input = null; + if (resInputParams.size() > 0) { + input = new MethodDeclaration( + ((Variable) cm.getStateTransition().getMessageExpression()).getName(), + false, typeVoid, resInputParams); + } else { + input = new MethodDeclaration( + ((Variable) cm.getStateTransition().getMessageExpression()).getName(), + false, typeVoid, null); + } + if (component != null) { + // A component is created for this resource. + component.addMethod(input); + } else { + // No component is created for this resource. + inputs.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), input)); + } + + // In the main type. String str = ((Variable) cm.getStateTransition().getMessageExpression()).getName(); - input = getMethod(mainType, str); + input = getMethod(mainComponent, str, mainInputParams); if (input == null) { - input = new MethodDeclaration(str, false, typeVoid, null); - mainType.addMethod(input); + if (mainInputParams.size() > 0) { + input = new MethodDeclaration(str, false, typeVoid, mainInputParams); + } else { + input = new MethodDeclaration(str, false, typeVoid, null); + } + mainComponent.addMethod(input); } } } } } - - // Declare the field to store the state in the type of each resource. - if (((StoreAttribute) rn.getAttribute()).isStored()) { - ResourcePath resId = rn.getResource(); - type.addField(new FieldDeclaration(resId.getResourceStateType(), "value", getInitializer(resId))); + } + + // Add leaf getter methods to the parent components. + for (Map.Entry entry: getters) { + resourceComponents.get(entry.getKey()).addMethod(entry.getValue()); + } + + // Add leaf input methods to the parent components. + for (Map.Entry entry: inputs) { + resourceComponents.get(entry.getKey()).addMethod(entry.getValue()); + } + + // Add leaf reference fields to the parent components. + for (Map.Entry entry: fields) { + boolean existsField = false; + for (FieldDeclaration field: resourceComponents.get(entry.getKey()).getFields()) { + if (field.getName().equals(entry.getValue().getName())) { + existsField = true; + break; + } } - - // Declare the getter method to obtain the state in the type of each resource. - type.addMethod(new MethodDeclaration("getValue", - rn.getResource().getResourceStateType())); - - // Add compilation unit for each resource. - CompilationUnit cu = new CompilationUnit(type); - cu.addImport(new ImportDeclaration("java.util.*")); - codes.add(cu); + if (!existsField) { + resourceComponents.get(entry.getKey()).addField(entry.getValue()); + } + } + + // Add constructor parameters to the ancestor components. + for (ResourceNode root: graph.getRootResourceNodes()) { + addConstructorParameters(root.getResourceHierarchy(), resourceComponents, resourceConstructors, constructorParams); } // Declare the Pair class. boolean isCreatedPair = false; - for(ResourceNode rn : resources) { - if(isCreatedPair) continue; - if(model.getType("Pair").isAncestorOf(rn.getResource().getResourceStateType())) { + for (ResourceNode rn : resources) { + if (isCreatedPair) continue; + if (rn.getResourceStateType() != null && model.getType("Pair").isAncestorOf(rn.getResourceStateType())) { TypeDeclaration type = new TypeDeclaration("Pair"); type.addField(new FieldDeclaration(new Type("Double", "T"), "left")); type.addField(new FieldDeclaration(new Type("Double", "T"), "right")); @@ -293,47 +770,165 @@ } } - // Declare getter methods in the main type. - for (Node n : graph.getNodes()) { - ResourceNode rn = (ResourceNode) n; - MethodDeclaration getter = new MethodDeclaration( - "get" + rn.getResource().getResourceName().substring(0, 1).toUpperCase() - + rn.getResource().getResourceName().substring(1), - rn.getResource().getResourceStateType()); - getter.setBody(new Block()); - getter.getBody().addStatement( - "return " + rn.getResource().getResourceName() + ".getValue();"); - mainType.addMethod(getter); - } - - - HashSet tmps = new HashSet<>(); - HashSet cont = new HashSet<>(); - for (MethodDeclaration method : mainType.getMethods()) { - if (!tmps.contains(method.getName())) - tmps.add(method.getName()); - else - cont.add(method.getName()); - } - for (MethodDeclaration method : mainType.getMethods()) { - if (cont.contains(method.getName())) { - method.setName(method.getName() + method.getParameters().get(0).getName().substring(0, 1).toUpperCase() - + method.getParameters().get(0).getName().substring(1)); - } - } +// HashSet tmps = new HashSet<>(); +// HashSet cont = new HashSet<>(); +// for (MethodDeclaration method : mainType.getMethods()) { +// if (!tmps.contains(method.getName())) +// tmps.add(method.getName()); +// else +// cont.add(method.getName()); +// } +// for (MethodDeclaration method : mainType.getMethods()) { +// if (cont.contains(method.getName())) { +// method.setName(method.getName() + method.getParameters().get(0).getName().substring(0, 1).toUpperCase() +// + method.getParameters().get(0).getName().substring(1)); +// } +// } return codes; } - private static String getInitializer(ResourcePath resId) { - Type stateType = resId.getResourceStateType(); + private static Map> getDependedRootComponentGraph(DataTransferModel model) { + Map> dependedComponentGraph = new HashMap<>(); + for (Channel ch: model.getChannels()) { + DataTransferChannel dtCh = (DataTransferChannel) ch; + Set inRes = new HashSet<>(); + Set outRes = new HashSet<>(); + for (ChannelMember cm: dtCh.getChannelMembers()) { + if (cm.isOutside()) { + outRes.add(cm.getResource().getResourceHierarchy()); + } else { + inRes.add(cm.getResource().getResourceHierarchy()); + } + } + if (outRes.size() > 0 && inRes.size() > 0) { + for (ResourceHierarchy out: outRes) { + for (ResourceHierarchy in: inRes) { + Set dependings = dependedComponentGraph.get(out.getRoot()); + if (dependings == null) { + dependings = new HashSet<>(); + dependedComponentGraph.put(out.getRoot(), dependings); + } + dependings.add(in.getRoot()); + } + } + } + } + return dependedComponentGraph; + } + + static private ArrayList determineResourceOrder(DataFlowGraph graph, Map> dependedRootComponentGraph) { + ArrayList resources = new ArrayList<>(); + Set visited = new HashSet<>(); + for (Node n : graph.getResourceNodes()) { + ResourceNode resNode = (ResourceNode) n; + topologicalSort(resNode, graph, dependedRootComponentGraph, visited, resources); + } + return resources; + } + + static private void topologicalSort(ResourceNode curResNode, DataFlowGraph graph, Map> dependedRootComponentGraph, Set visited, List orderedList) { + if (visited.contains(curResNode)) return; + visited.add(curResNode); + // For each incoming PUSH transfer. + for (Edge chToRes: curResNode.getInEdges()) { + for (Edge resToCh: chToRes.getSource().getInEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { + topologicalSort((ResourceNode) re.getSource(), graph, dependedRootComponentGraph, visited, orderedList); + } + } + } + // For each outgoing PULL transfer. + for (Edge resToCh: curResNode.getOutEdges()) { + DataFlowEdge de = (DataFlowEdge) resToCh; + if (((PushPullAttribute) de.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { + for (Edge chToRes : resToCh.getDestination().getOutEdges()) { + topologicalSort((ResourceNode) chToRes.getDestination(), graph, dependedRootComponentGraph, visited, orderedList); + } + } + } + // For each depending root node. + if (dependedRootComponentGraph.get(curResNode.getResourceHierarchy()) != null) { + for (ResourceHierarchy dependingRes: dependedRootComponentGraph.get(curResNode.getResourceHierarchy())) { + for (ResourceNode root: graph.getRootResourceNodes()) { + if (root.getResourceHierarchy().equals(dependingRes)) { + topologicalSort(root, graph, dependedRootComponentGraph, visited, orderedList); + } + } + } + } + // For each reference resource. + for (Node n: graph.getResourceNodes()) { + ResourceNode resNode = (ResourceNode) n; + for (Edge resToCh : resNode.getOutEdges()) { + ChannelNode chNode = (ChannelNode) resToCh.getDestination(); + for (ChannelMember m: chNode.getChannel().getReferenceChannelMembers()) { + if (m.getResource().equals(curResNode.getOutSideResource())) { + topologicalSort(resNode, graph, dependedRootComponentGraph, visited, orderedList); + } + } + } + } + orderedList.add(0, curResNode); + } + + private static List addConstructorParameters(ResourceHierarchy resource, + Map resourceComponents, + Map resourceConstructors, + List> constructorParams) { + List params = new ArrayList<>(); + for (ResourceHierarchy child: resource.getChildren()) { + params.addAll(addConstructorParameters(child, resourceComponents, resourceConstructors, constructorParams)); + } + for (Entry paramEnt: constructorParams) { + if (paramEnt.getKey().equals(resource)) { + params.add(paramEnt.getValue()); + } + } + if (params.size() > 0) { + MethodDeclaration constructor = resourceConstructors.get(resource); + if (constructor == null) { + if (resourceComponents.get(resource) != null) { + String resourceName = getComponentName(resource); + constructor = new MethodDeclaration(resourceName, true); + Block body = new Block(); + constructor.setBody(body); + resourceComponents.get(resource).addMethod(constructor); + resourceConstructors.put(resource, constructor); + } + } + if (constructor != null) { + for (VariableDeclaration param: params) { + boolean existsParam = false; + if (constructor.getParameters() != null) { + for (VariableDeclaration constParam: constructor.getParameters()) { + if (constParam.getName().equals(param.getName())) { + existsParam = true; + break; + } + } + } + if (!existsParam) { + constructor.addParameter(param); + constructor.getBody().addStatement("this." + toVariableName(param.getName()) + " = " + toVariableName(param.getName()) + ";"); + } + } + } + } + if (resource.getNumParameters() > 0) params.clear(); + return params; + } + + private static String getInitializer(ResourcePath res) { + Type stateType = res.getResourceStateType(); String initializer = null; - if (resId.getInitialValue() != null) { - initializer = resId.getInitialValue().toImplementation(new String[] {""}); - } else { + if (res.getResourceHierarchy().getInitialValue() != null) { + initializer = res.getResourceHierarchy().getInitialValue().toImplementation(new String[] {""}); + } else if (stateType != null) { if (DataConstraintModel.typeList.isAncestorOf(stateType)) { - initializer = "new " + resId.getResourceStateType().getImplementationTypeName() + "()"; + initializer = "new " + res.getResourceStateType().getImplementationTypeName() + "()"; } else if (DataConstraintModel.typeMap.isAncestorOf(stateType)) { - initializer = "new " + resId.getResourceStateType().getImplementationTypeName() + "()"; + initializer = "new " + res.getResourceStateType().getImplementationTypeName() + "()"; } } return initializer; @@ -384,92 +979,171 @@ return codes; } - static private ArrayList determineResourceOrder(DataFlowGraph graph) { - ArrayList resources = new ArrayList<>(); - Set visited = new HashSet<>(); - for (Node n : graph.getNodes()) { - ResourceNode rn = (ResourceNode) n; - topologicalSort(graph, rn, visited, resources); - } - return resources; - } - - static private void topologicalSort(DataFlowGraph graph, ResourceNode curNode, Set visited, List orderedList) { - if (visited.contains(curNode)) return; - visited.add(curNode); - for (Edge e : curNode.getInEdges()) { - DataFlowEdge re = (DataFlowEdge) e; - if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { - topologicalSort(graph, (ResourceNode) re.getSource(), visited, orderedList); - } - } - for (Edge e : curNode.getOutEdges()) { - DataFlowEdge re = (DataFlowEdge) e; - if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { - topologicalSort(graph, (ResourceNode) re.getDestination(), visited, orderedList); - } - } - for (Node n: graph.getNodes()) { // for reference resources. - ResourceNode rn = (ResourceNode) n; - for (Edge e : rn.getOutEdges()) { - DataFlowEdge re = (DataFlowEdge) e; - for (ChannelMember m: re.getChannel().getReferenceChannelMembers()) { - if (m.getResource() == curNode.getResource()) { - topologicalSort(graph, rn, visited, orderedList); + private static MethodDeclaration getMethod(TypeDeclaration type, String methodName, List params) { + for (MethodDeclaration m: type.getMethods()) { + if (m.getName().equals(methodName)) { + if (m.getParameters() == null && (params == null || params.size() == 0)) return m; + if (m.getParameters() != null && params != null && m.getParameters().size() == params.size()) { + boolean matchParams = true; + for (int i = 0; i < m.getParameters().size(); i++) { + if (!m.getParameters().get(i).getType().equals(params.get(i).getType())) { + matchParams = false; + break; + } } + if (matchParams) return m; } } } - orderedList.add(0, curNode); - } - - private static MethodDeclaration getMethod(TypeDeclaration type, String methodName) { - for (MethodDeclaration m: type.getMethods()) { - if (m.getName().equals(methodName)) return m; - } return null; } static public IResourceStateAccessor pushAccessor = new IResourceStateAccessor() { @Override - public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) { - if (target.equals(from)) { + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + ResourcePath fromRes = from.getResource(); + if (targetRes.equals(fromRes)) { return new Field("value", - target.getResourceStateType() != null ? target.getResourceStateType() + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } // for reference channel member - return new Parameter(target.getResourceName(), - target.getResourceStateType() != null ? target.getResourceStateType() + return new Parameter(targetRes.getResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } @Override - public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { - return new Parameter(target.getResourceName(), - target.getResourceStateType() != null ? target.getResourceStateType() + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + return new Parameter(targetRes.getResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { + if (fromRes != null && targetRes.equals(fromRes)) { + return new Field("value", + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + return new Parameter(targetRes.getResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } }; + static public IResourceStateAccessor pullAccessor = new IResourceStateAccessor() { @Override - public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) { - if (target.equals(from)) { - return new Field("value", - target.getResourceStateType() != null ? target.getResourceStateType() - : DataConstraintModel.typeInt); + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + if (from != null) { + ResourcePath fromRes = from.getResource(); + if (!target.isOutside()) { + return getDirectStateAccessorFor(targetRes, fromRes); + } + Term getter = null; + String targetComponentName = getComponentName(targetRes.getResourceHierarchy()); + if (generatesComponent(targetRes.getResourceHierarchy())) { + getter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); + getter.addChild(new Field(toVariableName(targetComponentName), targetRes.getResourceStateType())); + } else { + String parentName = toVariableName(getComponentName(targetRes.getResourceHierarchy().getParent())); + Type parentType = targetRes.getResourceHierarchy().getParent().getResourceStateType(); + getter = new Term(new Symbol("get" + targetComponentName, 1, Symbol.Type.METHOD)); + getter.addChild(new Field(parentName, parentType)); + } + return getter; + } else { + return getDirectStateAccessorFor(targetRes, null); } - // for reference channel member - Term getter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); - getter.addChild(new Field(target.getResourceName(), target.getResourceStateType())); - return getter; } @Override - public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { - Term getter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); - getter.addChild(new Field(target.getResourceName(), target.getResourceStateType())); - return getter; + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + if (from != null) { + ResourcePath fromRes = from.getResource(); + if (!target.isOutside()) { + return getDirectStateAccessorFor(targetRes, fromRes); + } + Term getter = null; + String targetComponentName = getComponentName(targetRes.getResourceHierarchy()); + if (generatesComponent(targetRes.getResourceHierarchy())) { + getter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); + getter.addChild(new Field(toVariableName(targetComponentName), targetRes.getResourceStateType())); + } else { + String parentName = toVariableName(getComponentName(targetRes.getResourceHierarchy().getParent())); + Type parentType = targetRes.getResourceHierarchy().getParent().getResourceStateType(); + getter = new Term(new Symbol("get" + targetComponentName, 1, Symbol.Type.METHOD)); + getter.addChild(new Field(parentName, parentType)); + } + return getter; + } else { + return getDirectStateAccessorFor(targetRes, null); + } + } + + @Override + public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { + if (fromRes != null) { + if (targetRes.equals(fromRes)) { + return new Field("value", + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + Term getter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); + getter.addChild(new Field(targetRes.getResourceName(), targetRes.getResourceStateType())); + return getter; + } else { + // access from the outside of the hierarchy + Stack pathStack = new Stack<>(); + ResourcePath curPath = targetRes; + do { + pathStack.push(curPath); + curPath = curPath.getParent(); + } while (curPath != null); + // iterate from the root resource + Term getter = null; + int v = 1; + while (!pathStack.empty()) { + curPath = pathStack.pop(); + String typeName = getComponentName(curPath.getResourceHierarchy()); + if (getter == null) { + // root resource + String fieldName = toVariableName(typeName); + getter = new Field(fieldName, new Type(typeName, typeName)); + } else { + Term newGetter = new Term(new Symbol("get" + typeName, -1, Symbol.Type.METHOD)); + newGetter.addChild(getter); + if (curPath.getResourceHierarchy().getNumParameters() > 0) { + Variable var = null; + Expression param = curPath.getLastParam(); + if (param instanceof Variable) { + var = (Variable) param; + } else if (param instanceof Term) { + var = new Variable("v" + v, ((Term) param).getType()); + } + if (var != null) { + newGetter.addChild(var); + newGetter.getSymbol().setArity(2); + } + v++; + } + getter = newGetter; + } + } + if (generatesComponent(targetRes.getResourceHierarchy())) { + Term newGetter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); + newGetter.addChild(getter); + getter = newGetter; + } + return getter; + } } }; } diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java index 86cd28a..b4fde5e 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java @@ -1,21 +1,26 @@ package generators; -import java.util.AbstractMap; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.Map.Entry; 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.Constant; import models.algebra.Expression; +import models.algebra.Field; import models.algebra.InvalidMessage; import models.algebra.ParameterizedIdentifierIsFutureWork; +import models.algebra.Position; +import models.algebra.Symbol; import models.algebra.Term; import models.algebra.Type; import models.algebra.UnificationFailed; @@ -24,12 +29,15 @@ import models.dataConstraintModel.Channel; import models.dataConstraintModel.ChannelMember; import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.JsonAccessor; +import models.dataConstraintModel.ResourceHierarchy; import models.dataConstraintModel.ResourcePath; import models.dataFlowModel.DataTransferModel; import models.dataFlowModel.DataTransferChannel; import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor; import models.dataFlowModel.PushPullAttribute; import models.dataFlowModel.PushPullValue; +import models.dataFlowModel.ChannelNode; import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork; import models.dataFlowModel.DataFlowEdge; import models.dataFlowModel.DataFlowGraph; @@ -39,10 +47,10 @@ public class JavaMethodBodyGenerator { public static ArrayList doGenerate(DataFlowGraph graph, DataTransferModel model, ArrayList codes) { // Create a map from type names (lower case) to their types. - Map typeMap = new HashMap<>(); + Map componentMap = new HashMap<>(); for (CompilationUnit code: codes) { - for (TypeDeclaration type: code.types()) { - typeMap.put(type.getTypeName().substring(0,1).toLowerCase() + type.getTypeName().substring(1), type); + for (TypeDeclaration component: code.types()) { + componentMap.put(component.getTypeName(), component); } } @@ -50,189 +58,459 @@ try { Map> referredResources = new HashMap<>(); for (Edge e: graph.getEdges()) { - DataFlowEdge d = (DataFlowEdge) e; - PushPullAttribute pushPull = (PushPullAttribute) d.getAttribute(); - ResourceNode src = (ResourceNode) d.getSource(); - ResourceNode dst = (ResourceNode) d.getDestination(); - String srcResourceName = src.getResource().getResourceName(); - String dstResourceName = dst.getResource().getResourceName(); - TypeDeclaration srcType = typeMap.get(srcResourceName); - TypeDeclaration dstType = typeMap.get(dstResourceName); - for (ChannelMember out: d.getChannel().getOutputChannelMembers()) { - if (out.getResource() == dst.getResource()) { - if (pushPull.getOptions().get(0) == PushPullValue.PUSH && srcType != null) { - // for push data transfer - MethodDeclaration update = getUpdateMethod(dstType, srcType); - if (((StoreAttribute) dst.getAttribute()).isStored()) { - // update stored state of dst side resource (when every incoming edge is in push style) - Expression updateExp = d.getChannel().deriveUpdateExpressionOf(out, JavaCodeGenerator.pushAccessor); - String[] sideEffects = new String[] {""}; - String curState = updateExp.toImplementation(sideEffects); - String updateStatement; - if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { - updateStatement = sideEffects[0]; - } else { - updateStatement = sideEffects[0] + "value = " + curState + ";"; + DataFlowEdge resToCh = (DataFlowEdge) e; + if (!resToCh.isChannelToResource()) { + PushPullAttribute pushPull = (PushPullAttribute) resToCh.getAttribute(); + ResourceNode src = (ResourceNode) resToCh.getSource(); + for (Edge chToRes: resToCh.getDestination().getOutEdges()) { + ResourceNode dst = (ResourceNode) chToRes.getDestination(); + String srcResourceName = JavaCodeGenerator.getComponentName(src.getResourceHierarchy()); + String dstResourceName = JavaCodeGenerator.getComponentName(dst.getResourceHierarchy()); + TypeDeclaration srcComponent = componentMap.get(srcResourceName); + TypeDeclaration dstComponent = componentMap.get(dstResourceName); + DataTransferChannel ch = ((ChannelNode) resToCh.getDestination()).getChannel(); + for (ChannelMember out: ch.getOutputChannelMembers()) { + if (dst.getInSideResources().contains(out.getResource())) { + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(src.getResourceHierarchy()) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } } - if (update.getBody() == null || !update.getBody().getStatements().contains(updateStatement)) { - update.addFirstStatement(updateStatement); + // Check if the output resource is outside of the channel scope. + boolean outsideOutputResource = false; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(dst.getResourceHierarchy()) && cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } } - } - if (dst.getIndegree() > 1) { - // update a cash of src side resource (when incoming edges are multiple) - String cashStatement = "this." + srcResourceName + " = " + srcResourceName + ";"; - if (update.getBody() == null || !update.getBody().getStatements().contains(cashStatement)) { - update.addFirstStatement(cashStatement); - } - } - MethodDeclaration getter = getGetterMethod(dstType); - if (((StoreAttribute) dst.getAttribute()).isStored()) { - // returns the current state stored in a field. - if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { - Type resourceType = dst.getResource().getResourceStateType(); - if (model.isPrimitiveType(resourceType)) { - getter.addStatement("return value;"); + if (pushPull.getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) { + // for push data transfer + String srcResName = null; + if (srcComponent == null) { + srcResName = srcResourceName; } else { - // copy the current state to be returned as a 'value' - String implTypeName = resourceType.getImplementationTypeName(); -// String interfaceTypeName = resourceType.getInterfaceTypeName(); -// String concreteTypeName; -// if (interfaceTypeName.contains("<")) { -// String typeName = implTypeName.substring(0, implTypeName.indexOf("<")); -//// String generics = interfaceTypeName.substring(interfaceTypeName.indexOf("<") + 1, interfaceTypeName.lastIndexOf(">")); -// concreteTypeName = typeName + "<>"; -// } else { -// concreteTypeName = implTypeName; -// } - getter.addStatement("return new " + implTypeName + "(value);"); + srcResName = srcComponent.getTypeName(); } - } - } - // src side (for a chain of update method invocations) - for (MethodDeclaration srcUpdate: getUpdateMethods(srcType)) { - String refParams = ""; - Set referredSet = referredResources.get(srcUpdate); - for (ChannelMember rc: d.getChannel().getReferenceChannelMembers()) { - // to get the value of reference member. - ResourcePath ref = rc.getResource(); - if (referredSet == null) { - referredSet = new HashSet<>(); - referredResources.put(srcUpdate, referredSet); + if (dstComponent == null) { + String dstParentResourceName = JavaCodeGenerator.getComponentName(dst.getResourceHierarchy().getParent()); + dstComponent = componentMap.get(dstParentResourceName); } - if (ref != dst.getResource()) { - String refVarName = ref.getResourceName(); - if (!referredSet.contains(ref)) { - referredSet.add(ref); - Expression refGetter = JavaCodeGenerator.pullAccessor.getCurrentStateAccessorFor(ref, src.getResource()); - String[] sideEffects = new String[] {""}; - String refExp = refGetter.toImplementation(sideEffects); - String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); - srcUpdate.addFirstStatement(sideEffects[0] + refTypeName + " " + refVarName + " = " + refExp + ";"); + MethodDeclaration update = getUpdateMethod(dstComponent, srcResName); + if (((StoreAttribute) dst.getAttribute()).isStored()) { + // update stored state of dst side resource (when every incoming edge is in push style) + Expression updateExp = ch.deriveUpdateExpressionOf(out, JavaCodeGenerator.pushAccessor); + // Replace Json constructor with a constructor of the child resource. + ResourceHierarchy outRes = out.getResource().getResourceHierarchy(); + if (outRes.getChildren().size() == 1 && outRes.getChildren().iterator().next().getNumParameters() > 0) { + ResourceHierarchy childRes = outRes.getChildren().iterator().next(); + Type childStateType = childRes.getResourceStateType(); + String childComponentName = JavaCodeGenerator.getComponentName(childRes); + TypeDeclaration childComponent = componentMap.get(childComponentName); + if (DataConstraintModel.typeJson.isAncestorOf(childStateType)) { + replaceJsonTermWithConstructorInvocation(updateExp, childStateType, childComponentName, childComponent); + } } - refParams += ", " + refVarName; - } - } - srcUpdate.addStatement("this." + dstResourceName + ".update" + srcType.getTypeName() + "(value" + refParams + ");"); - } - for (MethodDeclaration srcInput: getInputMethods(srcType, src, model)) { - String refParams = ""; - Set referredSet = referredResources.get(srcInput); - for (ChannelMember rc: d.getChannel().getReferenceChannelMembers()) { - // to get the value of reference member. - ResourcePath ref = rc.getResource(); - if (referredSet == null) { - referredSet = new HashSet<>(); - referredResources.put(srcInput, referredSet); - } - if (ref != dst.getResource()) { - String refVarName = ref.getResourceName(); - if (!referredSet.contains(ref)) { - referredSet.add(ref); - Expression refGetter = JavaCodeGenerator.pullAccessor.getCurrentStateAccessorFor(ref, src.getResource()); - String[] sideEffects = new String[] {""}; - String refExp = refGetter.toImplementation(sideEffects); - String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); - srcInput.addFirstStatement(sideEffects[0] + refTypeName + " " + refVarName + " = " + refExp + ";"); + // Add statements to the update method. + String[] sideEffects = new String[] {""}; + String curState = updateExp.toImplementation(sideEffects); + String updateStatement; + if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { + updateStatement = sideEffects[0]; + } else { + updateStatement = sideEffects[0] + "value = " + curState + ";"; } - refParams += ", " + refVarName; + if (update.getBody() == null || !update.getBody().getStatements().contains(updateStatement)) { + update.addFirstStatement(updateStatement); + } } - } - srcInput.addStatement("this." + dstResourceName + ".update" + srcType.getTypeName() + "(value" + refParams + ");"); - } - } else { - // for pull (or push/pull) data transfer - MethodDeclaration getter = getGetterMethod(dstType); - if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { - boolean isContainedPush = false; - HashMap inputResourceToStateAccessor = new HashMap<>(); - for (Edge eIn: dst.getInEdges()) { - DataFlowEdge dIn = (DataFlowEdge) eIn; - if (((PushPullAttribute) dIn.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { - isContainedPush = true; - inputResourceToStateAccessor.put(((ResourceNode) dIn.getSource()).getResource(), JavaCodeGenerator.pushAccessor); + if (resToCh.getDestination().getIndegree() > 1) { + // update a cash of src side resource (when incoming edges are multiple) + String cashStatement = "this." + JavaCodeGenerator.toVariableName(srcResourceName) + " = " + JavaCodeGenerator.toVariableName(srcResourceName) + ";"; + if (update.getBody() == null || !update.getBody().getStatements().contains(cashStatement)) { + update.addFirstStatement(cashStatement); + } + } + if (((StoreAttribute) dst.getAttribute()).isStored()) { + // returns the current state stored in a field. + MethodDeclaration getter = null; + if (JavaCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { + getter = getMethod(dstComponent, "getValue"); + } else { + getter = getGetterMethod(dstComponent, dstResourceName); + } + if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { + Type resourceType = dst.getResourceStateType(); + if (dst.getResourceHierarchy().getNumParameters() == 0) { + if (model.isPrimitiveType(resourceType)) { + getter.addStatement("return value;"); + } else { + // copy the current state to be returned as a 'value' + String implTypeName = resourceType.getImplementationTypeName(); + getter.addStatement("return new " + implTypeName + "(value);"); + } + } else { + String[] sideEffects = new String[] {""}; + if (DataConstraintModel.typeList.isAncestorOf(dst.getParent().getResourceStateType())) { + Term selector = new Term(DataConstraintModel.get); + selector.addChild(new Field("value")); + selector.addChild(dst.getSelectors().get(dst.getSelectors().size() - 1).getExpression()); + String curState = selector.toImplementation(sideEffects); + getter.addStatement(sideEffects[0] + "return " + curState + ";"); + } else if (DataConstraintModel.typeMap.isAncestorOf(dst.getParent().getResourceStateType())) { + Term selector = new Term(DataConstraintModel.lookup); + selector.addChild(new Field("value")); + selector.addChild(dst.getSelectors().get(dst.getSelectors().size() - 1).getExpression()); + String curState = selector.toImplementation(sideEffects); + getter.addStatement(sideEffects[0] + "return " + curState + ";"); + } + } + } + } + // src side (for a chain of update method invocations) + ChannelMember in = null; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().equals(src.getOutSideResource())) { + in = cm; + break; + } + } + for (MethodDeclaration srcUpdate: getUpdateMethods(srcComponent)) { + String refParams = ""; + Set referredSet = referredResources.get(srcUpdate); + for (ChannelMember rc: ch.getReferenceChannelMembers()) { + // to get the value of reference member. + ResourcePath ref = rc.getResource(); + if (referredSet == null) { + referredSet = new HashSet<>(); + referredResources.put(srcUpdate, referredSet); + } + if (!dst.getInSideResources().contains(ref)) { + String refVarName = ref.getResourceName(); + if (!referredSet.contains(ref)) { + referredSet.add(ref); + Expression refGetter = JavaCodeGenerator.pullAccessor.getCurrentStateAccessorFor(rc, in); + String[] sideEffects = new String[] {""}; + String refExp = refGetter.toImplementation(sideEffects); + String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); + srcUpdate.addFirstStatement(sideEffects[0] + refTypeName + " " + refVarName + " = " + refExp + ";"); + } + refParams += ", " + refVarName; + } + } + srcUpdate.addStatement("this." + dstResourceName + ".update" + srcComponent.getTypeName() + "(value" + refParams + ");"); + } + for (MethodDeclaration srcInput: getInputMethods(srcComponent, src, model)) { + String refParams = ""; + Set referredSet = referredResources.get(srcInput); + for (ChannelMember rc: ch.getReferenceChannelMembers()) { + // to get the value of reference member. + ResourcePath ref = rc.getResource(); + if (referredSet == null) { + referredSet = new HashSet<>(); + referredResources.put(srcInput, referredSet); + } + if (!dst.getInSideResources().contains(ref)) { + String refVarName = ref.getResourceName(); + if (!referredSet.contains(ref)) { + referredSet.add(ref); + Expression refGetter = JavaCodeGenerator.pullAccessor.getCurrentStateAccessorFor(rc, in); + String[] sideEffects = new String[] {""}; + String refExp = refGetter.toImplementation(sideEffects); + String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); + srcInput.addFirstStatement(sideEffects[0] + refTypeName + " " + refVarName + " = " + refExp + ";"); + } + refParams += ", " + refVarName; + } + } + srcInput.addStatement("this." + dstResourceName + ".update" + srcComponent.getTypeName() + "(value" + refParams + ");"); + } + } else if ((pushPull.getOptions().get(0) != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { + // for pull (or push/pull) data transfer + if (dstComponent == null) { + String dstParentResourceName = JavaCodeGenerator.getComponentName(dst.getResourceHierarchy().getParent()); + dstComponent = componentMap.get(dstParentResourceName); + } + MethodDeclaration getter = null; + if (JavaCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { + getter = getMethod(dstComponent, "getValue"); } else { - inputResourceToStateAccessor.put(((ResourceNode) dIn.getSource()).getResource(), JavaCodeGenerator.pullAccessor); + getter = getGetterMethod(dstComponent, dstResourceName); + } + if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { + boolean isContainedPush = false; + Map inputResourceToStateAccessor = new HashMap<>(); + for (Edge chToRes2: dst.getInEdges()) { + DataTransferChannel ch2 = ((ChannelNode) chToRes2.getSource()).getChannel(); + for (Edge resToCh2: chToRes2.getSource().getInEdges()) { + DataFlowEdge dIn = (DataFlowEdge) resToCh2; + ChannelMember in = null; + for (ChannelMember cm: ch2.getInputChannelMembers()) { + if (cm.getResource().equals(((ResourceNode) dIn.getSource()).getOutSideResource())) { + in = cm; + break; + } + } + if (((PushPullAttribute) dIn.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { + isContainedPush = true; + inputResourceToStateAccessor.put(in, JavaCodeGenerator.pushAccessor); + } else { + inputResourceToStateAccessor.put(in, JavaCodeGenerator.pullAccessor); + } + } + } + // for reference channel members + 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. + if (!isContainedPush) { + // All incoming edges are in PULL style. + String curState = ch.deriveUpdateExpressionOf(out, JavaCodeGenerator.pullAccessor).toImplementation(sideEffects); + getter.addStatement(sideEffects[0] + "return " + curState + ";"); + } else { + // At least one incoming edge is in PUSH style. + String curState = ch.deriveUpdateExpressionOf(out, JavaCodeGenerator.pullAccessor, inputResourceToStateAccessor).toImplementation(sideEffects); + getter.addStatement(sideEffects[0] + "return " + curState + ";"); + } + } + if (outsideInputResource) { + // Update fields to refer to outside resources. + Map>> resourcePaths = ch.fillOutsideResourcePaths(out, JavaCodeGenerator.pullAccessor); + if (resourcePaths != null && resourcePaths.size() > 0) { + for (ChannelMember outsideMember: resourcePaths.keySet()) { + ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey(); + if (!JavaCodeGenerator.generatesComponent(outsidePath.getResourceHierarchy())) { + outsidePath = outsidePath.getParent(); + } + String outsideResName = JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(outsidePath.getResourceHierarchy())); + Set dependingMembers = resourcePaths.get(outsideMember).getValue(); + for (ChannelMember dependingMember: dependingMembers) { + ResourcePath dependingRes = dependingMember.getResource(); + ResourceNode dependingNode = null; + PushPullAttribute pushPull2 = null; + for (Edge resToCh2: resToCh.getDestination().getInEdges()) { + if (((ResourceNode) resToCh2.getSource()).getOutSideResource().equals(dependingRes)) { + dependingNode = (ResourceNode) resToCh2.getSource(); + pushPull2 = (PushPullAttribute) resToCh.getAttribute(); + } + } + TypeDeclaration dependingComponent = null; + if (JavaCodeGenerator.generatesComponent(dependingRes.getResourceHierarchy())) { + String dependingResourceName = JavaCodeGenerator.getComponentName(dependingRes.getResourceHierarchy()); + dependingComponent = componentMap.get(dependingResourceName); + } else { + String dependingParentResourceName = JavaCodeGenerator.getComponentName(dependingRes.getParent().getResourceHierarchy()); + dependingComponent = componentMap.get(dependingParentResourceName); + } + Expression outsideExp = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(outsidePath, null); + if (JavaCodeGenerator.generatesComponent(outsidePath.getResourceHierarchy())) { + outsideExp = ((Term) outsideExp).getChild(0); + } + Expression nextExp = dependingMember.getStateTransition().getNextStateExpression(); + if (nextExp != null && outsideExp instanceof Term) { + if (nextExp instanceof Variable) { + outsideExp = ((Term) outsideExp).substitute((Variable) nextExp, new Field(JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(dependingRes.getResourceHierarchy())))); + } else { + // ToDo. + } + } + if (dstComponent == dependingComponent) { + // In the common parent. + if (dependingNode != null) { // Inspect further dependency. + for (Edge chToRes2: dependingNode.getInEdges()) { + DataTransferChannel ch2 = ((ChannelNode) chToRes2.getSource()).getChannel(); + if (ch2.getInputChannelMembers().size() == 0) { + // In an input method of the parent component. + MethodDeclaration input = getInputMethod(dependingComponent, ch2.getOutputChannelMembers().iterator().next()); + String[] sideEffects = new String[] {""}; + String outsideAccessor = outsideExp.toImplementation(sideEffects); + input.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // change the reference field. + // Update constructor. + MethodDeclaration constructor = getConstructor(dependingComponent); + constructor.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // initialize the reference field. + } else { + boolean isPush = true; + for (Edge resToCh2: chToRes2.getSource().getInEdges()) { + if (((PushPullAttribute) resToCh2.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { + isPush = false; + break; + } + } + if (isPush) { + String[] sideEffects = new String[] {""}; + String outsideAccessor = outsideExp.toImplementation(sideEffects); + for (Edge resToCh2: chToRes2.getSource().getInEdges()) { + // In an update method of the parent component. + ResourceNode dependingResSrc = (ResourceNode) resToCh2.getSource(); + MethodDeclaration update = getUpdateMethod(dependingComponent, JavaCodeGenerator.getComponentName(dependingResSrc.getResourceHierarchy())); + update.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // change the reference field. + } + // Update constructor. + MethodDeclaration constructor = getConstructor(dependingComponent); + constructor.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // initialize the reference field. + } + } + } + } + } else { + if (pushPull2.getOptions().get(0) == PushPullValue.PUSH) { + // In an update method of the destination component. + MethodDeclaration update = getUpdateMethod(dstComponent, JavaCodeGenerator.getComponentName(dependingRes.getResourceHierarchy())); + String[] sideEffects = new String[] {""}; + String outsideAccessor = outsideExp.toImplementation(sideEffects); + update.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // change the reference field. + // Update constructor. + MethodDeclaration constructor = getConstructor(dstComponent); + constructor.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // initialize the reference field. + } + } + } + } + } } } - // for reference channel members - for (ChannelMember c: d.getChannel().getReferenceChannelMembers()) { - inputResourceToStateAccessor.put(c.getResource(), JavaCodeGenerator.pullAccessor); // by pull data transfer - } - String[] sideEffects = new String[] {""}; - // generate a return statement. - if (!isContainedPush) { - // All incoming edges are in PULL style. - String curState = d.getChannel().deriveUpdateExpressionOf(out, JavaCodeGenerator.pullAccessor).toImplementation(sideEffects); - getter.addStatement(sideEffects[0] + "return " + curState + ";"); - } else { - // At least one incoming edge is in PUSH style. - String curState = d.getChannel().deriveUpdateExpressionOf(out, JavaCodeGenerator.pullAccessor, inputResourceToStateAccessor).toImplementation(sideEffects); - getter.addStatement(sideEffects[0] + "return " + curState + ";"); - } } - } + } } } } // for source nodes - String mainTypeName = JavaCodeGenerator.mainTypeName.substring(0,1).toLowerCase() + JavaCodeGenerator.mainTypeName.substring(1); - TypeDeclaration mainType = typeMap.get(mainTypeName); - for (Node n: graph.getNodes()) { - ResourceNode resource = (ResourceNode) n; - String resourceName = resource.getResource().getResourceName(); - TypeDeclaration type = typeMap.get(resourceName); - if (type != null) { - // getter method - MethodDeclaration getter = getGetterMethod(type); - if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { - Type resourceType = resource.getResource().getResourceStateType(); + TypeDeclaration mainComponent = componentMap.get(JavaCodeGenerator.mainTypeName); + for (ResourceHierarchy resource: model.getResourceHierarchies()) { +// ResourceNode resource = (ResourceNode) n; + String resourceName = JavaCodeGenerator.getComponentName(resource); + TypeDeclaration component = componentMap.get(resourceName); + if (component != null) { + // state getter method + Type resourceType = JavaCodeGenerator.getImplStateType(resource); + MethodDeclaration stateGetter = getMethod(component, "getValue"); + if (stateGetter != null && (stateGetter.getBody() == null || stateGetter.getBody().getStatements().size() == 0)) { if (model.isPrimitiveType(resourceType)) { - getter.addStatement("return value;"); + // primitive type + stateGetter.addStatement("return value;"); } else { - // copy the current state to be returned as a 'value' - String implTypeName = resourceType.getImplementationTypeName(); -// String interfaceTypeName = resourceType.getInterfaceTypeName(); -// String concreteTypeName; -// if (interfaceTypeName.contains("<")) { -// String typeName = implTypeName.substring(0, implTypeName.indexOf("<")); -// String generics = interfaceTypeName.substring(interfaceTypeName.indexOf("<") + 1, interfaceTypeName.lastIndexOf(">")); -// concreteTypeName = typeName + "<" + generics + ">"; -// } else { -// concreteTypeName = implTypeName; -// } - getter.addStatement("return new " + implTypeName + "(value);"); + if (resource.getChildren() != null && resource.getChildren().size() == 1 && resource.getChildren().iterator().next().getNumParameters() > 0) { + // list or map + String implTypeName = resourceType.getImplementationTypeName(); + // copy the current state to be returned as a 'value' + stateGetter.addStatement("return new " + implTypeName + "(value);"); + } else { + if (resource.getChildren() == null || resource.getChildren().size() == 0) { + // a leaf resource + String implTypeName = resourceType.getImplementationTypeName(); + stateGetter.addStatement("return new " + implTypeName + "(value);"); + } else { + Term composer = null; + Term composerSub = new Constant(DataConstraintModel.nil); + composerSub.setType(DataConstraintModel.typeMap); + for (ResourceHierarchy child: resource.getChildren()) { + String childTypeName = JavaCodeGenerator.getComponentName(child); + String fieldName = JavaCodeGenerator.toVariableName(childTypeName); + Term childGetter = null; + if ((child.getChildren() == null || child.getChildren().size() == 0) && child.getNumParameters() == 0) { + // the child is not a class + childGetter = new Term(new Symbol("get" + childTypeName, 1, Symbol.Type.METHOD)); + childGetter.addChild(new Constant("this")); + } else { + // the child is a class + childGetter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); + childGetter.addChild(new Field(fieldName, JavaCodeGenerator.getImplStateType(child))); + } + composer = new Term(DataConstraintModel.insert); + composer.addChild(composerSub); + composer.addChild(new Constant("\"" + fieldName + "\"", DataConstraintModel.typeString)); // key + composer.addChild(childGetter); // value + composer.setType(DataConstraintModel.typeMap); + composerSub = composer; + } + composer.setType(stateGetter.getReturnType()); + String[] sideEffects = new String[] {null}; + String returnValue = composer.toImplementation(sideEffects); + if (sideEffects[0] != null) { + stateGetter.addStatement(sideEffects[0] + "return " + returnValue+ ";"); + } else { + stateGetter.addStatement("return " + returnValue+ ";"); + } + } + } } } - // methods for input events - Map> ioChannelsAndMembers = getIOChannelsAndMembers(resource, model); - for (Map.Entry> entry: ioChannelsAndMembers.entrySet()) { - Set outs = entry.getValue(); - for (ChannelMember out: outs) { - MethodDeclaration input = getInputMethod(type, out); - if (input != null) { - String[] sideEffects = new String[] {""}; - Expression updateExp = entry.getKey().deriveUpdateExpressionOf(out, JavaCodeGenerator.pushAccessor); - String newState = updateExp.toImplementation(sideEffects); + + // child getter method + if (resource.getChildren().size() > 0) { + for (ResourceHierarchy child: resource.getChildren()) { + String methodName = "get" + JavaCodeGenerator.getComponentName(child); + MethodDeclaration childGetter = getMethod(component, methodName); + if (childGetter != null && (childGetter.getBody() == null || childGetter.getBody().getStatements().size() == 0)) { + if (DataConstraintModel.typeList.isAncestorOf(resource.getResourceStateType())) { + Term selector = new Term(DataConstraintModel.get); + selector.addChild(new Field("value")); + selector.addChild(new Variable(childGetter.getParameters().get(childGetter.getParameters().size() - 1).getName())); + selector.setType(childGetter.getReturnType()); + String[] sideEffects = new String[] {null}; + String returnValue = selector.toImplementation(sideEffects); + if (sideEffects[0] != null) childGetter.addStatement(sideEffects[0]); + childGetter.addStatement("return " + returnValue + ";"); + } else if (DataConstraintModel.typeMap.isAncestorOf(resource.getResourceStateType())) { + Term selector = new Term(DataConstraintModel.lookup); + selector.addChild(new Field("value")); + selector.addChild(new Variable(childGetter.getParameters().get(childGetter.getParameters().size() - 1).getName())); + selector.setType(childGetter.getReturnType()); + String[] sideEffects = new String[] {null}; + String returnValue = selector.toImplementation(sideEffects); + if (sideEffects[0] != null) childGetter.addStatement(sideEffects[0]); + childGetter.addStatement("return " + returnValue+ ";"); + } else { + String fieldName = JavaCodeGenerator.getComponentName(child); + String returnValue = JavaCodeGenerator.toVariableName(fieldName); + childGetter.addStatement("return this." + returnValue + ";"); + } + } + } + } + } + + // methods for input events + Map> ioChannelsAndMembers = getIOChannelsAndMembers(resource, model); + for (Map.Entry> entry: ioChannelsAndMembers.entrySet()) { + Set outs = entry.getValue(); + for (ChannelMember out: outs) { + MethodDeclaration input = null; + if (JavaCodeGenerator.generatesComponent(resource)) { + // A component is generated for this resource. + input = getInputMethod(component, out); + } else { + // No component is generated for this resource. + ResourceHierarchy parent = resource.getParent(); + if (parent != null) { + TypeDeclaration parentType = componentMap.get(JavaCodeGenerator.getComponentName(parent)); + input = getInputMethod(parentType, out); + } + } + if (input != null) { + // In each resource + Expression updateExp = entry.getKey().deriveUpdateExpressionOf(out, JavaCodeGenerator.pushAccessor); + // Replace Json constructor with a constructor of the child resource. + ResourceHierarchy outRes = out.getResource().getResourceHierarchy(); + if (outRes.getChildren().size() == 1 && outRes.getChildren().iterator().next().getNumParameters() > 0) { + ResourceHierarchy childRes = outRes.getChildren().iterator().next(); + Type childStateType = childRes.getResourceStateType(); + String childComponentName = JavaCodeGenerator.getComponentName(childRes); + TypeDeclaration childComponent = componentMap.get(childComponentName); + if (DataConstraintModel.typeJson.isAncestorOf(childStateType)) { + replaceJsonTermWithConstructorInvocation(updateExp, childStateType, childComponentName, childComponent); + } + } + // Add statements to the input method. + String[] sideEffects = new String[] {""}; + String newState = updateExp.toImplementation(sideEffects); + if (JavaCodeGenerator.generatesComponent(resource)) { String updateStatement; if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { updateStatement = sideEffects[0]; @@ -242,20 +520,61 @@ if (input.getBody() == null || !input.getBody().getStatements().contains(updateStatement)) { input.addFirstStatement(updateStatement); } - if (mainType != null) { - MethodDeclaration mainInput = getMethod(mainType, input.getName()); - if (mainInput != null) { - String args = ""; - String delimitar = ""; - if (out.getStateTransition().getMessageExpression() instanceof Term) { - Term message = (Term) out.getStateTransition().getMessageExpression(); - for (Variable var: message.getVariables().values()) { - args += delimitar + var.getName(); - delimitar = ", "; - } - } - mainInput.addStatement("this." + resourceName + "." + input.getName() + "(" + args + ");"); + } else { + String updateStatement = null; + if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { + // ToDo. + updateStatement = sideEffects[0]; + } else { + if (DataConstraintModel.typeList.isAncestorOf(resource.getParent().getResourceStateType())) { + Term selector = new Term(DataConstraintModel.set); + selector.addChild(new Field("value")); + selector.addChild(new Variable(input.getParameters().get(input.getParameters().size() - 2).getName())); + selector.addChild(new Constant(newState)); + String[] sideEffects2 = new String[] {""}; + String newList = selector.toImplementation(sideEffects2); + updateStatement = sideEffects[0] + sideEffects2[0]; + } else if (DataConstraintModel.typeMap.isAncestorOf(resource.getParent().getResourceStateType())) { + Term selector = new Term(DataConstraintModel.insert); + selector.addChild(new Field("value")); + selector.addChild(new Variable(input.getParameters().get(input.getParameters().size() - 2).getName())); + selector.addChild(new Constant(newState)); + String[] sideEffects2 = new String[] {""}; + String newMap = selector.toImplementation(sideEffects2); + updateStatement = sideEffects[0] + sideEffects2[0]; + } else { + updateStatement = sideEffects[0] + "this." + JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(resource)) + " = " + newState + ";"; } + if (updateStatement != null && (input.getBody() == null || !input.getBody().getStatements().contains(updateStatement))) { + input.addFirstStatement(updateStatement); + } + } + } + + // In the main type + if (mainComponent != null) { + MethodDeclaration inputAccessor = getMethod(mainComponent, input.getName()); + if (inputAccessor != null) { + Expression resExp = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(out.getResource(), null); + String args = ""; + String delimiter = ""; + if (resExp instanceof Term) { + // to access the parent + if (((Term) resExp).getChildren().size() > 1 && ((Term) resExp).getChild(1) instanceof Variable) { + args += delimiter + ((Variable)((Term) resExp).getChild(1)).getName(); + delimiter = ", "; + } + resExp = ((Term) resExp).getChild(0); + } + String resourceAccess = resExp.toImplementation(new String[] {null}); + if (out.getStateTransition().getMessageExpression() instanceof Term) { + Term message = (Term) out.getStateTransition().getMessageExpression(); + for (Variable var: message.getVariables().values()) { + args += delimiter + var.getName(); + delimiter = ", "; + } + } + inputAccessor.addStatement(resourceAccess + "." + input.getName() + "(" + args + ");"); } } } @@ -269,37 +588,74 @@ return codes; } - private static MethodDeclaration getUpdateMethod(TypeDeclaration type, TypeDeclaration from) { - for (MethodDeclaration m: type.getMethods()) { - if (m.getName().equals("update" + from.getTypeName())) return m; + private static void replaceJsonTermWithConstructorInvocation(Expression exp, Type replacedJsonType, String replacingClassName, TypeDeclaration childComponent) { + Map subTerms = ((Term) exp).getSubTerms(Term.class); + Iterator> termEntItr = subTerms.entrySet().iterator(); + while (termEntItr.hasNext()) { + Entry termEnt = termEntItr.next(); + Term jsonTerm = termEnt.getValue(); + if (jsonTerm.getType().equals(replacedJsonType)) { + String constructorInvocation = "new " + replacingClassName + "("; + MethodDeclaration childConstructor = getConstructor(childComponent); + String delimiter = ""; + for (VariableDeclaration var: childConstructor.getParameters()) { + JsonAccessor jsonMember = new JsonAccessor(DataConstraintModel.dot); + jsonMember.addChild(jsonTerm); + jsonMember.addChild(new Constant("\"" + var.getName() + "\"")); + Expression param = jsonMember.reduce(); + if (param != null) { + constructorInvocation = constructorInvocation + delimiter + param.toImplementation(new String[] {""}); + } else { + constructorInvocation = constructorInvocation + delimiter + var.getName(); + } + delimiter = ", "; + } + constructorInvocation += ")"; + ((Term) exp).replaceSubTerm(termEnt.getKey(), new Constant(constructorInvocation)); + subTerms = ((Term) exp).getSubTerms(Term.class); + termEntItr = subTerms.entrySet().iterator(); + } + } + } + + private static MethodDeclaration getConstructor(TypeDeclaration component) { + for (MethodDeclaration m: component.getMethods()) { + if (m.isConstructor()) return m; } return null; } - private static List getUpdateMethods(TypeDeclaration type) { + private static MethodDeclaration getUpdateMethod(TypeDeclaration component, String fromResName) { + for (MethodDeclaration m: component.getMethods()) { + if (m.getName().equals("update" + fromResName)) return m; + } + return null; + } + + private static List getUpdateMethods(TypeDeclaration component) { List updates = new ArrayList<>(); - for (MethodDeclaration m: type.getMethods()) { + for (MethodDeclaration m: component.getMethods()) { if (m.getName().startsWith("update")) { updates.add(m); } } return updates; } - - private static MethodDeclaration getGetterMethod(TypeDeclaration type) { - for (MethodDeclaration m: type.getMethods()) { - if (m.getName().startsWith("get")) return m; + + private static MethodDeclaration getGetterMethod(TypeDeclaration component, String resourceName) { + for (MethodDeclaration m: component.getMethods()) { + if (m.getName().startsWith("get" + resourceName)) return m; } return null; } - private static Map> getIOChannelsAndMembers(ResourceNode resource, DataTransferModel model) { + private static Map> getIOChannelsAndMembers(ResourceHierarchy resource, DataTransferModel model) { Map> ioChannelsAndMembers = new HashMap<>(); for (Channel c: model.getIOChannels()) { DataTransferChannel ch = (DataTransferChannel) c; // I/O channel for (ChannelMember out: ch.getOutputChannelMembers()) { - if (out.getResource().equals(resource.getResource())) { + if (resource.equals(out.getResource().getResourceHierarchy())) { if (out.getStateTransition().getMessageExpression() instanceof Term || out.getStateTransition().getMessageExpression() instanceof Variable) { Set channelMembers = ioChannelsAndMembers.get(ch); if (channelMembers == null) { @@ -314,14 +670,14 @@ return ioChannelsAndMembers; } - private static List getInputMethods(TypeDeclaration type, ResourceNode resource, DataTransferModel model) { + private static List getInputMethods(TypeDeclaration component, ResourceNode resource, DataTransferModel model) { List inputs = new ArrayList<>(); for (Channel c: model.getIOChannels()) { DataTransferChannel channel = (DataTransferChannel) c; // I/O channel for (ChannelMember out: channel.getOutputChannelMembers()) { - if (out.getResource().equals(resource.getResource())) { - MethodDeclaration input = getInputMethod(type, out); + if (resource.getInSideResources().contains(out.getResource())) { + MethodDeclaration input = getInputMethod(component, out); inputs.add(input); } } @@ -329,20 +685,35 @@ return inputs; } - private static MethodDeclaration getInputMethod(TypeDeclaration type, ChannelMember out) { + private static List getInputMethods(TypeDeclaration component, ResourceHierarchy resource, DataTransferModel model) { + List inputs = new ArrayList<>(); + for (Channel c: model.getIOChannels()) { + DataTransferChannel channel = (DataTransferChannel) c; + // I/O channel + for (ChannelMember out: channel.getOutputChannelMembers()) { + if (resource.equals(out.getResource().getResourceHierarchy())) { + MethodDeclaration input = getInputMethod(component, out); + inputs.add(input); + } + } + } + return inputs; + } + + private static MethodDeclaration getInputMethod(TypeDeclaration component, ChannelMember cm) { MethodDeclaration input = null; - if (out.getStateTransition().getMessageExpression() instanceof Term) { - Term message = (Term) out.getStateTransition().getMessageExpression(); - input = getMethod(type, message.getSymbol().getImplName()); - } else if (out.getStateTransition().getMessageExpression() instanceof Variable) { - Variable message = (Variable) out.getStateTransition().getMessageExpression(); - input = getMethod(type, message.getName()); + if (cm.getStateTransition().getMessageExpression() instanceof Term) { + Term message = (Term) cm.getStateTransition().getMessageExpression(); + input = getMethod(component, message.getSymbol().getImplName()); + } else if (cm.getStateTransition().getMessageExpression() instanceof Variable) { + Variable message = (Variable) cm.getStateTransition().getMessageExpression(); + input = getMethod(component, message.getName()); } return input; } - private static MethodDeclaration getMethod(TypeDeclaration type, String methodName) { - for (MethodDeclaration m: type.getMethods()) { + private static MethodDeclaration getMethod(TypeDeclaration component, String methodName) { + for (MethodDeclaration m: component.getMethods()) { if (m.getName().equals(methodName)) return m; } return null; diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JavaSpecific.java b/AlgebraicDataflowArchitectureModel/src/generators/JavaSpecific.java new file mode 100644 index 0000000..62743b9 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/generators/JavaSpecific.java @@ -0,0 +1,275 @@ +package generators; + +import java.util.ArrayList; +import java.util.List; + +import code.ast.CompilationUnit; +import code.ast.FieldDeclaration; +import code.ast.ImportDeclaration; +import code.ast.MethodDeclaration; +import code.ast.TypeDeclaration; +import code.ast.VariableDeclaration; +import models.algebra.Expression; +import models.algebra.Parameter; +import models.algebra.Term; +import models.algebra.Type; +import models.algebra.Variable; +import models.dataConstraintModel.DataConstraintModel; + +public class JavaSpecific implements ILanguageSpecific { + public static final Type typeVoid = new Type("Void", "void"); + public static final String self = "this"; + + @Override + public CompilationUnit newCompilationUnit(TypeDeclaration component) { + CompilationUnit cu = new CompilationUnit(component); + cu.addImport(new ImportDeclaration("java.util.*")); + return cu; + } + + @Override + public TypeDeclaration newTypeDeclaration(String typeName) { + return new TypeDeclaration(typeName); + } + + @Override + public VariableDeclaration newVariableDeclaration(Type type, String varName) { + return new VariableDeclaration(type, varName); + } + + @Override + public MethodDeclaration newMethodDeclaration(String methodName, Type returnType) { + if (returnType == null) { + returnType = typeVoid; + } + return new MethodDeclaration(methodName, returnType); + } + + @Override + public MethodDeclaration newMethodDeclaration(String methodName, boolean isConstructor, Type returnType, List parameters) { + if (returnType == null) { + returnType = typeVoid; + } + return new MethodDeclaration(methodName, isConstructor, returnType, parameters); + } + + @Override + public FieldDeclaration newFieldDeclaration(Type fieldType, String fieldName) { + return new FieldDeclaration(fieldType, fieldName); + } + + @Override + public FieldDeclaration newFieldDeclaration(Type fieldType, String fieldName, String fieldInitializer) { + return new FieldDeclaration(fieldType, fieldName, fieldInitializer); + } + + @Override + public Type newListType(String compTypeName) { + return new Type("List", "ArrayList<>", "List<" + compTypeName + ">", DataConstraintModel.typeList); + } + + @Override + public Type newMapType(Type keyType, String valueTypeName) { + return new Type("Map", "HashMap<>", "Map<" + keyType.getImplementationTypeName() + ", " + valueTypeName + ">", DataConstraintModel.typeMap); + } + + @Override + public Type newTupleType(List componentTypes) { + String implTypeName = "AbstractMap.SimpleEntry<>"; + String interfaceTypeName = "Map.Entry<$x>"; + if (componentTypes.size() >= 2) { + implTypeName = implTypeName.replace("$x", getImplementationTypeName(componentTypes.get(0)) + "$x"); + interfaceTypeName = interfaceTypeName.replace("$x", getInterfaceTypeName(componentTypes.get(0)) + "$x"); + for (Type argType : componentTypes.subList(1, componentTypes.size() - 1)) { + implTypeName = implTypeName.replace("$x", + ", AbstractMap.SimpleEntry<" + getImplementationTypeName(argType) + "$x>"); + interfaceTypeName = interfaceTypeName.replace("$x", + ", Map.Entry<" + getInterfaceTypeName(argType) + "$x>"); + } + implTypeName = implTypeName.replace("$x", + ", " + getImplementationTypeName(componentTypes.get(componentTypes.size() - 1))); + interfaceTypeName = interfaceTypeName.replace("$x", + ", " + getInterfaceTypeName(componentTypes.get(componentTypes.size() - 1))); + } + Type newTupleType = new Type("Tuple", implTypeName, interfaceTypeName, DataConstraintModel.typeTuple); + return newTupleType; + } + + @Override + public String getVariableDeclaration(String typeName, String varName) { + return typeName + " " + varName; + } + + @Override + public String getFieldInitializer(Type type, Expression initialValue) { + String initializer = null; + if (initialValue != null) { + initializer = initialValue.toImplementation(new String[] {""}); + } else if (type != null) { + if (DataConstraintModel.typeList.isAncestorOf(type)) { + initializer = "new " + type.getImplementationTypeName() + "()"; + } else if (DataConstraintModel.typeMap.isAncestorOf(type)) { + initializer = "new " + type.getImplementationTypeName() + "()"; + } + } + return initializer; + } + + @Override + public boolean declareField() { + return true; + } + + @Override + public String getFieldAccessor(String fieldName) { + return self + "." + fieldName; + } + + @Override + public String getMethodInvocation(String methodName) { + return self + "." + methodName + "()"; + } + + @Override + public String getMethodInvocation(String receiverName, String methodName) { + return receiverName + "." + methodName + "()"; + } + + @Override + public String getMethodInvocation(String receiverName, String methodName, List parameters) { + if (parameters == null) return getMethodInvocation(receiverName, methodName); + String invocation = receiverName + "." + methodName + "("; + if (parameters.size() > 0) { + for (int i = 0; i < parameters.size(); i++) { + if (i < parameters.size() - 1) { + invocation += parameters.get(i) + ", "; + } else { + invocation += parameters.get(i); + } + } + } + invocation += ")"; + return invocation; + } + + @Override + public String getConstructorInvocation(String componentName, List parameters) { + String invocation = "new " + componentName + "("; + if (parameters.size() > 0) { + for (int i = 0; i < parameters.size(); i++) { + if (i < parameters.size() - 1) { + invocation += parameters.get(i) + ", "; + } else { + invocation += parameters.get(i); + } + } + } + invocation += ")"; + return invocation; + } + + @Override + public String getReturnStatement(String returnValue) { + return "return " + returnValue; + } + + @Override + public String toComponentName(String name) { + return name.substring(0, 1).toUpperCase() + name.substring(1); + } + + @Override + public String toVariableName(String name) { + return name.substring(0, 1).toLowerCase() + name.substring(1); + } + + @Override + public String getMainComponentName() { + return "Main"; + } + + @Override + public String getAssignment() { + return " = "; + } + + @Override + public String getStatementDelimiter() { + return ";"; + } + + @Override + public String getStringDelimiter() { + return "\""; + } + + @Override + public String getTupleGet(String tupleExp, int idx, int length) { + Expression t = new Variable(tupleExp, DataConstraintModel.typeTuple); + for (int i = 0; i < idx; i++) { + Term next = new Term(DataConstraintModel.snd); + next.addChild(t); + t = next; + } + if (idx < length - 1) { + Term last = new Term(DataConstraintModel.fst); + last.addChild(t); + t = last; + } + return t.toImplementation(new String[]{}); + } + + @Override + public String getDecomposedTuple(String tupleExp, VariableDeclaration tupleVar, List vars) { + String statements = ""; + statements += getVariableDeclaration(tupleVar.getType().getInterfaceTypeName(), tupleVar.getName()) + + getAssignment() + tupleExp + getStatementDelimiter(); + for (int i = 0; i < vars.size(); i++) { + VariableDeclaration var = vars.get(i); + statements += "\n" + getVariableDeclaration(var.getType().getInterfaceTypeName(), var.getName()) + + getAssignment() + + getTupleGet(tupleVar.getName(), i, vars.size()) + + getStatementDelimiter(); + } + return statements; + } + + @Override + public boolean isValueType(Type type) { + if (type == DataConstraintModel.typeInt + || type == DataConstraintModel.typeLong + || type == DataConstraintModel.typeFloat + || type == DataConstraintModel.typeDouble + || type == DataConstraintModel.typeBoolean) { + return true; + } + return false; + } + + + @Override + public boolean isVoidType(Type type) { + if (type == typeVoid) { + return true; + } + return false; + } + + private String getImplementationTypeName(Type type) { + if (type == null) + return "Object"; + String wrapperType = DataConstraintModel.getWrapperType(type); + if (wrapperType != null) + return wrapperType; + return type.getImplementationTypeName(); + } + + private String getInterfaceTypeName(Type type) { + if (type == null) + return "Object"; + String wrapperType = DataConstraintModel.getWrapperType(type); + if (wrapperType != null) + return wrapperType; + return type.getInterfaceTypeName(); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JerseyCodeGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/JerseyCodeGenerator.java index dbd0b45..9aa4b39 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/JerseyCodeGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/JerseyCodeGenerator.java @@ -1,8 +1,14 @@ package generators; +import java.util.AbstractMap; import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; +import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.Stack; import code.ast.Annotation; import code.ast.Block; @@ -24,7 +30,9 @@ import models.dataConstraintModel.Channel; import models.dataConstraintModel.ChannelMember; import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.ResourceHierarchy; import models.dataConstraintModel.ResourcePath; +import models.dataConstraintModel.Selector; import models.dataFlowModel.DataTransferModel; import models.dataFlowModel.DataTransferChannel; import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor; @@ -33,6 +41,7 @@ import models.dataFlowModel.DataFlowEdge; import models.dataFlowModel.DataFlowGraph; import models.dataFlowModel.ResourceNode; +import models.dataFlowModel.ChannelNode; import models.dataFlowModel.StoreAttribute; /** @@ -44,6 +53,7 @@ public class JerseyCodeGenerator { public static final Type typeVoid = new Type("Void", "void"); public static final Type typeClient = new Type("Client", "Client"); + public static boolean differentTreesAsDifferentServices = true; private static String defaultMainTypeName = "Main"; static String mainTypeName = defaultMainTypeName; @@ -59,154 +69,596 @@ JerseyCodeGenerator.mainTypeName = defaultMainTypeName; } + public static String getComponentName(ResourceHierarchy res) { + String name = res.getResourceName(); + if (res.getNumParameters() > 0) { + if (name.length() > 3 && name.endsWith("ies")) { + name = name.substring(0, name.length() - 3) + "y"; + } else if (name.length() > 1 && name.endsWith("s")) { + name = name.substring(0, name.length() - 1); + } else { + name += "Element"; + } + } + return name.substring(0, 1).toUpperCase() + name.substring(1); + } + + public static String toVariableName(String name) { + return name.substring(0, 1).toLowerCase() + name.substring(1); + } + + public static Type getImplStateType(ResourceHierarchy res) { + Set children = res.getChildren(); + if (children == null || children.size() == 0) { + // leaf resource. + return res.getResourceStateType(); + } else { + ResourceHierarchy child = children.iterator().next(); + if (children.size() == 1 && child.getNumParameters() > 0) { + // map or list. + if (DataConstraintModel.typeList.isAncestorOf(res.getResourceStateType())) { + // list. + if (generatesComponent(child)) { + return new Type("List", "ArrayList<>", "List<" + getComponentName(child) + ">", DataConstraintModel.typeList); + } else { + return new Type("List", "ArrayList<>", "List<" + getImplStateType(child).getImplementationTypeName() + ">", DataConstraintModel.typeList); + } + } else if (DataConstraintModel.typeMap.isAncestorOf(res.getResourceStateType())) { + // map. + if (generatesComponent(child)) { + return new Type("Map", "HashMap<>", "Map", DataConstraintModel.typeMap); + } else { + return new Type("Map", "HashMap<>", "Map", DataConstraintModel.typeMap); + } + } + return null; + } else { + // class + return res.getResourceStateType(); + } + } + } + + public static boolean generatesComponent(ResourceHierarchy res) { + return res.getParent() == null || !(res.getChildren() == null || res.getChildren().size() == 0); +// Type resType = res.getResourceStateType(); +// return DataConstraintModel.typeJson.isAncestorOf(resType) || DataConstraintModel.typeList.isAncestorOf(resType) || DataConstraintModel.typeMap.isAncestorOf(resType); + } + static public ArrayList doGenerate(DataFlowGraph graph, DataTransferModel model) { ArrayList codes = new ArrayList<>(); // ArrayList resources = StoreResourceCheck(graph); - Set resources = graph.getNodes(); + Collection resources = graph.getResourceNodes(); + Map resourceComponents = new HashMap<>(); + Map resourceConstructors = new HashMap<>(); + List> getters = new ArrayList<>(); + List> inputs = new ArrayList<>(); + List> fields = new ArrayList<>(); + Map getterAccessors = new HashMap<>(); + Map inputAccessors = new HashMap<>(); + List> constructorParams = new ArrayList<>(); + // For each resource node. for (Node n : resources) { - ResourceNode rn = (ResourceNode) n; - String resourceName = rn.getResource().getResourceName().substring(0, 1).toUpperCase() - + rn.getResource().getResourceName().substring(1); - - // Declare the field to refer each resource in the main type. - TypeDeclaration type = new TypeDeclaration(resourceName); - type.addAnnotation(new Annotation("Component")); - type.addAnnotation(new Annotation("Path", "\"/" + rn.getResource().getResourceName() + "\"")); - - // Declare a client field and update methods from other resources. - boolean bDeclareClientField = false; - for (Edge e : rn.getOutEdges()) { - DataFlowEdge re = (DataFlowEdge) e; - if (!bDeclareClientField && ((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { - // Declare a client field to connect to the destination resource of push transfer. - type.addField(new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()")); - bDeclareClientField = true; - } - } - for (Edge e : rn.getInEdges()) { - DataFlowEdge re = (DataFlowEdge) e; - ResourcePath srcRes = ((ResourceNode) re.getSource()).getResource(); - String srcResName = srcRes.getResourceName().substring(0, 1).toUpperCase() + srcRes.getResourceName().substring(1); - if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { - if (!bDeclareClientField) { - // Declare a client field to connect to the source resource of pull transfer. - type.addField(new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()")); - bDeclareClientField = true; + ResourceNode rn = (ResourceNode) n; + TypeDeclaration component = null; + if (generatesComponent(rn.getResourceHierarchy())) { + String resourceName = getComponentName(rn.getResourceHierarchy()); + + component = resourceComponents.get(rn.getResourceHierarchy()); + if (component == null) { + // Add compilation unit for each resource. + component = new TypeDeclaration(resourceName); + if (rn.getResourceHierarchy().getParent() == null) { + // For a root node. + component.addAnnotation(new Annotation("Component")); + component.addAnnotation(new Annotation("Path", "\"/" + rn.getResourceName() + "\"")); } - } else { - // Declare an update method in the type of the destination resource. - ArrayList vars = new ArrayList<>(); - String srcName = srcRes.getResourceName(); - Type srcType = srcRes.getResourceStateType(); - VariableDeclaration param = new VariableDeclaration(srcType, srcName); - param.addAnnotation(new Annotation("FormParam", "\"" + srcName + "\"")); - vars.add(param); - for (ResourcePath refRes: re.getChannel().getReferenceResources()) { - if (refRes != rn.getResource()) { - param = new VariableDeclaration(refRes.getResourceStateType(), refRes.getResourceName()); - param.addAnnotation(new Annotation("FormParam", "\"" + refRes.getResourceName() + "\"")); - vars.add(param); + resourceComponents.put(rn.getResourceHierarchy(), component); + + CompilationUnit cu = new CompilationUnit(component); + cu.addImport(new ImportDeclaration("java.util.*")); + if (rn.getResourceHierarchy().getParent() == null) { + // For a root node. + cu.addImport(new ImportDeclaration("javax.ws.rs.*")); + cu.addImport(new ImportDeclaration("javax.ws.rs.client.*")); + cu.addImport(new ImportDeclaration("javax.ws.rs.core.*")); + cu.addImport(new ImportDeclaration("org.springframework.stereotype.Component")); + cu.addImport(new ImportDeclaration("com.fasterxml.jackson.databind.ObjectMapper")); + cu.addImport(new ImportDeclaration("com.fasterxml.jackson.core.JsonProcessingException")); + } + codes.add(cu); + + // Declare a client field and update methods from other resources. + boolean bDeclareClientField = false; + for (Edge resToCh: rn.getOutEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(rn.getResourceHierarchy()) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } } - } - MethodDeclaration update = new MethodDeclaration("update" + srcResName, false, typeVoid, vars); - for (ChannelMember cm: re.getChannel().getOutputChannelMembers()) { - if (cm.getResource() == rn.getResource()) { - if (cm.getStateTransition().isRightUnary()) { - update.addAnnotation(new Annotation("PUT")); - } else { - update.addAnnotation(new Annotation("POST")); + if (!bDeclareClientField && ((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) { + for (ChannelMember cm: ch.getOutputChannelMembers()) { + ResourcePath dstRes = cm.getResource(); + String dstResName = null; + if (generatesComponent(dstRes.getResourceHierarchy())) { + dstResName = getComponentName(dstRes.getResourceHierarchy()); + } else { + dstResName = getComponentName(dstRes.getResourceHierarchy().getParent()); + } + if (rn.getOutSideResource().getCommonPrefix(dstRes) == null && differentTreesAsDifferentServices) { + // Inter-service + if (bDeclareClientField) { + // Declare a client field to connect to the destination resource of push transfer. + component.addField(new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()")); + bDeclareClientField = true; + } + } else { + // Inner-service + // Declare a field to directly refer to the destination resource of push transfer. + component.addField(new FieldDeclaration(new Type(dstResName, dstResName), toVariableName(dstResName))); + } } } } - if (rn.getInEdges().size() > 1) { - // For each source resource, a child resource is defined in the destination resource so that its state can be updated separately. - update.addAnnotation(new Annotation("Path", "\"/" + srcName + "\"")); - // Declare a field to cash the state of the source resource in the type of the destination resource. - ResourcePath cashResId = ((ResourceNode) re.getSource()).getResource(); - type.addField(new FieldDeclaration(cashResId.getResourceStateType(), srcName, getInitializer(cashResId))); + for (Edge chToRes : rn.getInEdges()) { + for (Edge resToCh: chToRes.getSource().getInEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(); + DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + // Check if the input and output resources are outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(srcRes.getResourceHierarchy()) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + boolean outsideOutputResource = false; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(rn.getOutSideResource().getResourceHierarchy()) && cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + String srcResName = null; + if (generatesComponent(srcRes.getResourceHierarchy())) { + srcResName = getComponentName(srcRes.getResourceHierarchy()); + } else { + srcResName = getComponentName(srcRes.getResourceHierarchy().getParent()); + } + if ((((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { + if (rn.getOutSideResource().getCommonPrefix(srcRes) == null && differentTreesAsDifferentServices) { + // Inter-service + if (!bDeclareClientField) { + // Declare a client field to connect to the source resource of pull transfer. + component.addField(new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()")); + bDeclareClientField = true; + } + } else { + // Inner-service + // Declare a field to directly refer to the source resource of pull transfer. + component.addField(new FieldDeclaration(new Type(srcResName, srcResName), toVariableName(srcResName))); + } + } else { + // Declare an update method in the type of the destination resource. + ArrayList vars = new ArrayList<>(); + String srcName = srcRes.getResourceName(); + Type srcType = srcRes.getResourceStateType(); + VariableDeclaration param = new VariableDeclaration(srcType, srcName); + param.addAnnotation(new Annotation("FormParam", "\"" + srcName + "\"")); + vars.add(param); + for (ResourcePath refRes: ((ChannelNode) re.getDestination()).getChannel().getReferenceResources()) { + if (!refRes.equals(rn.getOutSideResource())) { + param = new VariableDeclaration(refRes.getResourceStateType(), refRes.getResourceName()); + param.addAnnotation(new Annotation("FormParam", "\"" + refRes.getResourceName() + "\"")); + vars.add(param); + } + } + MethodDeclaration update = new MethodDeclaration("update" + srcResName, false, typeVoid, vars); + for (ChannelMember cm: ((ChannelNode) re.getDestination()).getChannel().getOutputChannelMembers()) { + if (rn.getInSideResources().contains(cm.getResource())) { + if (cm.getStateTransition().isRightUnary()) { + update.addAnnotation(new Annotation("PUT")); + } else { + update.addAnnotation(new Annotation("POST")); + } + } + } + if (re.getDestination().getIndegree() > 1) { + // For each source resource, a child resource is defined in the destination resource so that its state can be updated separately. + update.addAnnotation(new Annotation("Path", "\"/" + srcName + "\"")); + // Declare a field to cash the state of the source resource in the type of the destination resource. + ResourcePath cashRes = ((ResourceNode) re.getSource()).getOutSideResource(); + component.addField(new FieldDeclaration(cashRes.getResourceStateType(), srcName, getInitializer(cashRes))); + } + component.addMethod(update); + } + } } - type.addMethod(update); + } + +// // Declare a client field to connect to the source resource of reference transfer. +// if (!bDeclareClientField) { +// for (ChannelGenerator cg : model.getChannelGenerators()) { +// DataflowChannelGenerator dcg = ((DataflowChannelGenerator) cg); +// for (ChannelMember cm : dcg.getOutputChannelMembers()) { +// if (cm.getIdentifierTemplate().getResourceName().equals(type.getTypeName().toLowerCase())) { +// if (dcg.getReferenceChannelMembers().size() > 0) { +// // If there exists one or more reference channel member. +// type.addField(new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()")); +// bDeclareClientField = true; +// break; +// } +// } +// } +// if (bDeclareClientField) break; +// } +// } + + // Declare the field to store the state in the type of each resource. + if (((StoreAttribute) rn.getAttribute()).isStored()) { + ResourcePath res = rn.getOutSideResource(); + Set children = rn.getResourceHierarchy().getChildren(); + if (children == null || children.size() == 0) { + // leaf resource. + Type fieldType = getImplStateType(res.getResourceHierarchy()); + component.addField(new FieldDeclaration(fieldType, "value", getInitializer(res))); + constructorParams.add(new AbstractMap.SimpleEntry<>(rn.getResourceHierarchy(), new VariableDeclaration(fieldType, toVariableName(resourceName)))); + } else { + ResourceHierarchy child = children.iterator().next(); + if (children.size() == 1 && child.getNumParameters() > 0) { + // map or list. + component.addField(new FieldDeclaration(getImplStateType(res.getResourceHierarchy()), "value", getInitializer(res))); + } else { + // class + for (ResourceHierarchy c: children) { + String childTypeName = getComponentName(c); + Type childType = null; + if (generatesComponent(c)) { + // The child has a component. + childType = new Type(childTypeName, childTypeName); + String fieldName = toVariableName(childTypeName); + component.addField(new FieldDeclaration(childType, fieldName, getInitializer(res))); + } + } + } + } + } + + // Declare the getter methods to obtain the children resources. + for (ResourceNode child: rn.getChildren()) { + if (generatesComponent(child.getResourceHierarchy())) { + // The child generates a component. + List pathParams = new ArrayList<>(); + int v = 1; + for (Selector pathParam: child.getSelectors()) { + if (pathParam.getExpression() instanceof Variable) { + Variable var = (Variable) pathParam.getExpression(); + pathParams.add(new VariableDeclaration(var.getType(), var.getName())); + } else if (pathParam.getExpression() instanceof Term) { + Term var = (Term) pathParam.getExpression(); + pathParams.add(new VariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + String childCompName = getComponentName(child.getResourceHierarchy()); + Type childType = new Type(childCompName, childCompName); + MethodDeclaration childGetter = null; + if (pathParams.size() == 0) { + childGetter = new MethodDeclaration("get" + childCompName, childType); + } else { + childGetter = new MethodDeclaration("get" + childCompName, false, childType, pathParams); + } + component.addMethod(childGetter); + } } } -// // Declare a client field to connect to the source resource of reference transfer. -// if (!bDeclareClientField) { -// for (ChannelGenerator cg : model.getChannelGenerators()) { -// DataflowChannelGenerator dcg = ((DataflowChannelGenerator) cg); -// for (ChannelMember cm : dcg.getOutputChannelMembers()) { -// if (cm.getIdentifierTemplate().getResourceName().equals(type.getTypeName().toLowerCase())) { -// if (dcg.getReferenceChannelMembers().size() > 0) { -// // If there exists one or more reference channel member. -// type.addField(new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()")); -// bDeclareClientField = true; -// break; -// } -// } -// } -// if (bDeclareClientField) break; -// } -// } + // Declare the state field and reference fields in the parent component. + if (component == null) { + // Declare reference fields for push/pull data transfer. + boolean bDeclareClientField = false; + boolean noPullTransfer = true; + for (Edge resToCh : rn.getOutEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + for (Edge chToRes: re.getDestination().getOutEdges()) { + ResourcePath dstRes = ((ResourceNode) chToRes.getDestination()).getOutSideResource(); + boolean outsideOutputResource = false; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(dstRes.getResourceHierarchy()) && cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + if (outsideOutputResource) { + // Declare a field in the parent component to refer to the destination resource of push transfer. + String dstResName = null; + if (generatesComponent(dstRes.getResourceHierarchy())) { + dstResName = getComponentName(dstRes.getResourceHierarchy()); + } else { + dstResName = getComponentName(dstRes.getResourceHierarchy().getParent()); + } + if (rn.getOutSideResource().getCommonPrefix(dstRes) == null && differentTreesAsDifferentServices) { + // Inter-service + if (!bDeclareClientField) { + // Declare a client field to connect to the destination resource of push transfer. + FieldDeclaration clientField = new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()"); + fields.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), clientField)); + bDeclareClientField = true; + } + } else { + // Inner-service + // Declare a field to directly refer to the destination resource of push transfer. + FieldDeclaration refFieldForPush = new FieldDeclaration(new Type(dstResName, dstResName), toVariableName(dstResName)); + fields.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), refFieldForPush)); + } + } + } + } + for (Edge chToRes : rn.getInEdges()) { + for (Edge resToCh: chToRes.getSource().getInEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(); + DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(srcRes.getResourceHierarchy()) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + if (outsideInputResource) { + // Declare a field in the parent component to refer to the source resource of pull transfer. + String srcResName = null; + if (generatesComponent(srcRes.getResourceHierarchy())) { + srcResName = getComponentName(srcRes.getResourceHierarchy()); + } else { + srcResName = getComponentName(srcRes.getResourceHierarchy().getParent()); + } + if (rn.getOutSideResource().getCommonPrefix(srcRes) == null && differentTreesAsDifferentServices) { + // Inter-service + if (!bDeclareClientField) { + // Declare a client field to connect to the source resource of pull transfer. + FieldDeclaration clientField = new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()"); + fields.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), clientField)); + bDeclareClientField = true; + } + } else { + // Inner-service + // Declare a field to directly refer to the source resource of pull transfer. + FieldDeclaration refFieldForPull = new FieldDeclaration(new Type(srcResName, srcResName), toVariableName(srcResName)); + fields.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), refFieldForPull)); + noPullTransfer = false; + } + } + } + } + // Declare the state field in the parent component. + ResourceHierarchy res = rn.getOutSideResource().getResourceHierarchy(); + if (((StoreAttribute) rn.getAttribute()).isStored() && noPullTransfer && res.getNumParameters() == 0) { + String resName = getComponentName(res); + FieldDeclaration stateField = new FieldDeclaration(res.getResourceStateType(), toVariableName(resName)); + fields.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), stateField)); + constructorParams.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), new VariableDeclaration(res.getResourceStateType(), toVariableName(resName)))); + } + } - // Declare input methods in resources. - for (Channel cg : model.getIOChannels()) { - for (ChannelMember cm : ((DataTransferChannel) cg).getOutputChannelMembers()) { - if (cm.getResource().equals(rn.getResource())) { + // Declare the getter method to obtain the resource state in the component of each resource. + if (component != null) { + // A component is created for this resource. + MethodDeclaration stateGetter = new MethodDeclaration("getValue", getImplStateType(rn.getResourceHierarchy())); + if (rn.getResourceHierarchy().getParent() == null) { + // Since this getter is also an accessor. + stateGetter.addAnnotation(new Annotation("Produces", "MediaType.APPLICATION_JSON")); + stateGetter.addAnnotation(new Annotation("GET")); + } + component.addMethod(stateGetter); + } else { + // No component is created for this resource. + List pathParams = new ArrayList<>(); + int v = 1; + for (Selector pathParam: rn.getSelectors()) { + if (pathParam.getExpression() instanceof Variable) { + Variable var = (Variable) pathParam.getExpression(); + pathParams.add(new VariableDeclaration(var.getType(), var.getName())); + } else if (pathParam.getExpression() instanceof Term) { + Term var = (Term) pathParam.getExpression(); + pathParams.add(new VariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + String resCompName = getComponentName(rn.getResourceHierarchy()); + Type resType = getImplStateType(rn.getResourceHierarchy()); + MethodDeclaration stateGetter = null; + if (pathParams.size() == 0) { + stateGetter = new MethodDeclaration("get" + resCompName, resType); + } else { + stateGetter = new MethodDeclaration("get" + resCompName, false, resType, pathParams); + } + getters.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), stateGetter)); + } + + // Declare the getter accessor in the root resource. + if (rn.getResourceHierarchy().getParent() != null) { + // For a non-root resource + MethodDeclaration getterAccessor = null; + List mainGetterParams = new ArrayList<>(); + String resourcePath = getGetterResourcePathAndPathParams(rn.getOutSideResource(), mainGetterParams); + if (resourcePath.indexOf('/') > 0) { + resourcePath = "\"" + resourcePath.substring(resourcePath.indexOf('/')) + "\""; + } else { + resourcePath = "\"" + resourcePath + "\""; + } + if (mainGetterParams.size() > 0) { + getterAccessor = new MethodDeclaration("get" + getComponentName(rn.getResourceHierarchy()) + "Value", + false, + getImplStateType(rn.getResourceHierarchy()), + mainGetterParams); + } else { + getterAccessor = new MethodDeclaration("get" + getComponentName(rn.getResourceHierarchy()) + "Value", + getImplStateType(rn.getResourceHierarchy())); + } + getterAccessor.setBody(new Block()); + Expression getState = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(rn.getOutSideResource(), rn.getOutSideResource().getRoot()); + getterAccessor.getBody().addStatement("return " + getState.toImplementation(new String[] {null}) + ";"); + + getterAccessor.addAnnotation(new Annotation("Produces", "MediaType.APPLICATION_JSON")); + getterAccessor.addAnnotation(new Annotation("GET")); + if (rn.getAllSelectors().size() > 0) { + getterAccessor.addAnnotation(new Annotation("Path", resourcePath)); + } + getterAccessors.put(rn.getResourceHierarchy(), getterAccessor); + } + + // Declare the input method in each resource and the root resource. + for (Channel ch : model.getIOChannels()) { + for (ChannelMember cm : ((DataTransferChannel) ch).getOutputChannelMembers()) { + if (rn.getInSideResources().contains(cm.getResource())) { Expression message = cm.getStateTransition().getMessageExpression(); - if (message.getClass() == Term.class) { - ArrayList params = new ArrayList<>(); + if (message instanceof Term) { + // In each resource. + ArrayList resInputParams = new ArrayList<>(); + ArrayList rootInputParams = new ArrayList<>(); + String resourcePath = getInputMethodResourcePathaAndPathParams(cm.getResource(), resInputParams, rootInputParams); + if (resourcePath.indexOf('/') > 0) { + resourcePath = "\"" + resourcePath.substring(resourcePath.indexOf('/')) + "\""; + } else { + resourcePath = "\"" + resourcePath + "\""; + } for (Variable var: message.getVariables().values()) { String paramName = var.getName(); VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); + resInputParams.add(param); + param = new VariableDeclaration(var.getType(), paramName); param.addAnnotation(new Annotation("FormParam", "\"" + paramName + "\"")); - params.add(param); + rootInputParams.add(param); } - MethodDeclaration input = new MethodDeclaration( - ((Term) cm.getStateTransition().getMessageExpression()).getSymbol().getImplName(), - false, typeVoid, params); + + if (rn.getResourceHierarchy().getParent() != null && rn.getResourceHierarchy().getParent().getParent() != null) { + MethodDeclaration input = new MethodDeclaration(((Term) cm.getStateTransition().getMessageExpression()).getSymbol().getImplName(), + false, typeVoid, resInputParams); + if (component != null) { + // A component is created for this resource. + component.addMethod(input); + } else { + // No component is created for this resource. + inputs.add(new AbstractMap.SimpleEntry<>(rn.getParent().getResourceHierarchy(), input)); + } + } + + // For the root resource. + String str = ((Term) cm.getStateTransition().getMessageExpression()).getSymbol().getImplName(); + MethodDeclaration inputAccessor = new MethodDeclaration(str, false, typeVoid, rootInputParams); if (cm.getStateTransition().isRightUnary()) { - input.addAnnotation(new Annotation("PUT")); + inputAccessor.addAnnotation(new Annotation("PUT")); } else { - input.addAnnotation(new Annotation("POST")); + inputAccessor.addAnnotation(new Annotation("POST")); } - type.addMethod(input); - } else if (message.getClass() == Variable.class) { - MethodDeclaration input = new MethodDeclaration( - ((Variable) cm.getStateTransition().getMessageExpression()).getName(), - false, typeVoid, null); + if (ch.getAllSelectors().size() > 0) { + inputAccessor.addAnnotation(new Annotation("Path", resourcePath)); + } + inputAccessors.put(rn.getResourceHierarchy(), inputAccessor); + } else if (message instanceof Variable) { + // In each resource. + ArrayList resInputParams = new ArrayList<>(); + int v = 1; + if (cm.getResource().getLastParam() != null) { + Expression pathParam = cm.getResource().getLastParam(); + if (pathParam instanceof Variable) { + Variable var = (Variable) pathParam; + String paramName = var.getName(); + VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); + resInputParams.add(param); + } else if (pathParam instanceof Term) { + Term var = (Term) pathParam; + String paramName = "v" + v; + VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); + resInputParams.add(param); + } + v++; + } + if (cm.getResource().getResourceHierarchy().getParent() != null && cm.getResource().getResourceHierarchy().getParent().getParent() != null) { + MethodDeclaration input = new MethodDeclaration(((Variable) cm.getStateTransition().getMessageExpression()).getName(), + false, typeVoid, null); + if (component != null) { + // A component is created for this resource. + component.addMethod(input); + } else { + // No component is created for this resource. + inputs.add(new AbstractMap.SimpleEntry<>(cm.getResource().getParent().getResourceHierarchy(), input)); + } + } + + // For the root resource. + ArrayList rootInputParams = new ArrayList<>(); + String resourcePath = getGetterResourcePathAndPathParams(cm.getResource(), rootInputParams); + if (resourcePath.indexOf('/') > 0) { + resourcePath = "\"" + resourcePath.substring(resourcePath.indexOf('/')) + "\""; + } else { + resourcePath = "\"" + resourcePath + "\""; + } + String str = ((Term) cm.getStateTransition().getMessageExpression()).getSymbol().getImplName(); + MethodDeclaration inputAccessor = new MethodDeclaration(str, false, typeVoid, rootInputParams); if (cm.getStateTransition().isRightUnary()) { - input.addAnnotation(new Annotation("PUT")); + inputAccessor.addAnnotation(new Annotation("PUT")); } else { - input.addAnnotation(new Annotation("POST")); + inputAccessor.addAnnotation(new Annotation("POST")); } - type.addMethod(input); + if (ch.getAllSelectors().size() > 0) { + inputAccessor.addAnnotation(new Annotation("Path", resourcePath)); + } + inputAccessors.put(rn.getResourceHierarchy(), inputAccessor); } } } } - - // Declare the field to store the state in the type of each resource. - if (((StoreAttribute) rn.getAttribute()).isStored()) { - ResourcePath resId = rn.getResource(); - type.addField(new FieldDeclaration(resId.getResourceStateType(), "value", getInitializer(resId))); + } + + // Add leaf getter methods to the parent components. + for (Map.Entry entry: getters) { + resourceComponents.get(entry.getKey()).addMethod(entry.getValue()); + } + + // Add leaf input methods to the parent components. + for (Map.Entry entry: inputs) { + resourceComponents.get(entry.getKey()).addMethod(entry.getValue()); + } + + // Add leaf reference fields to the parent components. + for (Map.Entry entry: fields) { + resourceComponents.get(entry.getKey()).addField(entry.getValue()); + } + + // Add constructor parameters to the ancestor components. + for (ResourceNode root: graph.getRootResourceNodes()) { + addConstructorParameters(root.getResourceHierarchy(), resourceComponents, resourceConstructors, constructorParams); + } + + // Add accessors. + for (ResourceHierarchy rootRes: model.getResourceHierarchies()) { + if (rootRes.getParent() == null) { + // root resource + TypeDeclaration rootComponent = resourceComponents.get(rootRes); + // Add getter accessors. + for (ResourceHierarchy res: getterAccessors.keySet()) { + if (rootRes.isAncestorOf(res)) { + rootComponent.addMethod(getterAccessors.get(res)); + } + } + // Add input accessors. + for (ResourceHierarchy res: inputAccessors.keySet()) { + if (rootRes.isAncestorOf(res)) { + rootComponent.addMethod(inputAccessors.get(res)); + } + } } - - // Declare the getter method to obtain the state in the type of each resource. - MethodDeclaration getter = new MethodDeclaration("getValue", rn.getResource().getResourceStateType()); - getter.addAnnotation(new Annotation("Produces", "MediaType.APPLICATION_JSON")); - getter.addAnnotation(new Annotation("GET")); - type.addMethod(getter); - - // Add compilation unit for each resource. - CompilationUnit cu = new CompilationUnit(type); - cu.addImport(new ImportDeclaration("java.util.*")); - cu.addImport(new ImportDeclaration("javax.ws.rs.*")); - cu.addImport(new ImportDeclaration("javax.ws.rs.client.*")); - cu.addImport(new ImportDeclaration("javax.ws.rs.core.*")); - cu.addImport(new ImportDeclaration("org.springframework.stereotype.Component")); - cu.addImport(new ImportDeclaration("com.fasterxml.jackson.databind.ObjectMapper")); - cu.addImport(new ImportDeclaration("com.fasterxml.jackson.core.JsonProcessingException")); - codes.add(cu); } // Declare the Pair class. @@ -214,7 +666,7 @@ for(Node n : resources) { ResourceNode rn = (ResourceNode) n; if(isCreatedPair) continue; - if(model.getType("Pair").isAncestorOf(rn.getResource().getResourceStateType())) { + if(model.getType("Pair").isAncestorOf(rn.getResourceStateType())) { TypeDeclaration type = new TypeDeclaration("Pair"); type.addField(new FieldDeclaration(new Type("Double", "T"), "left")); type.addField(new FieldDeclaration(new Type("Double", "T"), "right")); @@ -253,12 +705,122 @@ return codes; } + + private static List addConstructorParameters(ResourceHierarchy resource, + Map resourceComponents, + Map resourceConstructors, + List> constructorParams) { + List params = new ArrayList<>(); + for (ResourceHierarchy child: resource.getChildren()) { + params.addAll(addConstructorParameters(child, resourceComponents, resourceConstructors, constructorParams)); + } + for (Map.Entry paramEnt: constructorParams) { + if (paramEnt.getKey().equals(resource)) { + params.add(paramEnt.getValue()); + } + } + if (params.size() > 0) { + MethodDeclaration constructor = resourceConstructors.get(resource); + if (constructor == null) { + if (resourceComponents.get(resource) != null) { + String resourceName = getComponentName(resource); + constructor = new MethodDeclaration(resourceName, true); + Block body = new Block(); + constructor.setBody(body); + resourceComponents.get(resource).addMethod(constructor); + resourceConstructors.put(resource, constructor); + } + } + if (constructor != null) { + for (VariableDeclaration param: params) { + constructor.addParameter(param); + constructor.getBody().addStatement("this." + toVariableName(param.getName()) + " = " + toVariableName(param.getName()) + ";"); + } + } + } + if (resource.getNumParameters() > 0) params.clear(); + return params; + } + + private static String getGetterResourcePathAndPathParams(ResourcePath resPath, List pathParams) { + int v = 1; + List params = new ArrayList<>(); + for (Expression pathParam: resPath.getPathParams()) { + if (pathParam instanceof Variable) { + Variable var = (Variable) pathParam; + String paramName = var.getName(); + params.add("{" + paramName + "}"); + VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); + param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\"")); + pathParams.add(param); + } else if (pathParam instanceof Term) { + Term var = (Term) pathParam; + String paramName = "v" + v; + params.add("{" + paramName + "}"); + VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); + param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\"")); + pathParams.add(param); + } + v++; + } + return resPath.getResourceHierarchy().toResourcePath(params); + } + + private static String getInputMethodResourcePathaAndPathParams(ResourcePath resPath, ArrayList resInputParams, + ArrayList rootInputParams) { + int v = 1; + List params = new ArrayList<>(); + if (resPath.getLastParam() != null) { + Expression pathParam = resPath.getLastParam(); + if (pathParam instanceof Variable) { + Variable var = (Variable) pathParam; + String paramName = var.getName(); + params.add("{" + paramName + "}"); + VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); + resInputParams.add(param); + param = new VariableDeclaration(var.getType(), paramName); + param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\"")); + rootInputParams.add(param); + } else if (pathParam instanceof Term) { + Term var = (Term) pathParam; + String paramName = "v" + v; + params.add("{" + paramName + "}"); + VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); + resInputParams.add(param); + param = new VariableDeclaration(var.getType(), paramName); + param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\"")); + rootInputParams.add(param); + } + v++; + } + if (resPath.getParent() != null) { + for (Expression pathParam: resPath.getParent().getPathParams()) { + if (pathParam instanceof Variable) { + Variable var = (Variable) pathParam; + String paramName = var.getName(); + params.add("{" + paramName + "}"); + VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); + param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\"")); + rootInputParams.add(param); + } else if (pathParam instanceof Term) { + Term var = (Term) pathParam; + String paramName = "v" + v; + params.add("{" + paramName + "}"); + VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); + param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\"")); + rootInputParams.add(param); + } + v++; + } + } + return resPath.getResourceHierarchy().toResourcePath(params); + } private static String getInitializer(ResourcePath resId) { Type stateType = resId.getResourceStateType(); String initializer = null; - if (resId.getInitialValue() != null) { - initializer = resId.getInitialValue().toImplementation(new String[] {""}); + if (resId.getResourceHierarchy().getInitialValue() != null) { + initializer = resId.getResourceHierarchy().getInitialValue().toImplementation(new String[] {""}); } else { if (DataConstraintModel.typeList.isAncestorOf(stateType)) { initializer = "new " + resId.getResourceStateType().getImplementationTypeName() + "()"; @@ -316,41 +878,123 @@ static public IResourceStateAccessor pushAccessor = new IResourceStateAccessor() { @Override - public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) { - if (target.equals(from)) { + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + ResourcePath fromRes = from.getResource(); + if (targetRes.equals(fromRes)) { return new Field("value", - target.getResourceStateType() != null ? target.getResourceStateType() + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } return null; } @Override - public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { - return new Parameter(target.getResourceName(), - target.getResourceStateType() != null ? target.getResourceStateType() - : DataConstraintModel.typeInt); - } - }; - static public IResourceStateAccessor pullAccessor = new IResourceStateAccessor() { - @Override - public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) { - if (target.equals(from)) { - return new Field("value", - target.getResourceStateType() != null ? target.getResourceStateType() - : DataConstraintModel.typeInt); - } - // for reference channel member - return new Parameter(target.getResourceName(), - target.getResourceStateType() != null ? target.getResourceStateType() + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + return new Parameter(targetRes.getResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } @Override - public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { - return new Parameter(target.getResourceName(), - target.getResourceStateType() != null ? target.getResourceStateType() + public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { + if (fromRes != null && targetRes.equals(fromRes)) { + return new Field("value", + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + return null; + } + }; + static public IResourceStateAccessor pullAccessor = new IResourceStateAccessor() { + @Override + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + if (from != null && !target.isOutside()) { + ResourcePath fromRes = from.getResource(); + if (targetRes.getCommonPrefix(fromRes) != null) { + return getDirectStateAccessorFor(targetRes, fromRes); + } + } + return new Parameter(targetRes.getResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } + + @Override + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + if (from != null && !target.isOutside()) { + ResourcePath fromRes = from.getResource(); + if (targetRes.getCommonPrefix(fromRes) != null) { + return getDirectStateAccessorFor(targetRes, fromRes); + } + } + return new Parameter(targetRes.getResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { + if (fromRes != null && !fromRes.getResourceHierarchy().isAncestorOf(targetRes.getResourceHierarchy())) { + if (targetRes.equals(fromRes)) { + return new Field("value", + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + return new Parameter(targetRes.getResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } else { + // access from an ancestor or outside of the hierarchy + Stack pathStack = new Stack<>(); + ResourcePath curPath = targetRes; + do { + if (fromRes != null && curPath.equals(fromRes)) break; + pathStack.push(curPath); + curPath = curPath.getParent(); + } while (curPath != null); + // iterate from the `from' resource + Term getter = null; + int v = 1; + while (!pathStack.empty()) { + curPath = pathStack.pop(); + String typeName = getComponentName(curPath.getResourceHierarchy()); + if (getter == null && fromRes == null) { + // root resource + String fieldName = toVariableName(typeName); + getter = new Field(fieldName, new Type(typeName, typeName)); + } else { + Term newGetter = new Term(new Symbol("get" + typeName, -1, Symbol.Type.METHOD)); + newGetter.addChild(getter); + if (curPath.getResourceHierarchy().getNumParameters() > 0) { + Variable var = null; + Expression param = curPath.getLastParam(); + if (param instanceof Variable) { + var = (Variable) param; + } else if (param instanceof Term) { + var = new Variable("v" + v, ((Term) param).getType()); + } + if (var != null) { + newGetter.addChild(var); + newGetter.getSymbol().setArity(2); + } + v++; + } + getter = newGetter; + } + } + + if (generatesComponent(targetRes.getResourceHierarchy())) { + Term newGetter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); + newGetter.addChild(getter); + getter = newGetter; + } + return getter; + } + } }; } diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java index cdaf898..35c57b8 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java @@ -4,8 +4,10 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import algorithms.TypeInference; @@ -15,10 +17,13 @@ import code.ast.TypeDeclaration; import code.ast.VariableDeclaration; import models.Edge; -import models.Node; +import models.algebra.Constant; import models.algebra.Expression; +import models.algebra.Field; import models.algebra.InvalidMessage; import models.algebra.ParameterizedIdentifierIsFutureWork; +import models.algebra.Position; +import models.algebra.Symbol; import models.algebra.Term; import models.algebra.Type; import models.algebra.UnificationFailed; @@ -27,12 +32,15 @@ import models.dataConstraintModel.Channel; import models.dataConstraintModel.ChannelMember; import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.JsonAccessor; +import models.dataConstraintModel.ResourceHierarchy; import models.dataConstraintModel.ResourcePath; import models.dataFlowModel.DataTransferModel; import models.dataFlowModel.DataTransferChannel; import models.dataFlowModel.PushPullAttribute; import models.dataFlowModel.PushPullValue; import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork; +import models.dataFlowModel.ChannelNode; import models.dataFlowModel.DataFlowEdge; import models.dataFlowModel.DataFlowGraph; import models.dataFlowModel.ResourceNode; @@ -44,10 +52,10 @@ public static ArrayList doGenerate(DataFlowGraph graph, DataTransferModel model, ArrayList codes) { // Create a map from type names (lower case) to their types. - Map typeMap = new HashMap<>(); + Map componentMap = new HashMap<>(); for (CompilationUnit code: codes) { - for (TypeDeclaration type: code.types()) { - typeMap.put(type.getTypeName().substring(0,1).toLowerCase() + type.getTypeName().substring(1), type); + for (TypeDeclaration component: code.types()) { + componentMap.put(component.getTypeName(), component); } } @@ -56,263 +64,464 @@ Set chainedCalls = new HashSet<>(); Map> referredResources = new HashMap<>(); for (Edge e: graph.getEdges()) { - DataFlowEdge d = (DataFlowEdge) e; - PushPullAttribute pushPull = (PushPullAttribute) d.getAttribute(); - ResourceNode src = (ResourceNode) d.getSource(); - ResourceNode dst = (ResourceNode) d.getDestination(); - String srcResourceName = src.getResource().getResourceName(); - String dstResourceName = dst.getResource().getResourceName(); - TypeDeclaration srcType = typeMap.get(srcResourceName); - TypeDeclaration dstType = typeMap.get(dstResourceName); - for (ChannelMember out: d.getChannel().getOutputChannelMembers()) { - if (out.getResource() == dst.getResource()) { - if (pushPull.getOptions().get(0) == PushPullValue.PUSH && srcType != null) { - // for push data transfer - MethodDeclaration update = getUpdateMethod(dstType, srcType); - if (((StoreAttribute) dst.getAttribute()).isStored()) { - // update stored state of dst side resource (when every incoming edge is in push style) - Expression updateExp = null; - if (d.getChannel().getReferenceChannelMembers().size() == 0) { - updateExp = d.getChannel().deriveUpdateExpressionOf(out, JerseyCodeGenerator.pushAccessor); - } else { - // if there exists one or more reference channel member. - HashMap inputResourceToStateAccessor = new HashMap<>(); - for (Edge eIn: dst.getInEdges()) { - DataFlowEdge dIn = (DataFlowEdge) eIn; - inputResourceToStateAccessor.put(((ResourceNode) dIn.getSource()).getResource(), JerseyCodeGenerator.pushAccessor); + DataFlowEdge resToCh = (DataFlowEdge) e; + if (!resToCh.isChannelToResource()) { + PushPullAttribute pushPull = (PushPullAttribute) resToCh.getAttribute(); + ResourceNode src = (ResourceNode) resToCh.getSource(); + for (Edge chToRes: resToCh.getDestination().getOutEdges()) { + ResourceNode dst = (ResourceNode) chToRes.getDestination(); + String srcResourceName = JerseyCodeGenerator.getComponentName(src.getResourceHierarchy()); + String dstResourceName = JerseyCodeGenerator.getComponentName(dst.getResourceHierarchy()); + TypeDeclaration srcComponent = componentMap.get(srcResourceName); + TypeDeclaration dstComponent = componentMap.get(dstResourceName); + DataTransferChannel ch = ((ChannelNode) resToCh.getDestination()).getChannel(); + for (ChannelMember out: ch.getOutputChannelMembers()) { + if (dst.getInSideResources().contains(out.getResource())) { + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(src.getResourceHierarchy()) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; } - for (ChannelMember c: d.getChannel().getReferenceChannelMembers()) { - inputResourceToStateAccessor.put(c.getResource(), JerseyCodeGenerator.pullAccessor); + } + // Check if the output resource is outside of the channel scope. + boolean outsideOutputResource = false; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (cm.getResource().getResourceHierarchy().equals(dst.getResourceHierarchy()) && cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; } - updateExp = d.getChannel().deriveUpdateExpressionOf(out, JerseyCodeGenerator.pushAccessor, inputResourceToStateAccessor); } - String[] sideEffects = new String[] {""}; - String curState = updateExp.toImplementation(sideEffects); - String updateStatement; - if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { - updateStatement = sideEffects[0]; - } else { - updateStatement = sideEffects[0] + "this.value = " + curState + ";"; - } - if (update.getBody() == null || !update.getBody().getStatements().contains(updateStatement)) { - // add an update statement of the state of dst side resource. - update.addFirstStatement(updateStatement); - } - } - if (dst.getIndegree() > 1) { - // update a cash of src side resource (when incoming edges are multiple) - String cashStatement = "this." + srcResourceName + " = " + srcResourceName + ";"; - if (update.getBody() == null || !update.getBody().getStatements().contains(cashStatement)) { - update.addFirstStatement(cashStatement); - } - } - // to convert a json param to a tuple, pair or map 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 = convertFromEntryToMapType(compType); - paramConverter += "\t" + mapTypeName + " i = new ObjectMapper().readValue(str, HashMap.class);\n"; - paramConverter += "\t" + paramName + ".add(" + getCodeForConversionFromMapToTuple(compType, "i") + ");\n"; - paramConverter += "}"; - update.addThrow("JsonProcessingException"); - } else if (DataConstraintModel.typePair.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 = convertFromEntryToMapType(compType); - paramConverter += "\t" + mapTypeName + " i = new ObjectMapper().readValue(str, HashMap.class);\n"; - paramConverter += "\t" + paramName + ".add(" + getCodeForConversionFromMapToPair(compType, "i") + ");\n"; - paramConverter += "}"; - update.addThrow("JsonProcessingException"); - } else if (DataConstraintModel.typeMap.isAncestorOf(compType)) { - param.setType(DataConstraintModel.typeListStr); - // To do. + if (pushPull.getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) { + // for push data transfer + MethodDeclaration update = getUpdateMethod(dstComponent, srcComponent); + if (((StoreAttribute) dst.getAttribute()).isStored()) { + // update stored state of dst side resource (when every incoming edge is in push style) + Expression updateExp = null; + if (ch.getReferenceChannelMembers().size() == 0) { + updateExp = ch.deriveUpdateExpressionOf(out, JerseyCodeGenerator.pushAccessor); + } else { + // if there exists one or more reference channel member. + HashMap inputResourceToStateAccessor = new HashMap<>(); + for (Edge chToRes2: dst.getInEdges()) { + DataTransferChannel ch2 = ((ChannelNode) chToRes2.getSource()).getChannel(); + for (Edge resToCh2: chToRes2.getSource().getInEdges()) { + DataFlowEdge dIn = (DataFlowEdge) resToCh2; + ChannelMember in = null; + for (ChannelMember cm: ch2.getInputChannelMembers()) { + if (cm.getResource().equals(((ResourceNode) dIn.getSource()).getOutSideResource())) { + in = cm; + break; + } + } + inputResourceToStateAccessor.put(in, JerseyCodeGenerator.pushAccessor); + } + } + for (ChannelMember c: ch.getReferenceChannelMembers()) { + inputResourceToStateAccessor.put(c, JerseyCodeGenerator.pullAccessor); + } + updateExp = ch.deriveUpdateExpressionOf(out, JerseyCodeGenerator.pushAccessor, inputResourceToStateAccessor); + } + // Replace Json constructor with a constructor of the child resource. + ResourceHierarchy outRes = out.getResource().getResourceHierarchy(); + if (outRes.getChildren().size() == 1 && outRes.getChildren().iterator().next().getNumParameters() > 0) { + ResourceHierarchy childRes = outRes.getChildren().iterator().next(); + Type childStateType = childRes.getResourceStateType(); + String childComponentName = JerseyCodeGenerator.getComponentName(childRes); + TypeDeclaration childComponent = componentMap.get(childComponentName); + if (DataConstraintModel.typeJson.isAncestorOf(childStateType)) { + replaceJsonTermWithConstructorInvocation(updateExp, childStateType, childComponentName, childComponent); + } + } + // Add statements to the update method. + String[] sideEffects = new String[] {""}; + String curState = updateExp.toImplementation(sideEffects); + String updateStatement; + if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { + updateStatement = sideEffects[0]; + } else { + updateStatement = sideEffects[0] + "this.value = " + curState + ";"; + } + if (update.getBody() == null || !update.getBody().getStatements().contains(updateStatement)) { + // add an update statement of the state of dst side resource. + update.addFirstStatement(updateStatement); + } } - } else if (DataConstraintModel.typeTuple.isAncestorOf(paramType)) { - param.setType(DataConstraintModel.typeString); - param.setName(paramName + "_json"); - paramConverter += paramType.getInterfaceTypeName() + " " + paramName + ";\n"; - paramConverter += "{\n"; - String mapTypeName = convertFromEntryToMapType(paramType); - paramConverter += "\t" + mapTypeName + " i = new ObjectMapper().readValue(" + paramName + "_json" + ", HashMap.class);\n"; - paramConverter += "\t" + paramName + " = " + getCodeForConversionFromMapToTuple(paramType, "i") + ";\n"; - paramConverter += "}"; - update.addThrow("JsonProcessingException"); - } else if (DataConstraintModel.typePair.isAncestorOf(paramType)) { - param.setType(DataConstraintModel.typeString); - param.setName(paramName + "_json"); - paramConverter += paramType.getInterfaceTypeName() + " " + paramName + ";\n"; - paramConverter += "{\n"; - String mapTypeName = convertFromEntryToMapType(paramType); - paramConverter += "\t" + mapTypeName + " i = new ObjectMapper().readValue(" + paramName + "_json" + ", HashMap.class);\n"; - paramConverter += "\t" + paramName + " = " + getCodeForConversionFromMapToPair(paramType, "i") + ";\n"; - paramConverter += "}"; - update.addThrow("JsonProcessingException"); - } else if (DataConstraintModel.typeMap.isAncestorOf(paramType)) { - param.setType(DataConstraintModel.typeString); - param.setName(paramName + "_json"); - paramConverter += paramType.getInterfaceTypeName() + " " + paramName + " = " + "new " + paramType.getImplementationTypeName() + "();\n"; - paramConverter += "{\n"; - String mapTypeName = convertFromEntryToMapType(paramType); - paramConverter += "\t" + mapTypeName + " i = new ObjectMapper().readValue(" + paramName + "_json" + ", HashMap.class);\n"; - paramConverter += "\t" + getCodeForConversionFromMapToMap(paramType, "i", paramName) + "\n"; - paramConverter += "}"; - update.addThrow("JsonProcessingException"); - } - if (paramConverter.length() > 0) update.addFirstStatement(paramConverter); - } - MethodDeclaration getter = getGetterMethod(dstType); - if (((StoreAttribute) dst.getAttribute()).isStored()) { - // returns the state stored in a field. - if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { - getter.addStatement("return value;"); - } - } - // src side (for a chain of update method invocations) - String httpMethod = null; - if (out.getStateTransition().isRightUnary()) { - httpMethod = "put"; - } else { - httpMethod = "post"; - } - for (MethodDeclaration srcUpdate: getUpdateMethods(srcType)) { - if (srcUpdate != null) { - List>> params = new ArrayList<>(); - Set referredSet = referredResources.get(srcUpdate); - if (d.getChannel().getReferenceChannelMembers().size() > 0) { - for (ChannelMember rc: d.getChannel().getReferenceChannelMembers()) { + if (resToCh.getDestination().getIndegree() > 1) { + // update a cash of src side resource (when incoming edges are multiple) + String cashStatement = "this." + JerseyCodeGenerator.toVariableName(srcResourceName) + " = " + JerseyCodeGenerator.toVariableName(srcResourceName) + ";"; + if (update.getBody() == null || !update.getBody().getStatements().contains(cashStatement)) { + update.addFirstStatement(cashStatement); + } + } + // to convert a json param to a tuple, pair or map 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 = convertFromEntryToMapType(compType); + paramConverter += "\t" + mapTypeName + " i = new ObjectMapper().readValue(str, HashMap.class);\n"; + paramConverter += "\t" + paramName + ".add(" + getCodeForConversionFromMapToTuple(compType, "i") + ");\n"; + paramConverter += "}"; + update.addThrow("JsonProcessingException"); + } else if (DataConstraintModel.typePair.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 = convertFromEntryToMapType(compType); + paramConverter += "\t" + mapTypeName + " i = new ObjectMapper().readValue(str, HashMap.class);\n"; + paramConverter += "\t" + paramName + ".add(" + getCodeForConversionFromMapToPair(compType, "i") + ");\n"; + paramConverter += "}"; + update.addThrow("JsonProcessingException"); + } else if (DataConstraintModel.typeMap.isAncestorOf(compType)) { + param.setType(DataConstraintModel.typeListStr); + // To do. + } + } else if (DataConstraintModel.typeTuple.isAncestorOf(paramType)) { + param.setType(DataConstraintModel.typeString); + param.setName(paramName + "_json"); + paramConverter += paramType.getInterfaceTypeName() + " " + paramName + ";\n"; + paramConverter += "{\n"; + String mapTypeName = convertFromEntryToMapType(paramType); + paramConverter += "\t" + mapTypeName + " i = new ObjectMapper().readValue(" + paramName + "_json" + ", HashMap.class);\n"; + paramConverter += "\t" + paramName + " = " + getCodeForConversionFromMapToTuple(paramType, "i") + ";\n"; + paramConverter += "}"; + update.addThrow("JsonProcessingException"); + } else if (DataConstraintModel.typePair.isAncestorOf(paramType)) { + param.setType(DataConstraintModel.typeString); + param.setName(paramName + "_json"); + paramConverter += paramType.getInterfaceTypeName() + " " + paramName + ";\n"; + paramConverter += "{\n"; + String mapTypeName = convertFromEntryToMapType(paramType); + paramConverter += "\t" + mapTypeName + " i = new ObjectMapper().readValue(" + paramName + "_json" + ", HashMap.class);\n"; + paramConverter += "\t" + paramName + " = " + getCodeForConversionFromMapToPair(paramType, "i") + ";\n"; + paramConverter += "}"; + update.addThrow("JsonProcessingException"); + } else if (DataConstraintModel.typeMap.isAncestorOf(paramType)) { + param.setType(DataConstraintModel.typeString); + param.setName(paramName + "_json"); + paramConverter += paramType.getInterfaceTypeName() + " " + paramName + " = " + "new " + paramType.getImplementationTypeName() + "();\n"; + paramConverter += "{\n"; + String mapTypeName = convertFromEntryToMapType(paramType); + paramConverter += "\t" + mapTypeName + " i = new ObjectMapper().readValue(" + paramName + "_json" + ", HashMap.class);\n"; + paramConverter += "\t" + getCodeForConversionFromMapToMap(paramType, "i", paramName) + "\n"; + paramConverter += "}"; + update.addThrow("JsonProcessingException"); + } + if (paramConverter.length() > 0) update.addFirstStatement(paramConverter); + } + if (((StoreAttribute) dst.getAttribute()).isStored()) { + // returns the state stored in a field. + if (dstComponent == null) { + String dstParentResourceName = JerseyCodeGenerator.getComponentName(dst.getResourceHierarchy().getParent()); + dstComponent = componentMap.get(dstParentResourceName); + } + MethodDeclaration getter = null; + if (JerseyCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { + getter = getMethod(dstComponent, "getValue"); + } else { + getter = getGetterMethod(dstComponent, dstResourceName); + } + if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { + if (dst.getResourceHierarchy().getNumParameters() == 0) { + getter.addStatement("return value;"); + } else { + if (DataConstraintModel.typeList.isAncestorOf(dst.getParent().getResourceStateType())) { + Term selector = new Term(DataConstraintModel.get); + selector.addChild(new Field("value")); + selector.addChild(dst.getSelectors().get(dst.getSelectors().size() - 1).getExpression()); + getter.addStatement("return " + selector.toImplementation(new String[] {}) + ";"); + } else if (DataConstraintModel.typeMap.isAncestorOf(dst.getParent().getResourceStateType())) { + Term selector = new Term(DataConstraintModel.lookup); + selector.addChild(new Field("value")); + selector.addChild(dst.getSelectors().get(dst.getSelectors().size() - 1).getExpression()); + getter.addStatement("return " + selector.toImplementation(new String[] {}) + ";"); + } + } + } + } + // src side (for a chain of update method invocations) + String httpMethod = null; + if (out.getStateTransition().isRightUnary()) { + httpMethod = "put"; + } else { + httpMethod = "post"; + } + for (MethodDeclaration srcUpdate: getUpdateMethods(srcComponent)) { + if (srcUpdate != null) { + List>> params = new ArrayList<>(); + Set referredSet = referredResources.get(srcUpdate); + if (ch.getReferenceChannelMembers().size() > 0) { + for (ChannelMember rc: 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 (referredSet == null) { + referredSet = new HashSet<>(); + referredResources.put(srcUpdate, referredSet); + } + if (!dst.getInSideResources().contains(ref)) { + String refResourceName = ref.getResourceName(); + Type refResourceType = ref.getResourceStateType(); + if (!referredSet.contains(ref)) { + referredSet.add(ref); + generatePullDataTransfer(srcUpdate, refResourceName, refResourceName, refResourceType); + } + // Value of a reference side resource. + params.add(new AbstractMap.SimpleEntry<>(refResourceType, new AbstractMap.SimpleEntry<>(refResourceName, refResourceName))); + } + } + } + String srcResName = null; + if (dst.getIndegree() > 1) { + srcResName = srcResourceName; + } + if (!chainedCalls.contains(srcUpdate)) { + // The first call to an update method in this method + // Value of the source side (input side) resource. + params.add(0, new AbstractMap.SimpleEntry<>(src.getResourceStateType(), new AbstractMap.SimpleEntry<>(srcResourceName, "this.value"))); + srcUpdate.addStatement(getHttpMethodParamsStatement(srcComponent.getTypeName(), params, true)); + srcUpdate.addStatement("String result = " + getHttpMethodCallStatement(baseURL, dstResourceName, srcResName, httpMethod)); + chainedCalls.add(srcUpdate); + } else { + // After the second time of call to update methods in this method + // Value of the source side (input side) resource. + params.add(0, new AbstractMap.SimpleEntry<>(src.getResourceStateType(), new AbstractMap.SimpleEntry<>(srcResourceName, "this.value"))); + srcUpdate.addStatement(getHttpMethodParamsStatement(srcComponent.getTypeName(), params, false)); + srcUpdate.addStatement("result = " + getHttpMethodCallStatement(baseURL, dstResourceName, srcResName, httpMethod)); + } + srcUpdate.addThrow("JsonProcessingException"); + } + } + for (MethodDeclaration srcInput: getInputMethods(srcComponent, src, model)) { + List>> params = new ArrayList<>(); + Set referredSet = referredResources.get(srcInput); + for (ChannelMember rc: 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 (referredSet == null) { referredSet = new HashSet<>(); - referredResources.put(srcUpdate, referredSet); + referredResources.put(srcInput, referredSet); } - if (ref != dst.getResource()) { + if (!dst.getInSideResources().contains(ref)) { String refResourceName = ref.getResourceName(); Type refResourceType = ref.getResourceStateType(); if (!referredSet.contains(ref)) { referredSet.add(ref); - generatePullDataTransfer(srcUpdate, refResourceName, refResourceType); + generatePullDataTransfer(srcInput, refResourceName, refResourceName, refResourceType); } // Value of a reference side resource. params.add(new AbstractMap.SimpleEntry<>(refResourceType, new AbstractMap.SimpleEntry<>(refResourceName, refResourceName))); + } + } + String srcResName = null; + if (dst.getIndegree() > 1) { + srcResName = srcResourceName; + } + if (!chainedCalls.contains(srcInput)) { + // First call to an update method in this method + // Value of the source side (input side) resource. + params.add(0, new AbstractMap.SimpleEntry<>(src.getResourceStateType(), new AbstractMap.SimpleEntry<>(srcResourceName, "this.value"))); + srcInput.addStatement(getHttpMethodParamsStatement(srcComponent.getTypeName(), params, true)); + srcInput.addStatement("String result = " + getHttpMethodCallStatement(baseURL, dstResourceName, srcResName, httpMethod)); + chainedCalls.add(srcInput); + } else { + // After the second time of call to update methods in this method + // Value of the source side (input side) resource. + params.add(0, new AbstractMap.SimpleEntry<>(src.getResourceStateType(), new AbstractMap.SimpleEntry<>(srcResourceName, "this.value"))); + srcInput.addStatement(getHttpMethodParamsStatement(srcComponent.getTypeName(), params, false)); + srcInput.addStatement("result = " + getHttpMethodCallStatement(baseURL, dstResourceName, srcResName, httpMethod)); + } + srcInput.addThrow("JsonProcessingException"); + } + } else if ((pushPull.getOptions().get(0) != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { + // for pull (or push/pull) data transfer + if (dstComponent == null) { + String dstParentResourceName = JerseyCodeGenerator.getComponentName(dst.getResourceHierarchy().getParent()); + dstComponent = componentMap.get(dstParentResourceName); + } + MethodDeclaration getter = null; + if (JerseyCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { + getter = getMethod(dstComponent, "getValue"); + } else { + getter = getGetterMethod(dstComponent, dstResourceName); + } + if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { + // generate a return statement. + Expression curExp = ch.deriveUpdateExpressionOf(out, JerseyCodeGenerator.pullAccessor); // no pull data transfer is included. + Map>> resourcePaths = ch.fillOutsideResourcePaths(out, JerseyCodeGenerator.pullAccessor); + 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 c: ((ChannelNode) resToCh.getDestination()).getChannel().getReferenceChannelMembers()) { + String refResourceName = c.getResource().getResourceName(); + Type refResourceType = c.getResource().getResourceStateType(); + generatePullDataTransfer(getter, refResourceName, refResourceName, refResourceType); + } + 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.getOutSideResource()) == null) { + Type srcResourceType = src2.getResourceStateType(); + List pathParams = new ArrayList<>(); + for (Expression pathExp: src2.getPathParams()) { + pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); + } + generatePullDataTransfer(getter, src2.getResourceName(), src2.getResourceHierarchy().toResourcePath(pathParams), srcResourceType); } } } - String srcResName = null; - if (dst.getIndegree() > 1) { - srcResName = srcResourceName; + // get src resource state by pull data transfer. + if (src.getNumberOfParameters() == 0 && src.getOutSideResource().getCommonPrefix(dst.getOutSideResource()) == null) { + Type srcResourceType = src.getResourceStateType(); + List pathParams = new ArrayList<>(); + generatePullDataTransfer(getter, src.getResourceName(), src.getResourceHierarchy().toResourcePath(pathParams), srcResourceType); } - if (!chainedCalls.contains(srcUpdate)) { - // The first call to an update method in this method - // Value of the source side (input side) resource. - params.add(0, new AbstractMap.SimpleEntry<>(src.getResource().getResourceStateType(), new AbstractMap.SimpleEntry<>(srcResourceName, "this.value"))); - srcUpdate.addStatement(getHttpMethodParamsStatement(srcType.getTypeName(), params, true)); - srcUpdate.addStatement("String result = " + getHttpMethodCallStatement(baseURL, dstResourceName, srcResName, httpMethod)); - chainedCalls.add(srcUpdate); - } else { - // After the second time of call to update methods in this method - // Value of the source side (input side) resource. - params.add(0, new AbstractMap.SimpleEntry<>(src.getResource().getResourceStateType(), new AbstractMap.SimpleEntry<>(srcResourceName, "this.value"))); - srcUpdate.addStatement(getHttpMethodParamsStatement(srcType.getTypeName(), params, false)); - srcUpdate.addStatement("result = " + getHttpMethodCallStatement(baseURL, dstResourceName, srcResName, httpMethod)); - } - srcUpdate.addThrow("JsonProcessingException"); - } + } } - for (MethodDeclaration srcInput: getInputMethods(srcType, src, model)) { - List>> params = new ArrayList<>(); - Set referredSet = referredResources.get(srcInput); - for (ChannelMember rc: d.getChannel().getReferenceChannelMembers()) { - // For each reference channel member, get the current state of the reference side resource by pull data transfer. - ResourcePath ref = rc.getResource(); - if (referredSet == null) { - referredSet = new HashSet<>(); - referredResources.put(srcInput, referredSet); - } - if (ref != dst.getResource()) { - String refResourceName = ref.getResourceName(); - Type refResourceType = ref.getResourceStateType(); - if (!referredSet.contains(ref)) { - referredSet.add(ref); - generatePullDataTransfer(srcInput, refResourceName, refResourceType); - } - // Value of a reference side resource. - params.add(new AbstractMap.SimpleEntry<>(refResourceType, new AbstractMap.SimpleEntry<>(refResourceName, refResourceName))); - } - } - String srcResName = null; - if (dst.getIndegree() > 1) { - srcResName = srcResourceName; - } - if (!chainedCalls.contains(srcInput)) { - // First call to an update method in this method - // Value of the source side (input side) resource. - params.add(0, new AbstractMap.SimpleEntry<>(src.getResource().getResourceStateType(), new AbstractMap.SimpleEntry<>(srcResourceName, "this.value"))); - srcInput.addStatement(getHttpMethodParamsStatement(srcType.getTypeName(), params, true)); - srcInput.addStatement("String result = " + getHttpMethodCallStatement(baseURL, dstResourceName, srcResName, httpMethod)); - chainedCalls.add(srcInput); - } else { - // After the second time of call to update methods in this method - // Value of the source side (input side) resource. - params.add(0, new AbstractMap.SimpleEntry<>(src.getResource().getResourceStateType(), new AbstractMap.SimpleEntry<>(srcResourceName, "this.value"))); - srcInput.addStatement(getHttpMethodParamsStatement(srcType.getTypeName(), params, false)); - srcInput.addStatement("result = " + getHttpMethodCallStatement(baseURL, dstResourceName, srcResName, httpMethod)); - } - srcInput.addThrow("JsonProcessingException"); - } - } else { - // for pull (or push/pull) data transfer - MethodDeclaration getter = getGetterMethod(dstType); - if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { - // generate a return statement. - String[] sideEffects = new String[] {""}; - String curState = d.getChannel().deriveUpdateExpressionOf(out, JerseyCodeGenerator.pullAccessor).toImplementation(sideEffects); // no pull data transfer is included. - 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 c: d.getChannel().getReferenceChannelMembers()) { - String refResourceName = c.getResource().getResourceName(); - Type refResourceType = c.getResource().getResourceStateType(); - generatePullDataTransfer(getter, refResourceName, refResourceType); - } - } - // get src side resource state by pull data transfer. - Type srcResourceType = src.getResource().getResourceStateType(); - generatePullDataTransfer(getter, srcResourceName, srcResourceType); - } + } } } } // for source nodes - for (Node n: graph.getNodes()) { - ResourceNode resource = (ResourceNode) n; - String resourceName = resource.getResource().getResourceName(); - TypeDeclaration type = typeMap.get(resourceName); - if (type != null) { - // getter method - MethodDeclaration getter = getGetterMethod(type); - if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { - getter.addStatement("return value;"); + for (ResourceHierarchy resource: model.getResourceHierarchies()) { + String resourceName = JerseyCodeGenerator.getComponentName(resource); + TypeDeclaration component = componentMap.get(resourceName); + if (component != null) { + // state getter method + Type resourceType = JerseyCodeGenerator.getImplStateType(resource); + MethodDeclaration stateGetter = getMethod(component, "getValue"); + if (stateGetter.getBody() == null || stateGetter.getBody().getStatements().size() == 0) { + if (model.isPrimitiveType(resourceType)) { + // primitive type + stateGetter.addStatement("return value;"); + } else { + if (resource.getChildren() != null && resource.getChildren().size() == 1 && resource.getChildren().iterator().next().getNumParameters() > 0) { + // list or map + String implTypeName = resourceType.getImplementationTypeName(); + // copy the current state to be returned as a 'value' + stateGetter.addStatement("return new " + implTypeName + "(value);"); + } else { + if (resource.getChildren() == null || resource.getChildren().size() == 0) { + // a leaf resource + String implTypeName = resourceType.getImplementationTypeName(); + stateGetter.addStatement("return new " + implTypeName + "(value);"); + } else { + Term composer = null; + Term composerSub = new Constant(DataConstraintModel.nil); + composerSub.setType(DataConstraintModel.typeMap); + for (ResourceHierarchy child: resource.getChildren()) { + String childTypeName = JerseyCodeGenerator.getComponentName(child); + String fieldName = JerseyCodeGenerator.toVariableName(childTypeName); + Term childGetter = null; + if ((child.getChildren() == null || child.getChildren().size() == 0) && child.getNumParameters() == 0) { + // the child is not a class + childGetter = new Term(new Symbol("get" + childTypeName, 1, Symbol.Type.METHOD)); + childGetter.addChild(new Constant("this")); + } else { + // the child is a class + childGetter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); + childGetter.addChild(new Field(fieldName, JerseyCodeGenerator.getImplStateType(child))); + } + composer = new Term(DataConstraintModel.insert); + composer.addChild(composerSub); + composer.addChild(new Constant("\"" + fieldName + "\"", DataConstraintModel.typeString)); // key + composer.addChild(childGetter); // value + composer.setType(DataConstraintModel.typeMap); + composerSub = composer; + } + composer.setType(stateGetter.getReturnType()); + String[] sideEffects = new String[] {null}; + String returnValue = composer.toImplementation(sideEffects); + if (sideEffects[0] != null) { + stateGetter.addStatement(sideEffects[0] + "return " + returnValue+ ";"); + } else { + stateGetter.addStatement("return " + returnValue+ ";"); + } + } + } + } } - // methods for input events - Map> ioChannelsAndMembers = getIOChannelsAndMembers(resource, model); - for (Map.Entry> entry: ioChannelsAndMembers.entrySet()) { - Set outs = entry.getValue(); - for (ChannelMember out: outs) { - MethodDeclaration input = getInputMethod(type, out); - if (input != null) { - Expression updateExp = entry.getKey().deriveUpdateExpressionOf(out, JerseyCodeGenerator.pushAccessor); - String[] sideEffects = new String[] {""}; - String newState = updateExp.toImplementation(sideEffects); + + // child getter method + if (resource.getChildren().size() > 0) { + for (ResourceHierarchy child: resource.getChildren()) { + String methodName = "get" + JerseyCodeGenerator.getComponentName(child); + MethodDeclaration childGetter = getMethod(component, methodName); + if (childGetter != null && (childGetter.getBody() == null || childGetter.getBody().getStatements().size() == 0)) { + if (DataConstraintModel.typeList.isAncestorOf(resource.getResourceStateType())) { + Term selector = new Term(DataConstraintModel.get); + selector.addChild(new Field("value")); + selector.addChild(new Variable(childGetter.getParameters().get(childGetter.getParameters().size() - 1).getName())); + selector.setType(childGetter.getReturnType()); + String[] sideEffects = new String[] {null}; + String returnValue = selector.toImplementation(sideEffects); + if (sideEffects[0] != null) childGetter.addStatement(sideEffects[0]); + childGetter.addStatement("return " + returnValue + ";"); + } else if (DataConstraintModel.typeMap.isAncestorOf(resource.getResourceStateType())) { + Term selector = new Term(DataConstraintModel.lookup); + selector.addChild(new Field("value")); + selector.addChild(new Variable(childGetter.getParameters().get(childGetter.getParameters().size() - 1).getName())); + selector.setType(childGetter.getReturnType()); + String[] sideEffects = new String[] {null}; + String returnValue = selector.toImplementation(sideEffects); + if (sideEffects[0] != null) childGetter.addStatement(sideEffects[0]); + childGetter.addStatement("return " + returnValue+ ";"); + } else { + String fieldName = JerseyCodeGenerator.getComponentName(child); + String returnValue = JerseyCodeGenerator.toVariableName(fieldName); + childGetter.addStatement("return this." + returnValue + ";"); + } + } + } + } + } + + // methods for input events + Map> ioChannelsAndMembers = getIOChannelsAndMembers(resource, model); + for (Map.Entry> entry: ioChannelsAndMembers.entrySet()) { + Set outs = entry.getValue(); + for (ChannelMember out: outs) { + MethodDeclaration input = null; + if (JerseyCodeGenerator.generatesComponent(resource)) { + // A component is generated for this resource. + input = getInputMethod(component, out); + } else { + // No component is generated for this resource. + ResourceHierarchy parent = resource.getParent(); + if (parent != null) { + TypeDeclaration parentType = componentMap.get(JerseyCodeGenerator.getComponentName(parent)); + input = getInputMethod(parentType, out); + } + } + if (input != null) { + // In each resource + Expression updateExp = entry.getKey().deriveUpdateExpressionOf(out, JerseyCodeGenerator.pushAccessor); + // Replace Json constructor with a constructor of the child resource. + ResourceHierarchy outRes = out.getResource().getResourceHierarchy(); + if (outRes.getChildren().size() == 1 && outRes.getChildren().iterator().next().getNumParameters() > 0) { + ResourceHierarchy childRes = outRes.getChildren().iterator().next(); + Type childStateType = childRes.getResourceStateType(); + String childComponentName = JerseyCodeGenerator.getComponentName(childRes); + TypeDeclaration childComponent = componentMap.get(childComponentName); + if (DataConstraintModel.typeJson.isAncestorOf(childStateType)) { + replaceJsonTermWithConstructorInvocation(updateExp, childStateType, childComponentName, childComponent); + } + } + // Add statements to the input method. + String[] sideEffects = new String[] {""}; + String newState = updateExp.toImplementation(sideEffects); + if (JerseyCodeGenerator.generatesComponent(resource)) { String updateStatement; if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { updateStatement = sideEffects[0]; @@ -322,6 +531,62 @@ if (input.getBody() == null || !input.getBody().getStatements().contains(updateStatement)) { input.addFirstStatement(updateStatement); } + } else { + String updateStatement = null; + if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { + // ToDo. + updateStatement = sideEffects[0]; + } else { + if (DataConstraintModel.typeList.isAncestorOf(resource.getParent().getResourceStateType())) { + Term selector = new Term(DataConstraintModel.set); + selector.addChild(new Field("value")); + selector.addChild(new Variable(input.getParameters().get(input.getParameters().size() - 2).getName())); + selector.addChild(new Constant(newState)); + String[] sideEffects2 = new String[] {""}; + String newList = selector.toImplementation(sideEffects2); + updateStatement = sideEffects[0] + sideEffects2[0]; + } else if (DataConstraintModel.typeMap.isAncestorOf(resource.getParent().getResourceStateType())) { + Term selector = new Term(DataConstraintModel.insert); + selector.addChild(new Field("value")); + selector.addChild(new Variable(input.getParameters().get(input.getParameters().size() - 2).getName())); + selector.addChild(new Constant(newState)); + String[] sideEffects2 = new String[] {""}; + String newMap = selector.toImplementation(sideEffects2); + updateStatement = sideEffects[0] + sideEffects2[0]; + } else { + updateStatement = sideEffects[0] + "this." + JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(resource)) + " = " + newState + ";"; + } + if (updateStatement != null && (input.getBody() == null || !input.getBody().getStatements().contains(updateStatement))) { + input.addFirstStatement(updateStatement); + } + } + } + + if (out.getResource().getParent() != null && out.getResource().getParent().getParent() != null) { + // In the root resource + MethodDeclaration inputAccessor = getMethod(componentMap.get(JerseyCodeGenerator.getComponentName(resource.getRoot())), input.getName()); + if (inputAccessor != null) { + Expression resExp = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(out.getResource(), out.getResource().getRoot()); + String args = ""; + String delimiter = ""; + if (resExp instanceof Term) { + // to access the parent + if (((Term) resExp).getChildren().size() > 1 && ((Term) resExp).getChild(1) instanceof Variable) { + args += delimiter + ((Variable)((Term) resExp).getChild(1)).getName(); + delimiter = ", "; + } + resExp = ((Term) resExp).getChild(0); + } + String resourceAccess = resExp.toImplementation(new String[] {null}); + if (out.getStateTransition().getMessageExpression() instanceof Term) { + Term message = (Term) out.getStateTransition().getMessageExpression(); + for (Variable var: message.getVariables().values()) { + args += delimiter + var.getName(); + delimiter = ", "; + } + } + inputAccessor.addStatement(resourceAccess + "." + input.getName() + "(" + args + ");"); + } } } } @@ -334,7 +599,37 @@ return codes; } - private static void generatePullDataTransfer(MethodDeclaration methodBody, String fromResourceName, Type fromResourceType) { + private static void replaceJsonTermWithConstructorInvocation(Expression exp, Type replacedJsonType, String replacingClassName, TypeDeclaration childComponent) { + Map subTerms = ((Term) exp).getSubTerms(Term.class); + Iterator> termEntItr = subTerms.entrySet().iterator(); + while (termEntItr.hasNext()) { + Entry termEnt = termEntItr.next(); + Term jsonTerm = termEnt.getValue(); + if (jsonTerm.getType().equals(replacedJsonType)) { + String constructorInvocation = "new " + replacingClassName + "("; + MethodDeclaration childConstructor = getConstructor(childComponent); + String delimiter = ""; + for (VariableDeclaration var: childConstructor.getParameters()) { + JsonAccessor jsonMember = new JsonAccessor(DataConstraintModel.dot); + jsonMember.addChild(jsonTerm); + jsonMember.addChild(new Constant("\"" + var.getName() + "\"")); + Expression param = jsonMember.reduce(); + if (param != null) { + constructorInvocation = constructorInvocation + delimiter + param.toImplementation(new String[] {""}); + } else { + constructorInvocation = constructorInvocation + delimiter + var.getName(); + } + delimiter = ", "; + } + constructorInvocation += ")"; + ((Term) exp).replaceSubTerm(termEnt.getKey(), new Constant(constructorInvocation)); + subTerms = ((Term) exp).getSubTerms(Term.class); + termEntItr = subTerms.entrySet().iterator(); + } + } + } + + private static void generatePullDataTransfer(MethodDeclaration methodBody, String fromResourceName, String fromResourcePath, Type fromResourceType) { String varName = new String(fromResourceName); String respTypeName = fromResourceType.getInterfaceTypeName(); String respImplTypeName = fromResourceType.getImplementationTypeName(); @@ -373,7 +668,7 @@ if (respConverter.length() > 0) { methodBody.addFirstStatement(respConverter); } - methodBody.addFirstStatement(respTypeName + " " + varName + " = " + getHttpMethodCallStatementWithResponse(baseURL, fromResourceName, "get", respImplTypeName)); + methodBody.addFirstStatement(respTypeName + " " + varName + " = " + getHttpMethodCallStatementWithResponse(baseURL, fromResourcePath, "get", respImplTypeName)); } private static String convertFromEntryToMapType(Type type) { @@ -516,37 +811,44 @@ return "client.target(\"" + baseURL + "\").path(\"/" + resourceName + "\").request()." + httpMethod + "(" + responseShortTypeName + ".class);"; } - private static MethodDeclaration getUpdateMethod(TypeDeclaration type, TypeDeclaration from) { - for (MethodDeclaration m: type.getMethods()) { + private static MethodDeclaration getConstructor(TypeDeclaration component) { + for (MethodDeclaration m: component.getMethods()) { + if (m.isConstructor()) return m; + } + return null; + } + + private static MethodDeclaration getUpdateMethod(TypeDeclaration component, TypeDeclaration from) { + for (MethodDeclaration m: component.getMethods()) { if (m.getName().equals("update" + from.getTypeName())) return m; } return null; } - private static List getUpdateMethods(TypeDeclaration type) { + private static MethodDeclaration getGetterMethod(TypeDeclaration component, String resourceName) { + for (MethodDeclaration m: component.getMethods()) { + if (m.getName().startsWith("get" + resourceName)) return m; + } + return null; + } + + private static List getUpdateMethods(TypeDeclaration component) { List updates = new ArrayList<>(); - for (MethodDeclaration m: type.getMethods()) { + for (MethodDeclaration m: component.getMethods()) { if (m.getName().startsWith("update")) { updates.add(m); } } return updates; } - - private static MethodDeclaration getGetterMethod(TypeDeclaration type) { - for (MethodDeclaration m: type.getMethods()) { - if (m.getName().startsWith("get")) return m; - } - return null; - } - private static Map> getIOChannelsAndMembers(ResourceNode resource, DataTransferModel model) { + private static Map> getIOChannelsAndMembers(ResourceHierarchy resource, DataTransferModel model) { Map> ioChannelsAndMembers = new HashMap<>(); for (Channel c: model.getIOChannels()) { DataTransferChannel ch = (DataTransferChannel) c; // I/O channel for (ChannelMember out: ch.getOutputChannelMembers()) { - if (out.getResource().equals(resource.getResource())) { + if (resource.equals(out.getResource().getResourceHierarchy())) { if (out.getStateTransition().getMessageExpression() instanceof Term || out.getStateTransition().getMessageExpression() instanceof Variable) { Set channelMembers = ioChannelsAndMembers.get(ch); if (channelMembers == null) { @@ -561,14 +863,14 @@ return ioChannelsAndMembers; } - private static List getInputMethods(TypeDeclaration type, ResourceNode resource, DataTransferModel model) { + private static List getInputMethods(TypeDeclaration component, ResourceNode resource, DataTransferModel model) { List inputs = new ArrayList<>(); for (Channel c: model.getIOChannels()) { DataTransferChannel channel = (DataTransferChannel) c; // I/O channel for (ChannelMember out: channel.getOutputChannelMembers()) { - if (out.getResource().equals(resource.getResource())) { - MethodDeclaration input = getInputMethod(type, out); + if (resource.getInSideResources().contains(out.getResource())) { + MethodDeclaration input = getInputMethod(component, out); inputs.add(input); } } @@ -576,20 +878,20 @@ return inputs; } - private static MethodDeclaration getInputMethod(TypeDeclaration type, ChannelMember out) { + private static MethodDeclaration getInputMethod(TypeDeclaration component, ChannelMember out) { MethodDeclaration input = null; if (out.getStateTransition().getMessageExpression() instanceof Term) { Term message = (Term) out.getStateTransition().getMessageExpression(); - input = getMethod(type, message.getSymbol().getImplName()); + input = getMethod(component, message.getSymbol().getImplName()); } else if (out.getStateTransition().getMessageExpression() instanceof Variable) { Variable message = (Variable) out.getStateTransition().getMessageExpression(); - input = getMethod(type, message.getName()); + input = getMethod(component, message.getName()); } return input; } - private static MethodDeclaration getMethod(TypeDeclaration type, String methodName) { - for (MethodDeclaration m: type.getMethods()) { + private static MethodDeclaration getMethod(TypeDeclaration component, String methodName) { + for (MethodDeclaration m: component.getMethods()) { if (m.getName().equals(methodName)) return m; } return null; diff --git a/AlgebraicDataflowArchitectureModel/src/models/algebra/Constant.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/Constant.java index 440145e..cc1ffee 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/algebra/Constant.java +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Constant.java @@ -36,7 +36,7 @@ public String toImplementation(String[] sideEffects) { if (symbol.isImplGenerative()) { - String exp = symbol.generate(getType(), new String[] {}, new String[] {}, sideEffects); + String exp = symbol.generate(getType(), new String[] {}, new String[] {}, sideEffects); return exp; } return symbol.getImplName(); diff --git a/AlgebraicDataflowArchitectureModel/src/models/algebra/Expression.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/Expression.java index f0aeadb..7695979 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/algebra/Expression.java +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Expression.java @@ -4,7 +4,18 @@ public abstract class Expression implements Cloneable { public abstract Expression getSubTerm(Position pos); + /** + * Get the unification between this expression and another expression. + * @param another another expression + * @return unified expression + */ public abstract Expression unify(Expression another); + /** + * Get the inverse map to obtain a sub-term of a given output value back from the output value itself. + * @param outputValue an output value (usually a term) + * @param targetPos a position in outputValue + * @return inverse map + */ public abstract Expression getInverseMap(Expression outputValue, Position targetPos); public abstract boolean contains(Expression exp); public abstract Object clone(); diff --git a/AlgebraicDataflowArchitectureModel/src/models/algebra/LambdaAbstraction.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/LambdaAbstraction.java new file mode 100644 index 0000000..9898224 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/LambdaAbstraction.java @@ -0,0 +1,30 @@ +package models.algebra; + +import java.util.ArrayList; +import java.util.List; + +public class LambdaAbstraction extends Symbol { + private List variables = null; + private Term term = null; + + public LambdaAbstraction(Variable variable, Term term) { + super("($" + variable.getName() + ")->" + term.toString(), 1, Type.LAMBDA); + this.variables = new ArrayList<>(); + this.variables.add(variable); + this.term = term; + } + + public LambdaAbstraction(List variables, Term term) { + super("($" + variables + ")->" + term.toString(), variables.size(), Type.LAMBDA); + this.variables = variables; + this.term = term; + } + + public List getVariables() { + return variables; + } + + public Term getTerm() { + return term; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/algebra/Position.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/Position.java index 0a7c10b..de90a73 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/algebra/Position.java +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Position.java @@ -28,6 +28,14 @@ public boolean isEmpty() { return (orders == null || orders.size() == 0); } + + public boolean isAncestorOf(Position another) { + if (another.orders.size() < this.orders.size()) return false; + for (int i = 0; i < orders.size(); i++) { + if (this.orders.get(i) != another.orders.get(i)) return false; + } + return true; + } public Object clone() { return new Position((ArrayList) orders.clone()); diff --git a/AlgebraicDataflowArchitectureModel/src/models/algebra/Symbol.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/Symbol.java index 5a06873..2efb24a 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/algebra/Symbol.java +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Symbol.java @@ -1,15 +1,15 @@ package models.algebra; public class Symbol { - private String name; - private String implName; - private int arity = 0; // -1: variable number - private Type operatorType = Type.PREFIX; - private Type implOperatorType = Type.PREFIX; - private Symbol[] inverses = null; - private models.algebra.Type[] signature = null; - private int[] implParamOrder = null; - private IImplGenerator generator = null; + protected String name; + protected String implName; + protected int arity = 0; // -1: variable number + protected Type operatorType = Type.PREFIX; + protected Type implOperatorType = Type.PREFIX; + protected Symbol[] inverses = null; + protected models.algebra.Type[] signature = null; + protected int[] implParamOrder = null; + protected IImplGenerator generator = null; public Symbol(String name) { this.name = name; @@ -89,6 +89,10 @@ public boolean isMethod() { return (operatorType == Type.METHOD || operatorType == Type.METHOD_WITH_SIDE_EFFECT); } + + public boolean isLambda() { + return (operatorType == Type.LAMBDA); + } public Symbol[] getInverses() { return inverses; diff --git a/AlgebraicDataflowArchitectureModel/src/models/algebra/Term.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/Term.java index b7cad09..3c15d21 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/algebra/Term.java +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Term.java @@ -1,12 +1,14 @@ package models.algebra; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map.Entry; public class Term extends Expression { protected Symbol symbol = null; - protected ArrayList children = new ArrayList<>(); + protected List children = new ArrayList<>(); protected Type type = null; public Term(Symbol symbol) { @@ -14,12 +16,18 @@ this.symbol = symbol; } - public Term(Symbol symbol, ArrayList children) { + public Term(Symbol symbol, List children) { super(); this.symbol = symbol; this.children = children; } + public Term(Symbol symbol, Expression[] children) { + super(); + this.symbol = symbol; + this.children = new ArrayList<>(Arrays.asList(children)); + } + public Symbol getSymbol() { return symbol; } @@ -55,7 +63,7 @@ return children.get(n); } - public ArrayList getChildren() { + public List getChildren() { return children; } @@ -123,6 +131,55 @@ } } + public Expression reduce() { + if (symbol.isLambda()) { + // Lambda beta-reduction + LambdaAbstraction newSymbol = ((LambdaAbstraction) symbol); + Term newTerm = newSymbol.getTerm(); + List newVariables = newSymbol.getVariables(); + List newChildren = children; + while (newVariables.size() > 0 && newChildren.size() > 0) { + newTerm = newTerm.substitute(newVariables.get(0), newChildren.get(0)); + newVariables = newVariables.subList(1, newVariables.size()); + newChildren = newChildren.subList(1, newChildren.size()); + newSymbol = new LambdaAbstraction(newVariables, newTerm); + } + if (newSymbol.arity == 0 && newChildren.size() == 0) { + return newTerm; + } else { + return new Term(newSymbol, newChildren); + } + } else { + // Calculate inverse map + List newChildren = new ArrayList<>(); + boolean bReduced = false; + for (Expression child: children) { + if (child instanceof Term && !(child instanceof Constant)) { + child = ((Term) (child)).reduce(); + bReduced = true; + } + newChildren.add(child); + } + if (symbol.arity == 1 && newChildren.size() == 1) { + Expression child = newChildren.get(0); + if (child instanceof Term && !(child instanceof Constant)) { + Symbol childSymbol = ((Term) child).getSymbol(); + if (childSymbol.getInverses() != null) { + for (int i = 0; i < childSymbol.getInverses().length; i++) { + if (symbol.equals(childSymbol.getInverses()[i])) { + return ((Term) child).getChild(i); + } + } + } + } + } + if (!bReduced) return this; + Term newTerm = new Term(symbol, newChildren); + newTerm.setType(type); + return newTerm; + } + } + @Override public Expression getInverseMap(Expression outputValue, Position targetPos) { if (targetPos.isEmpty()) return outputValue; @@ -185,7 +242,7 @@ if (getArity() == 2 && symbol.isInfix()) { return "(" + children.get(0) + symbol.toString() + children.get(1) + ")"; } - if (getArity() >= 1 && symbol.isMethod()) { + if ((getArity() >= 1 || getArity() == -1) && symbol.isMethod()) { String exp = children.get(0).toString() + "." + symbol.toString() + "("; String delimiter = ""; for (int i = 1; i < children.size(); i++) { @@ -268,9 +325,14 @@ return "(" + children.get(implParamOrder[0]).toImplementation(sideEffects) + symbol.toImplementation() + children.get(implParamOrder[1]).toImplementation(sideEffects) + ")"; } } - if (getArity() >= 1 && symbol.isImplMethod()) { + if ((getArity() >= 1 || getArity() == -1) && symbol.isImplMethod()) { if (implParamOrder == null) { - String exp = children.get(0).toImplementation(sideEffects) + "." + symbol.toImplementation() + "("; + String exp = null; + if (children.get(0) != null) { + exp = children.get(0).toImplementation(sideEffects) + "." + symbol.toImplementation() + "("; + } else { + exp = symbol.toImplementation() + "("; + } String delimiter = ""; for (int i = 1; i < children.size(); i++) { Expression e = children.get(i); @@ -280,7 +342,11 @@ exp += ")"; if (symbol.isImplWithSideEffect()) { sideEffects[0] = sideEffects[0] + exp + ";\n"; - exp = children.get(0).toImplementation(new String[] {""}); + if (children.get(0) != null) { + exp = children.get(0).toImplementation(new String[] {""}); + } else { + exp = ""; + } } return exp; } else { diff --git a/AlgebraicDataflowArchitectureModel/src/models/algebra/Type.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/Type.java index 50b0285..e408a54 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/algebra/Type.java +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Type.java @@ -9,28 +9,28 @@ private String interfaceTypeName; private List parentTypes = new ArrayList<>(); - public Type(String typeName, String implementastionTypeName) { + public Type(String typeName, String implementationTypeName) { this.typeName = typeName; - this.implementationTypeName = implementastionTypeName; - this.interfaceTypeName = implementastionTypeName; + this.implementationTypeName = implementationTypeName; + this.interfaceTypeName = implementationTypeName; } - public Type(String typeName, String implementastionTypeName, String interfaceTypeName) { + public Type(String typeName, String implementationTypeName, String interfaceTypeName) { this.typeName = typeName; - this.implementationTypeName = implementastionTypeName; + this.implementationTypeName = implementationTypeName; this.interfaceTypeName = interfaceTypeName; } - public Type(String typeName, String implementastionTypeName, Type parentType) { + public Type(String typeName, String implementationTypeName, Type parentType) { this.typeName = typeName; - this.implementationTypeName = implementastionTypeName; - this.interfaceTypeName = implementastionTypeName; + this.implementationTypeName = implementationTypeName; + this.interfaceTypeName = implementationTypeName; this.parentTypes.add(parentType); } - public Type(String typeName, String implementastionTypeName, String interfaceTypeName, Type parentType) { + public Type(String typeName, String implementationTypeName, String interfaceTypeName, Type parentType) { this.typeName = typeName; - this.implementationTypeName = implementastionTypeName; + this.implementationTypeName = implementationTypeName; this.interfaceTypeName = interfaceTypeName; this.parentTypes.add(parentType); } @@ -79,6 +79,17 @@ } return false; } + + @Override + public boolean equals(Object another) { + if (this == another) return true; + if (another == null) return false; + if (!(another instanceof Type)) return false; + if (!typeName.equals(((Type) another).typeName)) return false; + if (!implementationTypeName.equals(((Type) another).implementationTypeName)) return false; + if (!interfaceTypeName.equals(((Type) another).interfaceTypeName)) return false; + return true; + } public Memento createMemento() { return new Memento(implementationTypeName, interfaceTypeName, parentTypes); diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Channel.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Channel.java index 8bf74da..a778387 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Channel.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Channel.java @@ -1,25 +1,43 @@ package models.dataConstraintModel; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Set; +import models.algebra.Expression; import models.algebra.Variable; public class Channel { protected String channelName; - protected Set selectors = null; + protected Channel parent = null; + protected List children = null; + protected List selectors = null; protected Set channelMembers = null; protected String sourceText = null; public Channel(String channelName) { this.channelName = channelName; - selectors = new HashSet<>(); + this.parent = null; + this.children = new ArrayList<>(); + selectors = new ArrayList<>(); channelMembers = new HashSet<>(); } - public Channel(String channelName, Set variables) { + public Channel(String channelName, Variable variable) { this.channelName = channelName; - selectors = new HashSet<>(); + this.parent = null; + this.children = new ArrayList<>(); + selectors = new ArrayList<>(); + selectors.add(new Selector(variable)); + channelMembers = new HashSet<>(); + } + + public Channel(String channelName, List variables) { + this.channelName = channelName; + this.parent = null; + this.children = new ArrayList<>(); + selectors = new ArrayList<>(); for (Variable var: variables) { selectors.add(new Selector(var)); } @@ -30,14 +48,38 @@ return channelName; } - public Set getChannelSelectors() { + public Channel getParent() { + return parent; + } + + public List getChildren() { + return children; + } + + public void addChild(Channel child) { + children.add(child); + child.parent = this; + } + + public List getSelectors() { return selectors; } - public void setChannelSelectors(Set selectors) { + public List getAllSelectors() { + List allSelectors = new ArrayList<>(); + if (parent != null) allSelectors.addAll(parent.getAllSelectors()); + allSelectors.addAll(selectors); + return allSelectors; + } + + public void setSelectors(List selectors) { this.selectors = selectors; } + public void addSelector(Variable var) { + selectors.add(new Selector(var)); + } + public void addSelector(Selector selector) { selectors.add(selector); } @@ -48,23 +90,24 @@ public void setChannelMembers(Set channelMembers) { this.channelMembers = channelMembers; - for (ChannelMember channelMember: channelMembers) { - for (Selector selector: channelMember.getSelectors()) { - addSelector(selector); - } - } } public void addChannelMember(ChannelMember channelMember) { channelMembers.add(channelMember); - for (Selector selector: channelMember.getSelectors()) { - addSelector(selector); + if (getAllSelectors().size() > 0) { + Set params = new HashSet<>(); + for (Selector s: getAllSelectors()) { + params.add(s.getExpression()); + } + if (!params.containsAll(channelMember.getResource().getPathParams())) { + channelMember.setOutside(true); + } } } - public void removeChannelMember(ResourcePath id) { + public void removeChannelMember(ResourcePath res) { for (ChannelMember cm: channelMembers) { - if (cm.getResource() == id) { + if (cm.getResource() == res) { channelMembers.remove(cm); break; } diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ChannelMember.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ChannelMember.java index 190a49f..93a3cea 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ChannelMember.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ChannelMember.java @@ -5,13 +5,13 @@ public class ChannelMember { private ResourcePath resourcePath = null; - private List selectors = null; private StateTransition stateTransition = null; + private boolean isOutside = false; public ChannelMember(ResourcePath resourcePath) { this.resourcePath = resourcePath; - selectors = new ArrayList<>(); stateTransition = new StateTransition(); + isOutside = false; } public ResourcePath getResource() { @@ -22,19 +22,6 @@ this.resourcePath = resourcePath; } - public List getSelectors() { - return selectors; - } - - public void setSelectors(List selectors) { - this.selectors = selectors; - } - - public ChannelMember addSelector(Selector selector) { - selectors.add(selector); - return this; - } - public StateTransition getStateTransition() { return stateTransition; } @@ -43,6 +30,14 @@ this.stateTransition = stateTransition; } + public boolean isOutside() { + return this.isOutside; + } + + public void setOutside(boolean isOutside) { + this.isOutside = isOutside; + } + @Override public String toString() { if (stateTransition.getNextStateExpression() == null) { diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/DataConstraintModel.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/DataConstraintModel.java index 35b2aa7..ed01563 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/DataConstraintModel.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/DataConstraintModel.java @@ -1,14 +1,21 @@ package models.dataConstraintModel; +import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.List; +import models.algebra.Expression; +import models.algebra.LambdaAbstraction; import models.algebra.Symbol; +import models.algebra.Term; import models.algebra.Type; +import models.algebra.Variable; import parser.Parser; public class DataConstraintModel { - protected HashMap resourcePaths = null; + protected List resourcePaths = null; + protected HashMap resourceHierarchies = null; protected HashMap channels = null; protected HashMap ioChannels = null; protected HashMap types = null; @@ -27,9 +34,10 @@ public static final Type typePairInt = new Type("Pair", "Pair", "Pair", typePair); public static final Type typePairStr = new Type("Pair", "Pair", "Pair", typePair); public static final Type typePairDouble = new Type("Pair", "Pair", "Pair", typePair); - public static final Type typeMap = new Type("Map", "HashMap", "Map"); + public static final Type typeMap = new Type("Map", "HashMap<>", "Map"); + public static final JsonType typeJson = new JsonType("Json", "HashMap<>", "Map"); 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);; + public static final Symbol mul = new Symbol(Parser.MUL, 2, Symbol.Type.INFIX); public static final Symbol sub = new Symbol(Parser.SUB, 2, Symbol.Type.INFIX); public static final Symbol div = new Symbol(Parser.DIV, 2, Symbol.Type.INFIX); public static final Symbol minus = new Symbol(Parser.MINUS, 1); @@ -45,6 +53,7 @@ public String generate(Type type, String[] children, String[] childrenSideEffects, String[] sideEffect) { String compType = ""; if (type != null) { + String temp = "temp_nil"; String interfaceType = type.getInterfaceTypeName(); if (interfaceType.contains("<")) { compType = interfaceType.substring(interfaceType.indexOf("<") + 1, interfaceType.lastIndexOf(">")); @@ -53,7 +62,8 @@ if (implType.indexOf('<') >= 0) { implType = implType.substring(0, implType.indexOf('<')); } - return "new " + implType + "<" + compType + ">()"; + sideEffect[0] = interfaceType + " " + temp + " = " + "new " + implType + "<" + compType + ">();\n"; + return temp; } return "new ArrayList<" + compType + ">()"; } @@ -125,24 +135,40 @@ public static final Symbol left = new Symbol("left", 1, Symbol.Type.PREFIX, "getLeft", Symbol.Type.METHOD); public static final Symbol right = new Symbol("right", 1, Symbol.Type.PREFIX, "getRight", Symbol.Type.METHOD); public static final Symbol insert = new Symbol("insert", 3, Symbol.Type.PREFIX, "put", Symbol.Type.METHOD_WITH_SIDE_EFFECT); -// public static final Symbol lookup = new Symbol("lookup", 2, Symbol.Type.PREFIX, "get", Symbol.Type.METHOD); - public static final Symbol lookup = new Symbol("lookup", 2, Symbol.Type.PREFIX, new Symbol.IImplGenerator() { - final int count[] = {0}; - @Override - public String generate(Type type, String[] childrenImpl, String[] childrenSideEffects, String[] sideEffect) { - String temp = "temp_get" + count[0]; - String impl = childrenSideEffects[0] + childrenSideEffects[1]; - impl += type.getInterfaceTypeName() + " " + temp + ";\n"; - impl += "if (" + childrenImpl[0] + ".get(" + childrenImpl[1] + ") != null) {\n"; - impl += "\t" + temp + " = " + childrenImpl[0] + ".get(" + childrenImpl[1] + ");\n"; - impl += "} else {\n"; - impl += "\t" + temp + " = " + getDefaultValue(type) + ";\n"; - impl += "}\n"; - sideEffect[0] = impl; - count[0]++; - return temp; - } - }); + public static final Symbol lookup = new Symbol("lookup", 2, Symbol.Type.PREFIX, "get", Symbol.Type.METHOD); +// public static final Symbol lookup = new Symbol("lookup", 2, Symbol.Type.PREFIX, new Symbol.IImplGenerator() { +// final int count[] = {0}; +// @Override +// public String generate(Type type, String[] childrenImpl, String[] childrenSideEffects, String[] sideEffect) { +// String temp = "temp_get" + count[0]; +// String impl = childrenSideEffects[0] + childrenSideEffects[1]; +// impl += type.getInterfaceTypeName() + " " + temp + ";\n"; +// impl += "if (" + childrenImpl[0] + ".get(" + childrenImpl[1] + ") != null) {\n"; +// impl += "\t" + temp + " = " + childrenImpl[0] + ".get(" + childrenImpl[1] + ");\n"; +// impl += "} else {\n"; +// impl += "\t" + temp + " = " + getDefaultValue(type) + ";\n"; +// impl += "}"; +// sideEffect[0] = impl; +// count[0]++; +// return temp; +// } +// }); + public static final Symbol addMember = new Symbol("addMember", 3, Symbol.Type.PREFIX, "put", Symbol.Type.METHOD_WITH_SIDE_EFFECT); + public static final Symbol dot = new Symbol(Parser.DOT, 2, Symbol.Type.INFIX, "get", Symbol.Type.METHOD); + public static final Symbol dotParam = new Symbol(Parser.DOT, 2, Symbol.Type.INFIX, "get", Symbol.Type.METHOD); + public static final Symbol pi = new Symbol("PI", 0, Symbol.Type.PREFIX, "Math.PI", Symbol.Type.PREFIX); + public static final Symbol E = new Symbol("E", 0, Symbol.Type.PREFIX, "Math.E", Symbol.Type.PREFIX); + public static final Symbol sqrt = new Symbol("sqrt", 1, Symbol.Type.PREFIX, "Math.sqrt", Symbol.Type.PREFIX); + public static final Symbol sin = new Symbol("sin", 1, Symbol.Type.PREFIX, "Math.sin", Symbol.Type.PREFIX); + public static final Symbol cos = new Symbol("cos", 1, Symbol.Type.PREFIX, "Math.cos", Symbol.Type.PREFIX); + public static final Symbol tan = new Symbol("tan", 1, Symbol.Type.PREFIX, "Math.tan", Symbol.Type.PREFIX); + public static final Symbol asin = new Symbol("asin", 1, Symbol.Type.PREFIX, "Math.asin", Symbol.Type.PREFIX); + public static final Symbol acos = new Symbol("acos", 1, Symbol.Type.PREFIX, "Math.acos", Symbol.Type.PREFIX); + public static final Symbol atan = new Symbol("atan", 1, Symbol.Type.PREFIX, "Math.atan", Symbol.Type.PREFIX); + public static final Symbol pow = new Symbol("pow", 2, Symbol.Type.PREFIX, "Math.pow", Symbol.Type.PREFIX); + public static final Symbol exp = new Symbol("exp", 1, Symbol.Type.PREFIX, "Math.exp", Symbol.Type.PREFIX); + public static final Symbol log = new Symbol("log", 1, Symbol.Type.PREFIX, "Math.log", Symbol.Type.PREFIX); + public static final Symbol abs = new Symbol("abs", 1, Symbol.Type.PREFIX, "Math.abs", Symbol.Type.PREFIX); static { add.setInverses(new Symbol[] {sub, sub}); @@ -176,13 +202,37 @@ tuple.setSignature(new Type[] {typeTuple, null, null}); tuple.setInverses(new Symbol[] {fst, snd}); fst.setSignature(new Type[] {null, typeTuple}); + fst.setInverses(new Symbol[] {new LambdaAbstraction(new Variable("x"), new Term(tuple, new Expression[] {new Variable("x"), new Variable("y")}))}); snd.setSignature(new Type[] {null, typeTuple}); + snd.setInverses(new Symbol[] {new LambdaAbstraction(new Variable("y"), new Term(tuple, new Expression[] {new Variable("x"), new Variable("y")}))}); insert.setSignature(new Type[] {typeMap, typeMap, null, null}); lookup.setSignature(new Type[] {null, typeMap, null}); + addMember.setSignature(new Type[] {typeJson, typeJson, typeString, null}); + dot.setSignature(new Type[] {null, typeJson, typeString}); + dotParam.setSignature(new Type[] {null, null, null}); + pi.setSignature(new Type[] {typeDouble}); + E.setSignature(new Type[] {typeDouble}); + sqrt.setSignature(new Type[] {typeDouble, typeDouble}); + sin.setSignature(new Type[] {typeDouble, typeDouble}); + cos.setSignature(new Type[] {typeDouble, typeDouble}); + tan.setSignature(new Type[] {typeDouble, typeDouble}); + asin.setSignature(new Type[] {typeDouble, typeDouble}); + asin.setInverses(new Symbol[] {sin}); + acos.setSignature(new Type[] {typeDouble, typeDouble}); + acos.setInverses(new Symbol[] {cos}); + atan.setSignature(new Type[] {typeDouble, typeDouble}); + atan.setInverses(new Symbol[] {tan}); + pow.setSignature(new Type[] {typeDouble, typeDouble, typeDouble}); + exp.setSignature(new Type[] {typeDouble, typeDouble}); + exp.setInverses(new Symbol[] {log}); + log.setSignature(new Type[] {typeDouble, typeDouble}); + log.setInverses(new Symbol[] {exp}); + abs.setSignature(new Type[] {typeDouble, typeDouble}); } public DataConstraintModel() { - resourcePaths = new HashMap<>(); + resourcePaths = new ArrayList<>(); + resourceHierarchies = new HashMap<>(); channels = new HashMap<>(); ioChannels = new HashMap<>(); types = new HashMap<>(); @@ -196,6 +246,7 @@ addType(typePair); addType(typeTuple); addType(typeMap); + addType(typeJson); symbols = new HashMap<>(); addSymbol(add); addSymbol(mul); @@ -232,32 +283,76 @@ addSymbol(snd); addSymbol(insert); addSymbol(lookup); + addSymbol(addMember); + addSymbol(dot); + addSymbol(dotParam); + addSymbol(pi); + addSymbol(E); + addSymbol(sqrt); + addSymbol(sin); + addSymbol(cos); + addSymbol(tan); + addSymbol(asin); + addSymbol(acos); + addSymbol(atan); + addSymbol(pow); + addSymbol(exp); + addSymbol(log); + addSymbol(abs); } - public Collection getResourcePaths() { - return resourcePaths.values(); + public Collection getResourceHierarchies() { + return resourceHierarchies.values(); } - public ResourcePath getResourcePath(String resourceName) { - return resourcePaths.get(resourceName); + public ResourceHierarchy getResourceHierarchy(String hierarchy) { + return resourceHierarchies.get(hierarchy); + } + + public List getResourcePaths() { + return resourcePaths; } public void addResourcePath(ResourcePath resourcePath) { - resourcePaths.put(resourcePath.getResourceName(), resourcePath); + resourcePaths.add(resourcePath); + ResourceHierarchy childHierarchy = null; + ResourceHierarchy hierarchy = null; + do { + hierarchy = resourcePath.getResourceHierarchy(); + if (hierarchy != null && resourceHierarchies.get(hierarchy.toString()) == null) { + resourceHierarchies.put(hierarchy.toString(), hierarchy); + } else { + hierarchy = resourceHierarchies.get(hierarchy.toString()); + resourcePath.setResourceHierarchy(hierarchy); + if (childHierarchy != null) { + childHierarchy.setParent(hierarchy); + } + } + resourcePath = resourcePath.getParent(); + childHierarchy = hierarchy; + } while (resourcePath != null); } - public void setResourcePaths(HashMap resourcePaths) { + public void setResourcePaths(List resourcePaths) { this.resourcePaths = resourcePaths; } - public void removeResourcePath(String resourceName) { - ResourcePath id = resourcePaths.get(resourceName); - resourcePaths.remove(resourceName); + public ResourcePath getResourcePath(String path) { + for (ResourcePath resourcePath: resourcePaths) { + if (resourcePath.toString().equals(path)) return resourcePath; + } + return null; + } + + public void removeResourcePath(String path) { + ResourcePath resourcePath = getResourcePath(path); + if (resourcePath == null) return; + resourcePaths.remove(resourcePath); for (Channel ch: channels.values()) { - ch.removeChannelMember(id); + ch.removeChannelMember(resourcePath); } for (Channel ch: ioChannels.values()) { - ch.removeChannelMember(id); + ch.removeChannelMember(resourcePath); } } @@ -271,18 +366,10 @@ public void setChannels(HashMap channels) { this.channels = channels; - for (Channel g: channels.values()) { - for (ResourcePath id: g.getResources()) { - resourcePaths.put(id.getResourceName(), id); - } - } } public void addChannel(Channel channel) { channels.put(channel.getChannelName(), channel); - for (ResourcePath id: channel.getResources()) { - resourcePaths.put(id.getResourceName(), id); - } } public void removeChannel(String channelName) { @@ -299,18 +386,10 @@ public void setIOChannels(HashMap ioChannels) { this.ioChannels = ioChannels; - for (Channel g: ioChannels.values()) { - for (ResourcePath id: g.getResources()) { - resourcePaths.put(id.getResourceName(), id); - } - } } public void addIOChannel(Channel ioChannel) { ioChannels.put(ioChannel.getChannelName(), ioChannel); - for (ResourcePath id: ioChannel.getResources()) { - resourcePaths.put(id.getResourceName(), id); - } } public void removeIOChannel(String ioChannelName) { @@ -395,7 +474,7 @@ public String getSourceText() { String out = ""; String init = ""; - for (ResourcePath resource: resourcePaths.values()) { + for (ResourceHierarchy resource: resourceHierarchies.values()) { String initializer = resource.getInitText(); if (initializer != null) { init += initializer; diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonAccessor.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonAccessor.java new file mode 100644 index 0000000..c84207e --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonAccessor.java @@ -0,0 +1,177 @@ +package models.dataConstraintModel; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.LambdaAbstraction; +import models.algebra.Position; +import models.algebra.Symbol; +import models.algebra.Term; +import models.algebra.Type; +import models.algebra.Variable; + +public class JsonAccessor extends Term { + + public JsonAccessor(Symbol symbol) { + super(symbol); + } + + public Type getType() { + if (symbol.equals(DataConstraintModel.dotParam)) { + Type valueType = null; + if (getChild(1) instanceof Term) { + valueType = ((Term) getChild(1)).getType(); + } else if (getChild(1) instanceof Variable) { + valueType = ((Variable) getChild(1)).getType(); + } + if (valueType != null) return valueType; + } + return super.getType(); + } + + @Override + public Expression reduce() { + Expression reducedTerm = super.reduce(); + if (reducedTerm instanceof Term) { + if (symbol.equals(DataConstraintModel.dot) && getChildren().size() >= 2) { + // this term is `json.key`. + Expression expJson = getChild(0); + Expression expKey = getChild(1); + if (expKey instanceof Constant && expJson instanceof Term) { + reducedTerm = getValue((Term) expJson, (Constant) expKey); + } + } + } + return reducedTerm; + } + + private Expression getValue(Term json, Constant key) { + if (!json.getSymbol().equals(DataConstraintModel.addMember)) return null; + if (json.getChild(1).equals(key)) { + return json.getChild(2); + } + if (json.getChild(0) == null || json.getChild(0).equals(DataConstraintModel.nil)) return null; + return getValue((Term) json.getChild(0), key); + } + + @Override + public Expression getInverseMap(Expression outputValue, Position targetPos) { + if (targetPos.isEmpty()) return outputValue; + targetPos = (Position) targetPos.clone(); + int i = targetPos.removeHeadOrder(); + Symbol[] inverseSymbols = symbol.getInverses(); + if (i == 0) { + if (symbol.equals(DataConstraintModel.dot) && getChildren().size() >= 2) { + // this term is `json.key`. + Expression expJson = getChild(0); + Expression expKey = getChild(1); + JsonType jsonType = null; + if (expJson instanceof Variable) { + jsonType = (JsonType) ((Variable) expJson).getType(); + } else if (expJson instanceof Term) { + jsonType = (JsonType) ((Term) expJson).getType(); + } + String keyName = null; + if (expKey instanceof Constant) { + keyName = ((Constant) expKey).getSymbol().getName(); + Term jsonTerm = new Constant(DataConstraintModel.nil); + jsonTerm.setType(DataConstraintModel.typeJson); + int v = 1; + Map vars = new HashMap<>(); + Set keySet = new HashSet<>(); + if (jsonType == null || jsonType == DataConstraintModel.typeJson) { + keySet.add(keyName); + } else { + keySet.addAll(jsonType.getKeys()); + } + for (String key: keySet) { + Term addMemberTerm = new Term(DataConstraintModel.addMember); // addMember(jsonTerm, key, v) + addMemberTerm.addChild(jsonTerm); + addMemberTerm.addChild(new Constant(key)); + Variable var = new Variable("v" + v); + addMemberTerm.addChild(var); + vars.put(key, var); + jsonTerm = addMemberTerm; + v++; + } + Variable var = vars.get(keyName); + LambdaAbstraction lambdaAbstraction = new LambdaAbstraction(var, jsonTerm); // v -> addMember(jsonTerm, key, v) + inverseSymbols = new Symbol[] { lambdaAbstraction }; + } + } else if (symbol.equals(DataConstraintModel.dotParam) && getChildren().size() >= 2) { + // this term is `json.{param}`. + Expression expListOrMap = getChild(0); + Expression expKey = getChild(1); + JsonType jsonType = null; + if (expListOrMap instanceof Variable) { + jsonType = (JsonType) ((Variable) expListOrMap).getType(); + } else if (expListOrMap instanceof Term) { + jsonType = (JsonType) ((Term) expListOrMap).getType(); + } + Type keyType = null; + if (expKey instanceof Variable) { + keyType = (JsonType) ((Variable) expKey).getType(); + } else if (expKey instanceof Term) { + keyType = (JsonType) ((Term) expKey).getType(); + } + if (jsonType != null && keyType != null) { + if (DataConstraintModel.typeList.isAncestorOf(jsonType) || keyType.equals(DataConstraintModel.typeInt)) { + Term setElementTerm = new Term(DataConstraintModel.set); // set(list, idx, v) + setElementTerm.addChild(new Constant(DataConstraintModel.nil)); + setElementTerm.addChild(expKey); + Variable var = new Variable("v"); + setElementTerm.addChild(var); + LambdaAbstraction lambdaAbstraction = new LambdaAbstraction(var, setElementTerm); // v -> set(list, idx, v) + inverseSymbols = new Symbol[] { lambdaAbstraction }; + } else if (DataConstraintModel.typeMap.isAncestorOf(jsonType) || keyType.equals(DataConstraintModel.typeString)) { + Term insertEntryTerm = new Term(DataConstraintModel.insert); // insert(map, key, v) + insertEntryTerm.addChild(new Constant(DataConstraintModel.nil)); + insertEntryTerm.addChild(expKey); + Variable var = new Variable("v"); + insertEntryTerm.addChild(var); + LambdaAbstraction lambdaAbstraction = new LambdaAbstraction(var, insertEntryTerm); // v -> insert(map, key, v) + inverseSymbols = new Symbol[] { lambdaAbstraction }; + } + } + } + } + if (inverseSymbols == null || i >= inverseSymbols.length || inverseSymbols[i] == null) return null; + Term inverseMap = new Term(inverseSymbols[i]); + inverseMap.addChild(outputValue); + for (int n = 0; n < inverseSymbols[i].getArity(); n++) { + if (n != i) { + inverseMap.addChild(children.get(n)); + } + } + return children.get(i).getInverseMap(inverseMap, targetPos); + } + + public String toString() { + if (symbol.equals(DataConstraintModel.dotParam)) { + return children.get(0).toString() + symbol.toString() + "{" + children.get(1).toString() + "}"; + } + return super.toString(); + } + + public String toImplementation(String[] sideEffects) { + if (symbol.equals(DataConstraintModel.dotParam)) { + return children.get(0).toImplementation(sideEffects) + symbol.toImplementation() + "{" + children.get(1).toImplementation(sideEffects) + "}"; + } + return super.toImplementation(sideEffects); + } + + @Override + public Object clone() { + JsonAccessor newTerm = new JsonAccessor(symbol); + for (Expression e: children) { + newTerm.addChild((Expression) e.clone()); + } + newTerm.type = type; + return newTerm; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonType.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonType.java new file mode 100644 index 0000000..9f9e835 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonType.java @@ -0,0 +1,65 @@ +package models.dataConstraintModel; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import models.algebra.Type; + +public class JsonType extends Type { + protected boolean isListOrMap = false; + protected Map memberTypes = null; + protected Type listOrMapElementType = null; + + public JsonType(String typeName, String implementationTypeName) { + super(typeName, implementationTypeName); + memberTypes = new HashMap<>(); + } + + public JsonType(String typeName, String implementationTypeName, String interfaceTypeName) { + super(typeName, implementationTypeName, interfaceTypeName); + memberTypes = new HashMap<>(); + } + + public JsonType(String typeName, String implementationTypeName, Type parentType) { + super(typeName, implementationTypeName, parentType); + memberTypes = new HashMap<>(); + } + + public JsonType(String typeName, String implementationTypeName, String interfaceTypeName, Type parentType) { + super(typeName, implementationTypeName, interfaceTypeName, parentType); + memberTypes = new HashMap<>(); + } + + public Map getMemberTypes() { + return memberTypes; + } + + public Type getMemberType(String key) { + return memberTypes.get(key); + } + + public Set getKeys() { + return memberTypes.keySet(); + } + + public void addMemberType(String key, Type valueType) { + memberTypes.put(key, valueType); + } + +// public boolean isListOrMap() { +// return isListOrMap; +// } +// +// public void setListOrMap(boolean isListOrMap) { +// this.isListOrMap = isListOrMap; +// } + + public Type getElementType() { + return listOrMapElementType; + } + + public void setElementType(Type listElementType) { + this.listOrMapElementType = listElementType; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourceHierarchy.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourceHierarchy.java new file mode 100644 index 0000000..7412266 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourceHierarchy.java @@ -0,0 +1,242 @@ +package models.dataConstraintModel; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import models.algebra.Expression; +import models.algebra.Term; +import models.algebra.Type; +import models.algebra.Variable; + +public class ResourceHierarchy { + private ResourceHierarchy parent = null; + private Set children = null; + private String resourceName = null; + private Type resourceStateType = null; + private int numParameters = 0; + protected Expression initialValue; + protected String initText; + + public ResourceHierarchy(String resourceName) { + this.parent = null; + this.children = new HashSet<>(); + this.resourceName = resourceName; + this.numParameters = 0; + } + + public ResourceHierarchy(String resourceName, Type resourceStateType) { + this.parent = null; + this.children = new HashSet<>(); + this.resourceName = resourceName; + this.numParameters = 0; + this.resourceStateType = resourceStateType; + } + + public ResourceHierarchy(ResourceHierarchy parent, String resourceName) { + this.parent = parent; + this.children = new HashSet<>(); + this.resourceName = resourceName; + this.numParameters = 0; + if (parent != null) { + parent.addChild(this); + if (parent.getResourceStateType() == null) { + parent.setResourceStateType(DataConstraintModel.typeJson); + } + } + } + + public ResourceHierarchy(ResourceHierarchy parent, String resourceName, Type resourceStateType) { + this.parent = parent; + this.children = new HashSet<>(); + this.resourceName = resourceName; + this.numParameters = 0; + this.resourceStateType = resourceStateType; + if (parent != null) { + parent.addChild(this); + if (parent.getResourceStateType() == null) { + parent.setResourceStateType(DataConstraintModel.typeJson); + } + } + } + + public ResourceHierarchy(ResourceHierarchy parent, int numParameters) { + this.parent = parent; + this.children = new HashSet<>(); + this.resourceName = null; + this.numParameters = numParameters; + if (parent != null) { + parent.addChild(this); + } + } + + public ResourceHierarchy(ResourceHierarchy parent, Expression parameterExp) { + this.parent = parent; + this.children = new HashSet<>(); + this.resourceName = null; + this.numParameters = 1; + if (parent != null) { + parent.addChild(this); + if (parent.getResourceStateType() == null) { + Type paramType = null; + if (parameterExp instanceof Variable) { + paramType = ((Variable) parameterExp).getType(); + } else if (parameterExp instanceof Term) { + paramType = ((Term) parameterExp).getType(); + } + if (paramType != null) { + if (paramType.equals(DataConstraintModel.typeInt)) { + parent.setResourceStateType(DataConstraintModel.typeList); + } else if (paramType.equals(DataConstraintModel.typeString)) { + parent.setResourceStateType(DataConstraintModel.typeMap); + } + } + } + } + } + + public ResourceHierarchy(ResourceHierarchy parent, Expression parameterExp, Type resourceStateType) { + this.parent = parent; + this.resourceName = null; + this.numParameters = 1; + this.resourceStateType = resourceStateType; + if (parent != null) { + parent.addChild(this); + if (parent.getResourceStateType() == null) { + Type paramType = null; + if (parameterExp instanceof Variable) { + paramType = ((Variable) parameterExp).getType(); + } else if (parameterExp instanceof Term) { + paramType = ((Term) parameterExp).getType(); + } + if (paramType.equals(DataConstraintModel.typeInt)) { + parent.setResourceStateType(DataConstraintModel.typeList); + } else if (paramType.equals(DataConstraintModel.typeString)) { + parent.setResourceStateType(DataConstraintModel.typeMap); + } + } + } + } + + public ResourceHierarchy(ResourceHierarchy parent, int numParameters, Type resourceStateType) { + this.parent = parent; + this.resourceName = null; + this.numParameters = numParameters; + this.resourceStateType = resourceStateType; + if (parent != null) { + parent.addChild(this); + } + } + + public ResourceHierarchy getParent() { + return parent; + } + + public void setParent(ResourceHierarchy parent) { + this.parent = parent; + if (parent != null) { + parent.addChild(this); + } + } + + public Set getChildren() { + return children; + } + + protected void addChild(ResourceHierarchy child) { + children.add(child); + } + + public ResourceHierarchy getRoot() { + if (parent == null) return this; + return parent.getRoot(); + } + + public boolean isAncestorOf(ResourceHierarchy another) { + if (another == null) return false; + if (this == another) return true; + return isAncestorOf(another.getParent()); + } + + public String getResourceName() { + if (resourceName == null) return parent.getResourceName(); + return resourceName; + } + + public void setResourceName(String resourceName) { + this.resourceName = resourceName; + } + + public Type getResourceStateType() { + return resourceStateType; + } + + public void setResourceStateType(Type resourceStateType) { + this.resourceStateType = resourceStateType; + if (initialValue != null) { + if (initialValue instanceof Term) { + ((Term) initialValue).setType(resourceStateType); + } + } + } + + public int getNumParameters() { + return numParameters; + } + + public int getTotalNumParameters() { + if (parent == null) return numParameters; + return numParameters + parent.getTotalNumParameters(); + } + + public void setNumParameters(int numParameters) { + this.numParameters = numParameters; + } + + public Expression getInitialValue() { + return initialValue; + } + + public void setInitialValue(Expression initialValue) { + this.initialValue = initialValue; + } + + public String getInitText() { + return initText; + } + + public void setInitText(String initText) { + this.initText = initText; + } + + public String toString() { + if (parent == null) return resourceName; + if (resourceName != null) { + return parent.toString() + "." + resourceName; + } else { + return parent.toString() + ".{}"; + } + } + + public String toString(List pathParams) { + if (parent == null) return resourceName; + if (resourceName != null) { + return parent.toString(pathParams) + "." + resourceName; + } else { + Expression lastParam = pathParams.get(pathParams.size() - 1); + pathParams = pathParams.subList(0, pathParams.size() - 1); + return parent.toString(pathParams) + ".{" + lastParam +"}"; + } + } + + public String toResourcePath(List pathParams) { + if (parent == null) return resourceName; + if (resourceName != null) { + return parent.toResourcePath(pathParams) + "/" + resourceName; + } else { + String lastParam = pathParams.get(pathParams.size() - 1); + pathParams = pathParams.subList(0, pathParams.size() - 1); + return parent.toResourcePath(pathParams) + "/" + lastParam; + } + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourcePath.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourcePath.java index e530016..f4d0e19 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourcePath.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourcePath.java @@ -1,70 +1,121 @@ package models.dataConstraintModel; -import models.algebra.Expression; -import models.algebra.Term; -import models.algebra.Type; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; -public class ResourcePath { - private String resourceName = null; - private Type resourceStateType = null; - private int numParameters = 0; - private Expression initialValue = null; - protected String initText = null; +import models.algebra.Expression; +import models.algebra.Type; +import models.algebra.Symbol; +import models.algebra.Term; + +public class ResourcePath extends Symbol { + protected ResourcePath parent = null; + protected ResourceHierarchy resourceHierarchy = null; + protected List pathParams = null; + + public ResourcePath(String resourceName) { + super(resourceName); + this.parent = null; + this.resourceHierarchy = new ResourceHierarchy(null, resourceName); + this.pathParams = new ArrayList<>(); + } - public ResourcePath(String resourceName, int numParameters) { - this.resourceName = resourceName; - this.numParameters = numParameters; + public ResourcePath(ResourcePath parent, String resourceName) { + super(parent.toString() + "." + resourceName); + this.parent = parent; + this.resourceHierarchy = new ResourceHierarchy(parent.getResourceHierarchy(), resourceName); + this.pathParams = parent.getPathParams(); + } + + public ResourcePath(ResourcePath parent, Expression exp) { + super(parent.toString() + ".{" + exp + "}"); + this.parent = parent; + this.resourceHierarchy = new ResourceHierarchy(parent.getResourceHierarchy(), exp); + this.pathParams = new ArrayList<>(parent.getPathParams()); + this.pathParams.add(exp); + } + + public ResourcePath(ResourcePath another) { + super(another.name); + this.parent = another.parent; + this.resourceHierarchy = another.resourceHierarchy; + this.pathParams = new ArrayList<>(another.getPathParams()); } - public ResourcePath(String resourceName, Type resourceStateType, int numParameters) { - this.resourceName = resourceName; - this.resourceStateType = resourceStateType; - this.numParameters = numParameters; + public ResourceHierarchy getResourceHierarchy() { + return resourceHierarchy; + } + + public void setResourceHierarchy(ResourceHierarchy resourceHierarchy) { + this.resourceHierarchy = resourceHierarchy; } public String getResourceName() { - return resourceName; + return resourceHierarchy.getResourceName(); } public int getNumberOfParameters() { - return numParameters; + return resourceHierarchy.getTotalNumParameters(); } - public Type getResourceStateType() { - return resourceStateType; + public models.algebra.Type getResourceStateType() { + return resourceHierarchy.getResourceStateType(); } - public void setResourceStateType(Type resourceStateType) { - this.resourceStateType = resourceStateType; - if (initialValue != null) { - if (initialValue instanceof Term) { - ((Term) initialValue).setType(resourceStateType); - } + public void setResourceStateType(models.algebra.Type resourceStateType) { + this.resourceHierarchy.setResourceStateType(resourceStateType); + } + + public List getPathParams() { + return pathParams; + } + + public void setPathParams(List pathParams) { + this.pathParams = pathParams; + } + + public void addPathParam(Expression pathParam) { + pathParams.add(pathParam); + } + + public boolean endsWithParam() { + if (resourceHierarchy.getNumParameters() > 0) return true; + return false; + } + + public Expression getLastParam() { + if (endsWithParam()) { + return pathParams.get(pathParams.size() - 1); } + return null; } - public Expression getInitialValue() { - return initialValue; + public ResourcePath getParent() { + return parent; } - public void setInitialValue(Expression initialValue) { - this.initialValue = initialValue; - } - - public void setInitText(String initText) { - this.initText = initText; + public ResourcePath getRoot() { + if (parent == null) return this; + return parent.getRoot(); } - public String getInitText() { - return initText; - } - - public boolean equals(Object another) { - if (!(another instanceof ResourcePath)) return false; - return resourceName.equals(((ResourcePath) another).resourceName); + public ResourcePath getCommonPrefix(ResourcePath another) { + Set ancestors = new HashSet<>(); + while (another != null) { + ancestors.add(another); + another = another.getParent(); + } + ResourcePath curPath = this; + while (!ancestors.contains(curPath)) { + curPath = curPath.getParent(); + if (curPath == null) return null; + } + return curPath; } - public int hashCode() { - return resourceName.hashCode(); + public String toString() { + return resourceHierarchy.toString(pathParams); } } diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Selector.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Selector.java index a29f8e3..f170a07 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Selector.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Selector.java @@ -1,19 +1,19 @@ package models.dataConstraintModel; -import models.algebra.Variable; +import models.algebra.Expression; public class Selector { - private Variable variable = null; + private Expression exp = null; - public Selector(Variable variable) { - this.setVariable(variable); + public Selector(Expression exp) { + this.exp = exp; } - public Variable getVariable() { - return variable; + public Expression getExpression() { + return exp; } - public void setVariable(Variable variable) { - this.variable = variable; + public void setExpression(Expression exp) { + this.exp = exp; } } diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/StateTransition.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/StateTransition.java index 011045a..0186360 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/StateTransition.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/StateTransition.java @@ -3,7 +3,9 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.Map.Entry; +import java.util.Set; +import models.algebra.Constant; import models.algebra.Expression; import models.algebra.InvalidMessage; import models.algebra.Position; @@ -48,7 +50,7 @@ return true; } - public Expression deriveMessageConstraintFor(Expression curStateValue, Expression nextStateValue) throws InvalidMessage, ResolvingMultipleDefinitionIsFutureWork { + public Expression deriveMessageConstraintFor(Expression curStateValue, Expression nextStateValue, Set substitutedPositions) throws InvalidMessage, ResolvingMultipleDefinitionIsFutureWork { HashMap> bindings = new HashMap<>(); Expression curStateTerm = getCurStateExpression(); @@ -58,6 +60,9 @@ Position varPos = curStateVarEnt.getKey(); Expression valueCalc = curStateTerm.getInverseMap(curStateValue, varPos); if (valueCalc != null) { + if (valueCalc instanceof Term && !(valueCalc instanceof Constant)) { + valueCalc = ((Term) valueCalc).reduce(); + } ArrayList values = bindings.get(var); if (values == null) { values = new ArrayList(); @@ -87,6 +92,9 @@ Position varPos = nextStateVarEnt.getKey(); Expression valueCalc = nextStateTerm.getInverseMap(nextStateValue, varPos); if (valueCalc != null) { + if (valueCalc instanceof Term) { + valueCalc = ((Term) valueCalc).reduce(); + } ArrayList values = bindings.get(var); if (values == null) { values = new ArrayList(); @@ -97,18 +105,10 @@ } Expression messageTerm = getMessageExpression(); - if (!(messageTerm instanceof Term)) throw new InvalidMessage(); - HashMap messageVars = messageTerm.getVariables(); - for (Variable var: messageVars.values()) { - if (bindings.get(var) != null) { - if (bindings.get(var).size() > 1) throw new ResolvingMultipleDefinitionIsFutureWork(); - messageTerm = ((Term) messageTerm).substitute(var, bindings.get(var).iterator().next()); - } - } - return messageTerm; + return substituteValues(messageTerm, bindings, substitutedPositions); } - public Expression deriveMessageConstraintFor(Expression curStateValue) throws InvalidMessage, ResolvingMultipleDefinitionIsFutureWork { + public Expression deriveMessageConstraintFor(Expression curStateValue, Set substitutedPositions) throws InvalidMessage, ResolvingMultipleDefinitionIsFutureWork { HashMap> bindings = new HashMap<>(); Expression curStateTerm = getCurStateExpression(); @@ -118,6 +118,9 @@ Position varPos = curStateVarEnt.getKey(); Expression valueCalc = curStateTerm.getInverseMap(curStateValue, varPos); if (valueCalc != null) { + if (valueCalc instanceof Term && !(valueCalc instanceof Constant)) { + valueCalc = ((Term) valueCalc).reduce(); + } ArrayList values = bindings.get(var); if (values == null) { values = new ArrayList(); @@ -128,12 +131,26 @@ } Expression messageTerm = getMessageExpression(); - if (!(messageTerm instanceof Term)) throw new InvalidMessage(); + return substituteValues(messageTerm, bindings, substitutedPositions); + } + + private Expression substituteValues(Expression messageTerm, HashMap> bindings, Set substitutedPositions) + throws InvalidMessage, ResolvingMultipleDefinitionIsFutureWork { + if (!(messageTerm instanceof Term) && !(messageTerm instanceof Variable)) throw new InvalidMessage(); HashMap messageVars = messageTerm.getVariables(); - for (Variable var: messageVars.values()) { + for (Entry varEnt: messageVars.entrySet()) { + Variable var = varEnt.getValue(); if (bindings.get(var) != null) { if (bindings.get(var).size() > 1) throw new ResolvingMultipleDefinitionIsFutureWork(); - messageTerm = ((Term) messageTerm).substitute(var, bindings.get(var).iterator().next()); + if (messageTerm instanceof Term) { + substitutedPositions.add(varEnt.getKey()); + messageTerm = ((Term) messageTerm).substitute(var, bindings.get(var).iterator().next()); + } else if (messageTerm instanceof Variable) { + if (messageTerm.equals(var)) { + substitutedPositions.add(new Position()); + return bindings.get(var).iterator().next(); + } + } } } return messageTerm; @@ -150,6 +167,9 @@ Position varPos = curStateVarEnt.getKey(); Expression valueCalc = curStateTerm.getInverseMap(curStateValue, varPos); if (valueCalc != null) { + if (valueCalc instanceof Term && !(valueCalc instanceof Constant)) { + valueCalc = ((Term) valueCalc).reduce(); + } if (bindings.get(var) != null) throw new ResolvingMultipleDefinitionIsFutureWork(); bindings.put(var, valueCalc); } @@ -178,6 +198,9 @@ nextStateTerm = ((Term) nextStateTerm).substitute(var, bindings.get(var)); } } + if (nextStateTerm instanceof Term && !(nextStateTerm instanceof Constant)) { + nextStateTerm = ((Term) nextStateTerm).reduce(); + } return nextStateTerm; } } diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/StateTransitionTerm.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/StateTransitionTerm.java new file mode 100644 index 0000000..6baeff1 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/StateTransitionTerm.java @@ -0,0 +1,12 @@ +package models.dataConstraintModel; + +import models.algebra.Symbol; +import models.algebra.Term; + +public class StateTransitionTerm extends Term { + + public StateTransitionTerm(ResourcePath path) { + super(path); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ChannelNode.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ChannelNode.java new file mode 100644 index 0000000..108bc21 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ChannelNode.java @@ -0,0 +1,39 @@ +package models.dataFlowModel; + +import java.util.HashSet; +import java.util.Set; + +import models.Node; + +public class ChannelNode extends Node { + protected ChannelNode parent = null; + protected Set children = null; + protected DataTransferChannel channel = null; + + public ChannelNode(ChannelNode parent, DataTransferChannel channel) { + this.parent = parent; + this.channel = channel; + this.children = new HashSet<>(); + } + + public ChannelNode getParent() { + return parent; + } + + public Set getChildren() { + return children; + } + + public void addChild(ChannelNode child) { + children.add(child); + child.parent = this; + } + + public DataTransferChannel getChannel() { + return channel; + } + + public void setChannel(DataTransferChannel channel) { + this.channel = channel; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataFlowEdge.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataFlowEdge.java index 9de9359..6b35380 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataFlowEdge.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataFlowEdge.java @@ -3,18 +3,23 @@ import models.*; public class DataFlowEdge extends Edge { - protected DataTransferChannel channel = null; + protected boolean channelToResource = false; - public DataFlowEdge(ResourceNode src, ResourceNode dst, DataTransferChannel channel) { + public DataFlowEdge(ResourceNode src, ChannelNode dst) { super(src, dst); - this.channel = channel; + channelToResource = false; } - - public DataTransferChannel getChannel() { - return channel; + + public DataFlowEdge(ChannelNode src, ResourceNode dst) { + super(src, dst); + channelToResource = true; } - - public String toString() { - return channel.getChannelName(); + + public boolean isChannelToResource() { + return channelToResource; } + +// public String toString() { +// return channel.getChannelName(); +// } } diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataFlowGraph.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataFlowGraph.java index 2c540ed..b241023 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataFlowGraph.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataFlowGraph.java @@ -1,40 +1,107 @@ package models.dataFlowModel; +import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import models.DirectedGraph; import models.dataConstraintModel.ResourcePath; public class DataFlowGraph extends DirectedGraph { - protected Map nodeMap = null; + protected Set rootResourceNodes = null; + protected Set rootChannelNodes = null; + protected Map channelNodeMap = null; + protected Map resourceNodeMap = null; public DataFlowGraph() { super(); - nodeMap = new HashMap<>(); + rootResourceNodes = new HashSet<>(); + rootChannelNodes = new HashSet<>(); + channelNodeMap = new HashMap<>(); + resourceNodeMap = new HashMap<>(); } - public void addNode(ResourcePath id) { - if (nodeMap.get(id) == null) { - ResourceNode node = new ResourceNode(id); - addNode(node); - nodeMap.put(id, node); + public ResourceNode addResourceNode(ResourceNode parent, + DataTransferChannel outSideChannel, + ResourcePath outSideResource) { + ResourceNode node = resourceNodeMap.get(outSideResource); + if (node != null) return node; + node = new ResourceNode(parent, outSideChannel, outSideResource); + addNode(node); + if (parent == null) { + rootResourceNodes.add(node); + } else { + parent.addChild(node); } + resourceNodeMap.put(outSideResource, node); + return node; + } + + public ResourceNode addResourceNode(ResourceNode parent, + Map inSide, + Map.Entry outSide) { + ResourceNode node = resourceNodeMap.get(outSide.getValue()); + if (node != null) return node; + node = new ResourceNode(parent, inSide, outSide); + addNode(node); + if (parent == null) { + rootResourceNodes.add(node); + } else { + parent.addChild(node); + } + resourceNodeMap.put(outSide.getValue(), node); + return node; + } + + public ChannelNode addChannelNode(ChannelNode parent, DataTransferChannel ch) { + ChannelNode node = channelNodeMap.get(ch); + if (node != null) return node; + node = new ChannelNode(parent, ch); + addNode(node); + if (parent == null) { + rootChannelNodes.add(node); + } else { + parent.addChild(node); + } + channelNodeMap.put(ch, node); + return node; + } + + public ResourceNode getResourceNode(ResourcePath outSide) { + return resourceNodeMap.get(outSide); + } + + public ChannelNode getChannelNode(DataTransferChannel channel) { + return channelNodeMap.get(channel); + } + + public Collection getResourceNodes() { + return resourceNodeMap.values(); + } + + public Collection getChannelNodes() { + return channelNodeMap.values(); } - public void addEdge(ResourcePath in, ResourcePath out, DataTransferChannel dfChannelGen) { - ResourceNode srcNode = nodeMap.get(in); - if (srcNode == null) { - srcNode = new ResourceNode(in); - addNode(srcNode); - nodeMap.put(in, srcNode); - } - ResourceNode dstNode = nodeMap.get(out); - if (dstNode == null) { - dstNode = new ResourceNode(out); - addNode(dstNode); - nodeMap.put(out, dstNode); - } - addEdge(new DataFlowEdge(srcNode, dstNode, dfChannelGen)); + public Set getRootResourceNodes() { + return rootResourceNodes; + } + + public Set getRootChannelNodes() { + return rootChannelNodes; + } + + public DataFlowEdge addEdge(ResourceNode srcNode, ChannelNode dstNode) { + DataFlowEdge edge =new DataFlowEdge(srcNode, dstNode); + addEdge(edge); + return edge; + } + + public DataFlowEdge addEdge(ChannelNode srcNode, ResourceNode dstNode) { + DataFlowEdge edge = new DataFlowEdge(srcNode, dstNode); + addEdge(edge); + return edge; } } diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferChannel.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferChannel.java index b3f3bf0..ce8c581 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferChannel.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferChannel.java @@ -1,9 +1,15 @@ package models.dataFlowModel; +import java.util.AbstractMap; +import java.util.AbstractMap.SimpleEntry; import java.util.HashMap; import java.util.HashSet; +import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.Map.Entry; +import models.algebra.Constant; import models.algebra.Expression; import models.algebra.InvalidMessage; import models.algebra.Parameter; @@ -27,6 +33,20 @@ referenceChannelMembers = new HashSet<>(); } + public DataTransferChannel(String channelName, Variable variable) { + super(channelName, variable); + inputChannelMembers = new HashSet<>(); + outputChannelMembers = new HashSet<>(); + referenceChannelMembers = new HashSet<>(); + } + + public DataTransferChannel(String channelName, List variables) { + super(channelName, variables); + inputChannelMembers = new HashSet<>(); + outputChannelMembers = new HashSet<>(); + referenceChannelMembers = new HashSet<>(); + } + public Set getInputChannelMembers() { return inputChannelMembers; } @@ -35,7 +55,7 @@ this.inputChannelMembers = inputChannelMembers; } - public void addInputChannelMember(ChannelMember inputChannelMember) { + private void addInputChannelMember(ChannelMember inputChannelMember) { inputChannelMembers.add(inputChannelMember); } @@ -47,7 +67,7 @@ this.outputChannelMembers = outputChannelMembers; } - public void addOutputChannelMember(ChannelMember outputChannelMember) { + private void addOutputChannelMember(ChannelMember outputChannelMember) { outputChannelMembers.add(outputChannelMember); } @@ -59,23 +79,23 @@ this.referenceChannelMembers = referenceChannelMembers; } - public void addReferenceChannelMember(ChannelMember referenceChannelMember) { + private void addReferenceChannelMember(ChannelMember referenceChannelMember) { referenceChannelMembers.add(referenceChannelMember); } - public void addChannelMemberAsInput(ChannelMember groupDependentResource) { - addChannelMember(groupDependentResource); - addInputChannelMember(groupDependentResource); + public void addChannelMemberAsInput(ChannelMember inputChannelMember) { + addChannelMember(inputChannelMember); + addInputChannelMember(inputChannelMember); } - public void addChannelMemberAsOutput(ChannelMember groupDependentResource) { - addChannelMember(groupDependentResource); - addOutputChannelMember(groupDependentResource); + public void addChannelMemberAsOutput(ChannelMember outputChannelMember) { + addChannelMember(outputChannelMember); + addOutputChannelMember(outputChannelMember); } - public void addChannelMemberAsReference(ChannelMember groupDependentResource) { - addChannelMember(groupDependentResource); - addReferenceChannelMember(groupDependentResource); + public void addChannelMemberAsReference(ChannelMember referenceChannelMember) { + addChannelMember(referenceChannelMember); + addReferenceChannelMember(referenceChannelMember); } public void removeChannelMember(ResourcePath id) { @@ -142,8 +162,8 @@ HashMap nextStateParams = new HashMap<>(); @Override - public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) { - String resource = target.getResourceName(); + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + String resource = target.getResource().getResourceName(); Parameter curStateParam = curStateParams.get(resource); if (curStateParam == null) { curStateParam = new Parameter("cur" + resource); @@ -153,24 +173,35 @@ } @Override - public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { - String resource = target.getResourceName(); + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + String resource = target.getResource().getResourceName(); Parameter nextStateParam = nextStateParams.get(resource); if (nextStateParam == null) { - nextStateParam = new Parameter("next" + resource); + nextStateParam = new Parameter("next" + target); nextStateParams.put(resource, nextStateParam); } return nextStateParam; } + + @Override + public Expression getDirectStateAccessorFor(ResourcePath target, ResourcePath from) { + String resource = target.getResourceName(); + Parameter curStateParam = curStateParams.get(resource); + if (curStateParam == null) { + curStateParam = new Parameter("cur" + resource); + curStateParams.put(resource, curStateParam); + } + return curStateParam; + } }; return deriveUpdateExpressionOf(targetMember, defaultStateAccessor); } /** - * Derive the update expression of the state of the target channel member with a given resource state accessor. + * Derive the state update calculation of the target channel member with a given resource push/pull state accessor. * @param targetMember a channel member whose state is to be updated - * @param stateAccessor a resource state accessor - * @return the derived update expression + * @param stateAccessor a push/pull resource state accessor + * @return the derived update calculation * @throws ParameterizedIdentifierIsFutureWork * @throws ResolvingMultipleDefinitionIsFutureWork * @throws InvalidMessage @@ -182,43 +213,146 @@ return deriveUpdateExpressionOf(targetMember, stateAccessor, null); } - public Expression deriveUpdateExpressionOf(ChannelMember targetMember, IResourceStateAccessor stateAccessor, HashMap inputResourceToStateAccessor) + public Expression deriveUpdateExpressionOf(ChannelMember targetMember, IResourceStateAccessor stateAccessor, Map inputResourceToStateAccessor) throws ParameterizedIdentifierIsFutureWork, ResolvingMultipleDefinitionIsFutureWork, InvalidMessage, UnificationFailed, ValueUndefined { if (!getOutputChannelMembers().contains(targetMember)) return null; - HashSet messageConstraints = new HashSet<>(); + + // Calculate unified message constraints + Map> substitutedPositionsFromChannels = new HashMap<>(); + Term unifiedMessage = calcUnifiedMessage(targetMember, stateAccessor, inputResourceToStateAccessor, substitutedPositionsFromChannels); + + // Calculate the next state of target resource from the unified message and the current resource state + Expression curOutputStateAccessor = stateAccessor.getCurrentStateAccessorFor(targetMember, targetMember); + if (unifiedMessage == null) { + // for IOChannel + if (targetMember.getStateTransition().getMessageExpression() instanceof Term) { + unifiedMessage = (Term) targetMember.getStateTransition().getMessageExpression(); + } + } + return targetMember.getStateTransition().deriveNextStateExpressionFor(curOutputStateAccessor, unifiedMessage); + } + + /** + * Fill outside resource paths with a given resource push/pull state accessor. + * @param stateAccessor a push/pull resource state accessor + * @param resourcePaths a container to be returned with filled outside resource paths + * @return the derived update calculation + * @throws ParameterizedIdentifierIsFutureWork + * @throws ResolvingMultipleDefinitionIsFutureWork + * @throws InvalidMessage + * @throws UnificationFailed + * @throws ValueUndefined + */ + public Map>> fillOutsideResourcePaths(ChannelMember targetMember, IResourceStateAccessor stateAccessor) + throws ParameterizedIdentifierIsFutureWork, ResolvingMultipleDefinitionIsFutureWork, InvalidMessage, UnificationFailed, ValueUndefined { + return fillOutsideResourcePaths(targetMember, stateAccessor, null); + } + + public Map>> 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 + Map> substitutedPositionsFromChannels = new HashMap<>(); + Term unifiedMessage = calcUnifiedMessage(targetMember, stateAccessor, inputResourceToStateAccessor, substitutedPositionsFromChannels); + + // Fill outside resource paths + if (unifiedMessage != null) { + for (ChannelMember cm: getInputChannelMembers()) { + if (cm.isOutside()) { + Set dependingVarPos = new HashSet<>(); + ResourcePath filledResPath = fillOutsideResourcePath(cm.getResource(), unifiedMessage, targetMember.getStateTransition().getMessageExpression(), dependingVarPos); + Set dependingChannelMembers = new HashSet<>(); + for (ChannelMember otherCm: substitutedPositionsFromChannels.keySet()) { + for (Position otherPos: substitutedPositionsFromChannels.get(otherCm)) { + for (Position thisPos: dependingVarPos) { + if (thisPos.isAncestorOf(otherPos)) { + dependingChannelMembers.add(otherCm); + break; + } + } + if (dependingChannelMembers.contains(otherCm)) break; + } + } + resourcePaths.put(cm, new AbstractMap.SimpleEntry(filledResPath, dependingChannelMembers)); + } + } + for (ChannelMember cm: getReferenceChannelMembers()) { + if (cm.isOutside()) { + Set dependingVarPos = new HashSet<>(); + ResourcePath filledResPath = fillOutsideResourcePath(cm.getResource(), unifiedMessage, targetMember.getStateTransition().getMessageExpression(), dependingVarPos); + Set dependingChannelMembers = new HashSet<>(); + for (ChannelMember otherCm: substitutedPositionsFromChannels.keySet()) { + for (Position otherPos: substitutedPositionsFromChannels.get(otherCm)) { + for (Position thisPos: dependingVarPos) { + if (thisPos.isAncestorOf(otherPos)) { + dependingChannelMembers.add(otherCm); + break; + } + } + if (dependingChannelMembers.contains(otherCm)) break; + } + } + resourcePaths.put(cm, new AbstractMap.SimpleEntry(filledResPath, dependingChannelMembers)); + } + } + for (ChannelMember cm: getOutputChannelMembers()) { + if (cm.isOutside()) { + Set dependingVarPos = new HashSet<>(); + ResourcePath filledResPath = fillOutsideResourcePath(cm.getResource(), unifiedMessage, targetMember.getStateTransition().getMessageExpression(), dependingVarPos); + Set dependingChannelMembers = new HashSet<>(); + for (ChannelMember otherCm: substitutedPositionsFromChannels.keySet()) { + for (Position otherPos: substitutedPositionsFromChannels.get(otherCm)) { + for (Position thisPos: dependingVarPos) { + if (thisPos.isAncestorOf(otherPos)) { + dependingChannelMembers.add(otherCm); + break; + } + } + if (dependingChannelMembers.contains(otherCm)) break; + } + } + resourcePaths.put(cm, new AbstractMap.SimpleEntry(filledResPath, dependingChannelMembers)); + } + } + } + return resourcePaths; + } + + private Term calcUnifiedMessage(ChannelMember targetMember, IResourceStateAccessor stateAccessor, + Map inputResourceToStateAccessor, Map> substitutedPositionsFromChannels) + throws InvalidMessage, ResolvingMultipleDefinitionIsFutureWork, UnificationFailed { + Set messageConstraints = new HashSet<>(); // Calculate message constraints from input state transitions for (ChannelMember inputMember: getInputChannelMembers()) { - ResourcePath inputResource = inputMember.getResource(); - if (inputResource.getNumberOfParameters() > 0) { - throw new ParameterizedIdentifierIsFutureWork(); - } Expression curInputStateAccessor = null; Expression nextInputStateAccessor = null; if (inputResourceToStateAccessor == null) { - curInputStateAccessor = stateAccessor.getCurrentStateAccessorFor(inputResource, targetMember.getResource()); - nextInputStateAccessor = stateAccessor.getNextStateAccessorFor(inputResource, targetMember.getResource()); + curInputStateAccessor = stateAccessor.getCurrentStateAccessorFor(inputMember, targetMember); + nextInputStateAccessor = stateAccessor.getNextStateAccessorFor(inputMember, targetMember); } else { - curInputStateAccessor = inputResourceToStateAccessor.get(inputResource).getCurrentStateAccessorFor(inputResource, targetMember.getResource()); - nextInputStateAccessor = inputResourceToStateAccessor.get(inputResource).getNextStateAccessorFor(inputResource, targetMember.getResource()); + curInputStateAccessor = inputResourceToStateAccessor.get(inputMember).getCurrentStateAccessorFor(inputMember, targetMember); + nextInputStateAccessor = inputResourceToStateAccessor.get(inputMember).getNextStateAccessorFor(inputMember, targetMember); } - Expression messageConstraintByInput = inputMember.getStateTransition().deriveMessageConstraintFor(curInputStateAccessor, nextInputStateAccessor); + Set substitutedPositions = new HashSet<>(); + Expression messageConstraintByInput = inputMember.getStateTransition().deriveMessageConstraintFor(curInputStateAccessor, nextInputStateAccessor, substitutedPositions); + if (substitutedPositions.size() > 0) substitutedPositionsFromChannels.put(inputMember, substitutedPositions); messageConstraints.add((Term) messageConstraintByInput); } // Calculate message constraints from reference state transitions for (ChannelMember referenceMember: getReferenceChannelMembers()) { - ResourcePath referenceResource = referenceMember.getResource(); - if (referenceResource.getNumberOfParameters() > 0) { - throw new ParameterizedIdentifierIsFutureWork(); - } Expression curInputStateAccessor = null; if (inputResourceToStateAccessor == null) { - curInputStateAccessor = stateAccessor.getCurrentStateAccessorFor(referenceResource, targetMember.getResource()); + curInputStateAccessor = stateAccessor.getCurrentStateAccessorFor(referenceMember, targetMember); } else { - curInputStateAccessor = inputResourceToStateAccessor.get(referenceResource).getCurrentStateAccessorFor(referenceResource, targetMember.getResource()); + curInputStateAccessor = inputResourceToStateAccessor.get(referenceMember).getCurrentStateAccessorFor(referenceMember, targetMember); } - Expression messageConstraintByReference = referenceMember.getStateTransition().deriveMessageConstraintFor(curInputStateAccessor); + Set substitutedPositions = new HashSet<>(); + Expression messageConstraintByReference = referenceMember.getStateTransition().deriveMessageConstraintFor(curInputStateAccessor, substitutedPositions); + if (substitutedPositions.size() > 0) substitutedPositionsFromChannels.put(referenceMember, substitutedPositions); messageConstraints.add((Term) messageConstraintByReference); } @@ -234,20 +368,46 @@ } } } + return unifiedMessage; + } + + private ResourcePath fillOutsideResourcePath(ResourcePath resource, Term unifiedMessage, Expression messageTerm, Set dependingVarPos) + throws ResolvingMultipleDefinitionIsFutureWork { + ResourcePath filledResourcePath = new ResourcePath(resource); - // Calculate the next state of target resource from the unified message and the current resource state - ResourcePath targetResource = targetMember.getResource(); - if (targetResource.getNumberOfParameters() > 0) { - throw new ParameterizedIdentifierIsFutureWork(); - } - Expression curOutputStateAccessor = stateAccessor.getCurrentStateAccessorFor(targetResource, targetResource); - if (unifiedMessage == null) { - // for IOChannel - if (targetMember.getStateTransition().getMessageExpression() instanceof Term) { - unifiedMessage = (Term) targetMember.getStateTransition().getMessageExpression(); + Map> bindings = new HashMap<>(); + Map messageVars = messageTerm.getVariables(); + for (Entry messageVarEnt: messageVars.entrySet()) { + Variable var = messageVarEnt.getValue(); + Position varPos = messageVarEnt.getKey(); + Expression valueCalc = unifiedMessage.getSubTerm(varPos); + if (valueCalc != null) { + if (bindings.get(var) != null) throw new ResolvingMultipleDefinitionIsFutureWork(); + bindings.put(var, new AbstractMap.SimpleEntry(varPos, valueCalc)); } } - return targetMember.getStateTransition().deriveNextStateExpressionFor(curOutputStateAccessor, unifiedMessage); + + List dstParams = filledResourcePath.getPathParams(); + for (int i = 0; i < filledResourcePath.getPathParams().size(); i++) { + Expression pathParam = dstParams.get(i); + if (pathParam instanceof Variable) { + dstParams.set(i, bindings.get((Variable) pathParam).getValue()); + dependingVarPos.add(bindings.get((Variable) pathParam).getKey()); + } else { + Map pathParamVars = ((Term) pathParam).getVariables(); + for (Variable var: bindings.keySet()) { + if (pathParamVars.values().contains(var)) { + pathParam = ((Term) pathParam).substitute(var, bindings.get(var).getValue()); + dependingVarPos.add(bindings.get((Variable) var).getKey()); + } + } + if (!(pathParam instanceof Constant)) { + pathParam = ((Term) pathParam).reduce(); + } + dstParams.set(i, pathParam); + } + } + return filledResourcePath; } @Override @@ -267,7 +427,8 @@ } public interface IResourceStateAccessor { - Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from); - Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from); + Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from); + Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from); + Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes); } } diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferModel.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferModel.java index db43b80..b339a66 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferModel.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferModel.java @@ -1,31 +1,85 @@ package models.dataFlowModel; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; import java.util.Set; import models.dataConstraintModel.Channel; import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.ResourceHierarchy; import models.dataConstraintModel.ResourcePath; public class DataTransferModel extends DataConstraintModel { public DataFlowGraph getDataFlowGraph() { DataFlowGraph dataFlowGraph = new DataFlowGraph(); + Map> resourceMap = new HashMap<>(); for (Channel channel: getChannels()) { - DataTransferChannel dfChannel = (DataTransferChannel)channel; - Set inputResources = dfChannel.getInputResources(); - Set outputResources = dfChannel.getOutputResources(); - for (ResourcePath in: inputResources) { - for (ResourcePath out: outputResources) { - dataFlowGraph.addEdge(in ,out, dfChannel); - } - } + addResourceToChannelEdges(dataFlowGraph, channel, null, resourceMap); + } + for (Channel channel: getChannels()) { + addChannelToResourceEdges(dataFlowGraph, channel, null, resourceMap); } for (Channel channel: getIOChannels()) { - DataTransferChannel dfChannel = (DataTransferChannel)channel; - Set outputResources = dfChannel.getOutputResources(); - for (ResourcePath out: outputResources) { - dataFlowGraph.addNode(out); - } + addChannelToResourceEdges(dataFlowGraph, channel, null, resourceMap); } return dataFlowGraph; } + + private void addResourceToChannelEdges(DataFlowGraph dataFlowGraph, Channel dstChannel, ChannelNode parentChannelNode, + Map> resourceMap) { + DataTransferChannel dstDfChannel = (DataTransferChannel)dstChannel; + ChannelNode dstChannelNode = dataFlowGraph.addChannelNode(parentChannelNode, dstDfChannel); + Set inputResources = dstDfChannel.getInputResources(); + for (ResourcePath srcRes: inputResources) { + ResourceNode srcResNode = addResourceNodes(dataFlowGraph, srcRes, dstDfChannel, resourceMap); + dataFlowGraph.addEdge(srcResNode, dstChannelNode); + } + for (Channel childChannel: dstDfChannel.getChildren()) { + addResourceToChannelEdges(dataFlowGraph, childChannel, dstChannelNode, resourceMap); + } + } + + private void addChannelToResourceEdges(DataFlowGraph dataFlowGraph, Channel srcChannel, ChannelNode parentChannelNode, + Map> resourceMap) { + DataTransferChannel srcDfChannel = (DataTransferChannel)srcChannel; + Set outputResources = srcDfChannel.getOutputResources(); + ChannelNode srcChannelNode = dataFlowGraph.addChannelNode(parentChannelNode, srcDfChannel); + for (ResourcePath dstRes: outputResources) { + Set dstResSet = resourceMap.get(dstRes.getResourceHierarchy()); // ResourceNodes that have the same ResourceHierarchy. + if (dstResSet == null || dstResSet.size() == 0) { + ResourceNode dstResNode = addResourceNodes(dataFlowGraph, dstRes, srcDfChannel, resourceMap); + if (dstResSet == null) { + dstResSet = new HashSet<>(); + resourceMap.put(dstRes.getResourceHierarchy(), dstResSet); + } + dstResSet.add(dstResNode); + } + for (ResourceNode dstResNode: dstResSet) { + dstResNode.addInSideResource(srcDfChannel, dstRes); + dataFlowGraph.addEdge(srcChannelNode, dstResNode); // Connect to each ResourceNode that has the same ResourceHierarchy. + } + } + for (Channel childChannel: srcDfChannel.getChildren()) { + addChannelToResourceEdges(dataFlowGraph, childChannel, srcChannelNode, resourceMap); + } + } + + private ResourceNode addResourceNodes(DataFlowGraph dataFlowGraph, ResourcePath resPath, DataTransferChannel dfChannel, + Map> resourceMap) { + ResourceNode resNode = null; + if (resPath.getParent() == null) { + resNode = dataFlowGraph.addResourceNode(null, dfChannel, resPath); + } else { + ResourceNode parent = addResourceNodes(dataFlowGraph, resPath.getParent(), null, resourceMap); + resNode = dataFlowGraph.addResourceNode(parent, dfChannel, resPath); + } + Set resSet = resourceMap.get(resPath.getResourceHierarchy()); + if (resSet == null) { + resSet = new HashSet<>(); + resourceMap.put(resPath.getResourceHierarchy(), resSet); + } + resSet.add(resNode); + return resNode; + } } diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ReferenceEdge.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ReferenceEdge.java new file mode 100644 index 0000000..c8ff371 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ReferenceEdge.java @@ -0,0 +1,11 @@ +package models.dataFlowModel; + +import models.Edge; + +public class ReferenceEdge extends Edge { + + public ReferenceEdge(ResourceNode src, ResourceNode dst) { + super(src, dst); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ResourceNode.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ResourceNode.java index ef286af..438a8c1 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ResourceNode.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ResourceNode.java @@ -1,30 +1,147 @@ package models.dataFlowModel; +import java.util.AbstractMap; +import java.util.AbstractMap.SimpleEntry; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + import models.Node; +import models.algebra.Expression; +import models.algebra.Type; +import models.dataConstraintModel.ResourceHierarchy; import models.dataConstraintModel.ResourcePath; +import models.dataConstraintModel.Selector; public class ResourceNode extends Node { - protected ResourcePath resourcePath = null; + protected ResourceNode parent = null; + protected Set children = null; + protected ResourceHierarchy resourceHierarchy = null; + protected Map inSide = null; + protected Map.Entry outSide = null; + protected List selectors = null; - public ResourceNode(ResourcePath resourcePath) { - this.resourcePath = resourcePath; + public ResourceNode(ResourceNode parent, + DataTransferChannel outSideChannel, + ResourcePath outSideResource) { + this.parent = parent; + this.children = new HashSet<>(); + this.inSide = new HashMap<>(); + this.outSide = new AbstractMap.SimpleEntry<>(outSideChannel, outSideResource); + this.resourceHierarchy = outSideResource.getResourceHierarchy(); + this.selectors = new ArrayList<>(); + if (resourceHierarchy.getNumParameters() > 0) { + if (outSideChannel != null) { + selectors.addAll(outSideChannel.getSelectors()); + } else { + List pathParams = outSideResource.getPathParams(); + selectors.add(new Selector(pathParams.get(pathParams.size() - 1))); + } + } } - public ResourcePath getResource() { - return resourcePath; + public ResourceNode(ResourceNode parent, + Map inSide, + Map.Entry outSide) { + this.parent = parent; + this.children = new HashSet<>(); + this.inSide = inSide; + this.outSide = outSide; + this.resourceHierarchy = outSide.getValue().getResourceHierarchy(); + this.selectors = new ArrayList<>(); + DataTransferChannel outSideChannel = outSide.getKey(); + ResourcePath outSideResource = outSide.getValue(); + if (resourceHierarchy.getNumParameters() > 0) { + if (outSideChannel != null) { + selectors.addAll(outSideChannel.getSelectors()); + } else { + List pathParams = outSideResource.getPathParams(); + selectors.add(new Selector(pathParams.get(pathParams.size() - 1))); + } + } } - public boolean equals(Object another) { - if (this == another) return true; - if (!(another instanceof ResourceNode)) return false; - return resourcePath.equals(((ResourceNode)another).resourcePath); + public ResourceHierarchy getResourceHierarchy() { + return resourceHierarchy; } - public int hashCode() { - return resourcePath.hashCode(); + public String getResourceName() { + return resourceHierarchy.getResourceName(); } - public String toString() { - return resourcePath.getResourceName(); + public Type getResourceStateType() { + return resourceHierarchy.getResourceStateType(); } + + public int getNumberOfParameters() { + return resourceHierarchy.getTotalNumParameters(); + } + + public ResourceNode getParent() { + return parent; + } + + public Set getChildren() { + return children; + } + + public void addChild(ResourceNode child) { + children.add(child); + child.parent = this; + } + + public Collection getInSideResources() { + return inSide.values(); + } + + public ResourcePath getInSideResource(DataTransferChannel channel) { + return inSide.get(channel); + } + + public ResourcePath getOutSideResource() { + return outSide.getValue(); + } + + public DataTransferChannel getOutSideChannel() { + return outSide.getKey(); + } + + public void addInSideResource(DataTransferChannel channel, ResourcePath inResource) { + inSide.put(channel, inResource); + } + + public void setOutSideResource(DataTransferChannel channel, ResourcePath outResource) { + outSide = new AbstractMap.SimpleEntry<>(channel, outResource); + } + + public List getSelectors() { + return selectors; + } + + public List getAllSelectors() { + List selectors = new ArrayList<>(); + if (parent != null) { + selectors.addAll(parent.getAllSelectors()); + } + selectors.addAll(this.selectors); + return selectors; + } + +// public boolean equals(Object another) { +// if (this == another) return true; +// if (!(another instanceof ResourceNode)) return false; +// return resourcePath.equals(((ResourceNode)another).resourcePath); +// } +// +// public int hashCode() { +// return resourcePath.hashCode(); +// } +// +// public String toString() { +// return resourcePath.toString(); +// } } diff --git a/AlgebraicDataflowArchitectureModel/src/parser/Parser.java b/AlgebraicDataflowArchitectureModel/src/parser/Parser.java index 876ee6f..022588b 100644 --- a/AlgebraicDataflowArchitectureModel/src/parser/Parser.java +++ b/AlgebraicDataflowArchitectureModel/src/parser/Parser.java @@ -1,6 +1,5 @@ package parser; -import java.awt.image.DataBufferDouble; import java.io.BufferedReader; import java.io.IOException; import java.util.ArrayList; @@ -14,20 +13,28 @@ import models.algebra.Type; import models.algebra.Variable; import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.JsonAccessor; +import models.dataConstraintModel.ResourceHierarchy; import models.dataConstraintModel.ResourcePath; import models.dataConstraintModel.StateTransition; +import models.dataConstraintModel.StateTransitionTerm; import models.dataFlowModel.DataTransferModel; import models.dataFlowModel.DataTransferChannel; import parser.exceptions.ExpectedAssignment; import parser.exceptions.ExpectedChannel; import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; import parser.exceptions.ExpectedEquals; -import parser.exceptions.ExpectedInOrOutOrRefKeyword; +import parser.exceptions.ExpectedInOrOutOrRefOrSubKeyword; import parser.exceptions.ExpectedLeftCurlyBracket; import parser.exceptions.ExpectedRHSExpression; import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.ExpectedRightCurlyBracket; import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; import parser.exceptions.WrongLHSExpression; +import parser.exceptions.WrongPathExpression; import parser.exceptions.WrongRHSExpression; public class Parser { @@ -35,6 +42,10 @@ public static final String CHANNEL = "channel"; public static final String INIT = "init"; + public static final String IN = "in"; + public static final String OUT = "out"; + public static final String REF = "ref"; + public static final String SUB_CHANNEL = "sub"; public static final String LEFT_CURLY_BRACKET = "{"; public static final String RIGHT_CURLY_BRACKET = "}"; public static final String LEFT_CURLY_BRACKET_REGX = "\\{"; @@ -43,6 +54,10 @@ public static final String RIGHT_BRACKET = ")"; public static final String LEFT_BRACKET_REGX = "\\("; public static final String RIGHT_BRACKET_REGX = "\\)"; + public static final String LEFT_SQUARE_BRACKET = "["; + public static final String RIGHT_SQUARE_BRACKET = "]"; + public static final String LEFT_SQUARE_BRACKET_REGX = "\\["; + public static final String RIGHT_SQUARE_BRACKET_REGX = "\\]"; public static final String ADD = "+"; public static final String MUL = "*"; public static final String SUB = "-"; @@ -52,14 +67,12 @@ public static final String MUL_REGX = "\\*"; public static final String SUB_REGX = "\\-"; public static final String DIV_REGX = "/"; - public static final String IN = "in"; - public static final String OUT = "out"; - public static final String REF = "ref"; public static final String EQUALS = "=="; public static final String ASSIGNMENT = "="; public static final String COMMA = ","; public static final String COLON = ":"; - + public static final String DOT = "."; + public static final String DOT_REGX = "\\."; public Parser(final TokenStream stream) { this.stream = stream; @@ -79,12 +92,16 @@ } public DataTransferModel doParse() - throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedInOrOutOrRefKeyword, ExpectedStateTransition, ExpectedEquals, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment { + throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedRightCurlyBracket, + ExpectedInOrOutOrRefOrSubKeyword, ExpectedStateTransition, ExpectedEquals, + ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, WrongPathExpression, WrongJsonExpression, ExpectedColon { return parseDataFlowModel(); } public DataTransferModel parseDataFlowModel() - throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedInOrOutOrRefKeyword, ExpectedStateTransition, ExpectedEquals, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment { + throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedRightCurlyBracket, + ExpectedInOrOutOrRefOrSubKeyword, ExpectedStateTransition, ExpectedEquals, + ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, WrongPathExpression, WrongJsonExpression, ExpectedColon { DataTransferModel model = new DataTransferModel(); DataTransferChannel channel; while ((channel = parseChannel(model)) != null) { @@ -98,17 +115,15 @@ } public DataTransferChannel parseChannel(DataTransferModel model) - throws - ExpectedLeftCurlyBracket, ExpectedRightBracket, ExpectedAssignment, - ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, - ExpectedChannel, ExpectedChannelName, ExpectedInOrOutOrRefKeyword, - ExpectedStateTransition, ExpectedEquals - { + throws ExpectedLeftCurlyBracket, ExpectedRightBracket, ExpectedRightCurlyBracket, ExpectedAssignment, + ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, + ExpectedChannel, ExpectedChannelName, ExpectedInOrOutOrRefOrSubKeyword, + ExpectedStateTransition, ExpectedEquals, WrongPathExpression, WrongJsonExpression, ExpectedColon { if (!stream.hasNext()) return null; if (stream.checkNext().equals(RIGHT_CURLY_BRACKET)) return null; String channelOrInitKeyword = stream.next(); - if (!channelOrInitKeyword.equals(CHANNEL)) { + if (!channelOrInitKeyword.equals(CHANNEL) && !channelOrInitKeyword.equals(SUB_CHANNEL)) { if (!channelOrInitKeyword.equals(INIT)) throw new ExpectedChannel(stream.getLine()); parseInit(model); channelOrInitKeyword = stream.next(); @@ -121,49 +136,61 @@ int fromLine = stream.getLine(); DataTransferChannel channel = new DataTransferChannel(channelName); String leftBracket = stream.next(); + if (leftBracket.equals(LEFT_BRACKET)) { + // has a selector + String selector = stream.next(); + Variable var = parseVariable(stream, model, selector); + channel.addSelector(var); + String rightBracket = stream.next(); + if (!rightBracket.equals(RIGHT_BRACKET)) throw new ExpectedRightBracket(stream.getLine()); + leftBracket = stream.next(); + } if (!leftBracket.equals(LEFT_CURLY_BRACKET)) throw new ExpectedLeftCurlyBracket(stream.getLine()); - String inOrOutOrRef = null; - while (stream.hasNext() && !(inOrOutOrRef = stream.next()).equals(RIGHT_CURLY_BRACKET)) { + String inOrOutOrRefOrSub = null; + while (stream.hasNext() && !(inOrOutOrRefOrSub = stream.checkNext()).equals(RIGHT_CURLY_BRACKET)) { ChannelMember channelMember = null; - if (inOrOutOrRef.equals(IN)) { - channelMember = parseChannelMember(model, inOrOutOrRef); + if (inOrOutOrRefOrSub.equals(IN)) { + stream.next(); + channelMember = parseChannelMember(model, inOrOutOrRefOrSub); if (channelMember != null) { channel.addChannelMemberAsInput(channelMember); } - } else if (inOrOutOrRef.equals(OUT)) { - channelMember = parseChannelMember(model, inOrOutOrRef); + } else if (inOrOutOrRefOrSub.equals(OUT)) { + stream.next(); + channelMember = parseChannelMember(model, inOrOutOrRefOrSub); if (channelMember != null) { channel.addChannelMemberAsOutput(channelMember); } - } else if (inOrOutOrRef.equals(REF)) { - channelMember = parseChannelMember(model, inOrOutOrRef); + } else if (inOrOutOrRefOrSub.equals(REF)) { + stream.next(); + channelMember = parseChannelMember(model, inOrOutOrRefOrSub); if (channelMember != null) { channel.addChannelMemberAsReference(channelMember); - } + } + } else if (inOrOutOrRefOrSub.equals(SUB_CHANNEL)) { + DataTransferChannel subChannel = parseChannel(model); + if (subChannel != null) { + channel.addChild(subChannel); + } } else { - throw new ExpectedInOrOutOrRefKeyword(stream.getLine()); + throw new ExpectedInOrOutOrRefOrSubKeyword(stream.getLine()); } } + if (stream.hasNext()) stream.next(); int toLine = stream.getLine(); channel.setSourceText(stream.getSourceText(fromLine, toLine)); return channel; } public void parseInit(DataTransferModel model) - throws - ExpectedLeftCurlyBracket, ExpectedAssignment, ExpectedRHSExpression, WrongRHSExpression, ExpectedRightBracket - { + throws ExpectedLeftCurlyBracket, ExpectedAssignment, ExpectedRHSExpression, WrongRHSExpression, + ExpectedRightBracket, ExpectedRightCurlyBracket, WrongPathExpression, WrongJsonExpression, ExpectedColon { String leftBracket = stream.next(); if (!leftBracket.equals(LEFT_CURLY_BRACKET)) throw new ExpectedLeftCurlyBracket(stream.getLine()); - String resourceName = null; - while (stream.hasNext() && !(resourceName = stream.next()).equals(RIGHT_CURLY_BRACKET)) { + while (stream.hasNext() && !stream.checkNext().equals(RIGHT_CURLY_BRACKET)) { int fromLine = stream.getLine(); - ResourcePath resourcePath = model.getResourcePath(resourceName); - if (resourcePath == null) { - resourcePath = new ResourcePath(resourceName, 0); - model.addResourcePath(resourcePath); - } + ResourceHierarchy resourceHierarchy = parseResourceHierarchy(stream, model); if (!stream.hasNext()) throw new ExpectedAssignment(stream.getLine()); String colon = stream.next(); @@ -178,18 +205,16 @@ rightTerm = parseTerm(stream, model); if (rightTerm == null) throw new WrongRHSExpression(stream.getLine()); - resourcePath.setInitialValue(rightTerm); - resourcePath.setInitText(stream.getSourceText(fromLine, toLine)); + resourceHierarchy.setInitialValue(rightTerm); + resourceHierarchy.setInitText(stream.getSourceText(fromLine, toLine)); } } public ChannelMember parseChannelMember(DataTransferModel model, final String inOrOutOrRef) - throws - ExpectedRightBracket, ExpectedStateTransition, ExpectedEquals, - ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression - { + throws ExpectedRightBracket, ExpectedRightCurlyBracket, ExpectedStateTransition, ExpectedEquals, + ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, WrongPathExpression, WrongJsonExpression, ExpectedColon { if (!stream.hasNext()) throw new ExpectedStateTransition(stream.getLine()); - Expression leftTerm = parseTerm(stream, model); + StateTransitionTerm leftTerm = parseStateTransitionTerm(stream, model); if (leftTerm == null || !(leftTerm instanceof Term)) throw new WrongLHSExpression(stream.getLine()); Expression rightTerm = null; @@ -203,12 +228,7 @@ if (rightTerm == null) throw new WrongRHSExpression(stream.getLine()); } - String resourceName = ((Term) leftTerm).getSymbol().getName(); - ResourcePath resourcePath = model.getResourcePath(resourceName); - if (resourcePath == null) { - resourcePath = new ResourcePath(resourceName, 0); - model.addResourcePath(resourcePath); - } + ResourcePath resourcePath = (ResourcePath) leftTerm.getSymbol(); ChannelMember channelMember = new ChannelMember(resourcePath); StateTransition stateTransition = new StateTransition(); stateTransition.setCurStateExpression(((Term) leftTerm).getChild(0)); @@ -239,9 +259,7 @@ return channelMember; } - public Expression parseTerm(TokenStream stream, DataTransferModel model) - throws ExpectedRightBracket - { + public Expression parseTerm(TokenStream stream, DataTransferModel model) throws ExpectedRightBracket, WrongJsonExpression, ExpectedColon { ArrayList expressions = new ArrayList<>(); ArrayList operators = new ArrayList<>(); String operator = null; @@ -252,6 +270,16 @@ String rightBracket = stream.next(); if (!rightBracket.equals(RIGHT_BRACKET)) throw new ExpectedRightBracket(stream.getLine()); expressions.add(exp); + } else if (leftBracketOrMinus.equals(LEFT_CURLY_BRACKET)) { + Expression exp = parseJsonTerm(stream, model); + String rightBracket = stream.next(); + if (!rightBracket.equals(RIGHT_CURLY_BRACKET)) throw new ExpectedRightBracket(stream.getLine()); + expressions.add(exp); + } else if (leftBracketOrMinus.equals(LEFT_SQUARE_BRACKET)) { + Expression exp = parseListTerm(stream, model); + String rightBracket = stream.next(); + if (!rightBracket.equals(RIGHT_SQUARE_BRACKET)) throw new ExpectedRightBracket(stream.getLine()); + expressions.add(exp); } else { Symbol minus = null; String symbolName = null; @@ -283,31 +311,29 @@ symbol.setArity(arity); exp = term; } else { - // constant or variable - try { - Symbol symbol = model.getSymbol(symbolName); - if (symbol != null && symbol.getArity() == 0) { - exp = new Constant(symbol); - } else { + // constant or variable or json access + Symbol symbol = model.getSymbol(symbolName); + if (symbol != null && symbol.getArity() == 0) { + // a constant + exp = new Constant(symbol); + } else { + if (Character.isDigit(symbolName.charAt(0))) { + // maybe a numerical value + if (stream.checkNext() != null && stream.checkNext().equals(DOT)) { + // Because tokens are separated by a DOT. + stream.next(); + symbolName += DOT + stream.next(); // decimal fraction + } Double d = Double.parseDouble(symbolName); - if (symbolName.contains(".")) { + // a numerical value + if (symbolName.contains(DOT)) { exp = new Constant(symbolName, DataTransferModel.typeDouble); } else { exp = new Constant(symbolName, DataTransferModel.typeInt); } - } - } catch (NumberFormatException e) { - if (stream.checkNext() != null && stream.checkNext().equals(COLON)) { - // when a type is specified. - stream.next(); - String typeName = stream.next(); - Type type = model.getType(typeName); - if (type == null) { - type = new Type(typeName, typeName); - } - exp = new Variable(symbolName, type); } else { - exp = new Variable(symbolName); + // a variable + exp = parseVariable(stream, model, symbolName); } } } @@ -324,16 +350,52 @@ break; } else if (operator.equals(ADD)) { operators.add(DataTransferModel.add); + stream.next(); } else if (operator.equals(MUL)) { operators.add(DataTransferModel.mul); + stream.next(); } else if (operator.equals(SUB)) { operators.add(DataTransferModel.sub); // not minus + stream.next(); } else if (operator.equals(DIV)) { operators.add(DataTransferModel.div); + stream.next(); + } else if (operator.equals(DOT)) { + // json accessor + Expression exp = expressions.remove(0); + stream.next(); // DOT + if (stream.checkNext() == null) throw new WrongJsonExpression(stream.getLine()); + String literalOrLeftCurlyBracket = stream.next(); + Expression paramTerm = null; + if (literalOrLeftCurlyBracket.equals(LEFT_CURLY_BRACKET)) { + // parameter + paramTerm = parseTerm(stream, model); + String rightCurlyBracket = stream.next(); + if (rightCurlyBracket == null || !rightCurlyBracket.equals(RIGHT_CURLY_BRACKET)) throw new WrongJsonExpression(stream.getLine()); + } else { + // literal + paramTerm = new Constant(literalOrLeftCurlyBracket, DataTransferModel.typeString); + } + Type paramType = null; + if (paramTerm instanceof Variable) { + paramType = ((Variable) paramTerm).getType(); + } else if (paramTerm instanceof Term) { + paramType = ((Term) paramTerm).getType(); + } + Term term = null; + if (paramType != null && DataConstraintModel.typeInt.isAncestorOf(paramType)) { + term = new JsonAccessor(DataConstraintModel.dotParam); + } else { + term = new JsonAccessor(DataConstraintModel.dot); + } + term.addChild(exp); + term.addChild(paramTerm); + expressions.add(term); + operator = stream.checkNext(); + if (operator == null || !operator.equals(DOT)) break; } else { break; } - stream.next(); // an arithmetic operator } if (expressions.size() == 1) { // no arithmetic operators @@ -372,24 +434,129 @@ return firstMonomial; } - /**-------------------------------------------------------------------------------- - * [protected] - /**-------------------------------------------------------------------------------- - * checking the token has a token. - * - * @param token - * @param specificTokenName - */ - protected Boolean isMatchKeyword(final String token, final String specificTokenName) { + private Expression parseJsonTerm(TokenStream stream, DataTransferModel model) throws ExpectedRightBracket, WrongJsonExpression, ExpectedColon { + Term jsonTerm = new Constant(DataConstraintModel.nil); + jsonTerm.setType(DataConstraintModel.typeJson); + while (stream.checkNext() != null && !stream.checkNext().equals(RIGHT_CURLY_BRACKET)) { + String key = stream.next(); + Constant keyExp = new Constant(key); + keyExp.setType(DataConstraintModel.typeString); + if (stream.checkNext() == null || !stream.checkNext().equals(COLON)) throw new ExpectedColon(stream.getLine()); + String colon = stream.next(); + Expression value = parseTerm(stream, model); + Term nextTerm = new Term(DataConstraintModel.addMember); + nextTerm.addChild(jsonTerm); + nextTerm.addChild(keyExp); + nextTerm.addChild(value); + jsonTerm = nextTerm; + if (stream.checkNext() == null || !stream.checkNext().equals(COMMA)) break; + String comma = stream.next(); + } + return jsonTerm; + } + + private Expression parseListTerm(TokenStream stream2, DataTransferModel model) throws ExpectedRightBracket, WrongJsonExpression, ExpectedColon { + Term listTerm = new Constant(DataConstraintModel.nil); + listTerm.setType(DataConstraintModel.typeList); + while (stream.checkNext() != null && !stream.checkNext().equals(RIGHT_SQUARE_BRACKET)) { + Expression element = parseTerm(stream, model); + Term nextTerm = new Term(DataConstraintModel.cons); + nextTerm.addChild(element); + nextTerm.addChild(listTerm); + listTerm = nextTerm; + if (stream.checkNext() == null || !stream.checkNext().equals(COMMA)) break; + String comma = stream.next(); + } + return listTerm; + } + + private Variable parseVariable(TokenStream stream, DataTransferModel model, String symbolName) { + Variable var; + if (stream.checkNext() != null && stream.checkNext().equals(COLON)) { + // when a type is specified. + stream.next(); + String typeName = stream.next(); + Type type = model.getType(typeName); + if (type == null) { + type = new Type(typeName, typeName); + } + var = new Variable(symbolName, type); + } else { + var = new Variable(symbolName); + } + return var; + } + + public StateTransitionTerm parseStateTransitionTerm(TokenStream stream, DataTransferModel model) + throws ExpectedRightBracket, ExpectedRightCurlyBracket, WrongPathExpression, WrongJsonExpression, ExpectedColon { + ResourcePath resourcePath = parseResourcePath(stream, model); + StateTransitionTerm term = new StateTransitionTerm(resourcePath); + int arity = 0; + do { + stream.next(); // LEFT_BRACKET or COMMA + arity++; + Expression subTerm = parseTerm(stream, model); + term.addChild(subTerm, true); + if (!stream.hasNext()) throw new ExpectedRightBracket(stream.getLine()); + } while (stream.checkNext().equals(COMMA)); + String rightBracket = stream.next(); + if (!rightBracket.equals(RIGHT_BRACKET)) throw new ExpectedRightBracket(stream.getLine()); + resourcePath.setArity(arity); + return term; + } + + public ResourceHierarchy parseResourceHierarchy(TokenStream stream, DataTransferModel model) + throws ExpectedRightBracket, ExpectedRightCurlyBracket, WrongPathExpression { + ResourceHierarchy hierarchy = null; + do { + String literalOrLeftCurlyBracket = stream.next(); + if (literalOrLeftCurlyBracket.equals(LEFT_CURLY_BRACKET)) { + // No path parameter + String rightCurlyBracket = stream.next(); + if (rightCurlyBracket == null || !rightCurlyBracket.equals(RIGHT_CURLY_BRACKET)) throw new ExpectedRightCurlyBracket(stream.getLine()); + hierarchy = new ResourceHierarchy(hierarchy, 1); + } else { + // Path literal + hierarchy = new ResourceHierarchy(hierarchy, literalOrLeftCurlyBracket); + } + if (!stream.hasNext()) throw new WrongPathExpression(stream.getLine()); + if (stream.checkNext().equals(LEFT_BRACKET)) break; + } while (stream.next().equals(DOT)); + return hierarchy; + } + + public ResourcePath parseResourcePath(TokenStream stream, DataTransferModel model) + throws ExpectedRightBracket, ExpectedRightCurlyBracket, WrongPathExpression, WrongJsonExpression, ExpectedColon { + ResourcePath path = null; + do { + String literalOrLeftCurlyBracket = stream.next(); + if (literalOrLeftCurlyBracket.equals(LEFT_CURLY_BRACKET)) { + // Path parameter + Expression paramTerm = parseTerm(stream, model); + String rightCurlyBracket = stream.next(); + if (rightCurlyBracket == null || !rightCurlyBracket.equals(RIGHT_CURLY_BRACKET)) throw new ExpectedRightCurlyBracket(stream.getLine()); + path = new ResourcePath(path, paramTerm); + } else { + // Path literal + if (path == null) { + path = new ResourcePath(literalOrLeftCurlyBracket); + } else { + path = new ResourcePath(path, literalOrLeftCurlyBracket); + } + } + if (!stream.hasNext()) throw new WrongPathExpression(stream.getLine()); + if (stream.checkNext().equals(LEFT_BRACKET)) break; + } while (stream.next().equals(DOT)); + model.addResourcePath(path); + return path; + } + + protected Boolean doesMatchToKeyword(final String token, final String specificTokenName) { if(token == null) return false; if(specificTokenName == null) return false; return token.equals(specificTokenName); } - /**-------------------------------------------------------------------------------- - * [inner class] - * "TokenStream" has a token what is read from description of "Architecture Language Model". - */ public static class TokenStream { private ArrayList> tokens = new ArrayList<>(); private ArrayList lines = new ArrayList<>(); @@ -416,29 +583,38 @@ splitBy( splitBy( splitBy( - Arrays.asList(line.split("[ \t]")), - ADD, - ADD_REGX), - MUL, - MUL_REGX), - SUB, - SUB_REGX), - DIV, - DIV_REGX), - COMMA, - COMMA), - COLON, - COLON), - LEFT_BRACKET, - LEFT_BRACKET_REGX), - RIGHT_BRACKET, - RIGHT_BRACKET_REGX), - EQUALS, - EQUALS), - LEFT_CURLY_BRACKET, - LEFT_CURLY_BRACKET_REGX), - RIGHT_CURLY_BRACKET, - RIGHT_CURLY_BRACKET_REGX)); + splitBy( + splitBy( + splitBy( + Arrays.asList(line.split("[ \t]")), + ADD, + ADD_REGX), + MUL, + MUL_REGX), + SUB, + SUB_REGX), + DIV, + DIV_REGX), + DOT, + DOT_REGX), + COMMA, + COMMA), + COLON, + COLON), + LEFT_BRACKET, + LEFT_BRACKET_REGX), + RIGHT_BRACKET, + RIGHT_BRACKET_REGX), + EQUALS, + EQUALS), + LEFT_CURLY_BRACKET, + LEFT_CURLY_BRACKET_REGX), + RIGHT_CURLY_BRACKET, + RIGHT_CURLY_BRACKET_REGX), + LEFT_SQUARE_BRACKET, + LEFT_SQUARE_BRACKET_REGX), + RIGHT_SQUARE_BRACKET, + RIGHT_SQUARE_BRACKET_REGX)); } private ArrayList splitBy(final List tokens, final String delimiter, final String delimiterRegx) { diff --git a/AlgebraicDataflowArchitectureModel/src/parser/ParserDTRAM.java b/AlgebraicDataflowArchitectureModel/src/parser/ParserDTRAM.java index 830bd5f..6ea7dec 100644 --- a/AlgebraicDataflowArchitectureModel/src/parser/ParserDTRAM.java +++ b/AlgebraicDataflowArchitectureModel/src/parser/ParserDTRAM.java @@ -14,10 +14,11 @@ import parser.exceptions.ExpectedAssignment; import parser.exceptions.ExpectedChannel; import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; import parser.exceptions.ExpectedEquals; import parser.exceptions.ExpectedFormulaChannel; import parser.exceptions.ExpectedGeometry; -import parser.exceptions.ExpectedInOrOutOrRefKeyword; +import parser.exceptions.ExpectedInOrOutOrRefOrSubKeyword; import parser.exceptions.ExpectedIoChannel; import parser.exceptions.ExpectedLeftCurlyBracket; import parser.exceptions.ExpectedModel; @@ -25,8 +26,11 @@ import parser.exceptions.ExpectedRHSExpression; import parser.exceptions.ExpectedResource; import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.ExpectedRightCurlyBracket; import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; import parser.exceptions.WrongLHSExpression; +import parser.exceptions.WrongPathExpression; import parser.exceptions.WrongRHSExpression; public class ParserDTRAM extends Parser { @@ -61,9 +65,11 @@ /**-------------------------------------------------------------------------------- * * @param reader + * @throws WrongJsonExpression + * @throws ExpectedColon */ public DataTransferModel doParseModel() - throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedInOrOutOrRefKeyword, ExpectedStateTransition, ExpectedEquals, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, ExpectedModel, ExpectedGeometry { + throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedInOrOutOrRefOrSubKeyword, ExpectedStateTransition, ExpectedEquals, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, ExpectedModel, ExpectedGeometry, ExpectedRightCurlyBracket, WrongPathExpression, WrongJsonExpression, ExpectedColon { DataTransferModel model = getParsedModel(); return model; } @@ -73,7 +79,7 @@ * @param graph */ public void doParseGeometry(mxGraph graph) - throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedInOrOutOrRefKeyword, ExpectedStateTransition, ExpectedEquals, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, ExpectedModel, ExpectedGeometry, ExpectedNode, ExpectedResource, ExpectedFormulaChannel, ExpectedIoChannel{ + throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedInOrOutOrRefOrSubKeyword, ExpectedStateTransition, ExpectedEquals, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, ExpectedModel, ExpectedGeometry, ExpectedNode, ExpectedResource, ExpectedFormulaChannel, ExpectedIoChannel{ parseGeometry(graph); } @@ -83,9 +89,11 @@ /**-------------------------------------------------------------------------------- * * @param stream + * @throws WrongJsonExpression + * @throws ExpectedColon */ private DataTransferModel getParsedModel() - throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedInOrOutOrRefKeyword, ExpectedStateTransition, ExpectedEquals, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, ExpectedModel, ExpectedGeometry { + throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedInOrOutOrRefOrSubKeyword, ExpectedStateTransition, ExpectedEquals, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, ExpectedModel, ExpectedGeometry, ExpectedRightCurlyBracket, WrongPathExpression, WrongJsonExpression, ExpectedColon { if (!stream.hasNext()) throw new NullPointerException(); @@ -110,11 +118,11 @@ * @param graph */ private void parseGeometry(mxGraph graph) - throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedInOrOutOrRefKeyword, ExpectedStateTransition, ExpectedEquals, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment,ExpectedModel, ExpectedGeometry, ExpectedNode, ExpectedResource, ExpectedFormulaChannel, ExpectedIoChannel { + throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedInOrOutOrRefOrSubKeyword, ExpectedStateTransition, ExpectedEquals, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment,ExpectedModel, ExpectedGeometry, ExpectedNode, ExpectedResource, ExpectedFormulaChannel, ExpectedIoChannel { - if (!isMatchKeyword(stream.next(), GEOMETRY_GROUP)) throw new ExpectedGeometry(stream.getLine()); + if (!doesMatchToKeyword(stream.next(), GEOMETRY_GROUP)) throw new ExpectedGeometry(stream.getLine()); - if (!isMatchKeyword(stream.next(), LEFT_CURLY_BRACKET)) throw new ExpectedLeftCurlyBracket(stream.getLine()); + if (!doesMatchToKeyword(stream.next(), LEFT_CURLY_BRACKET)) throw new ExpectedLeftCurlyBracket(stream.getLine()); String node = stream.next(); while (node.equals(GEOMETORY_NODE)) { @@ -128,22 +136,22 @@ String name = stream.next(); - if (!isMatchKeyword(stream.next(), COLON)) throw new ExpectedAssignment(stream.getLine()); + if (!doesMatchToKeyword(stream.next(), COLON)) throw new ExpectedAssignment(stream.getLine()); String x = stream.next(); int xC = Integer.parseInt(x); // C = Coordinate(x,y,w,h) - if (!isMatchKeyword(stream.next(), COMMA))throw new ExpectedAssignment(stream.getLine()); + if (!doesMatchToKeyword(stream.next(), COMMA))throw new ExpectedAssignment(stream.getLine()); String y = stream.next(); int yC = Integer.parseInt(y); - if (!isMatchKeyword(stream.next(), COMMA))throw new ExpectedAssignment(stream.getLine()); + if (!doesMatchToKeyword(stream.next(), COMMA))throw new ExpectedAssignment(stream.getLine()); String w = stream.next(); int wC = Integer.parseInt(w); - if (!isMatchKeyword(stream.next(), COMMA))throw new ExpectedAssignment(stream.getLine()); + if (!doesMatchToKeyword(stream.next(), COMMA))throw new ExpectedAssignment(stream.getLine()); String h = stream.next(); int hC = Integer.parseInt(h); diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedColon.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedColon.java new file mode 100644 index 0000000..426aaf1 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedColon.java @@ -0,0 +1,8 @@ +package parser.exceptions; + +public class ExpectedColon extends ParseException { + + public ExpectedColon(int line) { + super(line); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedInOrOutOrRefKeyword.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedInOrOutOrRefKeyword.java deleted file mode 100644 index a44a604..0000000 --- a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedInOrOutOrRefKeyword.java +++ /dev/null @@ -1,9 +0,0 @@ -package parser.exceptions; - -public class ExpectedInOrOutOrRefKeyword extends ParseException { - - public ExpectedInOrOutOrRefKeyword(int line) { - super(line); - } - -} diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedInOrOutOrRefOrSubKeyword.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedInOrOutOrRefOrSubKeyword.java new file mode 100644 index 0000000..8d99767 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedInOrOutOrRefOrSubKeyword.java @@ -0,0 +1,9 @@ +package parser.exceptions; + +public class ExpectedInOrOutOrRefOrSubKeyword extends ParseException { + + public ExpectedInOrOutOrRefOrSubKeyword(int line) { + super(line); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedRightCurlyBracket.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedRightCurlyBracket.java new file mode 100644 index 0000000..df87cb3 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedRightCurlyBracket.java @@ -0,0 +1,9 @@ +package parser.exceptions; + +public class ExpectedRightCurlyBracket extends ParseException { + + public ExpectedRightCurlyBracket(int line) { + super(line); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/WrongJsonExpression.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/WrongJsonExpression.java new file mode 100644 index 0000000..6a84d6f --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/WrongJsonExpression.java @@ -0,0 +1,9 @@ +package parser.exceptions; + +public class WrongJsonExpression extends ParseException { + + public WrongJsonExpression(int line) { + super(line); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/WrongPathExpression.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/WrongPathExpression.java new file mode 100644 index 0000000..7f580a5 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/WrongPathExpression.java @@ -0,0 +1,9 @@ +package parser.exceptions; + +public class WrongPathExpression extends ParseException { + + public WrongPathExpression(int line) { + super(line); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/tests/CodeGeneratorTest.java b/AlgebraicDataflowArchitectureModel/src/tests/CodeGeneratorTest.java index a89ea3c..b318c30 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/CodeGeneratorTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/CodeGeneratorTest.java @@ -17,13 +17,17 @@ import parser.exceptions.ExpectedAssignment; import parser.exceptions.ExpectedChannel; import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; import parser.exceptions.ExpectedEquals; -import parser.exceptions.ExpectedInOrOutOrRefKeyword; +import parser.exceptions.ExpectedInOrOutOrRefOrSubKeyword; import parser.exceptions.ExpectedLeftCurlyBracket; import parser.exceptions.ExpectedRHSExpression; import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.ExpectedRightCurlyBracket; import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; import parser.exceptions.WrongLHSExpression; +import parser.exceptions.WrongPathExpression; import parser.exceptions.WrongRHSExpression; public class CodeGeneratorTest { @@ -39,9 +43,9 @@ DataTransferMethodAnalyzer.decideToStoreResourceStates(graph); ArrayList codetree = JavaMethodBodyGenerator.doGenerate(graph, model, JavaCodeGenerator.doGenerate(graph, model)); System.out.println(codetree); - } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefKeyword + } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression - | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment e) { + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket | WrongPathExpression | WrongJsonExpression | ExpectedColon e) { e.printStackTrace(); } } catch (FileNotFoundException e) { diff --git a/AlgebraicDataflowArchitectureModel/src/tests/DataConstraintModelTest.java b/AlgebraicDataflowArchitectureModel/src/tests/DataConstraintModelTest.java index afe405a..238df91 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/DataConstraintModelTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/DataConstraintModelTest.java @@ -4,6 +4,7 @@ import org.junit.Test; +import models.algebra.Variable; import models.dataConstraintModel.*; public class DataConstraintModelTest { @@ -12,46 +13,46 @@ public void test() { // Construct a data constraint architecture model. DataConstraintModel model = new DataConstraintModel(); - ResourcePath customer_off = new ResourcePath("customers.{customer_id}.off", 1); - ResourcePath customer_add = new ResourcePath("customers.{customer_id}.add", 1); - ResourcePath company_add = new ResourcePath("companies.{company_id}.add", 1); - Channel gin_1 = new Channel("gin_1"); // set customer's office - GroupSelector x1 = new GroupSelector(); + ResourcePath customers = new ResourcePath("customers"); // "customers" + ResourcePath customer = new ResourcePath(customers, new Variable("uid")); // "customers.{uid}" + ResourcePath customer_off = new ResourcePath(customer, "off"); // "customers.{uid}.off" + ResourcePath customer_add = new ResourcePath(customer, "add"); // "customers.{uid}.add" + ResourcePath companies = new ResourcePath("companies"); // "companies" + ResourcePath company = new ResourcePath(companies, new Variable("cid")); // "companies.{cid}" + ResourcePath company_add = new ResourcePath(company, "add"); // "companies.{cid}.add" + model.addResourcePath(customer_off); + model.addResourcePath(customer_add); + model.addResourcePath(company_add); + + Channel cio_setCustomerOff = new Channel("CIO_SetCustomerOff", new Variable("uid")); // set customer's office ChannelMember customer_off_1 = new ChannelMember(customer_off); - customer_off_1.addSelector(x1); - gin_1.addChannelMember(customer_off_1); - assertEquals(customer_off.getNumberOfParameters(), customer_off_1.getSelectors().size()); + cio_setCustomerOff.addChannelMember(customer_off_1); + assertEquals(customer_off.getPathParams().get(0), cio_setCustomerOff.getSelectors().iterator().next().getExpression()); - Channel gin_2 = new Channel("gin_2"); // set companie's address - GroupSelector x2 = new GroupSelector(); + Channel cio_setCompanyAdd = new Channel("CIO_SetCompanyAdd", new Variable("cid")); // set companie's address ChannelMember company_add_1 = new ChannelMember(company_add); - company_add_1.addSelector(x2); - gin_2.addChannelMember(company_add_1); - assertEquals(company_add.getNumberOfParameters(), company_add_1.getSelectors().size()); + cio_setCompanyAdd.addChannelMember(company_add_1); + assertEquals(company_add.getPathParams().get(0), cio_setCompanyAdd.getSelectors().iterator().next().getExpression()); - Channel g = new Channel("g"); // update customer's address - GroupSelector x3 = new GroupSelector(); - ChannelSelector y = new ChannelSelector(); + Channel c = new Channel("c", new Variable("uid")); // update customer's address ChannelMember customer_off_2 = new ChannelMember(customer_off); ChannelMember company_add_2 = new ChannelMember(company_add); ChannelMember customer_add_2 = new ChannelMember(customer_add); - customer_off_2.addSelector(x3); - company_add_2.addSelector(y); - customer_add_2.addSelector(x3); - g.addChannelMember(customer_off_2); - g.addChannelMember(customer_add_2); - g.addChannelMember(company_add_2); - assertEquals(customer_off.getNumberOfParameters(), customer_off_2.getSelectors().size()); - assertEquals(customer_add.getNumberOfParameters(), customer_add_2.getSelectors().size()); - assertEquals(company_add.getNumberOfParameters(), company_add_2.getSelectors().size()); + c.addChannelMember(customer_off_2); + c.addChannelMember(company_add_2); + c.addChannelMember(customer_add_2); + assertEquals(customer_off.getPathParams().get(0), c.getSelectors().iterator().next().getExpression()); + assertEquals(customer_add.getPathParams().get(0), c.getSelectors().iterator().next().getExpression()); + assertEquals(company_add.getPathParams().get(0), new Variable("cid")); - model.addIOChannel(gin_1); - model.addIOChannel(gin_2); - model.addChannel(g); + model.addIOChannel(cio_setCustomerOff); + model.addIOChannel(cio_setCompanyAdd); + model.addChannel(c); // Check the model. assertEquals(3, model.getResourcePaths().size()); + assertEquals(7, model.getResourceHierarchies().size()); assertEquals(2, model.getIOChannels().size()); assertEquals(1, model.getChannels().size()); } diff --git a/AlgebraicDataflowArchitectureModel/src/tests/DataFlowModelTest.java b/AlgebraicDataflowArchitectureModel/src/tests/DataFlowModelTest.java index 605b006..ffbef05 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/DataFlowModelTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/DataFlowModelTest.java @@ -5,6 +5,7 @@ import org.junit.Test; import models.*; +import models.algebra.Variable; import models.dataConstraintModel.*; import models.dataFlowModel.*; @@ -14,63 +15,61 @@ public void test() { // Construct a data-flow architecture model. DataTransferModel model = new DataTransferModel(); - ResourcePath customer_off = new ResourcePath("customers.{x1}.off", 1); // an identifier template to specify a customer's office resource - ResourcePath company_add = new ResourcePath("companies.{x2}.add", 1); // an identifier template to specify a companie's address resource - ResourcePath customer_add = new ResourcePath("customers.{x1}.add", 1); // an identifier template to specify a customer's address resource - // === gin_1 === + ResourcePath customers = new ResourcePath("customers"); // "customers" + ResourcePath customer = new ResourcePath(customers, new Variable("uid")); // "customers.{uid}" + ResourcePath customer_off = new ResourcePath(customer, "off"); // "customers.{uid}.off" + ResourcePath customer_add = new ResourcePath(customer, "add"); // "customers.{uid}.add" + ResourcePath companies = new ResourcePath("companies"); // "companies" + ResourcePath company = new ResourcePath(companies, new Variable("cid")); // "companies.{cid}" + ResourcePath company_add = new ResourcePath(company, "add"); // "companies.{cid}.add" + model.addResourcePath(customer_off); + model.addResourcePath(customer_add); + model.addResourcePath(company_add); + + // === cio_setCustomerOff(uid) === // - // customers.{x1}.off(c, set(x)) == x - // customers.{x1}.off(c, e) == c + // out customers.{uid}.off(c, set(x)) == x // - DataTransferChannel gin_1 = new DataTransferChannel("gin_1"); // set customer's office (an input channel) - GroupSelector x1 = new GroupSelector(); + DataTransferChannel cio_setCustomerOff = new DataTransferChannel("CIO_SetCustomerOff", new Variable("uid")); // set customer's office (an input channel) ChannelMember customer_off_1 = new ChannelMember(customer_off); - customer_off_1.addSelector(x1); // x1 is determined by the path parameter in customer's office template, and serves as a group selector in this channel - gin_1.addChannelMember(customer_off_1); - assertEquals(customer_off.getNumberOfParameters(), customer_off_1.getSelectors().size()); + cio_setCustomerOff.addChannelMemberAsOutput(customer_off_1); + assertEquals(customer_off.getPathParams().get(0), cio_setCustomerOff.getSelectors().iterator().next().getExpression()); - // === gin_2 === + // === cio_setCompanyAdd(cid) === // - // companies.{x2}.add(a, set(y)) == y - // companies.{x2}.add(a, e) == a + // out companies.{cid}.add(a, set(y)) == y // - DataTransferChannel gin_2 = new DataTransferChannel("gin_2"); // set companie's address (an input channel) - GroupSelector x2 = new GroupSelector(); + DataTransferChannel cio_setCompanyAdd = new DataTransferChannel("CIO_SetCompanyAdd", new Variable("cid")); // set companie's address (an input channel) ChannelMember company_add_1 = new ChannelMember(company_add); - company_add_1.addSelector(x2); // x2 is determined by the path parameter in companie's address template, and serves as a group selector in this channel - gin_2.addChannelMember(company_add_1); - assertEquals(company_add.getNumberOfParameters(), company_add_1.getSelectors().size()); + cio_setCompanyAdd.addChannelMemberAsOutput(company_add_1); + assertEquals(company_add.getPathParams().get(0), cio_setCompanyAdd.getSelectors().iterator().next().getExpression()); - // === g === + // === c === // - // customers.{x3}.off( c, update(y, z)) == y - // companies.{y}.add( a1, update(y, z)) == z - // customers.{x3}.add(a2, update(y, z)) == z + // in customers.{uid}.off( c, update(cid, a2)) == cid + // in companies.{cid}.add( a, update(cid, a2)) == a2 + // out customers.{uid}.add(b, update(cid, a2)) == a2 // - DataTransferChannel g = new DataTransferChannel("g"); // update customer's address - GroupSelector x3 = new GroupSelector(); - ChannelSelector y = new ChannelSelector(); + DataTransferChannel c = new DataTransferChannel("c", new Variable("uid")); // update customer's address ChannelMember customer_off_2 = new ChannelMember(customer_off); ChannelMember company_add_2 = new ChannelMember(company_add); ChannelMember customer_add_2 = new ChannelMember(customer_add); - customer_off_2.addSelector(x3); // x3 is determined by the path parameter in customer's office template, and serves as a group selector in this channel - company_add_2.addSelector(y); // y is determined by the value of the customer's office resource, and serves as a channel selector in this channel - customer_add_2.addSelector(x3); // x3 determines the path parameter in customer's address template to update - g.addChannelMemberAsInput(customer_off_2); - g.addChannelMemberAsInput(customer_add_2); - g.addChannelMemberAsOutput(company_add_2); - assertEquals(customer_off.getNumberOfParameters(), customer_off_2.getSelectors().size()); - assertEquals(customer_add.getNumberOfParameters(), customer_add_2.getSelectors().size()); - assertEquals(company_add.getNumberOfParameters(), company_add_2.getSelectors().size()); + c.addChannelMemberAsInput(customer_off_2); + c.addChannelMemberAsInput(company_add_2); + c.addChannelMemberAsOutput(customer_add_2); + assertEquals(customer_off.getPathParams().get(0), c.getSelectors().iterator().next().getExpression()); + assertEquals(customer_add.getPathParams().get(0), c.getSelectors().iterator().next().getExpression()); + assertEquals(company_add.getPathParams().get(0), new Variable("cid")); // Construct a data-flow architecture model. - model.addIOChannel(gin_1); - model.addIOChannel(gin_2); - model.addChannel(g); + model.addIOChannel(cio_setCustomerOff); + model.addIOChannel(cio_setCompanyAdd); + model.addChannel(c); // Check the model. assertEquals(3, model.getResourcePaths().size()); + assertEquals(7, model.getResourceHierarchies().size()); assertEquals(2, model.getIOChannels().size()); assertEquals(1, model.getChannels().size()); @@ -78,8 +77,9 @@ DataFlowGraph resourceDependencyGraph = model.getDataFlowGraph(); // Check the graph. - assertEquals(3, resourceDependencyGraph.getNodes().size()); - assertEquals(2, resourceDependencyGraph.getEdges().size()); + assertEquals(7, resourceDependencyGraph.getResourceNodes().size()); + assertEquals(3, resourceDependencyGraph.getChannelNodes().size()); + assertEquals(5, resourceDependencyGraph.getEdges().size()); for (Edge e: resourceDependencyGraph.getEdges()) { System.out.println(e.getSource() + "-(" + e + ")->" + e.getDestination()); } diff --git a/AlgebraicDataflowArchitectureModel/src/tests/DataStorageDecisionTest.java b/AlgebraicDataflowArchitectureModel/src/tests/DataStorageDecisionTest.java index 9d28936..b228862 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/DataStorageDecisionTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/DataStorageDecisionTest.java @@ -13,13 +13,17 @@ import parser.exceptions.ExpectedAssignment; import parser.exceptions.ExpectedChannel; import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; import parser.exceptions.ExpectedEquals; -import parser.exceptions.ExpectedInOrOutOrRefKeyword; +import parser.exceptions.ExpectedInOrOutOrRefOrSubKeyword; import parser.exceptions.ExpectedLeftCurlyBracket; import parser.exceptions.ExpectedRHSExpression; import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.ExpectedRightCurlyBracket; import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; import parser.exceptions.WrongLHSExpression; +import parser.exceptions.WrongPathExpression; import parser.exceptions.WrongRHSExpression; public class DataStorageDecisionTest { @@ -34,12 +38,12 @@ DataFlowGraph graph = DataTransferModelAnalyzer.createDataFlowGraphWithStateStoringAttribute(model); DataTransferModelAnalyzer.annotateWithSelectableDataTransferAttiribute(graph); DataTransferMethodAnalyzer.decideToStoreResourceStates(graph); - for(Node n:graph.getNodes()) { - System.out.println(((ResourceNode) n).getResource().getResourceName() + ":" + ((StoreAttribute) ((ResourceNode) n).getAttribute()).isStored()); + for(Node resNode: graph.getResourceNodes()) { + System.out.println(((ResourceNode) resNode).getResourceName() + ":" + ((StoreAttribute) ((ResourceNode) resNode).getAttribute()).isStored()); } - } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefKeyword + } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression - | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment e) { + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket | WrongPathExpression | WrongJsonExpression | ExpectedColon e) { e.printStackTrace(); } } catch (FileNotFoundException e) { diff --git a/AlgebraicDataflowArchitectureModel/src/tests/DataStorageNecessityTest.java b/AlgebraicDataflowArchitectureModel/src/tests/DataStorageNecessityTest.java index 9a7c300..73eff57 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/DataStorageNecessityTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/DataStorageNecessityTest.java @@ -12,13 +12,17 @@ import parser.exceptions.ExpectedAssignment; import parser.exceptions.ExpectedChannel; import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; import parser.exceptions.ExpectedEquals; -import parser.exceptions.ExpectedInOrOutOrRefKeyword; +import parser.exceptions.ExpectedInOrOutOrRefOrSubKeyword; import parser.exceptions.ExpectedLeftCurlyBracket; import parser.exceptions.ExpectedRHSExpression; import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.ExpectedRightCurlyBracket; import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; import parser.exceptions.WrongLHSExpression; +import parser.exceptions.WrongPathExpression; import parser.exceptions.WrongRHSExpression; public class DataStorageNecessityTest { @@ -37,9 +41,9 @@ System.out.println(resource.toString() + ":" + ((StoreAttribute)resource.getAttribute()).isNeeded()); } } - } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefKeyword + } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression - | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment e) { + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket | WrongPathExpression | WrongJsonExpression | ExpectedColon e) { e.printStackTrace(); } } catch (FileNotFoundException e) { diff --git a/AlgebraicDataflowArchitectureModel/src/tests/EdgeTransitionSelectableTest.java b/AlgebraicDataflowArchitectureModel/src/tests/EdgeTransitionSelectableTest.java index 847fb6f..c3b11ab 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/EdgeTransitionSelectableTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/EdgeTransitionSelectableTest.java @@ -12,13 +12,17 @@ import parser.exceptions.ExpectedAssignment; import parser.exceptions.ExpectedChannel; import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; import parser.exceptions.ExpectedEquals; -import parser.exceptions.ExpectedInOrOutOrRefKeyword; +import parser.exceptions.ExpectedInOrOutOrRefOrSubKeyword; import parser.exceptions.ExpectedLeftCurlyBracket; import parser.exceptions.ExpectedRHSExpression; import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.ExpectedRightCurlyBracket; import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; import parser.exceptions.WrongLHSExpression; +import parser.exceptions.WrongPathExpression; import parser.exceptions.WrongRHSExpression; public class EdgeTransitionSelectableTest { @@ -36,9 +40,9 @@ DataFlowEdge re = (DataFlowEdge) e; System.out.println(re.getSource() + "-" + re.getDestination() + ":" + ((PushPullAttribute)(re.getAttribute())).getOptions()); } - } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefKeyword + } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression - | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment e) { + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket | WrongPathExpression | WrongJsonExpression | ExpectedColon e) { e.printStackTrace(); } } catch (FileNotFoundException e) { diff --git a/AlgebraicDataflowArchitectureModel/src/tests/FormulaChannelTest.java b/AlgebraicDataflowArchitectureModel/src/tests/FormulaChannelTest.java index a8f3503..6bf26f4 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/FormulaChannelTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/FormulaChannelTest.java @@ -5,6 +5,7 @@ import models.algebra.Symbol; import models.dataConstraintModel.ChannelMember; import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.ResourceHierarchy; import models.dataConstraintModel.ResourcePath; import models.visualModel.FormulaChannel; @@ -12,23 +13,23 @@ @Test public void test() { - ResourcePath id1 = new ResourcePath("r1", 0); - ResourcePath id2 = new ResourcePath("r2", 0); - ResourcePath id3 = new ResourcePath("r3", 0); + ResourcePath res1 = new ResourcePath("r1"); + ResourcePath res2 = new ResourcePath("r2"); + ResourcePath res3 = new ResourcePath("r3"); FormulaChannel ch1 = new FormulaChannel("ch1", DataConstraintModel.add); System.out.println(ch1.getFormula()); System.out.println(ch1.getFormulaTerm()); System.out.println(ch1.getSourceText()); - ch1.addChannelMemberAsInput(new ChannelMember(id1)); + ch1.addChannelMemberAsInput(new ChannelMember(res1)); System.out.println(ch1.getFormula()); System.out.println(ch1.getFormulaTerm()); System.out.println(ch1.getSourceText()); - ch1.addChannelMemberAsInput(new ChannelMember(id2)); + ch1.addChannelMemberAsInput(new ChannelMember(res2)); System.out.println(ch1.getFormula()); System.out.println(ch1.getFormulaTerm()); System.out.println(ch1.getSourceText()); - ch1.addChannelMemberAsOutput(new ChannelMember(id3)); + ch1.addChannelMemberAsOutput(new ChannelMember(res3)); System.out.println(ch1.getFormula()); System.out.println(ch1.getFormulaTerm()); System.out.println(ch1.getSourceText()); @@ -37,15 +38,15 @@ System.out.println(ch2.getFormula()); System.out.println(ch2.getFormulaTerm()); System.out.println(ch2.getSourceText()); - ch2.addChannelMemberAsOutput(new ChannelMember(id3)); + ch2.addChannelMemberAsOutput(new ChannelMember(res3)); System.out.println(ch2.getFormula()); System.out.println(ch2.getFormulaTerm()); System.out.println(ch2.getSourceText()); - ch2.addChannelMemberAsInput(new ChannelMember(id1)); + ch2.addChannelMemberAsInput(new ChannelMember(res1)); System.out.println(ch2.getFormula()); System.out.println(ch2.getFormulaTerm()); System.out.println(ch2.getSourceText()); - ch2.addChannelMemberAsInput(new ChannelMember(id2)); + ch2.addChannelMemberAsInput(new ChannelMember(res2)); System.out.println(ch2.getFormula()); System.out.println(ch2.getFormulaTerm()); System.out.println(ch2.getSourceText()); diff --git a/AlgebraicDataflowArchitectureModel/src/tests/InverseTest.java b/AlgebraicDataflowArchitectureModel/src/tests/InverseTest.java index bfa8957..165013e 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/InverseTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/InverseTest.java @@ -14,18 +14,23 @@ import models.algebra.Expression; import models.algebra.Position; +import models.algebra.Term; import models.algebra.Variable; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.JsonType; import models.dataFlowModel.DataTransferModel; import parser.Parser; import parser.Parser.TokenStream; +import parser.exceptions.ExpectedColon; import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.WrongJsonExpression; public class InverseTest { @Test public void test() { - String lhs = "y"; DataTransferModel model = new DataTransferModel(); try { + String lhs = "y"; String rhs = "(a * x + b) * c"; TokenStream stream = new Parser.TokenStream(); @@ -33,7 +38,7 @@ stream.addLine(rhs); Expression rhsExp = parser.parseTerm(stream, model); - System.out.println(lhs + " = " + rhsExp); + System.out.println("=== solve{" + lhs + " = " + rhsExp + "} for a, b, d, x ==="); HashMap rhsVars = rhsExp.getVariables(); assertEquals(4, rhsVars.size()); @@ -47,7 +52,66 @@ assertFalse(inv.contains(v)); System.out.println(rhsVars.get(vPos) + " = " + inv); } - } catch (ExpectedRightBracket e) { + + // Extract an element in a tuple + TokenStream stream2 = new Parser.TokenStream(); + Parser parser2 = new Parser(stream2); + stream2.addLine("fst(tuple(x, y))"); + Expression tupleExp = parser2.parseTerm(stream2, model); + stream2.addLine("snd(tuple(x, y))"); + Expression tupleExp2 = parser2.parseTerm(stream2, model); + Expression reduced = ((Term) tupleExp).reduce(); + Expression reduced2 = ((Term) tupleExp2).reduce(); + Variable x = new Variable("x"); + assertEquals(reduced, x); + assertEquals(reduced2, y); + System.out.println("=== simplify ==="); + System.out.println(tupleExp + " = " + reduced); + System.out.println(tupleExp2 + " = " + reduced2); + + // Solve {z = fst(x)} for x + TokenStream stream3 = new Parser.TokenStream(); + Parser parser3 = new Parser(stream3); + stream3.addLine("fst(x)"); + Expression rhsExp3 = parser3.parseTerm(stream3, model); + Variable z = new Variable("z"); + System.out.println("=== solve{" + z + " = " + rhsExp3 + "} for x ==="); + HashMap rhsVars3 = rhsExp3.getVariables(); + for (Position vPos: rhsVars3.keySet()) { + Variable v = rhsVars3.get(vPos); + Expression inv = rhsExp3.getInverseMap(z, vPos); // inverse map to get v back from the output value z + if (inv instanceof Term) { + inv = ((Term) inv).reduce(); + } + assertTrue(inv.contains(z)); + assertFalse(inv.contains(v)); + System.out.println(rhsVars3.get(vPos) + " = " + inv); + } + + // Solve {z = x.id} for x + TokenStream stream4 = new Parser.TokenStream(); + Parser parser4 = new Parser(stream4); + stream4.addLine("x.id"); + Expression rhsExp4 = parser4.parseTerm(stream4, model); + System.out.println("=== solve{" + z + " = " + rhsExp4 + "} for x ==="); + HashMap rhsVars4 = rhsExp4.getVariables(); + for (Position vPos: rhsVars4.keySet()) { + Variable v = rhsVars4.get(vPos); + if (x.getName().equals("x")) { + JsonType jsonType = new JsonType("Json", "HashMap<>", DataConstraintModel.typeJson); + jsonType.addMemberType("id", DataConstraintModel.typeInt); + jsonType.addMemberType("name", DataConstraintModel.typeString); + v.setType(jsonType); + } + Expression inv = rhsExp4.getInverseMap(z, vPos); // inverse map to get v back from the output value z + if (inv instanceof Term) { + inv = ((Term) inv).reduce(); + } + System.out.println(rhsVars4.get(vPos) + " = " + inv); + assertTrue(inv.contains(z)); + assertFalse(inv.contains(v)); + } + } catch (ExpectedRightBracket | WrongJsonExpression | ExpectedColon e) { e.printStackTrace(); } } diff --git a/AlgebraicDataflowArchitectureModel/src/tests/SimplifiedDataFlowModelTest.java b/AlgebraicDataflowArchitectureModel/src/tests/SimplifiedDataFlowModelTest.java index acb5333..7c30c68 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/SimplifiedDataFlowModelTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/SimplifiedDataFlowModelTest.java @@ -14,10 +14,14 @@ public void test() { // Construct a data-flow architecture model. DataTransferModel model = new DataTransferModel(); - ResourcePath payment = new ResourcePath("payment", 0); // a resource to specify the payment resource - ResourcePath loyalty = new ResourcePath("loyalty", 0); // a resource to specify the loyalty resource - ResourcePath history = new ResourcePath("history", 0); // a resource to specify the payment history resource - ResourcePath total = new ResourcePath("total", 0); // a resource to specify the total payment resource + ResourcePath payment = new ResourcePath("payment"); // a resource to specify the payment resource + ResourcePath points = new ResourcePath("points"); // a resource to specify the points resource + ResourcePath history = new ResourcePath("history"); // a resource to specify the payment history resource + ResourcePath total = new ResourcePath("total"); // a resource to specify the total payment resource + model.addResourcePath(payment); + model.addResourcePath(points); + model.addResourcePath(history); + model.addResourcePath(total); // === cin === // @@ -31,11 +35,11 @@ // === c1 === // // payment(p1, update1(y)) == y - // loyalty(l, update1(y)) == floor(y * 0.05) + // points(l, update1(y)) == floor(y * 0.05) // DataTransferChannel c1 = new DataTransferChannel("c1"); ChannelMember c1_payment = new ChannelMember(payment); - ChannelMember c1_loyalty = new ChannelMember(loyalty); + ChannelMember c1_loyalty = new ChannelMember(points); c1.addChannelMemberAsInput(c1_payment); c1.addChannelMemberAsOutput(c1_loyalty); assertEquals(c1.getChannelMembers().size(), 2); @@ -85,8 +89,9 @@ DataFlowGraph resourceDependencyGraph = model.getDataFlowGraph(); // Check the graph. - assertEquals(4, resourceDependencyGraph.getNodes().size()); - assertEquals(3, resourceDependencyGraph.getEdges().size()); + assertEquals(4, resourceDependencyGraph.getResourceNodes().size()); + assertEquals(4, resourceDependencyGraph.getChannelNodes().size()); + assertEquals(6, resourceDependencyGraph.getEdges().size()); for (Edge e: resourceDependencyGraph.getEdges()) { System.out.println(e.getSource() + "-(" + e + ")->" + e.getDestination()); } diff --git a/AlgebraicDataflowArchitectureModel/src/tests/UpdateCodeGenerationTest.java b/AlgebraicDataflowArchitectureModel/src/tests/UpdateCodeGenerationTest.java index 69a5b1f..92c754d 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/UpdateCodeGenerationTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/UpdateCodeGenerationTest.java @@ -25,92 +25,110 @@ Symbol sum = new Symbol("sum", 1, Symbol.Type.PREFIX, "stream().mapToInt(x->x).sum", Symbol.Type.METHOD); // resources - ResourcePath payment = new ResourcePath("payment", DataConstraintModel.typeInt, 0); // a resource to specify the payment resource - ResourcePath loyalty = new ResourcePath("loyalty", DataConstraintModel.typeInt, 0); // a resource to specify the loyalty resource - ResourcePath history = new ResourcePath("history", DataConstraintModel.typeList, 0);// a resource to specify the payment history resource - ResourcePath total = new ResourcePath("total", DataConstraintModel.typeInt, 0); // a resource to specify the total payment resource + ResourcePath payment = new ResourcePath("payment"); // a resource to specify the payment resource + ResourcePath points = new ResourcePath("points"); // a resource to specify the loyalty resource + ResourcePath history = new ResourcePath("history"); // a resource to specify the payment history resource + ResourcePath total = new ResourcePath("total"); // a resource to specify the total payment resource + payment.setResourceStateType(DataConstraintModel.typeInt); + points.setResourceStateType(DataConstraintModel.typeInt); + history.setResourceStateType(DataConstraintModel.typeInt); + total.setResourceStateType(DataConstraintModel.typeInt); // fields in the Java program final Field fPayment = new Field("payment", DataConstraintModel.typeInt); - final Field fLoyalty = new Field("loyalty", DataConstraintModel.typeInt); + final Field fPoints = new Field("points", DataConstraintModel.typeInt); final Field fHistory = new Field("history", DataConstraintModel.typeList); final Field fTotal = new Field("total", DataConstraintModel.typeInt); // parameters in the Java program final Parameter pPayment = new Parameter("payment", DataConstraintModel.typeInt); - final Parameter pLoyalty = new Parameter("loyalty", DataConstraintModel.typeInt); + final Parameter pPoints = new Parameter("points", DataConstraintModel.typeInt); final Parameter pHistory = new Parameter("history", DataConstraintModel.typeList); final Parameter pTotal = new Parameter("total", DataConstraintModel.typeInt); IResourceStateAccessor pushAccessor = new IResourceStateAccessor() { @Override - public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) { - if (target.equals(from)) { - if (target.equals(payment)) return fPayment; - if (target.equals(loyalty)) return fLoyalty; - if (target.equals(history)) return fHistory; - if (target.equals(total)) return fTotal; + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + ResourcePath fromRes = from.getResource(); + if (targetRes.equals(fromRes)) { + if (targetRes.equals(payment)) return fPayment; + if (targetRes.equals(points)) return fPoints; + if (targetRes.equals(history)) return fHistory; + if (targetRes.equals(total)) return fTotal; } return null; } @Override - public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { - if (target.equals(payment)) return pPayment; - if (target.equals(loyalty)) return pLoyalty; - if (target.equals(history)) return pHistory; - if (target.equals(total)) return pTotal; + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + if (targetRes.equals(payment)) return pPayment; + if (targetRes.equals(points)) return pPoints; + if (targetRes.equals(history)) return pHistory; + if (targetRes.equals(total)) return pTotal; + return null; + } + @Override + public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { return null; } }; // methods in the Java program final Symbol paymentGetter = new Symbol("getPayment", 1, Symbol.Type.METHOD); - final Symbol loyltyGetter = new Symbol("getLoyalty", 1, Symbol.Type.METHOD); + final Symbol pointsGetter = new Symbol("getPoints", 1, Symbol.Type.METHOD); final Symbol historyGetter = new Symbol("getHistory", 1, Symbol.Type.METHOD); final Symbol totalGetter = new Symbol("getTotal", 1, Symbol.Type.METHOD); IResourceStateAccessor pullAccessor = new IResourceStateAccessor() { @Override - public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) { - if (target.equals(from)) { - if (target.equals(payment)) return fPayment; - if (target.equals(loyalty)) return fLoyalty; - if (target.equals(history)) return fHistory; - if (target.equals(total)) return fTotal; + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + ResourcePath fromRes = from.getResource(); + if (targetRes.equals(fromRes)) { + if (targetRes.equals(payment)) return fPayment; + if (targetRes.equals(points)) return fPoints; + if (targetRes.equals(history)) return fHistory; + if (targetRes.equals(total)) return fTotal; } return null; } @Override - public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { - if (target.equals(payment)) { + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + if (targetRes.equals(payment)) { Term getter = new Term(paymentGetter); getter.addChild(fPayment); return getter; } - if (target.equals(loyalty)) { - Term getter = new Term(loyltyGetter); - getter.addChild(fLoyalty); + if (targetRes.equals(points)) { + Term getter = new Term(pointsGetter); + getter.addChild(fPoints); return getter; } - if (target.equals(history)) { + if (targetRes.equals(history)) { Term getter = new Term(historyGetter); getter.addChild(fHistory); return getter; } - if (target.equals(total)) { + if (targetRes.equals(total)) { Term getter = new Term(totalGetter); getter.addChild(fTotal); return getter; } return null; + } + @Override + public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { + return null; } }; // === c1 === // // payment(p1, update1(y)) == y - // loyalty(l, update1(y)) == floor(y * 0.05) + // points(l, update1(y)) == floor(y * 0.05) // DataTransferChannel c1 = new DataTransferChannel("c1"); ChannelMember c1_payment = new ChannelMember(payment); - ChannelMember c1_loyalty = new ChannelMember(loyalty); + ChannelMember c1_loyalty = new ChannelMember(points); c1.addChannelMemberAsInput(c1_payment); c1.addChannelMemberAsOutput(c1_loyalty); @@ -121,11 +139,11 @@ Symbol update1 = new Symbol("update1", 1); Term c1_message = new Term(update1); // update1(y) c1_message.addChild(y); - Term rawLoyality = new Term(DataConstraintModel.mul); // y*0.05 - rawLoyality.addChild(y); - rawLoyality.addChild(c_0_05); - Term nextLoyality = new Term(floor); // floor(y*0.05) - nextLoyality.addChild(rawLoyality); + Term rawPoints = new Term(DataConstraintModel.mul); // y*0.05 + rawPoints.addChild(y); + rawPoints.addChild(c_0_05); + Term nextPoints = new Term(floor); // floor(y*0.05) + nextPoints.addChild(rawPoints); StateTransition c1_payment_transition = new StateTransition(); c1_payment_transition.setCurStateExpression(p1); @@ -133,11 +151,11 @@ c1_payment_transition.setNextStateExpression(y); c1_payment.setStateTransition(c1_payment_transition); - StateTransition c1_loyalty_transition = new StateTransition(); - c1_loyalty_transition.setCurStateExpression(l); - c1_loyalty_transition.setMessageExpression(c1_message); - c1_loyalty_transition.setNextStateExpression(nextLoyality); - c1_loyalty.setStateTransition(c1_loyalty_transition); + StateTransition c1_points_transition = new StateTransition(); + c1_points_transition.setCurStateExpression(l); + c1_points_transition.setMessageExpression(c1_message); + c1_points_transition.setNextStateExpression(nextPoints); + c1_loyalty.setStateTransition(c1_points_transition); System.out.println(c1); @@ -150,17 +168,17 @@ Expression loyaltyPushUpdate = c1.deriveUpdateExpressionOf(c1_loyalty, pushAccessor); Parameter param = null; for (Parameter p: loyaltyPushUpdate.getSubTerms(Parameter.class).values()) { - if (p.equals(pPayment) || p.equals(pLoyalty) || p.equals(pHistory) || p.equals(pTotal)) { + if (p.equals(pPayment) || p.equals(pPoints) || p.equals(pHistory) || p.equals(pTotal)) { param = p; break; } } System.out.println("void update(" + param.getType().getImplementationTypeName() + " " + param.toImplementation(sideEffects) + ") {"); - System.out.println("\t" + fLoyalty + " = " + loyaltyPushUpdate.toImplementation(sideEffects) + ";"); + System.out.println("\t" + fPoints + " = " + loyaltyPushUpdate.toImplementation(sideEffects) + ";"); System.out.println("}"); System.out.println("-- PULL --"); - System.out.println(loyalty.getResourceStateType().getImplementationTypeName() + " " + loyltyGetter.toImplementation() + "() {"); + System.out.println(points.getResourceStateType().getImplementationTypeName() + " " + pointsGetter.toImplementation() + "() {"); System.out.println("\t return " + c1.deriveUpdateExpressionOf(c1_loyalty, pullAccessor).toImplementation(sideEffects) + ";"); System.out.println("}"); } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage @@ -213,7 +231,7 @@ Expression historyPushUpdate = c2.deriveUpdateExpressionOf(c2_history, pushAccessor); Parameter param = null; for (Parameter p: historyPushUpdate.getSubTerms(Parameter.class).values()) { - if (p.equals(pPayment) || p.equals(pLoyalty) || p.equals(pHistory) || p.equals(pTotal)) { + if (p.equals(pPayment) || p.equals(pPoints) || p.equals(pHistory) || p.equals(pTotal)) { param = p; break; } @@ -276,7 +294,7 @@ Expression totalPushUpdate = c3.deriveUpdateExpressionOf(c3_total, pushAccessor); Parameter param = null; for (Parameter p: totalPushUpdate.getSubTerms(Parameter.class).values()) { - if (p.equals(pPayment) || p.equals(pLoyalty) || p.equals(pHistory) || p.equals(pTotal)) { + if (p.equals(pPayment) || p.equals(pPoints) || p.equals(pHistory) || p.equals(pTotal)) { param = p; break; } diff --git a/AlgebraicDataflowArchitectureModel/src/tests/UpdateConflictCheckTest.java b/AlgebraicDataflowArchitectureModel/src/tests/UpdateConflictCheckTest.java index 5f51af7..c507ae9 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/UpdateConflictCheckTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/UpdateConflictCheckTest.java @@ -11,13 +11,17 @@ import parser.exceptions.ExpectedAssignment; import parser.exceptions.ExpectedChannel; import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; import parser.exceptions.ExpectedEquals; -import parser.exceptions.ExpectedInOrOutOrRefKeyword; +import parser.exceptions.ExpectedInOrOutOrRefOrSubKeyword; import parser.exceptions.ExpectedLeftCurlyBracket; import parser.exceptions.ExpectedRHSExpression; import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.ExpectedRightCurlyBracket; import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; import parser.exceptions.WrongLHSExpression; +import parser.exceptions.WrongPathExpression; import parser.exceptions.WrongRHSExpression; public class UpdateConflictCheckTest { @@ -25,41 +29,14 @@ File file = new File("models/POS2.model"); try { Parser parser = new Parser(new BufferedReader(new FileReader(file))); + DataTransferModel model; try { - DataTransferModel model = parser.doParse(); + model = parser.doParse(); System.out.println(Validation.checkUpdateConflict(model)); - } catch (ExpectedRightBracket e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (ExpectedChannel e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (ExpectedChannelName e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (ExpectedLeftCurlyBracket e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (ExpectedInOrOutOrRefKeyword e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (ExpectedStateTransition e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (ExpectedEquals e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (ExpectedRHSExpression e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (WrongLHSExpression e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (WrongRHSExpression e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (ExpectedAssignment e) { - // TODO Auto-generated catch block + } catch (ExpectedRightBracket | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket + | ExpectedRightCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword | ExpectedStateTransition | ExpectedEquals + | ExpectedRHSExpression | WrongLHSExpression | WrongRHSExpression | ExpectedAssignment + | WrongPathExpression | WrongJsonExpression | ExpectedColon e) { e.printStackTrace(); } } catch (FileNotFoundException e) { diff --git a/AlgebraicDataflowArchitectureModel/src/tests/parser/ParseTest.java b/AlgebraicDataflowArchitectureModel/src/tests/parser/ParseTest.java index a1ea0a3..93ef4e8 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/parser/ParseTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/parser/ParseTest.java @@ -17,13 +17,17 @@ import parser.exceptions.ExpectedAssignment; import parser.exceptions.ExpectedChannel; import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; import parser.exceptions.ExpectedEquals; -import parser.exceptions.ExpectedInOrOutOrRefKeyword; +import parser.exceptions.ExpectedInOrOutOrRefOrSubKeyword; import parser.exceptions.ExpectedLeftCurlyBracket; import parser.exceptions.ExpectedRHSExpression; import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.ExpectedRightCurlyBracket; import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; import parser.exceptions.WrongLHSExpression; +import parser.exceptions.WrongPathExpression; import parser.exceptions.WrongRHSExpression; public class ParseTest { @@ -50,11 +54,11 @@ for (Edge e: resourceDependencyGraph.getEdges()) { System.out.println(e.getSource() + "-(" + e + ")->" + e.getDestination()); } - } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefKeyword + } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression | WrongRHSExpression | ExpectedRightBracket | ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage - | UnificationFailed | ValueUndefined | ExpectedAssignment e) { + | UnificationFailed | ValueUndefined | ExpectedAssignment | ExpectedRightCurlyBracket | WrongPathExpression | WrongJsonExpression | ExpectedColon e) { e.printStackTrace(); } } catch (FileNotFoundException e) {