diff --git a/AlgebraicDataflowArchitectureModel/models/Accounts.model b/AlgebraicDataflowArchitectureModel/models/Accounts.model new file mode 100644 index 0000000..dba68ba --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/Accounts.model @@ -0,0 +1,7 @@ +channel CIO1 { + out accounts(l:Map, signup(id:Int, name:Str)) = append(l, {"name": name}) +} + +channel CIO2(uid:Int) { + out accounts.{uid}.name(n:Str, changeName(name)) = name +} diff --git a/AlgebraicDataflowArchitectureModel/models/Algo.model b/AlgebraicDataflowArchitectureModel/models/Algo.model deleted file mode 100644 index 6228431..0000000 --- a/AlgebraicDataflowArchitectureModel/models/Algo.model +++ /dev/null @@ -1,12 +0,0 @@ -channel turnA{ - ref handsB(b:List, drawA(target:Int, guess:Int, b, d)) - ref deck(d:List, drawA(target, guess, b, d)) - out handsA(a:List, drawA(target, guess, b, d)) == if(eq(get(b, target), guess), cons(tuple(fst(head(d)), false), a), cons(tuple(fst(head(d)), true), a)) - out handsB(a:List, drawA(target, guess, b, d)) == if(eq(get(b, target), guess), set(a, target, tuple(fst(get(a, target)), true)), a) - out deck(t:List, drawA(target, guess, b, d)) == tail(d) -} -channel turnAA{ - ref handsB(b:List, selectA(target:Int, guess:Int, attacker:Int, b)) - out handsA(a:List, selectA(target, guess, attacker, b)) == if(eq(get(b, target), guess), a, set(a, attacker, tuple(fst(get(a, attacker)), true))) - out handsB(a:List, selectA(target, guess, attacker, b)) == if(eq(get(b, target), guess), set(a, target, tuple(fst(get(a, target)), true)), a) -} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/models/Algolike.model b/AlgebraicDataflowArchitectureModel/models/Algolike.model index 97aef04..8ea8dc2 100644 --- a/AlgebraicDataflowArchitectureModel/models/Algolike.model +++ b/AlgebraicDataflowArchitectureModel/models/Algolike.model @@ -6,25 +6,25 @@ guessB := 0 } channel targetAInput{ - out targetA(t:Int, setTargetA(a:Int)) == a + out targetA(t:Int, setTargetA(a:Int)) = a } channel targetBInput{ - out targetB(t:Int, setTargetB(b:Int)) == b + out targetB(t:Int, setTargetB(b:Int)) = b } channel attackerAInput{ - out attackerA(t:Int, setAttackerA(a:Int)) == a + out attackerA(t:Int, setAttackerA(a:Int)) = a } channel attackerBInput{ - out attackerB(t:Int, setAttackerB(b:Int)) == b + out attackerB(t:Int, setAttackerB(b:Int)) = b } channel guessAInput{ - out guessA(t:Int, setGuessA(a:Int)) == a + out guessA(t:Int, setGuessA(a:Int)) = a } channel guessBInput{ - out guessB(t:Int, setGuessB(b:Int)) == b + out guessB(t:Int, setGuessB(b:Int)) = b } channel addDeck{ - out deck(d:List, addCard(num:Integer)) == cons(tuple(num, false), d) + out deck(d:List, addCard(num:Integer)) = cons(tuple(num, false), d) } channel drawAInput{ @@ -32,21 +32,21 @@ ref targetA(t:Int, drawAndAttackA(t, g, b)) ref guessA(g:Int, drawAndAttackA(t, g, b)) ref handsB(b:List, drawAndAttackA(t, g, b)) - out resultByDrawingA(sda:Tuple, drawAndAttackA(t, g, b)) == tuple(eq(fst(get(b, t)), g), t) + out resultByDrawingA(sda:Tuple, drawAndAttackA(t, g, b)) = tuple(fst(get(b, t)) == g, t) } channel inputDrawArgsA{ - in resultByDrawingA(sda:Tuple, drawA(sucTrg, d)) == sucTrg + in resultByDrawingA(sda:Tuple, drawA(sucTrg, d)) = sucTrg ref deck(d:List, drawA(sucTrg, d)) - out handsA(outA:List, drawA(sucTrg, d)) == if(fst(sucTrg), - sortByKey(cons(tuple(fst(head(d)),false), outA)), + out handsA(outA:List, drawA(sucTrg, d)) = if(fst(sucTrg), + sortByKey(cons(tuple(fst(head(d)), false), outA)), sortByKey(cons(tuple(fst(head(d)), true), outA))) - out handsB(outB:List, drawA(sucTrg, d)) == if(fst(sucTrg), + out handsB(outB:List, drawA(sucTrg, d)) = if(fst(sucTrg), set(outB, snd(sucTrg), tuple(fst(get(outB, snd(sucTrg))), true)), outB) - out deck(t:List, drawA(sucTrg, d)) == tail(t) + out deck(t:List, drawA(sucTrg, d)) = tail(t) } channel selectAInput{ @@ -54,14 +54,14 @@ ref targetA(t:Int, selectAndAttackA(a, t, g, b)) ref guessA(g:Int, selectAndAttackA(a, t, g, b)) ref handsB(b:List, selectAndAttackA(a, t, g, b)) - out resultBySelectingA(ssa:Tuple, selectAndAttackA(a, t, g, b)) == tuple(eq(fst(get(b, t)), g), t, a) + out resultBySelectingA(ssa:Tuple, selectAndAttackA(a, t, g, b)) = tuple(fst(get(b, t)) == g, t, a) } channel inputSelectArgA{ - in resultBySelectingA(ssa, selectA(sucTrgAtk)) == sucTrgAtk - out handsA(outA, selectA(sucTrgAtk)) == if(fst(sucTrgAtk), + in resultBySelectingA(ssa, selectA(sucTrgAtk)) = sucTrgAtk + out handsA(outA, selectA(sucTrgAtk)) = if(fst(sucTrgAtk), outA, set(outA, snd(snd(sucTrgAtk)), tuple(fst(get(outA, snd(snd(sucTrgAtk)))), true))) - out handsB(outB, selectA(sucTrgAtk)) == if(fst(sucTrgAtk), + out handsB(outB, selectA(sucTrgAtk)) = if(fst(sucTrgAtk), set(outB, fst(snd(sucTrgAtk)), tuple(fst(get(outB, fst(snd(sucTrgAtk)))), true)), outB) } @@ -69,21 +69,21 @@ ref targetB(t:Int, drawAndAttackB(t, g, a)) ref guessB(g:Int, drawAndAttackB(t, g, a)) ref handsA(a:List, drawAndAttackB(t, g, a)) - out resultByDrawingB(sdb:Tuple, drawAndAttackB(t, g, a)) == tuple(eq(fst(get(a, t)), g), t) + out resultByDrawingB(sdb:Tuple, drawAndAttackB(t, g, a)) = tuple(fst(get(a, t)) == g, t) } channel inputDrawArgsB{ - in resultByDrawingB(sdb:Tuple, drawB(sucTrg, d)) == sucTrg + in resultByDrawingB(sdb:Tuple, drawB(sucTrg, d)) = sucTrg ref deck(d:List, drawB(sucTrg, d)) - out handsB(outB:List, drawB(sucTrg, d)) == if(fst(sucTrg), + out handsB(outB:List, drawB(sucTrg, d)) = if(fst(sucTrg), sortByKey(cons(tuple(fst(head(d)),false), outB)), sortByKey(cons(tuple(fst(head(d)), true), outB))) - out handsA(outA:List, drawB(sucTrg, d)) == if(fst(sucTrg), + out handsA(outA:List, drawB(sucTrg, d)) = if(fst(sucTrg), set(outA, snd(sucTrg), tuple(fst(get(outA, snd(sucTrg))), true)), outA) - out deck(t:List, drawB(sucTrg, d)) == tail(t) + out deck(t:List, drawB(sucTrg, d)) = tail(t) } channel selectBInput{ @@ -91,23 +91,23 @@ ref targetB(t:Int, selectAndAttackB(atk, t, g, a)) ref guessB(g:Int, selectAndAttackB(atk, t, g, a)) ref handsA(a:List, selectAndAttackB(atk, t, g, a)) - out resultBySelectingB(ssb:Tuple, selectAndAttackB(atk, t, g, a)) == tuple(eq(fst(get(a, t)), g), t, atk) + out resultBySelectingB(ssb:Tuple, selectAndAttackB(atk, t, g, a)) = tuple(fst(get(a, t)) == g, t, atk) } channel inputSelectArgB{ - in resultBySelectingB(ssb, selectB(sucTrgAtk)) == sucTrgAtk - out handsB(outB, selectB(sucTrgAtk)) == if(fst(sucTrgAtk), + in resultBySelectingB(ssb, selectB(sucTrgAtk)) = sucTrgAtk + out handsB(outB, selectB(sucTrgAtk)) = if(fst(sucTrgAtk), outB, set(outB, snd(snd(sucTrgAtk)), tuple(fst(get(outB, snd(snd(sucTrgAtk)))), true))) - out handsA(outA, selectB(sucTrgAtk)) == if(fst(sucTrgAtk), + out handsA(outA, selectB(sucTrgAtk)) = if(fst(sucTrgAtk), set(outA, fst(snd(sucTrgAtk)), tuple(fst(get(outA, fst(snd(sucTrgAtk)))), true)), outA) } channel judgeA{ - in handsA(a:List, judge(j)) == j - out loseA(la:Bool, judge(j)) == eq(length(extractFaceDown(j)), 0) + in handsA(a:List, judge(j)) = j + out loseA(la:Bool, judge(j)) = length(extractFaceDown(j)) == 0 } channel judgeB{ - in handsB(b:List, judge(j)) == j - out loseB(lb:Bool, judge(j)) == eq(length(extractFaceDown(j)), 0) + in handsB(b:List, judge(j)) = j + out loseB(lb:Bool, judge(j)) = length(extractFaceDown(j)) == 0 } \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/models/Base.model b/AlgebraicDataflowArchitectureModel/models/Base.model index ac44843..bb460e4 100644 --- a/AlgebraicDataflowArchitectureModel/models/Base.model +++ b/AlgebraicDataflowArchitectureModel/models/Base.model @@ -1,13 +1,13 @@ channel CIO{ - out r1(x1:Int, set1(y1:Int)) == y1 - out r1(x1, e) == x1 + out r1(x1:Int, set1(y1:Int)) = y1 + out r1(x1, e) = x1 } channel CIO2{ - out r2(x2:Int, set2(y2:Int)) == y2 - out r2(x2, e) == x2 + out r2(x2:Int, set2(y2:Int)) = y2 + out r2(x2, e) = x2 } channel C1{ - in r1(x1, update(x1:Int, y1:Int, x2:Int, y2:Int)) == y1 - in r2(x2, update(x1, y1, x2, y2)) == y2 - out r3(x3:Int, update(x1, y1, x2, y2)) == x1 + y1 + x2 + y2 + in r1(x1, update(x1:Int, y1:Int, x2:Int, y2:Int)) = y1 + in r2(x2, update(x1, y1, x2, y2)) = y2 + out r3(x3:Int, update(x1, y1, x2, y2)) = x1 + y1 + x2 + y2 } \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/models/Bug.model b/AlgebraicDataflowArchitectureModel/models/Bug.model deleted file mode 100644 index f31727b..0000000 --- a/AlgebraicDataflowArchitectureModel/models/Bug.model +++ /dev/null @@ -1,7 +0,0 @@ -channel CIO{ - out aUa(a, input(x:Int)) == x -} -channel C1{ - in aUa(a, update(x)) == x - out aUb(b, update(x)) == x + 3 -} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/models/Clock.dtram b/AlgebraicDataflowArchitectureModel/models/Clock.dtram new file mode 100644 index 0000000..bbeb6dc --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/Clock.dtram @@ -0,0 +1,27 @@ +model { +channel CIO1 { + out min(m: Int, tick) = (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(m2 == 0, (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..0ecb11b --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/Clock.model @@ -0,0 +1,15 @@ +channel CIO1 { + out min(m: Int, tick) = (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(m2 == 0, (h + 1) % 24, h) +} diff --git a/AlgebraicDataflowArchitectureModel/models/CustomerOffice.model b/AlgebraicDataflowArchitectureModel/models/CustomerOffice.model index f140bff..42cdf13 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/Game.model b/AlgebraicDataflowArchitectureModel/models/Game.model index bc90ba4..1952ff0 100644 --- a/AlgebraicDataflowArchitectureModel/models/Game.model +++ b/AlgebraicDataflowArchitectureModel/models/Game.model @@ -1,32 +1,32 @@ channel CIO { - out force(f:Double, action(x:Double)) == x - out time(t:Double, action(x)) == t + 0.01 - out force(f, e) == x - out time(t, e) == t + 0.01 + out force(f:Double, action(x:Double)) = x + out time(t:Double, action(x)) = t + 0.01 + out force(f, e) = x + out time(t, e) = t + 0.01 } channel CIO2 { - out velocity(v:Double, setVel(x:Double)) == x - out velocity(v, e) == x + out velocity(v:Double, setVel(x:Double)) = x + out velocity(v, e) = x } channel CIO3 { - out mass(m:Double, setMass(x:Double)) == x - out mass(m, e) == m + out mass(m:Double, setMass(x:Double)) = x + out mass(m, e) = m } channel C1 { - in force(f, update1(y, z)) == y - in mass(m, update1(y, z)) == z - out acceleration(a: Double, update1(y, z)) == y / z + in force(f, update1(y, z)) = y + in mass(m, update1(y, z)) = z + out acceleration(a: Double, update1(y, z)) = y / z } channel C2 { - in acceleration(a, update2(z)) == z - out velocity(v:Double, update2(z)) == v + 0.01 * z + in acceleration(a, update2(z)) = z + out velocity(v:Double, update2(z)) = v + 0.01 * z } channel C3 { - in velocity(v, update3(u)) == u - out position(p:Double, update3(u)) == p + 0.01 * u + in velocity(v, update3(u)) = u + out position(p:Double, update3(u)) = p + 0.01 * u } \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/models/Hello.model b/AlgebraicDataflowArchitectureModel/models/Hello.model deleted file mode 100644 index 46bb932..0000000 --- a/AlgebraicDataflowArchitectureModel/models/Hello.model +++ /dev/null @@ -1,3 +0,0 @@ -channel cio { - out r1(x1:Int,set(x2)) == x2 -} diff --git a/AlgebraicDataflowArchitectureModel/models/Hello2.model b/AlgebraicDataflowArchitectureModel/models/Hello2.model deleted file mode 100644 index 4815cf6..0000000 --- a/AlgebraicDataflowArchitectureModel/models/Hello2.model +++ /dev/null @@ -1,3 +0,0 @@ -channel cio { - out r1(x1:Int,set(x2:Int)) == x2:Int -} diff --git a/AlgebraicDataflowArchitectureModel/models/HelloWorld.model b/AlgebraicDataflowArchitectureModel/models/HelloWorld.model deleted file mode 100644 index 44aa310..0000000 --- a/AlgebraicDataflowArchitectureModel/models/HelloWorld.model +++ /dev/null @@ -1,7 +0,0 @@ -channel cio { - out r1(x1:Int,set(x2:Int)) == x2:Int -} -channel c2 { - in r1(x1:Int,update(x2:Int)) == x2:Int - out r2(y1:Int,update(x2:Int)) == (x2:Int+1) -} diff --git a/AlgebraicDataflowArchitectureModel/models/InventoryManagement.model b/AlgebraicDataflowArchitectureModel/models/InventoryManagement.model new file mode 100644 index 0000000..578d56c --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/InventoryManagement.model @@ -0,0 +1,7 @@ +channel ItemRegistration { + out inventory(itemDB:Map, registerItem(itemId:Str, itemName:Str, quantity:Int)) = insert(itemDB, itemId, {"count": quantity, "name": itemName}) +} + +channel ReceivingOrShipping(itemId:Str) { + out inventory.{itemId}.count(prev_quantity:Int, receiveOrShip(quantity:Int)) = prev_quantity + quantity +} diff --git a/AlgebraicDataflowArchitectureModel/models/JumpGame.model b/AlgebraicDataflowArchitectureModel/models/JumpGame.model index 1981df9..43b8d0c 100644 --- a/AlgebraicDataflowArchitectureModel/models/JumpGame.model +++ b/AlgebraicDataflowArchitectureModel/models/JumpGame.model @@ -1,7 +1,8 @@ init { force := pair(0.0, 0.0) time := 0.0 - move := pair(0.0, 0.0) + movex := 0.0 + movey := 0.0 mass := 1.0 ground := true acceleration := pair(0.0, 0.0) @@ -12,59 +13,59 @@ gameover := false } channel CIO { - out force(f:Pair, gravity(y:Double)) == pair(0.0, y) - out time(t:Double, gravity(y)) == t + 0.01 + out force(f:Pair, gravity(y:Double)) = pair(0.0, y) + out time(t:Double, gravity(y)) = t + 0.01 } channel CIO2 { - out movex(x:Double, run(x2:Double)) == x2 + out movex(x:Double, run(x2:Double)) = x2 } channel CIO3 { - out movey(y:Double, jump(y2:Double)) == y2 + out movey(y:Double, jump(y2:Double)) = y2 } channel CIO4 { - out mass(m:Double, setMass(x:Double)) == x + out mass(m:Double, setMass(x:Double)) = x } channel CIO5 { - out ground(g:Bool, openHole) == false - out ground(g, closeHole) == true + out ground(g:Bool, openHole) = false + out ground(g, closeHole) = true } channel C1 { ref onground(o, update1(f2, m2, o)) - in force(f, update1(f2, m2, o)) == f2 - in mass(m, update1(f2, m2, o)) == m2 - out acceleration(a:Pair, update1(f2, m2, o)) == if(o, pair(left(f2) / m2, 0.0), pair(left(f2) / m2, right(f2) / m2)) + in force(f, update1(f2, m2, o)) = f2 + in mass(m, update1(f2, m2, o)) = m2 + out acceleration(a:Pair, update1(f2, m2, o)) = if(o, pair(left(f2) / m2, 0.0), pair(left(f2) / m2, right(f2) / m2)) } channel C2 { ref onground(o, update3(a2, o)) - in acceleration(a, update3(a2, o)) == a2 - out velocity(v:Pair, update3(a2, o)) == if(and(o, lt(right(v), 0.0)), + in acceleration(a, update3(a2, o)) = a2 + out velocity(v:Pair, update3(a2, o)) = if(o && right(v) < 0.0, pair(left(v) + 0.01 * left(a2), 0.0), pair(left(v) + 0.01 * left(a2), right(v) + 0.01 * right(a2))) } channel C3 { ref onground(o, update4(x2, o)) - in movex(x, update4(x2, o)) == x2 - out velocity(v:Pair, update4(x2, o)) == if(o, pair(x2, right(v)), v) + in movex(x, update4(x2, o)) = x2 + out velocity(v:Pair, update4(x2, o)) = if(o, pair(x2, right(v)), v) } channel C4 { ref onground(o, update5(x2, o)) - in movey(y, update5(y2, o)) == y2 - out velocity(v:Pair, update5(y2, o)) == if(o, pair(left(v), y2), v) + in movey(y, update5(y2, o)) = y2 + out velocity(v:Pair, update5(y2, o)) = if(o, pair(left(v), y2), v) } channel C5 { - in velocity(v, update6(v2, g)) == v2 + in velocity(v, update6(v2, g)) = v2 ref ground(g, update6(v2, g)) - out position(p:Pair, update6(v2, g)) == if(and(eq(g, true), lt(right(p) + 0.01 * right(v2), 0.0)), + out position(p:Pair, update6(v2, g)) = if(g == true && right(p) + 0.01 * right(v2) > 0.0, pair(left(p) + 0.01 * left(v2), 0.0), pair(left(p) + 0.01 * left(v2), right(p) + 0.01 * right(v2))) } channel C6 { - in position(p, update2(p2, g2)) == p2 - in ground(g, update2(p2, g2)) == g2 - out onground(o:Bool, update2(p2, g2)) == and(eq(g2, true), le(right(p2), 0.0)) + in position(p, update2(p2, g2)) = p2 + in ground(g, update2(p2, g2)) = g2 + out onground(o:Bool, update2(p2, g2)) = (g2 == true && right(p2) <= 0.0) } channel C7 { - in position(p, update7(p2)) == p2 - out clear(c:Bool, update7(p2)) == if(gt(left(p2), 100.0), true, false) - out gameover(go:Bool, update7(p2)) == if(lt(right(p2), -1.0), true, false) + in position(p, update7(p2)) = p2 + out clear(c:Bool, update7(p2)) = (left(p2) > 100.0) + out gameover(go:Bool, update7(p2)) = (right(p2) < -1.0) } \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/models/Kinematics.model b/AlgebraicDataflowArchitectureModel/models/Kinematics.model new file mode 100644 index 0000000..3d10af4 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/Kinematics.model @@ -0,0 +1,20 @@ +channel CIO { + out force(f:Double, action(x)) = x + out time(t:Double, action(x)) = t + 0.01 +} + +channel C1 { + in force(f, update1(y, m)) = y + in mass(m, update1(y, m)) = m + out acceleration(a:Double, update1(y, m)) = y / m +} + +channel C2 { + in acceleration(a, update2(z)) = z + out velocity(v:Double, update2(z)) = v + 0.01 * z +} + +channel C3 { + in velocity(v, update3(u)) = u + out position(p:Double, update3(u)) = p + 0.01 * u +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/models/Kinetics.model b/AlgebraicDataflowArchitectureModel/models/Kinetics.model deleted file mode 100644 index 5218206..0000000 --- a/AlgebraicDataflowArchitectureModel/models/Kinetics.model +++ /dev/null @@ -1,20 +0,0 @@ -channel CIO { - out force(f:Double, action(x)) == x - out time(t:Double, action(x)) == t + 0.01 -} - -channel C1 { - in force(f, update1(y, m)) == y - in mass(m, update1(y, m)) == m - out acceleration(a:Double, update1(y, m)) == y / m -} - -channel C2 { - in acceleration(a, update2(z)) == z - out velocity(v:Double, update2(z)) == v + 0.01 * z -} - -channel C3 { - in velocity(v, update3(u)) == u - out position(p:Double, update3(u)) == p + 0.01 * u -} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/models/NemophilaAccounts.model b/AlgebraicDataflowArchitectureModel/models/NemophilaAccounts.model new file mode 100644 index 0000000..c2cc23a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/NemophilaAccounts.model @@ -0,0 +1,86 @@ +channel CIO_CreateAccount { + out accounts(acList:List, createAccount(name:Str)) = append(acList, { + "name": name, + "posts": nil, + "newFriend": null, + "delReqFriend": null, + "newRequesting": null, + "friends": nil, + "requesting": nil, + "requested": nil, + }) +} + +channel CIO_ChangeName(uid:Str) { + out accounts.{uid}.name(prevName:Str, changeName(name)) = name +} + +channel CIO_CreatePost(uid:Str) { + out accounts.{uid}.posts(postsList:List, createPost(comment:Str)) = cons(comment:Str, postsList) +} + +channel CIO_DeletePost(uid:Str) { + out accounts.{uid}.posts(postsList:List, deletePost(pid:Str)) = remove(postsList, pid:Str) +} + +channel CIO_PostRequesting(sendId:Str) { + out accounts.{sendId}.newRequesting(preNewResuesting:Str, postRequesting(recvId:Str)) = recvId +} + +channel C_AddRequesting(sendId:Str) { + in accounts.{sendId}.newRequesting(preNewResuesting:Str, sync1(recvId:Str, sendRequested:Map, recvRequesting:Map, sendFriends:Map)) = recvId + ref accounts.{sendId}.friends(sendFriends:Map, sync1(recvId:Str, sendRequested:Map, recvRequesting:Map, sendFriends:Map)) + ref accounts.{sendId}.requested(sendRequested:Map, sync1(recvId:Str, sendRequested:Map, recvRequesting:Map, sendFriends:Map)) + ref accounts.{recvId}.requesting(recvRequesting:Map, sync1(recvId:Str, sendRequested:Map, recvRequesting:Map, sendFriends:Map)) + + out accounts.{sendId}.requesting(sendRequesting:Map, sync1(recvId:Str, sendRequested:Map, recvRequesting:Map, sendFriends:Map)) = if(contains(sendFriends:Map, recvId) == true, + sendRequesting, + insert(sendRequesting:Map, recvId:Str, true)) + out accounts.{recvId}.requested(recvRequested:Map, sync1(recvId:Str, sendRequested:Map, recvRequesting:Map, sendFriends:Map)) = if(ontains(sendFriends:Map, recvId) == true, + recvRequested, + insert(recvRequested:Map, sendId:Str, true)) +} + +channel CIO_RejectRequest(recvId:Str) { + out accounts.{sendId}.requesting(sendRequesting:Map, rejectRequest(sendId:Str)) = delete(sendRequesting:Map, recvId:Str) + out accounts.{recvId}.requested(recvRequested:Map, rejectRequest(sendId:Str)) = delete(recvRequested:Map, sendId:Str) +} + +channel CIO_AdmitFriend(recvId:Str) { + out accounts.{recvId}.newFriend(preNewFriend:Str, admitFriend(sendId:Str)) = sendId +} + +channel C_AddFriend(recvId:Str) { + in accounts.{recvId}.newFriend(preSendId:Str, sync2(sendId:Str, preSendTing:Map, preRecvTed:Map)) = sendId + ref accounts.{sendId}.requesting(preSendTing:Map, sync2(sendId:Str, preSendTing:Map, preRecvTed:Map)) + ref accounts.{recvId}.requested(preRecvTed:Map, sync2(sendId:Str, preSendTing:Map, preRecvTed:Map)) + + out accounts.{sendId}.friends(sendFriends:Map, sync2(sendId:Str, preSendTing:Map, preRecvTed:Map)) = if(contains(preSendTing:Map, recvId) == true && contains(preRecvTed:Map, sendId) == true, + insert(sendFriends, recvId:Str, true), + sendFriends) + out accounts.{recvId}.friends(recvFriends:Map, sync2(sendId:Str, preSendTing:Map, preRecvTed:Map)) = if(contains(preSendTing:Map, recvId) == true && contains(preRecvTed:Map, sendId) == true, + insert(recvFriends, sendId:Str, true), + recvFriends) + out accounts.{sendId}.requesting(sendRequesting:Map, sync2(sendId:Str, preSendTing:Map, preRecvTed:Map)) = if(contains(preSendTing:Map, recvId) == true && contains(preRecvTed:Map, sendId) == true, + delete(sendRequesting:Map, recvId:Str), + sendRequesting) + out accounts.{recvId}.requested(recvRequested:Map, sync2(sendId:Str, preSendTing:Map, preRecvTed:Map)) = if(contains(preSendTing:Map, recvId) == true && contains(preRecvTed:Map, sendId) == true, + delete(recvRequested:Map, sendId:Str), + recvRequested) +} + +channel CIO_DeleteFriendRequest(sendId:Str) { + out accounts.{sendId}.delReqFriend(preDelReqFriend:Str, deleteFriend(recvId:Str)) = recvId +} + +channel CIO_DeleteFriend(sendId:Str) { + in accounts.{sendId}.delReqFriend(preRecvId:Str, sync3(recvId:Str, preSendFriends:Map, preRecvFriends:Map)) = recvId + ref accounts.{sendId}.friends(preSendFriends:Map, sync3(recvId:Str, preSendFriends:Map, preRecvFriends:Map)) + ref accounts.{recvId}.friends(preRecvFriends:Map, sync3(recvId:Str, preSendFriends:Map, preRecvFriends:Map)) + out accounts.{sendId}.friends(sendFriends:Map, sync3(recvId:Str, preSendFriends:Map, preRecvFriends:Map)) = if(contains(preSendFriends:Map, recvId) == true || contains(preRecvFriends:Map, sendId) == true, + delete(sendFriends:Map, recvId:Str), + sendFriends) + out accounts.{recvId}.friends(recvFriends:Map, sync3(recvId:Str, preSendFriends:Map, preRecvFriends:Map)) = if(contains(preSendFriends:Map, recvId) == true || contains(preRecvFriends:Map, sendId) == true, + delete(recvFriends:Map, sendId:Str), + recvFriends) +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/models/OnlineBattleGame.model b/AlgebraicDataflowArchitectureModel/models/OnlineBattleGame.model new file mode 100644 index 0000000..846ffbf --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/OnlineBattleGame.model @@ -0,0 +1,31 @@ +channel CIO_Signup{ + out accounts(adb:Map, signUp(aid:Str, name:Str)) = insert(adb, aid, {"name": name}) +} + +channel CIO_ChangeName(aid:Str) { + out accounts.{aid}.name(prev_name:Str, changeName(name)) = name +} + +channel CIO_CreateRoom { + out rooms(rdb:Map, createRoom(rid:Str, blue_id:Str, red_id:Str)) = insert(rdb, rid, {"blue_id": blue_id, "red_id": red_id}) +} + +channel CIO_ChangeRed_id(rid:Str) { + out rooms.{rid}.red_id(prev_red_id:Str, changeRed_id(red_id)) = red_id +} + +channel CIO_ChangeBlue_id(rid:Str) { + out rooms.{rid}.blue_id(prev_blue_id:Str, changeBlue_id(blue_id)) = blue_id +} + +channel C_red(rid:Str) { + in rooms.{rid}.red_id(prev_aid:Str, sync(aid, name)) = aid + in accounts.{aid}.name(prev_name:Str, sync(aid, name)) = name + out rooms.{rid}.red_name(prev_name:Str, sync(aid, name)) = name +} + +channel C_blue(rid:Str) { + in rooms.{rid}.blue_id(prev_aid:Str, sync(aid, name)) = aid + in accounts.{aid}.name(prev_name:Str, sync(aid, name)) = name + out rooms.{rid}.blue_name(prev_name:Str, sync(aid, name)) = name +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/models/POS.dtram b/AlgebraicDataflowArchitectureModel/models/POS.dtram index dc3d2d1..d1cb11c 100644 --- a/AlgebraicDataflowArchitectureModel/models/POS.dtram +++ b/AlgebraicDataflowArchitectureModel/models/POS.dtram @@ -1,18 +1,18 @@ model { channel CIO { - out payment(p:Int, purchase(x:Int)) == x + out payment(p:Int, purchase(x:Int)) = x } channel C3 { - in history(h, update3(u)) == u - out total(t:Int, update3(u)) == sum(u) + in history(h, update3(u)) = u + out total(t:Int, update3(u)) = sum(u) } channel C1 { - in payment(p, update1(y)) == y - out points(l:Int, update1(y)) == floor(y * 0.05) + in payment(p, update1(y)) = y + out points(l:Int, update1(y)) = floor(y * 0.05) } channel C2 { - in payment(p, update2(z)) == z - out history(h:List, update2(z)) == cons(z, h) + in payment(p, update2(z)) = z + out history(h:List, update2(z)) = cons(z, h) } } geometry { diff --git a/AlgebraicDataflowArchitectureModel/models/POS.model b/AlgebraicDataflowArchitectureModel/models/POS.model index 150c106..c15c931 100644 --- a/AlgebraicDataflowArchitectureModel/models/POS.model +++ b/AlgebraicDataflowArchitectureModel/models/POS.model @@ -1,15 +1,15 @@ channel CIO { - out payment(p:Int, purchase(x:Int)) == x + out payment(p:Int, purchase(x:Int)) = x } channel C3 { - in history(h, update3(u)) == u - out total(t:Int, update3(u)) == sum(u) + in history(h, update3(u)) = u + out total(t:Int, update3(u)) = sum(u) } channel C1 { - in payment(p, update1(y)) == y - out points(l:Int, update1(y)) == floor(y * 0.05) + in payment(p, update1(y)) = y + out points(l:Int, update1(y)) = floor(y * 0.05) } channel C2 { - in payment(p, update2(z)) == z - out history(h:List, update2(z)) == cons(z, h) + in payment(p, update2(z)) = z + out history(h:List, update2(z)) = cons(z, h) } diff --git a/AlgebraicDataflowArchitectureModel/models/POS2.model b/AlgebraicDataflowArchitectureModel/models/POS2.model index 6ae676d..0ee31d2 100644 --- a/AlgebraicDataflowArchitectureModel/models/POS2.model +++ b/AlgebraicDataflowArchitectureModel/models/POS2.model @@ -1,14 +1,14 @@ channel CIO { - out payment(p:Int, purchase(x:Int)) == x + out payment(p:Int, purchase(x:Int)) = x } channel C1 { - in payment(p1, update1(y)) == y - out points(l:Int, update1(y)) == floor(y * 0.05) + in payment(p1, update1(y)) = y + out points(l:Int, update1(y)) = floor(y * 0.05) } channel C2 { - in payment(p1, update2(z)) == z - out history(h:List, update2(z)) == cons(z, h) - out total(t:Int, update2(z)) == z + t + in payment(p1, update2(z)) = z + out history(h:List, update2(z)) = cons(z, h) + out total(t:Int, update2(z)) = z + t } \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/models/SimpleAddressBook.model b/AlgebraicDataflowArchitectureModel/models/SimpleAddressBook.model new file mode 100644 index 0000000..9a9f38a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/SimpleAddressBook.model @@ -0,0 +1,11 @@ +channel Init { + out book.owner(pre_name: Str, init(name: Str)) = name +} + +channel Add { + out book.addr(pre_addr: Map, add(name: Str, addr: Str)) = insert(pre_addr, name, addr) +} + +channel Del { + out book.addr(pre_addr: Map, del(name: Str)) = delete(pre_addr, name) +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/models/SimpleTwitter.model b/AlgebraicDataflowArchitectureModel/models/SimpleTwitter.model new file mode 100644 index 0000000..486fafb --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/SimpleTwitter.model @@ -0,0 +1,7 @@ +channel Signup { + out accounts(accountDB:Map, signUp(accountId:Str, name:Str)) = insert(accountDB, accountId, {"name": name, "tweets": nil}) +} + +channel Tweet(accountId:Str) { + out accounts.{accountId}.tweets(tweetList:List, tweet(contents:Str)) = append(tweetList, contents) +} diff --git a/AlgebraicDataflowArchitectureModel/models/SimpleUI.model b/AlgebraicDataflowArchitectureModel/models/SimpleUI.model new file mode 100644 index 0000000..f393c9d --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/SimpleUI.model @@ -0,0 +1,19 @@ +native channel ScreenUpdate { + in screen(curSc: Json, update(nextSc)) = nextSc +} + +native channel SetVisible(wid: Str) { + in screen.widgets.{wid}.visible(curVisible: Bool, setVisible(nextVisible)) = nextVisible +} + +native channel SetText(wid: Str) { + in screen.widgets.{wid}.text(curText: Str, setText(nextText)) = nextText +} + +native channel MouseEvent(wid: Str) { + out screen.widgets.{wid}.state(curState: Int, mouseEvent(nextState)) = nextState +} + +native channel TextEvent(wid: Str) { + out screen.widgets.{wid}.text(curText: Str, textEvent(nextText)) = nextText +} diff --git a/AlgebraicDataflowArchitectureModel/models/StockManagement.model b/AlgebraicDataflowArchitectureModel/models/StockManagement.model index b133f44..4528cb7 100644 --- a/AlgebraicDataflowArchitectureModel/models/StockManagement.model +++ b/AlgebraicDataflowArchitectureModel/models/StockManagement.model @@ -4,47 +4,47 @@ } channel CIO_enter { - out arrival(s:Tuple, arrive(item:Str, num:Int)) == tuple(item, num) + out arrival(s:Tuple, arrive(item:Str, num:Int)) = tuple(item, num) } channel CIO_req { - out request(r:Tuple, req(item:Str, num:Int)) == tuple(item, num) + out request(r:Tuple, req(item:Str, num:Int)) = tuple(item, num) } channel C1 { - in arrival(ar, update1(ar2, st)) == ar2 + in arrival(ar, update1(ar2, st)) = ar2 ref stock(st:Map, update1(ar2, st)) - out available(av:Tuple, update1(ar2, st)) == tuple(fst(ar2), snd(ar2) + lookup(st, fst(ar2))) + out available(av:Tuple, update1(ar2, st)) = tuple(fst(ar2), snd(ar2) + lookup(st, fst(ar2))) } channel C2 { - in available(av, update2(av2, sh)) == av2 + in available(av, update2(av2, sh)) = av2 ref shortage(sh, update2(av2, sh)) - out deriver(dr:Tuple, update2(av2, sh)) == if(ge(snd(av2), lookup(sh, fst(av2))), + out deriver(dr:Tuple, update2(av2, sh)) = if(snd(av2) >= lookup(sh, fst(av2)), tuple(fst(av2), lookup(sh, fst(av2)), snd(av2) - lookup(sh, fst(av2))), tuple(fst(av2), 0, snd(av2))) - out shortage(s, update2(av2, sh)) == if(ge(snd(av2), lookup(s, fst(av2))), + out shortage(s, update2(av2, sh)) = if(snd(av2) >= lookup(s, fst(av2)), insert(s, fst(av2), 0), s) } channel C3 { - in deriver(dr, update3(dr2)) == dr2 - out stock(st, update3(dr2)) == insert(st, fst(dr2), snd(snd(dr2))) + in deriver(dr, update3(dr2)) = dr2 + out stock(st, update3(dr2)) = insert(st, fst(dr2), snd(snd(dr2))) } channel C4 { - in deriver(dr, update4(dr2)) == dr2 - out shipping(sp:Tuple, update4(dr2)) == tuple(fst(dr2), fst(snd(dr2))) + in deriver(dr, update4(dr2)) = dr2 + out shipping(sp:Tuple, update4(dr2)) = tuple(fst(dr2), fst(snd(dr2))) } channel C5 { - in request(rq, update5(rq2, st)) == rq2 + in request(rq, update5(rq2, st)) = rq2 ref stock(st, update5(rq2, st)) - out deriver(dr, update5(rq2, st)) == if(ge(lookup(st, fst(rq2)), snd(rq2)), + out deriver(dr, update5(rq2, st)) = if(lookup(st, fst(rq2)) >= snd(rq2), tuple(fst(rq2), snd(rq2), lookup(st, fst(rq2)) - snd(rq2)), tuple(fst(rq2), 0, lookup(st, fst(rq2)))) - out shortage(sh, update5(rq2, st)) == if(ge(lookup(st, fst(rq2)), snd(rq2)), + out shortage(sh, update5(rq2, st)) = if(lookup(st, fst(rq2)) >= snd(rq2), sh, insert(sh, fst(rq2), lookup(sh, fst(rq2)) + snd(rq2))) } \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/models/StockManagement2.model b/AlgebraicDataflowArchitectureModel/models/StockManagement2.model index 6ee866a..36d6966 100644 --- a/AlgebraicDataflowArchitectureModel/models/StockManagement2.model +++ b/AlgebraicDataflowArchitectureModel/models/StockManagement2.model @@ -4,10 +4,10 @@ } channel CIO_enter { - out deriver(s:Tuple, arrive(item:Str, num:Int)) == tuple(item, num, num) + out deriver(s:Tuple, arrive(item:Str, num:Int)) = tuple(item, num, num) } channel C3 { - in deriver(dr, update3(dr2)) == dr2 - out shipping(sp:Tuple, update3(dr2)) == tuple(fst(dr2), fst(snd(dr2))) + in deriver(dr, update3(dr2)) = dr2 + out shipping(sp:Tuple, update3(dr2)) = tuple(fst(dr2), fst(snd(dr2))) } diff --git a/AlgebraicDataflowArchitectureModel/models/Test.model b/AlgebraicDataflowArchitectureModel/models/Test.model deleted file mode 100644 index a8a1348..0000000 --- a/AlgebraicDataflowArchitectureModel/models/Test.model +++ /dev/null @@ -1,9 +0,0 @@ -channel CIO { - out a(x:Int, set(n:Int)) == n -} - -channel C1 { - in a(x, update(y)) == y - out b(z, update(y)) == y + 1 - out c(v, update(y)) == v + y -} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/models/TravelDistance.model b/AlgebraicDataflowArchitectureModel/models/TravelDistance.model new file mode 100644 index 0000000..07d12db --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/TravelDistance.model @@ -0,0 +1,13 @@ +channel CX { + out pos_x(prev_x: Double, setX(cur_x: Double)) = cur_x +} + +channel CY { + out pos_y(prev_y: Double, setY(cur_y: Double)) = cur_y +} + +channel C { + in pos_x(prev_x, move(dx, dy)) = prev_x + dx + in pos_y(prev_y, move(dx, dy)) = prev_y + dy + out dist(prev_d, move(dx, dy)) = prev_d + sqrt(dx * dx + dy * dy) +} diff --git a/AlgebraicDataflowArchitectureModel/models/Triangle.model b/AlgebraicDataflowArchitectureModel/models/Triangle.model new file mode 100644 index 0000000..cf3d0e0 --- /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..5067fa0 --- /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..491d904 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/models/WOS.dtram b/AlgebraicDataflowArchitectureModel/models/WOS.dtram index 45f640c..81b0779 100644 --- a/AlgebraicDataflowArchitectureModel/models/WOS.dtram +++ b/AlgebraicDataflowArchitectureModel/models/WOS.dtram @@ -1,17 +1,17 @@ model { channel CIO2 { - out highest(h:Double, reset(v)) == v + out highest(h:Double, reset(v)) = v } channel CIO1 { - out temp_f(p:Double, observe(x)) == x + out temp_f(p:Double, observe(x)) = x } channel C1{ - in temp_f(q:Double, conversion(y)) == y - out temp_c(r:Double, conversion(z)) == (z-32) / 1.8 + in temp_f(q:Double, conversion(y)) = y + out temp_c(r:Double, conversion(z)) = (z-32) / 1.8 } channel C2{ - in temp_f(q:Double, update(y)) == y - out highest(h:Double, update(z)) == if(gt(z, h), z, h) + in temp_f(q:Double, update(y)) = y + out highest(h:Double, update(z)) = if(z >= h, z, h) } } geometry { diff --git a/AlgebraicDataflowArchitectureModel/models/WeatherObservationSystem.model b/AlgebraicDataflowArchitectureModel/models/WeatherObservationSystem.model index 2cd84d7..0096407 100644 --- a/AlgebraicDataflowArchitectureModel/models/WeatherObservationSystem.model +++ b/AlgebraicDataflowArchitectureModel/models/WeatherObservationSystem.model @@ -1,14 +1,14 @@ channel CIO2 { - out highest(h:Double, reset(v)) == v + out highest(h:Double, reset(v)) = v } channel CIO1 { - out temp_f(p:Double, observe(x)) == x + out temp_f(p:Double, observe(x)) = x } channel C1{ - in temp_f(q:Double, conversion(y)) == y - out temp_c(r:Double, conversion(z)) == (z-32) / 1.8 + in temp_f(q:Double, conversion(y)) = y + out temp_c(r:Double, conversion(z)) = (z-32) / 1.8 } channel C2{ - in temp_f(q:Double, update(y)) == y - out highest(h:Double, update(z)) == if(gt(z, h), z, h) + in temp_f(q:Double, update(y)) = y + out highest(h:Double, update(z)) = if(z >= h, z, h) } diff --git a/AlgebraicDataflowArchitectureModel/src/algorithms/DataTransferModelAnalyzer.java b/AlgebraicDataflowArchitectureModel/src/algorithms/DataTransferModelAnalyzer.java index 836227a..f54aea4 100644 --- a/AlgebraicDataflowArchitectureModel/src/algorithms/DataTransferModelAnalyzer.java +++ b/AlgebraicDataflowArchitectureModel/src/algorithms/DataTransferModelAnalyzer.java @@ -24,41 +24,47 @@ */ static public DataFlowGraph createDataFlowGraphWithStateStoringAttribute(DataTransferModel model) { DataFlowGraph graph = model.getDataFlowGraph(); - Collection channels = new HashSet<>(model.getIOChannels()); + Collection channels = new HashSet<>(model.getInputChannels()); 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..1be6a1d 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); @@ -123,30 +146,32 @@ mapComponentTypes.put(DataConstraintModel.typeMap, Arrays.asList(new Type[] { null, null })); // 1. Collect type information from the architecture model. - Collection channels = new HashSet<>(model.getIOChannels()); + Collection channels = new HashSet<>(model.getInputChannels()); channels.addAll(model.getChannels()); 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 && !newJsonType.isAncestorOf(jsonType)) { + // 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 && !newJsonType.isAncestorOf(jsonType)) { + // 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 && !newJsonType.isAncestorOf(jsonType)) { + // 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,118 @@ } } + + 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)); + if (jsonType != null && jsonMemberTypes.get(jsonType) != null) { + 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 +2133,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 +2191,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 +2247,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 +2255,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 +2273,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/ApplicationMenuBar.java b/AlgebraicDataflowArchitectureModel/src/application/ApplicationMenuBar.java index 38e2370..1835abf 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/ApplicationMenuBar.java +++ b/AlgebraicDataflowArchitectureModel/src/application/ApplicationMenuBar.java @@ -17,6 +17,7 @@ import application.actions.OpenAction; import application.actions.SaveAction; import application.actions.SaveAsAction; +import application.actions.SimulateAction; import application.actions.TreeLayoutAction; import application.actions.ZoomInAction; import application.actions.ZoomOutAction; @@ -37,6 +38,8 @@ private DAGLayoutAction dagLayoutAction = null; private TreeLayoutAction treeLayoutAction = null; private CircleLayoutAction circleLayoutAction = null; + //private SimulateAction simulateAction = null; + public ApplicationMenuBar(ApplicationWindow applicationWindow) { this.applicationWindow = applicationWindow; @@ -72,6 +75,9 @@ menu.add(new ZoomInAction(applicationWindow.getGraphComponent())); menu.add(new ZoomOutAction(applicationWindow.getGraphComponent())); + menu = add(new JMenu("Simulate")); + menu.add(new SimulateAction(applicationWindow)); + menu = add(new JMenu("Generate")); menu.add(javaPrototypeGenerateAction = new JavaPrototypeGenerateAction(applicationWindow.getEditor())); menu.add(jerseyPrototypeGenerateAction = new JerseyPrototypeGenerateAction(applicationWindow.getEditor())); diff --git a/AlgebraicDataflowArchitectureModel/src/application/SimulatorWindow.java b/AlgebraicDataflowArchitectureModel/src/application/SimulatorWindow.java new file mode 100644 index 0000000..6a0684b --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/SimulatorWindow.java @@ -0,0 +1,198 @@ +package application; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.swing.JFrame; + +import com.mxgraph.model.mxCell; +import com.mxgraph.model.mxGeometry; +import com.mxgraph.model.mxGraphModel; +import com.mxgraph.swing.mxGraphComponent; +import com.mxgraph.swing.handler.mxRubberband; +import com.mxgraph.util.mxEvent; +import com.mxgraph.util.mxEventObject; +import com.mxgraph.util.mxPoint; +import com.mxgraph.util.mxEventSource.mxIEventListener; +import com.mxgraph.view.mxGraph; + +import algorithms.TypeInference; +import application.actions.SimulateAction; +import application.editor.DataTransferModelingCellEditor; +import application.editor.Editor; +import application.editor.InputEventCellEditor; +import application.layouts.DAGLayout; +import generators.JavaCodeGenerator; +import models.Edge; +import models.EdgeAttribute; +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.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.ResolvingMultipleDefinitionIsFutureWork; +import models.dataFlowModel.ResourceNode; +import parser.Parser; +import parser.ParserDTRAM; +import parser.exceptions.ExpectedColon; +import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.WrongJsonExpression; +import parser.Parser.TokenStream; +import simulator.Event; +import simulator.Resource; +import simulator.ResourceIdentifier; +import simulator.Simulator; + +public class SimulatorWindow extends JFrame{ + + /** + * + */ + private static final long serialVersionUID = -2425820512017088254L; + public static final String title = "Simulation Tool"; + + final int PORT_DIAMETER = 8; + final int PORT_RADIUS = PORT_DIAMETER / 2; + + private Editor editor = null; + private mxGraph graph = null; + private mxGraphComponent graphComponent = null; + private Simulator simulator = null; + private boolean bReflectingArchitectureModel = false; + private double x = 20; + private double y = 20; + + public SimulatorWindow(Editor editor) { + setTitle(title); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + this.graph = new mxGraph() { + public boolean isPort(Object cell) { + mxGeometry geo = getCellGeometry(cell); + + return (geo != null) ? geo.isRelative() : false; + } + + public boolean isCellFoldable(Object cell, boolean collapse) { + return false; + } + }; + + this.graphComponent = new mxGraphComponent(graph); + this.editor = editor; + + graph.getModel().addListener(mxEvent.CHANGE, new mxIEventListener() { + public void invoke(Object sender, mxEventObject evt) { + List terminals = new ArrayList<>(); + mxCell cell = null; + for (Object change: ((List) evt.getProperties().get("changes"))) { + if (change instanceof mxGraphModel.mxTerminalChange) { + mxGraphModel.mxTerminalChange terminalChange = (mxGraphModel.mxTerminalChange) change; + cell = (mxCell) terminalChange.getCell(); + mxCell terminal = (mxCell) terminalChange.getTerminal(); + terminals.add(terminal); + } + } + if (terminals.size() == 2) { + if (!editor.connectEdge(cell, terminals.get(0), terminals.get(1))) { + graph.removeCells(new mxCell[] {cell}); + } + } + } + }); + getContentPane().add(graphComponent); + new mxRubberband(graphComponent); + graph.setAllowDanglingEdges(false); + graph.setCellsDisconnectable(true); + + setTitle(title); + setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + setSize(870,640); + setVisible(true); + + DataTransferModel model = this.editor.getModel(); + TypeInference.infer(model); + simulator = new Simulator(model); + constructSimulateGraph(simulator.getCurState().getRootResources(), this.editor.getModel(),this.editor.getDataFlowGraph()); + graphComponent.setCellEditor(new InputEventCellEditor(graphComponent, simulator, this.editor, graph)); + } + + + public mxGraph constructSimulateGraph(Set simulateRes, DataTransferModel model, DataFlowGraph dataFlowGraph) { + bReflectingArchitectureModel = true; + ((mxGraphModel) graph.getModel()).clear(); + Object parent = graph.getDefaultParent(); + graph.getModel().beginUpdate(); + + try { + Map resources = new HashMap<>(); + + // create resource vertices + for (Resource resNode: simulateRes) { + int w = 80; + int h = 30; + ResourcePath res = resNode.getResourceIdentifier(); + Object resource = graph.insertVertex(parent, null, + res.toString(), x, y, w, h, + "shape=ellipse;perimeter=ellipsePerimeter;verticalAlign=top;"); // insert a resource as a vertex + resources.put(resNode, resource); + createChildSimulateResourceVerticies(resource, resNode, resources, w, h); + x+=80; + } + + } finally { + graph.getModel().endUpdate(); + } + + bReflectingArchitectureModel = false; + return graph; + } + + + public void createChildSimulateResourceVerticies(Object resource, Resource resNode, Map resources, int w, int h) { //sample + + if(resNode.getChildren() != null) { + for (Resource childNode: resNode.getChildren()) { + ResourcePath childRes = childNode.getResourceIdentifier(); + Object childResource = graph.insertVertex(resource, null, + childRes.toString(), 0, 0, w/2, h/2, + "shape=ellipse;perimeter=ellipsePerimeter;verticalAlign=top;"); // insert a resource as a vertex + resources.put(childNode, childResource); + createChildSimulateResourceVerticies(childResource, childNode, resources, w, h); + } + } + if (resNode.getChildren() == null || resNode.getChildren().size() == 0) { + Object state = graph.insertVertex(resource, null, + resNode.getState().getValue().toString(), 0.5, 0.5, 0.25, 0.25, "opacity=0;verticalAlign=down;", true); // insert a state label as a vertex + } + } + + public mxGraphComponent getGraphComponent() { + return graphComponent; + } + + public Editor getEditor() { + return editor; + } + + public void setEditor(Editor editor) { + this.editor = editor; + } + +} 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/actions/SimulateAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/SimulateAction.java new file mode 100644 index 0000000..9f0e5a2 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/SimulateAction.java @@ -0,0 +1,38 @@ +package application.actions; + +import java.awt.event.ActionEvent; +import java.io.File; + +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JOptionPane; +import javax.swing.filechooser.FileFilter; +import javax.swing.filechooser.FileNameExtensionFilter; + +import application.ApplicationWindow; +import application.SimulatorWindow; +import application.editor.Editor; +import models.dataFlowModel.DataTransferChannel; + +public class SimulateAction extends AbstractSystemAction { + + private SimulatorWindow simulatorWindow = null; + + /** + * + */ + private static final long serialVersionUID = -995609232489316795L; + + public SimulateAction(ApplicationWindow frame) { + super("Simulate", frame); + //this.simulatorWindow = new SimulatorWindow(frame.getEditor()); + } + + + @Override + public void actionPerformed(ActionEvent e) { + this.simulatorWindow = new SimulatorWindow(frame.getEditor()); + } + +} + diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/DataTransferModelingCellEditor.java b/AlgebraicDataflowArchitectureModel/src/application/editor/DataTransferModelingCellEditor.java index b4d81a6..fa88e8d 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; @@ -67,7 +69,7 @@ DataTransferModel model = editor.getModel(); DataTransferChannel ch = (DataTransferChannel) model.getChannel((String) ((mxCell) cell).getValue()); if (ch == null) { - ch = (DataTransferChannel) model.getIOChannel((String) ((mxCell) cell).getValue()); + ch = (DataTransferChannel) model.getInputChannel((String) ((mxCell) cell).getValue()); if(ch == null) { //resource return; @@ -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..6fe7965 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/editor/Editor.java +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/Editor.java @@ -6,12 +6,16 @@ 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; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import com.mxgraph.layout.mxCircleLayout; @@ -19,6 +23,7 @@ import com.mxgraph.model.mxCell; import com.mxgraph.model.mxGeometry; import com.mxgraph.model.mxGraphModel; +import com.mxgraph.model.mxICell; import com.mxgraph.model.mxIGraphModel; import com.mxgraph.swing.mxGraphComponent; import com.mxgraph.util.mxConstants; @@ -32,15 +37,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 +66,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 +79,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 +99,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 +127,7 @@ public DataFlowGraph getDataFlowGraph() { if (dataFlowGraph == null) { analyzeDataTransferModel(getModel()); + updateEdgeAttiributes(dataFlowGraph); } return dataFlowGraph; } @@ -112,7 +135,6 @@ public DataFlowGraph analyzeDataTransferModel(DataTransferModel model) { DataFlowGraph flowGraph = DataTransferModelAnalyzer.createDataFlowGraphWithStateStoringAttribute(model); dataFlowGraph = DataTransferModelAnalyzer.annotateWithSelectableDataTransferAttiribute(flowGraph); - updateEdgeAttiributes(dataFlowGraph); return dataFlowGraph; } @@ -175,17 +197,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 +236,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) { @@ -303,11 +337,11 @@ } for (ResourcePath res: model.getResourcePaths()){ - if(res instanceof ResourcePath && state.getLabel().equals(res.getResourceName())) + if(res instanceof ResourcePath && state.getLabel().equals(res.getLeafResourceName())) fileString += "\tnode r " + state.getLabel() + ":" + x + "," + y + "," + w + "," + h + "\n"; } - for (Channel ioC: model.getIOChannels()) { + for (Channel ioC: model.getInputChannels()) { if(ioC instanceof Channel && state.getLabel().equals(ioC.getChannelName())) { fileString += "\tnode ioc " + state.getLabel() + ":" + x + "," + y + "," + w + "," + h + "\n"; } @@ -322,10 +356,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(); @@ -340,69 +375,139 @@ Map channelsIn = new HashMap<>(); Map channelsOut = new HashMap<>(); - 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); - } - } + Map resources = new HashMap<>(); // create resource vertices - for (ResourcePath res: model.getResourcePaths()) { + for (ResourceNode resNode: dataFlowGraph.getRootResourceNodes()) { + int w = 80; + int h = 30; + ResourcePath res = resNode.getPrimaryResourcePath(); Object resource = graph.insertVertex(parent, null, - res.getResourceName(), 20, 20, 80, 30, + res.getLeafResourceName(), 20, 20, w, h, "shape=ellipse;perimeter=ellipsePerimeter"); // insert a resource as a vertex - resources.put(res, resource); + resources.put(resNode, resource); + createChildResourceVerticies(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 (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"); + + // create channel vertices + 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); + } + } + } + } + } + + // add input, output and reference edges + 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()).getInSideResource(channel); + graph.insertEdge(parent, null, new SrcDstAttribute(channel, dstRes), channelsOut.get(channel), resources.get((ResourceNode) dfEdge.getDestination()), "movable=false"); + } else { + // input edge + DataTransferChannel channel = ((ChannelNode) dfEdge.getDestination()).getChannel(); + ResourcePath srcRes = ((ResourceNode) dfEdge.getSource()).getOutSideResource(channel); + Set> toRes = getResourceDependencyForChannel(channel, model, dataFlowGraph); + for(Map.Entry RtoR: toRes) { + graph.insertEdge(parent, null, null, + resources.get(RtoR.getValue()), + resources.get(RtoR.getKey()), "dashed=true;movable=false"); + } + + graph.insertEdge(parent, null, new SrcDstAttribute(srcRes, channel), resources.get((ResourceNode) dfEdge.getSource()), channelsIn.get(channel), "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(dataFlowGraph.getResourceNode(refRes)), channelsIn.get(channel), "dashed=true;movable=false"); } } } finally { graph.getModel().endUpdate(); } - setTreeLayout(); - + setDAGLayout(); + + bReflectingArchitectureModel = false; return graph; } + public void createChildResourceVerticies(Object resource, ResourceNode resNode, Map resources, int w, int h) { + + for (ResourceNode childNode: resNode.getChildren()) { + ResourcePath childRes = childNode.getPrimaryResourcePath(); + Object childResource = graph.insertVertex(resource, null, + childRes.getName(), 0, 0, w, h, + "shape=ellipse;perimeter=ellipsePerimeter"); // insert a resource as a vertex + resources.put(childNode, childResource); + createChildResourceVerticies(childResource, childNode, resources, w, h); + } + } + public void setDAGLayout() { Object parent = graph.getDefaultParent(); graph.getModel().beginUpdate(); @@ -422,16 +527,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.getPrimaryResourcePath().equals(edgeAttr.getSource()) && channel.equals(edgeAttr.getDestination())) { + edgeCell.setValue(dataFlow.getAttribute()); + break; + } } } } @@ -468,17 +575,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) { @@ -508,7 +637,7 @@ } public void addIOChannel(DataTransferChannel ioChannelGen) { - getModel().addIOChannel(ioChannelGen); + getModel().addInputChannel(ioChannelGen); resetDataFlowGraph(); graph.getModel().beginUpdate(); Object parent = graph.getDefaultParent(); @@ -553,10 +682,11 @@ } 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) { - srcCh = model.getIOChannel((String) src.getValue()); + srcCh = model.getInputChannel((String) src.getValue()); if (srcCh == null) { ResourcePath srcRes = model.getResourcePath((String) src.getValue()); Channel dstCh = model.getChannel((String) dst.getValue()); @@ -593,7 +723,7 @@ // channel to resource edge Channel ch = model.getChannel(srcName); if (ch == null) { - ch = model.getIOChannel(srcName); + ch = model.getInputChannel(srcName); } ch.removeChannelMember(model.getResourcePath(dstName)); } @@ -601,8 +731,8 @@ String name = (String) cell.getValue(); if (model.getChannel(name) != null) { model.removeChannel(name); - } else if (model.getIOChannel(name) != null) { - model.removeIOChannel(name); + } else if (model.getInputChannel(name) != null) { + model.removeInputChannel(name); } else if (model.getResourcePath(name) != null) { model.removeResourcePath(name); } @@ -648,11 +778,36 @@ } 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<>(); + if (ch.getOutputChannelMembers().size() > 0) { + try { + Map>> dependency = ch.fillOutsideResourcePaths(ch.getOutputChannelMembers().iterator().next(), JavaCodeGenerator.pullAccessor); + for (ChannelMember srcMem: dependency.keySet()) { + ResourceNode srcNode = dataFlowGraph.getResourceNode(srcMem.getResource()); + if (srcNode != null) { + for (ChannelMember dstMem: dependency.get(srcMem).getValue()) { + ResourceNode dstNode = dataFlowGraph.getResourceNode(dstMem.getResource()); + while (srcNode.getResourceHierarchy().getNumParameters() == 0 && srcNode.getParent() != null) { + srcNode = srcNode.getParent(); + } + 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 +818,7 @@ this.dst = dst; } - public Object getSrouce() { + public Object getSource() { return src; } diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/InputEventCellEditor.java b/AlgebraicDataflowArchitectureModel/src/application/editor/InputEventCellEditor.java new file mode 100644 index 0000000..e945773 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/InputEventCellEditor.java @@ -0,0 +1,341 @@ +package application.editor; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Rectangle; +import java.util.EventObject; +import java.util.HashMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.swing.BorderFactory; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JTextArea; +import javax.swing.JTextField; + +import com.mxgraph.model.mxCell; +import com.mxgraph.model.mxGraphModel; +import com.mxgraph.model.mxIGraphModel; +import com.mxgraph.swing.mxGraphComponent; +import com.mxgraph.swing.view.mxICellEditor; +import com.mxgraph.util.mxConstants; +import com.mxgraph.util.mxUtils; +import com.mxgraph.view.mxCellState; +import com.mxgraph.view.mxGraph; +import com.mxgraph.view.mxGraphView; + +import application.SimulatorWindow; +import application.layouts.DAGLayout; +import models.algebra.Expression; +import models.algebra.InvalidMessage; +import models.algebra.ParameterizedIdentifierIsFutureWork; +import models.algebra.Term; +import models.algebra.UnificationFailed; +import models.algebra.ValueUndefined; +import models.algebra.Variable; +import models.dataConstraintModel.Channel; +import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.ResourceHierarchy; +import models.dataConstraintModel.ResourcePath; +import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.DataFlowGraph; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.PushPullAttribute; +import models.dataFlowModel.PushPullValue; +import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork; +import models.visualModel.FormulaChannel; +import parser.Parser; +import parser.Parser.TokenStream; +import parser.exceptions.ExpectedColon; +import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.WrongJsonExpression; +import simulator.ChannelState; +import simulator.Resource; +import simulator.ResourceIdentifier; +import simulator.Simulator; +import simulator.Event; +import simulator.SystemState; + +public class InputEventCellEditor implements mxICellEditor { + public int DEFAULT_MIN_WIDTH = 70; + public int DEFAULT_MIN_HEIGHT = 30; + public double DEFAULT_MINIMUM_EDITOR_SCALE = 1; + private double x = 20; + private double y = 20; + + protected double minimumEditorScale = DEFAULT_MINIMUM_EDITOR_SCALE; + protected int minimumWidth = DEFAULT_MIN_WIDTH; + protected int minimumHeight = DEFAULT_MIN_HEIGHT; + + private Object editingCell; + private EventObject trigger; + private JComboBox comboBox; + private mxGraphComponent graphComponent; + private Simulator simulator; + private JComboBox pulldownMenu; + private Editor editor; + private boolean bReflectingArchitectureModel = false; + private mxGraph graph; + + public InputEventCellEditor(mxGraphComponent graphComponent, Simulator simulator, Editor editor, mxGraph graph) { + this.graphComponent = graphComponent; + this.simulator = simulator; + this.editor = editor; + this.graph = graph; + } + + @Override + public Object getEditingCell() { + return editingCell; + } + + @Override + public void startEditing(Object cell, EventObject evt) { + if (editingCell != null) { + stopEditing(true); + } + if (!graphComponent.getGraph().getModel().isEdge(cell)) { + + Resource res = simulator.getCurState().getResource((String) ((mxCell) cell).getValue()); + ResourceIdentifier resId = res.getResourceIdentifier();//resource + ArrayList eventChs = new ArrayList<>();//iOchannelList + ArrayList messages = new ArrayList<>();//ADLmessage + ArrayList eventMessages = new ArrayList<>();//messageList + ResourcePath eventResPath = null; + + for(Channel ch : simulator.getModel().getInputChannels()) {//all channel + if(((DataTransferChannel)ch).getInputResources().size()== 0) {//ioch Or normalch + for(ChannelMember out: ((DataTransferChannel)ch).getOutputChannelMembers()) { + ResourcePath resPath = out.getResource(); + if(resId.isInstanceOf(resPath)) {//account.uid == acounts.123 + eventResPath = resPath; + eventChs.add(((DataTransferChannel)ch)); + String message = null; + Expression mesExp = out.getStateTransition().getMessageExpression();//sync(a,b) + eventMessages.add(mesExp); + if(mesExp instanceof Term) { + message = ((Term) mesExp).getSymbol().toString();//sync + }else if(mesExp instanceof Variable) { + message = ((Variable) mesExp).getName();//x,y,z.. + } + messages.add(message);//pulldown + + } + } + } + } + + if(messages.isEmpty()) { + return; + } + String[] eventList = messages.toArray(new String[messages.size()]); + JComboBox event = new JComboBox(eventList); + + JPanel eventChoice = new JPanel(); + eventChoice.add(event);//FirstEventChoice + + int ret = JOptionPane.showConfirmDialog(null, eventChoice, "Event Choice", JOptionPane.OK_CANCEL_OPTION); + if (ret == JOptionPane.OK_OPTION) { + + JPanel inputEvent = new JPanel(); + int i , eventNum; + i = eventNum = 0; + + for(String eventString : eventList) { + if(eventString.equals(event.getSelectedItem().toString())) { + eventNum = i; + } + i++; + } + + JTextArea textArea = new JTextArea(eventMessages.get(eventNum).toString(), 10, 30);//EventInput + inputEvent.add(textArea); + + int approve = JOptionPane.showConfirmDialog(null, inputEvent, "Event Code", JOptionPane.OK_CANCEL_OPTION); + if (approve == JOptionPane.OK_OPTION) { + try { + + TokenStream stream = new Parser.TokenStream(); + Parser parser = new Parser(stream); + stream.addLine(textArea.getText()); + Expression eventMessage = parser.parseTerm(stream,simulator.getModel()); + + Event newEvent = new Event(eventChs.get(eventNum), eventMessage, eventResPath, simulator.getCurState().getResource(resId)); + simulator.transition(newEvent); + + constructNextSimulateGraph(simulator.getCurState().getRootResources(), simulator.getModel(),simulator.getModel().getDataFlowGraph(), graph); + graphComponent.setCellEditor(new InputEventCellEditor(graphComponent, simulator, this.editor, graph)); + + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork + | InvalidMessage | UnificationFailed | ValueUndefined | ExpectedRightBracket | WrongJsonExpression | ExpectedColon e) { + e.printStackTrace(); + } + } + } + //resource + return; + } + + mxCellState state = graphComponent.getGraph().getView().getState(cell); + if (state != null && state.getLabel() != null && !state.getLabel().equals("")) { + editingCell = cell; + trigger = evt; + + double scale = Math.max(minimumEditorScale, graphComponent.getGraph().getView().getScale()); + Object value = graphComponent.getGraph().getModel().getValue(cell); + if (value != null && value instanceof PushPullAttribute) { + PushPullAttribute attr = (PushPullAttribute) value; + comboBox = new JComboBox<>(attr.getOptionStrings()); + comboBox.setBorder(BorderFactory.createEmptyBorder()); + comboBox.setOpaque(false); + comboBox.setBounds(getEditorBounds(state, scale)); + comboBox.setVisible(true); + graphComponent.getGraphControl().add(comboBox, 0); + comboBox.updateUI(); + } + } + } + + private mxGraph constructNextSimulateGraph(Set simulateRes, DataTransferModel model,DataFlowGraph dataFlowGraph, mxGraph nextGraph) { + bReflectingArchitectureModel = true; + ((mxGraphModel) nextGraph.getModel()).clear(); + Object parent = nextGraph.getDefaultParent(); + nextGraph.getModel().beginUpdate(); + + try { + Map resources = new HashMap<>(); + + // create resource vertices + for (Resource resNode: simulateRes) { + int w = 400; + int h = 150; + ResourcePath res = resNode.getResourceIdentifier(); + Object resource = nextGraph.insertVertex(parent, null, + res.toString(), x, y, w, h, + "shape=ellipse;perimeter=ellipsePerimeter;verticalAlign=top;"); // insert a resource as a vertex + resources.put(resNode, resource); + + createNextChildSimulateResourceVerticies(resource, resNode, resources, w, h); + x+=400; + } + + } finally { + nextGraph.getModel().endUpdate(); + } + + bReflectingArchitectureModel = false; + return nextGraph; + } + + private void createNextChildSimulateResourceVerticies(Object resource, Resource resNode, Map resources, int w, int h) { //sample + + if(resNode.getChildren() != null) { + for (Resource childNode: resNode.getChildren()) { + int i = 1; + ResourcePath childRes = childNode.getResourceIdentifier(); + Object childResource = graph.insertVertex(resource, null, + childRes.toString(), x/i, y, w/(i+1), h/(i+2), + "shape=ellipse;perimeter=ellipsePerimeter;verticalAlign=top;"); // insert a resource as a vertex + resources.put(childNode, childResource); + i++; + createNextChildSimulateResourceVerticies(childResource, childNode, resources, w, h); + } + } + if (resNode.getChildren() == null || resNode.getChildren().size() == 0) { + Object state = graph.insertVertex(resource, null, + resNode.getState().getValue().toString(), 0.5, 0.5, 0.25, 0.25, "opacity=0;verticalAlign=down;", true); // insert a state label as a vertex + } + } + + @Override + public void stopEditing(boolean cancel) { + if (editingCell != null) { + comboBox.transferFocusUpCycle(); + Object cell = editingCell; + editingCell = null; + if (!cancel) { + EventObject trig = trigger; + trigger = null; + Object value = graphComponent.getGraph().getModel().getValue(cell); + if (value != null && value instanceof PushPullAttribute) { + PushPullAttribute attr = (PushPullAttribute) value; + List options = attr.getOptions(); + PushPullValue selected = null; + for (PushPullValue option: options) { + if (option.toString().equals(getCurrentValue())) { + selected = option; + break; + } + } + if (selected != null) { + options.remove(selected); + options.add(0, selected); + } + graphComponent.labelChanged(cell, attr, trig); + } + } else { + mxCellState state = graphComponent.getGraph().getView().getState(cell); + graphComponent.redraw(state); + } + + if (comboBox.getParent() != null) { + comboBox.setVisible(false); + comboBox.getParent().remove(comboBox); + } + + graphComponent.requestFocusInWindow(); + } + } + + public String getCurrentValue() { + return (String) comboBox.getSelectedItem(); + } + + /** + * Returns the bounds to be used for the editor. + */ + public Rectangle getEditorBounds(mxCellState state, double scale) { + mxIGraphModel model = state.getView().getGraph().getModel(); + Rectangle bounds = null; + + bounds = state.getLabelBounds().getRectangle(); + bounds.height += 10; + + // Applies the horizontal and vertical label positions + if (model.isVertex(state.getCell())) { + String horizontal = mxUtils.getString(state.getStyle(), mxConstants.STYLE_LABEL_POSITION, mxConstants.ALIGN_CENTER); + + if (horizontal.equals(mxConstants.ALIGN_LEFT)) { + bounds.x -= state.getWidth(); + } else if (horizontal.equals(mxConstants.ALIGN_RIGHT)) { + bounds.x += state.getWidth(); + } + + String vertical = mxUtils.getString(state.getStyle(), + mxConstants.STYLE_VERTICAL_LABEL_POSITION, + mxConstants.ALIGN_MIDDLE); + + if (vertical.equals(mxConstants.ALIGN_TOP)) { + bounds.y -= state.getHeight(); + } else if (vertical.equals(mxConstants.ALIGN_BOTTOM)) { + bounds.y += state.getHeight(); + } + } + + bounds.setSize( + (int) Math.max(bounds.getWidth(), + Math.round(minimumWidth * scale)), + (int) Math.max(bounds.getHeight(), + Math.round(minimumHeight * scale))); + + return bounds; + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/layouts/DAGLayout.java b/AlgebraicDataflowArchitectureModel/src/application/layouts/DAGLayout.java index d493d10..bf59db4 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/layouts/DAGLayout.java +++ b/AlgebraicDataflowArchitectureModel/src/application/layouts/DAGLayout.java @@ -1,8 +1,13 @@ package application.layouts; import java.util.ArrayList; -import java.util.Collections; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; import com.mxgraph.layout.mxGraphLayout; import com.mxgraph.model.mxCell; @@ -13,131 +18,893 @@ import com.mxgraph.view.mxGraphView; public class DAGLayout extends mxGraphLayout { + private mxIGraphModel graphModel; + private mxGraphView view; + private Map> dotEdges; + private List> lines; + private List order; + private Map> hierarchicalRecursionOrder; + private Set usedLineIndex; + private List cells; + private Set roots; + private Map myRoot; + private Set resources; + private Set channels; + private Set eventChannels; + private Map> vertexToDist; + private Map> cellToLineIndex; + private Map> cellGeo; + final double MOVE_X = 100; + final double MOVE_Y = 50; + final double WIDTH = 80; + final double HEIGHT = 30; + final double SHIFT = WIDTH / 4; + final double SPACE = 10; + final double MARGIN_WIDTH = 30; + final double MARGIN_HEIGHT = 30; + final double delta = Math.pow(10, -3); public DAGLayout(mxGraph graph) { super(graph); + + graphModel = graph.getModel(); + view = graph.getView(); + dotEdges = new HashMap<>(); + lines = new ArrayList<>(); + order = new ArrayList<>(); + hierarchicalRecursionOrder = new HashMap<>(); + usedLineIndex = new HashSet<>(); + cells = new ArrayList<>(); + roots = new HashSet<>(); + myRoot = new HashMap<>(); + resources = new HashSet<>(); + channels = new HashSet<>(); + eventChannels = new HashSet<>(); + vertexToDist = new HashMap<>(); + cellToLineIndex = new HashMap<>(); + cellGeo = new HashMap<>(); } public void execute(Object parent) { - mxIGraphModel model = graph.getModel(); - - model.beginUpdate(); + graphModel.beginUpdate(); try { - List> map = new ArrayList>(); - List moved = new ArrayList<>(); - - for (int i = 0; i < model.getChildCount(parent); i++) { + // 繝「繝�繝ォ縺九iresource, channel, eventChannel縺ォ蛻�縺代k + for (int i = 0; i < graphModel.getChildCount(parent); i++) { + mxCell cell = (mxCell) graphModel.getChildAt(parent, i); - mxCell cell = (mxCell) model.getChildAt(parent, i); - - if (model.isVertex(cell)) { - mxGraphView view = graph.getView(); + if (graphModel.isVertex(cell)) { mxCellState state = view.getState(cell); + cells.add(cell); - if (!"ellipse".equals(state.getStyle().get("shape")) && (cell.getEdgeCount() == 1) && !"true".equals(state.getStyle().get("dashed"))) { - List newline = new ArrayList(); - map.add(newline); - lines(map, cell); + // cell縺罫esource縺ァ縺ゅk + if ("ellipse".equals(state.getStyle().get("shape"))) { + roots.add(cell); + hierarchicalRecursionOrder.put(cell, new ArrayList<>()); + familySearch(cell, cell, 0); + } + + if ("rectangle".equals(state.getStyle().get("shape"))) { + if ("true".equals(state.getStyle().get("dashed"))) { + continue; + } + + // cell縺憩ventChannel縺ァ縺ゅk縺句愛螳� + boolean bEventChannel = true; + for (int j = 0; j < cell.getEdgeCount(); j++) { + mxCell edge = (mxCell) cell.getEdgeAt(j); + if (edge.getTarget() == cell) { + bEventChannel = false; + } + } + + // cell縺憩ventChannel縺ァ縺ゅk + if (bEventChannel) { + eventChannels.add(cell); + // cell縺慶hannel縺ァ縺ゅk + } else { + channels.add(cell); + } + hierarchicalRecursionOrder.put(cell, new ArrayList<>()); } } } - sort(map, 0, false); - - // layout - int count; - int skip = 0; - mxGraphView view = graph.getView(); - for (int i = 0; i < map.size(); i++) { - count = 0; - for (int j = 0; j < map.get(i).size(); j++) { - mxGeometry geom = (mxGeometry) map.get(i).get(j).getGeometry().clone(); - mxCellState state = view.getState(map.get(i).get(j)); - if (checkmoved(moved, map.get(i).get(j))) { - if ("ellipse".equals(state.getStyle().get("shape"))){ - geom.setX(50 + j*200); - } else { - geom.setX(100 + j*200); - } - geom.setY(100 + (i-skip)*100); - model.setGeometry(map.get(i).get(j), geom); - moved.add(map.get(i).get(j).getId()); - } else if (geom.getX() < 100 + j*150) { - if ("ellipse".equals(state.getStyle().get("shape"))){ - geom.setX(50 + j*200); - } else { - geom.setX(100 + j*200); - } - geom.setY(100 + (i-skip)*100); - model.setGeometry(map.get(i).get(j), geom); - } else { - count++; - } + // 蜈ィ縺ヲ縺ョcell縺ョ蠎ァ讓吶�∝、ァ縺阪&縺ェ縺ゥ繧貞�晄悄蛹� + for (mxCell c : cells) { + c.getGeometry().setX(0); + c.getGeometry().setY(0); + + if (resources.contains(c)) { + view.getState(c).getStyle().put("verticalAlign", "top"); + c.getGeometry().setWidth(WIDTH); + c.getGeometry().setHeight(HEIGHT); } - if (count >= map.get(i).size())skip++; } + // dotEdges繧貞�晄悄蛹� + for (mxCell c : cells) { + dotEdges.put(c, new HashSet<>()); + } + + // line繧呈ァ狗ッ峨☆繧� + for (mxCell ec : eventChannels) { + List newline = new ArrayList(); + lines.add(newline); + constructionLine(ec, 0); + } + + // cell縺ョ霍晞屬繧貞�崎ィ育ョ� + reculcDist(); + + // cell縺悟ア槭☆繧詰ine縺ョindex繧呈アゅa繧� + for (int i = 0; i < lines.size(); i++) { + for (mxCell cell : lines.get(i)) { + if (cellToLineIndex.get(cell) == null) { + cellToLineIndex.put(cell, new ArrayList<>()); + } + cellToLineIndex.get(cell).add(i); + } + } + + // 荳ヲ縺ケ繧詰ine縺ョ鬆�逡ェ繧偵た繝シ繝医☆繧� + sortLines(); + + Boolean isVersion1 = true; + + System.out.println(vertexToDist); + // cell繧帝�咲スョ縺吶k + if (isVersion1) { + // channel縺ョ謇�螳壹�ョ菴咲スョ縺碁嚴螻、讒矩��蜀�驛ィ縺ォ縺ェ繧九→縺阪�∽ク九↓縺壹i縺励※驟咲スョ縺吶k繝ャ繧、繧「繧ヲ繝� + layout1((mxCell) parent); + } else { + // channel繧帝嚴螻、讒矩��蜀�驛ィ縺ァ繧よ園螳壹�ョ菴咲スョ縺ォ驟咲スョ縺吶k繝ャ繧、繧「繧ヲ繝� + layout2((mxCell) parent); + } } finally { - model.endUpdate(); + graphModel.endUpdate(); } } + // 髫主ア、讒矩��繧貞�榊クー逧�縺ォ謗「邏「 + public void familySearch(mxCell r, mxCell p, int d) { + int childNum = graphModel.getChildCount(p); + + myRoot.put(p, r); + resources.add(p); + if (r != p) { + hierarchicalRecursionOrder.get(r).add(p); + } + + for (int i = 0; i < childNum; i++) { + mxCell child = (mxCell) graphModel.getChildAt(p, i); + cells.add(child); + resources.add(child); + familySearch(r, child, d + 1); + } + } - public void lines(List> mapping, mxCell next) { - mapping.get(mapping.size()-1).add(next); - int tagcount = 0; - mxCell edge; - mxGraphView view = graph.getView(); - for (int i = 0; i < next.getEdgeCount(); i++) { - edge = (mxCell) next.getEdgeAt(i); + // line繧呈ァ狗ッ峨☆繧� + public void constructionLine(mxCell cur, int d) { + // vertexToDist{key : cell, value : [霍晞屬縺ョ譛�蟆丞�、, 霍晞屬縺ョ譛�螟ァ蛟、]} 繧貞�晄悄蛹� + if (vertexToDist.get(cur) == null) { + vertexToDist.put(cur, Arrays.asList(d, d)); + } else { + // 閾ェ霄ォ縺悟性縺セ繧後kline縺ョ荳ュ縺ァ荳�逡ェ螟ァ縺阪>霍晞屬繧剃ソ晏ュ� + List dists = vertexToDist.get(cur); + dists.set(0, Math.max(dists.get(0), d)); + dists.set(1, dists.get(0)); + } + + lines.get(lines.size() - 1).add(cur); + + int tagCount = 0; // edge縺ョ蟋狗せ縺ィ縺ェ繧議ell縺ィ縺励※蜿ら�ァ縺輔l縺溷屓謨ー + for (int i = 0; i < cur.getEdgeCount(); i++) { + mxCell edge = (mxCell) cur.getEdgeAt(i); mxCellState state = view.getState(edge); - if (next != (mxCell) edge.getTarget() && ((mxCell) edge.getTarget() != null) && !"true".equals(state.getStyle().get("dashed"))) { - tagcount++; - if (tagcount > 1) { - List newline = new ArrayList(mapping.get(mapping.size()-1)); - while (newline.get(newline.size()-1).getId() != next.getId()) { - newline.remove(newline.size()-1); - } - mapping.add(newline); - lines(mapping, (mxCell) edge.getTarget()); - + mxCell target = (mxCell) edge.getTarget(); + + // cur縺悟ァ狗せ縺ィ縺ェ繧菊dge縺ョ邨らせ縺ィ縺ェ繧逆arget縺瑚�ェ霄ォ縲√b縺励¥縺ッ蟄伜惠縺励↑縺�蝣エ蜷医r髯、縺� + if ((cur != target) && (target != null)) { + // edge縺ョ遞ョ鬘槭′"dashed"縺ョ蝣エ蜷� + if ("true".equals(state.getStyle().get("dashed"))) { + state.getStyle().put("strokeColor", "#800080"); // edge縺ョ濶イ繧貞、画峩 + dotEdges.get(cur).add(target); // dotEdges{key : 蟋狗せ縺ィ縺ェ繧議ell, value : 邨らせ縺ィ縺ェ繧議ell} } else { - lines(mapping, (mxCell) edge.getTarget()); + tagCount++; + // 蟋狗せ縺ィ縺励※隍�謨ー蝗槫盾辣ァ縺輔l縺ヲ縺�繧句�エ蜷� + if (tagCount > 1) { + // cur縺セ縺ァ縺ョline縺ョ諠�蝣ア繧貞�肴ァ狗ッ峨@縺ヲ縲√◎縺薙°繧画眠縺励>line繧呈ァ狗ッ峨☆繧� + List newline = new ArrayList(lines.get(lines.size() - 1)); + while (newline.get(newline.size() - 1).getId() != cur.getId()) { + newline.remove(newline.size() - 1); + } + lines.add(newline); + constructionLine(target, d + 1); + // 蟋狗せ縺ィ縺励※蛻昴a縺ヲ蜿ら�ァ縺輔l縺溷�エ蜷� + } else { + constructionLine(target, d + 1); + } } } } } - public boolean checkmoved(List list, mxCell cell) { - for (int i = 0; i < list.size(); i++) { - if (list.get(i).equals(cell.getId()))return false; + // cell縺ョ霍晞屬繧貞�崎ィ育ョ励☆繧� + public void reculcDist() { + // 霍晞屬縺ョ譖エ譁ー縺瑚。後o繧後↑縺上↑繧九∪縺ァ郢ー繧願ソ斐☆ + while (true) { + boolean isChanging = false; + + // 蜷御ク�縺ョline縺ォ蜷ォ縺セ繧後k閾ェ霄ォ縺ョ1縺、謇句燕縺ョ霍晞屬縺ィ閾ェ霄ォ縺ョ霍晞屬繧呈ッ碑シ�縺励※縲∵ュ」縺励>霍晞屬繧定ィ育ョ� + for (List line : lines) { + for (int i = 1; i < line.size(); i++) { + mxCell cur = line.get(i); + mxCell pre = line.get(i - 1); + if (!(resources.contains(cur) && resources.contains(pre))) { + if (vertexToDist.get(cur).get(0) < vertexToDist.get(pre).get(1) + 1) { + List dists = vertexToDist.get(cur); + dists.set(0, Math.max(dists.get(0), vertexToDist.get(pre).get(1) + 1)); + dists.set(1, Math.max(dists.get(0), dists.get(1))); + isChanging = true; + } + } + } + } + + // 髫主ア、讒矩��繧定��諷ョ縺励◆cell縺ョ霍晞屬繧定ィ育ョ� + for (mxCell r : roots) { + for (mxCell c : hierarchicalRecursionOrder.get(r)) { + mxCell p = (mxCell) graphModel.getParent(c); + if (!vertexToDist.containsKey(c)) { + vertexToDist.put(c, Arrays.asList(vertexToDist.get(p).get(0), vertexToDist.get(p).get(0))); + } else { + if (vertexToDist.get(p).get(0) > vertexToDist.get(c).get(0)) { + List dists = vertexToDist.get(c); + dists.set(0, vertexToDist.get(p).get(0)); + dists.set(1, Math.max(dists.get(0), dists.get(1))); + isChanging = true; + } + + if (vertexToDist.get(p).get(1) < vertexToDist.get(c).get(1)) { + List dists = vertexToDist.get(p); + dists.set(1, vertexToDist.get(c).get(1)); + isChanging = true; + } + } + } + } + + // 1蠎ヲ繧ょ、画峩縺檎┌縺�縺ィ繝ォ繝シ繝励r謚懊¢繧� + if (!isChanging) { + break; + } } - return true; } - public void sort(List> map, int n, boolean check) { - int msize = -1; - int mnum = -1; - if (check) { - for (int i = n; i < map.size(); i++) { - if (map.get(i).size() > msize && (map.get(n-1).get(0).getId().equals(map.get(i).get(0).getId()))) { - mnum = i; + // line繧偵た繝シ繝医☆繧�(縺溘□縺励�∝ョ滄圀縺ォ縺ッlines蜀�縺ョ隕∫エ�縺ョ鬆�逡ェ縺ッ蜈・繧梧崛縺医★縲∝他縺ウ蜃コ縺咎��逡ェ繧剃ソ晏ュ倥@縺殪rder繧剃ス懈��) + public void sortLines() { + for (int i = 0; i < lines.size(); i++) { + List line = lines.get(i); + + // eventChannel縺九i縺ョ蜃コ蜉帛�医′root縺ァ縺ゅk蝣エ蜷� + if (roots.contains(line.get(1))) { + for (mxCell c : line) { + addLine(c); // order縺ォline縺ョindex繧呈�シ邏阪☆繧� } } + } + } + + // order縺ォline縺ョindex繧呈�シ邏阪☆繧� + public void addLine(mxCell c) { + if (cellToLineIndex.get(c) != null) { + for (int i : cellToLineIndex.get(c)) { + if (!usedLineIndex.contains(i)) { + order.add(i); + } + usedLineIndex.add(i); + } + } + + // root縺悟ア槭☆繧詰ine縺ョindex繧呈�シ邏阪@縺溷セ後�∝ュ仙ュォ縺悟ア槭☆繧詰ine縺ョindex繧帝��谺。譬シ邏阪☆繧� + for (int i = 0; i < graphModel.getChildCount(c); i++) { + mxCell child = (mxCell) graphModel.getChildAt(c, i); + addLine(child); + } + } + + // cell繧帝�咲スョ縺吶k + public void layout1(mxCell s) { + /* cell縺ョ蠎ァ讓吶�ッ縲…ell縺ョparent縺ョ蟾ヲ荳翫�ョ蠎ァ讓吶r蝓コ貅悶↓x縺ィy繧偵←繧後□縺代★繧峨☆縺九〒螳夂セゥ縺輔l縺ヲ縺�繧� + 繧医▲縺ヲ險育ョ励�ョ驛ス蜷井ク翫�(d=1 縺ョcell(繝ャ繧、繧「繧ヲ繝育判髱「)縺ョ蟾ヲ荳翫r蝓コ貅悶↓縺励◆蠎ァ讓吶rcellGeo縺ォ菫晏ュ倥@縺ヲ縺翫¥ */ + + Set movedSet = new HashSet<>(); // 驟咲スョ貂医∩縺ョcell繧堤ョ。逅� + Map> movedMap = new TreeMap<>(); // 驟咲スョ貂医∩縺ョcell縺ョ霍晞屬縺斐→縺ョ驟咲スョ鬆�繧堤ョ。逅� + Map distToMaxX = new HashMap<>(); // 霍晞屬縺斐→縺ョcell縺ョ蜿ウ遶ッ縺ョx蠎ァ讓吶�ョ譛�螟ァ蛟、繧堤ョ。逅� + double maxY = 0; + int maxD = 0; + + putCellGeo(s, 0, 0); + + // 霍晞屬縺ョ譛�螟ァ蛟、繧呈アゅa繧� + for (mxCell c : cells) { + maxD = Math.max(maxD, vertexToDist.get(c).get(1)); + } + + // 蛻晄悄蛹� + distToMaxX.put(-1, 0.0); + for (int d = 0; d <= maxD; d++) { + movedMap.put(d, new ArrayList<>()); + distToMaxX.put(d, -1.0); + } + + for (int i : order) { + double centerY = -1; // 驟咲スョ莠亥ョ壹�ョy蠎ァ讓�(荳ュ螟ョ) + for (mxCell cur : lines.get(i)) { + // 譌「縺ォ驟咲スョ貂医∩縺ェ繧峨せ繧ュ繝�繝� + if (movedSet.contains(cur)) { + continue; + } + + int d = vertexToDist.get(cur).get(0); // cur縺ョ霍晞屬 + double x = distToMaxX.get(d - 1) + MOVE_X; // 驟咲スョ莠亥ョ壹�ョx蠎ァ讓� + double endX = 0; // 驟咲スョ縺励◆cell縺ョ蜿ウ遶ッ縺ョx蠎ァ讓� + double endY = 0; // 驟咲スョ縺励◆cell縺ョ荳狗ォッ縺ョy蠎ァ讓� + + // cur縺罫oot, channel, eventChannel縺ォ螻槭☆繧議ell縺ァ縺ゅk蝣エ蜷� + if (roots.contains(cur) || channels.contains(cur) || eventChannels.contains(cur)) { + if (centerY == -1) { + centerY = maxY + MOVE_Y + cur.getGeometry().getHeight() / 2; + } + double y = centerY - cur.getGeometry().getHeight() / 2; // 驟咲スョ莠亥ョ壹�ョy蠎ァ讓� + double ny = y; + double nx = x; + + // 莠亥ョ壹@縺ヲ縺�繧句コァ讓吶↓驟咲スョ縺吶k縺ィ莉悶�ョ繝ェ繧ス繝シ繧ケ蜀�驛ィ縺ォ驟咲スョ縺輔l縺ヲ縺励∪縺�蝣エ蜷医�∽ク九↓縺壹i縺� + for (mxCell m : movedSet) { + if (roots.contains(m) || channels.contains(m)) { + if (cellOverlap(m.getGeometry().getX(), m.getGeometry().getY(), m.getGeometry().getWidth(), m.getGeometry().getHeight(), x, y, cur.getGeometry().getWidth(), cur.getGeometry().getHeight())) { + ny = m.getGeometry().getY() + m.getGeometry().getHeight() + MOVE_Y; + } + } + } + + // 莠亥ョ壹�ョ蠎ァ讓吶↓驟咲スョ縺励※縲∝推遞ョ蜃ヲ逅�繧定。後≧ + cur.getGeometry().setX(nx); + cur.getGeometry().setY(ny); + endX = nx + cur.getGeometry().getWidth(); + endY = ny + cur.getGeometry().getHeight(); + putCellGeo(cur, nx, ny); + addMoved(movedSet, movedMap, cur, d); + graphModel.setGeometry(cur, (mxGeometry) cur.getGeometry().clone()); + + // 霍晞屬縺慧縺ョ譛�螟ァ縺ョx蠎ァ讓吶′譖エ譁ー縺輔l繧句�エ蜷医�∵里縺ォ驟咲スョ貂医∩縺ョcell繧偵◎繧後↓蠢懊§縺ヲ蜿ウ縺ォ縺壹i縺� + if (distToMaxX.get(d) < endX) { + updateDistToMaxX(distToMaxX, movedSet, movedMap, d, maxD, endX); + } + maxY = Math.max(maxY, endY); // 驟咲スョ貂医∩縺ョcell縺ョ荳ュ縺ァ縺ョ荳狗ォッ縺ョ蠎ァ讓吶�ョ譛�螟ァ + + // root縺ョ蟄仙ュォ繧偵∪縺ィ繧√※驟咲スョ縺吶k + for (mxCell c : hierarchicalRecursionOrder.get(cur)) { + d = vertexToDist.get(c).get(0); + nx = x; // 驟咲スョ莠亥ョ壹�ョx蠎ァ讓� + mxCell parent = (mxCell) graphModel.getParent(c); + + // 霍晞屬縺瑚ヲェ縺ィ遲峨@縺�蝣エ蜷医�∬ヲェ縺ョx蠎ァ讓吶h繧雁ー代@螟ァ縺阪¥縺吶k + if (d == vertexToDist.get(parent).get(0)) { + nx = cellGeo.get(parent).get("x") + MARGIN_WIDTH; + } else { + nx = distToMaxX.get(d - 1) + MOVE_X; + } + + // 驥阪↑縺」縺ヲ縺励∪縺�蜈�蠑溘Μ繧ス繝シ繧ケ縺悟ュ伜惠縺吶k蝣エ蜷医�∽ク九↓縺壹i縺� + double brotherMaxY = MOVE_Y; + for (int k = 0; k < graphModel.getChildCount(parent); k++) { + mxCell child = (mxCell) graphModel.getChildAt(parent, k); + if (child == c) { + continue; + } + + if (movedSet.contains(child) && (vertexToDist.get(child).get(0) <= d && d <= vertexToDist.get(child).get(1))) { + brotherMaxY = Math.max(brotherMaxY, child.getGeometry().getY() + child.getGeometry().getHeight() + SPACE); + } + } + + // 莠亥ョ壹�ョ蠎ァ讓吶↓驟咲スョ縺励※縲∝推遞ョ蜃ヲ逅�繧定。後≧ + c.getGeometry().setX(nx - cellGeo.get(parent).get("x")); + c.getGeometry().setY(brotherMaxY); + graphModel.setGeometry(c, (mxGeometry) c.getGeometry().clone()); + addMoved(movedSet, movedMap, c, d); + putCellGeo(c, nx, cellGeo.get(parent).get("y") + brotherMaxY); + + endX = cellGeo.get(c).get("x") + c.getGeometry().getWidth(); + endY = cellGeo.get(c).get("y") + c.getGeometry().getHeight(); + endY = resize(parent, endX, endY); // 蟄仙ュォ縺瑚ヲェ縺ョ蜀�驛ィ縺ォ蜷ォ縺セ繧後↑縺�蝣エ蜷医�∬ヲェ縺ョ螟ァ縺阪&繧貞、画峩縺吶k + + // 霍晞屬縺慧縺ョ譛�螟ァ縺ョx蠎ァ讓吶′譖エ譁ー縺輔l繧句�エ蜷医�∵里縺ォ驟咲スョ貂医∩縺ョcell繧偵◎繧後↓蠢懊§縺ヲ蜿ウ縺ォ縺壹i縺� + if (distToMaxX.get(d) < endX) { + updateDistToMaxX(distToMaxX, movedSet, movedMap, d, maxD, endX); + } + maxY = Math.max(maxY, endY); + } + } + } + } + + // 轤ケ邱壹�ョ霎コ縺碁㍾縺ェ縺」縺ヲ縺�繧句�エ蜷医�∝承縺ォ縺壹i縺� + for (mxCell c : resources) { + for (mxCell cc : dotEdges.get(c)) { + for (Map.Entry> entry : dotEdges.entrySet()) { + mxCell u = entry.getKey(); + for (mxCell v : entry.getValue()) { + // c-cc, u-v縺悟酔縺賄otEdge繧貞盾辣ァ縺励※縺�繧句�エ蜷� + if ((c == u && cc == v) || (c == v && cc == u)) { + continue; + } + + double []posC = {cellGeo.get(c).get("x") + c.getGeometry().getWidth() / 2, cellGeo.get(c).get("y") + c.getGeometry().getHeight() / 2}; + double []posCC = {cellGeo.get(cc).get("x") + cc.getGeometry().getWidth() / 2, cellGeo.get(cc).get("y") + cc.getGeometry().getHeight() / 2}; + double []posU = {cellGeo.get(u).get("x") + u.getGeometry().getWidth() / 2, cellGeo.get(u).get("y") + u.getGeometry().getHeight() / 2}; + double []posV = {cellGeo.get(v).get("x") + v.getGeometry().getWidth() / 2, cellGeo.get(v).get("y") + v.getGeometry().getHeight() / 2}; + + if (isStraightLine(posC, posCC, posU, posV)) { + c.getGeometry().setX(c.getGeometry().getX() + SHIFT); + cellGeo.get(c).replace("x", cellGeo.get(c).get("x")); + cc.getGeometry().setX(cc.getGeometry().getX() + SHIFT); + cellGeo.get(cc).replace("x", cellGeo.get(cc).get("x") + SHIFT); + graphModel.setGeometry(c, c.getGeometry()); + graphModel.setGeometry(cc, cc.getGeometry()); + } + } + } + } + } + + // eventChannel繧貞�コ蜉帛�医�ョy蠎ァ讓吶↓謠�縺医k + for (int i : order) { + mxCell cur = lines.get(i).get(1); + List ecs = new ArrayList<>(); + for (int j : cellToLineIndex.get(cur)) { + if (!ecs.contains(lines.get(j).get(0)) && cur == lines.get(j).get(1)) { + ecs.add(lines.get(j).get(0)); + } + } + + double centerY = cellGeo.get(cur).get("y") + cur.getGeometry().getHeight() / 2; + double y = 0; + if (ecs.size() % 2 != 0) { + y = centerY - ecs.size()*HEIGHT / 2 - ((ecs.size() - 1) / 2)*SPACE; + } else { + y = centerY - ecs.size()*HEIGHT / 2 - (ecs.size() - 1)*SPACE / 2; + } + + for (int j : order) { + mxCell other = lines.get(j).get(0); + if (ecs.contains(other)) { + continue; + } + } + + for (mxCell ec : ecs) { + ec.getGeometry().setY(y); + graphModel.setGeometry(ec, ec.getGeometry()); + cellGeo.get(ec).replace("y", y); + y += HEIGHT + SPACE; + } + } + + // eventChannel繧馳蠎ァ讓吶↓縺、縺�縺ヲ譏�鬆�縺ォ繧ス繝シ繝医☆繧� + List sortedEcs = new ArrayList<>(); + Set usedEcs = new HashSet<>(); + for (int i = 0; i < eventChannels.size(); i++) { + mxCell minEc = movedMap.get(0).get(0); + double ecMinY = Double.MAX_VALUE; + for (mxCell ec : movedMap.get(0)) { + if (usedEcs.contains(ec)) { + continue; + } + if (cellGeo.get(ec).get("y") < ecMinY) { + minEc = ec; + ecMinY = cellGeo.get(ec).get("y"); + } + } + sortedEcs.add(minEc); + usedEcs.add(minEc); + } + + // 繧ス繝シ繝医�ョ鬆�逡ェ縺ォ蠢懊§縺ヲ縲�驥阪↑縺」縺ヲ縺�繧菊ventChannel繧剃ク九↓縺壹i縺� + for (int i = 1; i < sortedEcs.size(); i++) { + mxCell cur = sortedEcs.get(i); + mxCell pre = sortedEcs.get(i - 1); + + if (cellGeo.get(cur).get("y") < cellGeo.get(pre).get("y") + pre.getGeometry().getHeight() + SPACE) { + double dy = cellGeo.get(pre).get("y") + pre.getGeometry().getHeight() + SPACE - cellGeo.get(cur).get("y"); + cur.getGeometry().setY(cur.getGeometry().getY() + dy); + cellGeo.get(cur).replace("y", cellGeo.get(cur).get("y") + dy); + graphModel.setGeometry(cur, cur.getGeometry()); + } + } + } + + // cell繧帝�咲スョ縺吶k + public void layout2(mxCell s) { + /* cell縺ョ蠎ァ讓吶�ッ縲…ell縺ョparent縺ョ蟾ヲ荳翫�ョ蠎ァ讓吶r蝓コ貅悶↓x縺ィy繧偵←繧後□縺代★繧峨☆縺九〒螳夂セゥ縺輔l縺ヲ縺�繧� + 繧医▲縺ヲ險育ョ励�ョ驛ス蜷井ク翫�(d=1 縺ョcell(繝ャ繧、繧「繧ヲ繝育判髱「)縺ョ蟾ヲ荳翫r蝓コ貅悶↓縺励◆蠎ァ讓吶rcellGeo縺ォ菫晏ュ倥@縺ヲ縺翫¥ */ + + Set movedSet = new HashSet<>(); // 驟咲スョ貂医∩縺ョcell繧堤ョ。逅� + Map> movedMap = new TreeMap<>(); // 驟咲スョ貂医∩縺ョcell縺ョ霍晞屬縺斐→縺ョ驟咲スョ鬆�繧堤ョ。逅� + Map distToMaxX = new HashMap<>(); // 霍晞屬縺斐→縺ョcell縺ョ蜿ウ遶ッ縺ョx蠎ァ讓吶�ョ譛�螟ァ蛟、繧堤ョ。逅� + double maxY = 0; + int maxD = 0; + + putCellGeo(s, 0, 0); + + // 霍晞屬縺ョ譛�螟ァ蛟、繧呈アゅa繧� + for (mxCell c : cells) { + maxD = Math.max(maxD, vertexToDist.get(c).get(1)); + } + + // 蛻晄悄蛹� + distToMaxX.put(-1, 0.0); + for (int d = 0; d <= maxD; d++) { + movedMap.put(d, new ArrayList<>()); + distToMaxX.put(d, -1.0); + } + + for (int i : order) { + double centerY = -1; // 驟咲スョ莠亥ョ壹�ョy蠎ァ讓�(荳ュ螟ョ) + for (int j = 0; j < lines.get(i).size(); j++) { + mxCell cur = lines.get(i).get(j); + + // 譌「縺ォ驟咲スョ貂医∩縺ェ繧峨せ繧ュ繝�繝� + if (movedSet.contains(cur)) { + continue; + } + + int d = vertexToDist.get(cur).get(0); // cur縺ョ霍晞屬 + double x = distToMaxX.get(d - 1) + MOVE_X; // 驟咲スョ莠亥ョ壹�ョx蠎ァ讓� + double endX = 0; // 驟咲スョ縺励◆cell縺ョ蜿ウ遶ッ縺ョx蠎ァ讓� + double endY = 0; // 驟咲スョ縺励◆cell縺ョ荳狗ォッ縺ョy蠎ァ讓� + + // cur縺罫oots縺ィeventChannel縺ォ螻槭☆繧九→縺阪�ョcell縺ョ驟咲スョ + if (roots.contains(cur) || eventChannels.contains(cur)) { + if (centerY == -1) { + centerY = maxY + MOVE_Y + cur.getGeometry().getHeight() / 2; + } + double y = centerY - cur.getGeometry().getHeight() / 2; // 驟咲スョ莠亥ョ壹�ョy蠎ァ讓� + double ny = y; + double nx = x; + + // 莠亥ョ壹�ョ蠎ァ讓吶↓驟咲スョ縺励※縲∝推遞ョ蜃ヲ逅�繧定。後≧ + cur.getGeometry().setX(nx); + cur.getGeometry().setY(ny); + endX = nx + cur.getGeometry().getWidth(); + endY = ny + cur.getGeometry().getHeight(); + putCellGeo(cur, nx, ny); + addMoved(movedSet, movedMap, cur, d); + graphModel.setGeometry(cur, (mxGeometry) cur.getGeometry().clone()); + + // 霍晞屬縺慧縺ョ譛�螟ァ縺ョx蠎ァ讓吶′譖エ譁ー縺輔l繧句�エ蜷医�∵里縺ォ驟咲スョ貂医∩縺ョcell繧偵◎繧後↓蠢懊§縺ヲ蜿ウ縺ォ縺壹i縺� + if (distToMaxX.get(d) < endX) { + updateDistToMaxX(distToMaxX, movedSet, movedMap, d, maxD, endX); + } + maxY = Math.max(maxY, endY); // 驟咲スョ貂医∩縺ョcell縺ョ荳ュ縺ァ縺ョ荳狗ォッ縺ョ蠎ァ讓吶�ョ譛�螟ァ + + // root縺ョ蟄仙ュォ繧偵∪縺ィ繧√※驟咲スョ縺吶k + for (mxCell c : hierarchicalRecursionOrder.get(cur)) { + d = vertexToDist.get(c).get(0); + nx = x; // 驟咲スョ莠亥ョ壹�ョx蠎ァ讓� + mxCell parent = (mxCell) graphModel.getParent(c); + + // 霍晞屬縺瑚ヲェ縺ィ遲峨@縺�蝣エ蜷医�∬ヲェ縺ョx蠎ァ讓吶h繧雁ー代@螟ァ縺阪¥縺吶k + if (d == vertexToDist.get(parent).get(0)) { + nx = cellGeo.get(parent).get("x") + MARGIN_WIDTH; + } else { + nx = distToMaxX.get(d - 1) + MOVE_X; + } + + // 驥阪↑縺」縺ヲ縺励∪縺�蜈�蠑溘Μ繧ス繝シ繧ケ縺悟ュ伜惠縺吶k蝣エ蜷医�∽ク九↓縺壹i縺� + double brotherMaxY = MOVE_Y; + for (int k = 0; k < graphModel.getChildCount(parent); k++) { + mxCell child = (mxCell) graphModel.getChildAt(parent, k); + if (child == c) { + continue; + } + + if (movedSet.contains(child) && (vertexToDist.get(child).get(0) <= d && d <= vertexToDist.get(child).get(1))) { + brotherMaxY = Math.max(brotherMaxY, child.getGeometry().getY() + child.getGeometry().getHeight() + SPACE); + } + } + + // 莠亥ョ壹�ョ蠎ァ讓吶↓驟咲スョ縺励※縲∝推遞ョ蜃ヲ逅�繧定。後≧ + c.getGeometry().setX(nx - cellGeo.get(parent).get("x")); + c.getGeometry().setY(brotherMaxY); + graphModel.setGeometry(c, (mxGeometry) c.getGeometry().clone()); + addMoved(movedSet, movedMap, c, d); + putCellGeo(c, nx, cellGeo.get(parent).get("y") + brotherMaxY); + + endX = cellGeo.get(c).get("x") + c.getGeometry().getWidth(); + endY = cellGeo.get(c).get("y") + c.getGeometry().getHeight(); + endY = resize(parent, endX, endY); // 蟄仙ュォ縺瑚ヲェ縺ョ蜀�驛ィ縺ォ蜷ォ縺セ繧後↑縺�蝣エ蜷医�∬ヲェ縺ョ螟ァ縺阪&繧貞、画峩縺吶k + + // 霍晞屬縺慧縺ョ譛�螟ァ縺ョx蠎ァ讓吶′譖エ譁ー縺輔l繧句�エ蜷医�∵里縺ォ驟咲スョ貂医∩縺ョcell繧偵◎繧後↓蠢懊§縺ヲ蜿ウ縺ォ縺壹i縺� + if (distToMaxX.get(d) < endX) { + updateDistToMaxX(distToMaxX, movedSet, movedMap, d, maxD, endX); + } + maxY = Math.max(maxY, endY); // 驟咲スョ貂医∩縺ョcell縺ョ荳ュ縺ァ縺ョ荳狗ォッ縺ョ蠎ァ讓吶�ョ譛�螟ァ + } + } + + // cur縺慶hannel縺ォ螻槭☆繧九→縺阪�ョcell縺ョ驟咲スョ + if (channels.contains(cur)) { + mxCell pre = lines.get(i).get(j - 1); + centerY = cellGeo.get(pre).get("y") + pre.getGeometry().getHeight() / 2; + double y = centerY - cur.getGeometry().getHeight() / 2; + + cur.getGeometry().setX(x); + cur.getGeometry().setY(y); + endX = x + cur.getGeometry().getWidth(); + endY = y + cur.getGeometry().getHeight(); + putCellGeo(cur, x, y); + addMoved(movedSet, movedMap, cur, d); + graphModel.setGeometry(cur, (mxGeometry) cur.getGeometry().clone()); + + // 霍晞屬縺慧縺ョ譛�螟ァ縺ョx蠎ァ讓吶′譖エ譁ー縺輔l繧句�エ蜷医�∵里縺ォ驟咲スョ貂医∩縺ョcell繧偵◎繧後↓蠢懊§縺ヲ蜿ウ縺ォ縺壹i縺� + if (distToMaxX.get(d) < endX) { + updateDistToMaxX(distToMaxX, movedSet, movedMap, d, maxD, endX); + } + maxY = Math.max(maxY, endY); // 驟咲スョ貂医∩縺ョcell縺ョ荳ュ縺ァ縺ョ荳狗ォッ縺ョ蠎ァ讓吶�ョ譛�螟ァ + } + } + } + + // 轤ケ邱壹�ョ霎コ縺碁㍾縺ェ縺」縺ヲ縺�繧句�エ蜷医�∝承縺ォ縺壹i縺� + for (mxCell c : resources) { + for (mxCell cc : dotEdges.get(c)) { + for (Map.Entry> entry : dotEdges.entrySet()) { + mxCell u = entry.getKey(); + for (mxCell v : entry.getValue()) { + if ((c == u && cc == v) || (c == v && cc == u)) { + continue; + } + + double []posC = {cellGeo.get(c).get("x") + c.getGeometry().getWidth() / 2, cellGeo.get(c).get("y") + c.getGeometry().getHeight() / 2}; + double []posCC = {cellGeo.get(cc).get("x") + cc.getGeometry().getWidth() / 2, cellGeo.get(cc).get("y") + cc.getGeometry().getHeight() / 2}; + double []posU = {cellGeo.get(u).get("x") + u.getGeometry().getWidth() / 2, cellGeo.get(u).get("y") + u.getGeometry().getHeight() / 2}; + double []posV = {cellGeo.get(v).get("x") + v.getGeometry().getWidth() / 2, cellGeo.get(v).get("y") + v.getGeometry().getHeight() / 2}; + + if (isStraightLine(posC, posCC, posU, posV)) { + c.getGeometry().setX(c.getGeometry().getX() + SHIFT); + cellGeo.get(c).replace("x", cellGeo.get(c).get("x")); + cc.getGeometry().setX(cc.getGeometry().getX() + SHIFT); + cellGeo.get(cc).replace("x", cellGeo.get(cc).get("x") + SHIFT); + graphModel.setGeometry(c, c.getGeometry()); + graphModel.setGeometry(cc, cc.getGeometry()); + } + } + } + } + } + + // eventChannel繧貞�コ蜉帛�医�ョy蠎ァ讓吶↓謠�縺医k + for (int i : order) { + mxCell cur = lines.get(i).get(1); + List ecs = new ArrayList<>(); + for (int j : cellToLineIndex.get(cur)) { + if (!ecs.contains(lines.get(j).get(0)) && cur == lines.get(j).get(1)) { + ecs.add(lines.get(j).get(0)); + } + } + + double centerY = cellGeo.get(cur).get("y") + cur.getGeometry().getHeight() / 2; + double y = 0; + if (ecs.size() % 2 != 0) { + y = centerY - ecs.size()*HEIGHT / 2 - ((ecs.size() - 1) / 2)*SPACE; + } else { + y = centerY - ecs.size()*HEIGHT / 2 - (ecs.size() - 1)*SPACE / 2; + } + + for (int j : order) { + mxCell other = lines.get(j).get(0); + if (ecs.contains(other)) { + continue; + } + } + + for (mxCell ec : ecs) { + ec.getGeometry().setY(y); + graphModel.setGeometry(ec, ec.getGeometry()); + cellGeo.get(ec).replace("y", y); + y += HEIGHT + SPACE; + } + } + + // eventChannel繧馳蠎ァ讓吶↓縺、縺�縺ヲ譏�鬆�縺ォ繧ス繝シ繝医☆繧� + List sortedEcs = new ArrayList<>(); + Set usedEcs = new HashSet<>(); + for (int i = 0; i < eventChannels.size(); i++) { + mxCell minEc = movedMap.get(0).get(0); + double ecMinY = Double.MAX_VALUE; + for (mxCell ec : movedMap.get(0)) { + if (usedEcs.contains(ec)) { + continue; + } + if (cellGeo.get(ec).get("y") < ecMinY) { + minEc = ec; + ecMinY = cellGeo.get(ec).get("y"); + } + } + sortedEcs.add(minEc); + usedEcs.add(minEc); + } + + // 繧ス繝シ繝医�ョ鬆�逡ェ縺ォ蠢懊§縺ヲ縲�驥阪↑縺」縺ヲ縺�繧菊ventChannel繧剃ク九↓縺壹i縺� + for (int i = 1; i < sortedEcs.size(); i++) { + mxCell cur = sortedEcs.get(i); + mxCell pre = sortedEcs.get(i - 1); + + if (cellGeo.get(cur).get("y") < cellGeo.get(pre).get("y") + pre.getGeometry().getHeight() + SPACE) { + double dy = cellGeo.get(pre).get("y") + pre.getGeometry().getHeight() + SPACE - cellGeo.get(cur).get("y"); + cur.getGeometry().setY(cur.getGeometry().getY() + dy); + cellGeo.get(cur).replace("y", cellGeo.get(cur).get("y") + dy); + graphModel.setGeometry(cur, cur.getGeometry()); + } + } + } + + // cell縺ョ蠎ァ讓吶r逋サ骭イ + public void putCellGeo(mxCell c, double x, double y) { + cellGeo.put(c, new HashMap<>()); + cellGeo.get(c).put("x", x); + cellGeo.get(c).put("y", y); + } + + // movedSet, movedMap縺ォ驟咲スョ貂医∩縺ョcell繧定ソス蜉� + public void addMoved(Set ms, Map> mm, mxCell c, int d) { + ms.add(c); + mm.get(d).add(c); + } + + // 2縺、縺ョ霎コ縺碁㍾縺ェ縺」縺ヲ縺�繧九°蛻、螳� + public boolean isStraightLine(double []u1, double []v1, double []u2, double []v2) { + double gradient1 = 0; + double length1 = Math.pow(u1[0] - v1[0], 2) + Math.pow(u1[1] - v1[1], 2); + double gradient2 = 0; + double length2 = Math.pow(u2[0] - v2[0], 2) + Math.pow(u2[1] - v2[1], 2); + boolean isVertical = false; + + if (u1[0] == v1[0]) { + isVertical = true; + } else { + gradient1 = (u1[1] - v1[1]) / (u1[0] - v1[0]); + } + + if (isVertical) { + if (u2[0] != v2[0]) { + return false; + } else { + gradient2 = (u2[1] - v2[1]) / (u2[0] - v2[0]); + } } else { - for (int i = n; i < map.size(); i++) { - if (map.get(i).size() > msize) { - mnum = i; + if (gradient1 - gradient2 > delta) { + return false; + } + } + + double [][]a = {u1, v1}; + double [][]b = {u2, v2}; + double maxLength = 0; + for (double []c1 : a) { + for (double []c2 : b) { + double gradient3 = 0; + double length3 = Math.pow(c1[0] - c2[0], 2) + Math.pow(c1[1] - c2[1], 2); + + if(isVertical) { + if (c1[0] == c2[0]) { + maxLength = Math.max(maxLength, length3); + } else { + return false; + } + } else { + if (c1[0] == c2[0]) { + return false; + } else { + gradient3 = (c2[1] - c1[1]) / (c2[0] - c1[0]); + if (gradient1 - gradient3 > delta) { + return false; + } + maxLength = Math.max(maxLength, length3); + } } } } - if (mnum >= 0) { - Collections.swap(map, n, mnum); - sort(map, n+1, true); - } else if(n < map.size()) { - sort(map, n+1, false); + + if (length1 + length2 < maxLength) { + return false; + } else { + return true; } } + // 2縺、縺ョcell縺碁㍾縺ェ縺」縺ヲ縺�繧九°蛻、螳� + public boolean cellOverlap(double ax, double ay, double aw, double ah, double bx, double by, double bw, double bh) { + if (((ax <= bx) && (bx <= ax + aw)) || ((bx <= ax) && (ax <= bx + bw))) { + if (((ay <= by) && (by <= ay + ah)) || ((by <= ay) && (ay <= by + bh))) { + return true; + } + } + return false; + } + // 豺ア縺輔′d縺ョdistToMaxX縺ォ譖エ譁ー縺悟�・繧九→縲∵キア縺輔′d繧医j螟ァ縺阪>驟咲スョ貂医∩縺ョcell繧貞承縺ォ縺壹i縺� + public void updateDistToMaxX(Map distToMaxX, Set movedSet, Map> movedMap, int d, int md, double endX) { + double preEndX = distToMaxX.get(d); + distToMaxX.replace(d, endX); + for (int i = d + 1; i <= md; i++) { + if (distToMaxX.get(i) == -1) { + distToMaxX.replace(i, endX); + } + } + + for (Map.Entry> entry : movedMap.entrySet()) { + if (entry.getKey() <= d) { + continue; + } + + for (mxCell cell : entry.getValue()) { + double newX = cellGeo.get(cell).get("x") + endX - preEndX; + mxCell cp = (mxCell) cell.getParent(); + relocateX(cell, newX, cellGeo.get(cp).get("x")); + + if (!(roots.contains(cell) || channels.contains(cell))) { + mxCell ancestor = (mxCell) graphModel.getParent(cell); + resize(ancestor, cellGeo.get(cell).get("x") + cell.getGeometry().getWidth(), cellGeo.get(cell).get("y") + cell.getGeometry().getHeight()); + } + if (distToMaxX.get(entry.getKey()) < cellGeo.get(cell).get("x") + cell.getGeometry().getWidth()) { + distToMaxX.replace(entry.getKey(), cellGeo.get(cell).get("x") + cell.getGeometry().getWidth()); + for (int i = entry.getKey() + 1; i <= md; i++) { + if (distToMaxX.get(i) == -1) { + distToMaxX.replace(i, cellGeo.get(cell).get("x") + cell.getGeometry().getWidth()); + } + } + } + } + } + } + + // 蟄舌r隕�縺�繧医≧縺ォ隕ェ縺ョ螟ァ縺阪&繧貞、画峩 + public double resize(mxCell ancestor, double endX, double endY) { + while (true) { + boolean isChanging = false; + + if (ancestor.getGeometry().getWidth() < endX - cellGeo.get(ancestor).get("x") + MARGIN_WIDTH) { + ancestor.getGeometry().setWidth(endX - cellGeo.get(ancestor).get("x") + MARGIN_WIDTH); + isChanging = true; + } + if (ancestor.getGeometry().getHeight() < endY - cellGeo.get(ancestor).get("y") + MARGIN_HEIGHT) { + ancestor.getGeometry().setHeight(endY - cellGeo.get(ancestor).get("y") + MARGIN_HEIGHT); + isChanging = true; + } + if (isChanging) { + graphModel.setGeometry(ancestor, ancestor.getGeometry()); + } + + endX = cellGeo.get(ancestor).get("x") + ancestor.getGeometry().getWidth(); + endY = cellGeo.get(ancestor).get("y") + ancestor.getGeometry().getHeight(); + + if (roots.contains(ancestor)) { + break; + } + ancestor = (mxCell) ancestor.getParent(); + } + + return endY; + } + + // x蠎ァ讓吶r螟画峩 + public void relocateX(mxCell c, double nx, double dx) { + c.getGeometry().setX(nx - dx); + graphModel.setGeometry(c, (mxGeometry) c.getGeometry().clone()); + cellGeo.get(c).replace("x", nx); + } } \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/src/code/ast/MethodDeclaration.java b/AlgebraicDataflowArchitectureModel/src/code/ast/MethodDeclaration.java index fad7629..5f5aafc 100644 --- a/AlgebraicDataflowArchitectureModel/src/code/ast/MethodDeclaration.java +++ b/AlgebraicDataflowArchitectureModel/src/code/ast/MethodDeclaration.java @@ -109,6 +109,10 @@ } thrws.addException(exception); } + + public Throws getThrows() { + return thrws; + } @Override public Annotation getAnnotation(String name) { diff --git a/AlgebraicDataflowArchitectureModel/src/generators/CodeGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/CodeGenerator.java new file mode 100644 index 0000000..5db9ecd --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/generators/CodeGenerator.java @@ -0,0 +1,683 @@ +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 updateMethodPrefix = "update"; + public static final String from = "From"; + public static final String _for = "For"; + 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 (curResNode.getOutSideResources().contains(m.getResource())) { + topologicalSort(resNode, allNodes, dependedRootComponentGraph, visited, orderedList); + } + } + } + } + } + } + orderedList.add(0, curResNode); + } + + protected void updateMainComponent(TypeDeclaration mainType, MethodDeclaration mainConstructor, Node componentNode, + MethodDeclaration constructor, 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) { + parameters.add(res.getResourceName()); + } + if (constructor.getParameters() != null) { + for (VariableDeclaration var: constructor.getParameters()) { + parameters.add(var.getName()); + } + } + + 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 (Expression param: accessRes.getPrimaryResourcePath().getPathParams()) { + if (param instanceof Variable) { + Variable var = (Variable) param; + mainGetterParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + } else if (param instanceof Term) { + Term var = (Term) param; + 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.getPrimaryResourcePath(), 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 (resourceNode.getOutSideResource(c) != null) { + for (ResourcePath res: c.getReferenceResources()) { + if (!refs.contains(res) && !depends.contains(res.getResourceHierarchy())) { + refs.add(res); + String refResName = langSpec.toComponentName(res.getLeafResourceName()); + component.addField(langSpec.newFieldDeclaration(new Type(refResName, refResName), res.getLeafResourceName())); + constructor.addParameter(langSpec.newVariableDeclaration(new Type(refResName, refResName), res.getLeafResourceName())); + constructor.getBody().addStatement(langSpec.getFieldAccessor(res.getLeafResourceName()) + langSpec.getAssignment() + res.getLeafResourceName() + 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 = updateMethodPrefix; + 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); + } + // use the cached value as the current state + return new Field(targetRes.getLeafResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targerRes= target.getResource(); + return new Parameter(targerRes.getLeafResourceName(), + 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.getLeafResourceName(), + 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.getLeafResourceName(), 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; + } + } + }; + } + + protected IResourceStateAccessor getRefAccessor() { + 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); + } + // use the cached value as the current state + return new Parameter(targetRes.getLeafResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targerRes= target.getResource(); + return new Parameter(targerRes.getLeafResourceName(), + 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.getLeafResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + }; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java b/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java new file mode 100644 index 0000000..ab82982 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java @@ -0,0 +1,1704 @@ +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.Parameter; +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); + Type resStateType = getImplStateType(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. + MethodDeclaration constructor = declareConstructor(resourceNode, component, dependedRootComponentGraph, depends, langSpec); + + if (resourceNode.getResourceHierarchy().getParent() == null) { + // For each root resource + // Update the main component for this component. + updateMainComponent(mainComponent, mainConstructor, componentNode, constructor, depends, langSpec); + } + + // Declare the fields to refer to reference resources. + declareFieldsToReferenceResources(model, resourceNode, component, constructor, depends, langSpec); + + if (constructor.getParameters() == null || constructor.getParameters().size() == 0) { + component.removeMethod(constructor); + } else { + resourceConstructors.put(resourceNode.getResourceHierarchy(), constructor); + } + + // Declare the field to store the state in this resource. + if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { + declareStateField(resourceNode, resourceName, component, resStateType, constructorParams, langSpec); + } + + // Declare the getter method in this resource to obtain the state. + MethodDeclaration stateGetter = declareStateGetterMethod(resourceNode, component, resStateType, langSpec); + + // Declare the accessor method in the main component to call the getter method. + declareAccessorInMainComponent(mainComponent, resourceNode, stateGetter, langSpec); + + // Declare the getter methods in this resource to obtain the children resources. + declareChildGetterMethod(resourceNode, component, langSpec); + } + } + } + + // 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())) { + component = resourceComponents.get(resourceNode.getResourceHierarchy()); + } + 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 fields to refer to other resources in the parent/this component, and the state field in the parent component. + declareFieldsToReferToOtherResourcesAndStateFieldInParentComponent(resourceNode, component, parentComponent, constructorParams, langSpec); + + // Declare the getter method in this resource to obtain the state. + if (component == null) { + MethodDeclaration stateGetter = declareStateGetterMethodInParent(resourceNode, parentComponent, resStateType, langSpec); + + if (stateGetter != null) { + // 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 = langSpec.newMethodDeclaration(resourceName, true, null, null); + 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) { + if (param instanceof Term) { + if (((Term) param).getType() == null) { + ((Term) param).setType(var.getType()); + } + } else if (param instanceof Variable) { + if (((Variable) param).getType() == null) { + ((Variable) param).setType(var.getType()); + } + } + params.add(param.toImplementation(null)); + } 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 declareConstructor(ResourceNode resourceNode, TypeDeclaration component, Map> dependedRootComponentGraph, + List depends, ILanguageSpecific langSpec) { + // Declare a constructor in each component. + String resourceName = getComponentName(resourceNode.getResourceHierarchy(), langSpec); + MethodDeclaration constructor = langSpec.newMethodDeclaration(resourceName, true, null, null); + Block block = new Block(); + constructor.setBody(block); + component.addMethod(constructor); + + 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 (resourceNode.getOutSideResources().contains(cm.getResource()) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + for (Edge chToRes: resToCh.getDestination().getOutEdges()) { + if (chToRes.getDestination() instanceof ResourceNode) { + ResourceHierarchy dstRes = ((ResourceNode) chToRes.getDestination()).getResourceHierarchy(); + // Check if the output resource is outside of the channel scope. + boolean outsideOutputResource = false; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (((ResourceNode) chToRes.getDestination()).getInSideResources().contains(cm.getResource()) && cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + if ((((PushPullAttribute) ((DataFlowEdge) resToCh).getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) { + // for PUSH transfer +// 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 (!generatesComponent(dstRes)) { + dstRes = dstRes.getParent(); + } + if (!depends.contains(dstRes)) depends.add(dstRes); + } + } + } + } + for (Edge chToRes: resourceNode.getInEdges()) { + for (Edge resToCh: chToRes.getSource().getInEdges()) { + ResourceHierarchy srcRes = ((ResourceNode) resToCh.getSource()).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 (((ResourceNode) resToCh.getSource()).getOutSideResources().contains(cm.getResource()) && 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 (resourceNode.getInSideResources().contains(cm.getResource()) && 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 (!generatesComponent(srcRes)) { + srcRes = srcRes.getParent(); + } + 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); + } 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 declareFieldsToReferToOtherResourcesAndStateFieldInParentComponent(ResourceNode resourceNode, TypeDeclaration component, TypeDeclaration parentComponent, List> constructorParams, ILanguageSpecific langSpec) { + // Declare reference fields for push data transfer. + boolean noPullTransfer = true; + for (Edge resToCh : resourceNode.getOutEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + 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 (resourceNode.getOutSideResources().contains(cm.getResource()) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + for (Edge chToRes: re.getDestination().getOutEdges()) { + if (chToRes.getDestination() instanceof ResourceNode) { + ResourceHierarchy dstRes = ((ResourceNode) chToRes.getDestination()).getResourceHierarchy(); + // Check if the destination resource is outside of the channel scope. + boolean outsideOutputResource = false; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (((ResourceNode) chToRes.getDestination()).getInSideResources().contains(cm.getResource()) && cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + if ((((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) { + // Declare a field in the parent component to refer to the destination resource of push transfer. + if (!generatesComponent(dstRes)) { + dstRes = dstRes.getParent(); + } + String dstResName = getComponentName(dstRes, langSpec); + FieldDeclaration refFieldForPush = langSpec.newFieldDeclaration(new Type(dstResName, dstResName), langSpec.toVariableName(dstResName)); + VariableDeclaration refVarForPush = langSpec.newVariableDeclaration(new Type(dstResName, dstResName), langSpec.toVariableName(dstResName)); + if (component != null) { + // A component is created for this resource. + if (resourceNode.getResourceHierarchy() != dstRes) { + component.addField(refFieldForPush); + if (!outsideOutputResource) { + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy(), refVarForPush)); + } + } + } else { + // No component is created for this resource. + if (resourceNode.getParent().getResourceHierarchy() != dstRes) { + parentComponent.addField(refFieldForPush); + if (!outsideOutputResource) { + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), refVarForPush)); + } + } + } + if (outsideOutputResource) { + // When the reference to the destination resource can vary. + if (dstRes.getParent() != null) { + // Reference to its root resource. + String dstRootResName = getComponentName(dstRes.getRoot(), langSpec); + Type dstRootResType = new Type(dstRootResName, dstRootResName); + dstRootResName = langSpec.toVariableName(dstRootResName); + FieldDeclaration refRootFieldForPush = langSpec.newFieldDeclaration(dstRootResType, dstRootResName); + VariableDeclaration refRootVarForPush = langSpec.newVariableDeclaration(dstRootResType, dstRootResName); + if (component != null) { + // A component is created for this resource. + boolean existsField = false; + for (FieldDeclaration field: component.getFields()) { + if (dstRootResName.equals(field.getName())) { + existsField = true; + break; + } + } + if (!existsField) { + component.addField(refRootFieldForPush); + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy(), refRootVarForPush)); + } + } else { + // No component is created for this resource. + 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(), refRootVarForPush)); + } + } + } + } + } + } + } + } + // Declare reference fields for pull data transfer. + for (Edge chToRes : resourceNode.getInEdges()) { + for (Edge resToCh: chToRes.getSource().getInEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(ch); + // Check if the source resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().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 (resourceNode.getInSideResources().contains(cm.getResource()) && cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + if ((((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { + noPullTransfer = false; + // Declare a field in the parent/this component to refer to the source resource of pull transfer. + if (!generatesComponent(srcRes.getResourceHierarchy())) { + srcRes = srcRes.getParent(); + } + String srcResName = getComponentName(srcRes.getResourceHierarchy(), langSpec); + FieldDeclaration refFieldForPull = langSpec.newFieldDeclaration(new Type(srcResName, srcResName), langSpec.toVariableName(srcResName)); + VariableDeclaration refVarForPull = langSpec.newVariableDeclaration(new Type(srcResName, srcResName), langSpec.toVariableName(srcResName)); + if (component != null) { + // A component is created for this resource. + if (resourceNode.getResourceHierarchy() != srcRes.getResourceHierarchy()) { + component.addField(refFieldForPull); + if (!outsideInputResource) { + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy(), refVarForPull)); + } + } + } else { + // No component is created for this resource. + if (resourceNode.getParent().getResourceHierarchy() != srcRes.getResourceHierarchy()) { + parentComponent.addField(refFieldForPull); + if (!outsideInputResource) { + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), refVarForPull)); + } + } + } + if (outsideInputResource) { + // When the reference to the source resource can vary. + if (srcRes.getParent() != null) { + // Reference to its 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); + VariableDeclaration refRootVarForPull = langSpec.newVariableDeclaration(srcRootResType, srcRootResName); + if (component != null) { + // A component is created for this resource. + boolean existsField = false; + for (FieldDeclaration field: component.getFields()) { + if (srcRootResName.equals(field.getName())) { + existsField = true; + break; + } + } + if (!existsField) { + component.addField(refRootFieldForPull); + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy(), refRootVarForPull)); + } + } else { + // No component is created for this resource. + 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(), refRootVarForPull)); + } + } + } + } + } + } + } + // Declare the state field in the parent component. + if (component == null) { + ResourceHierarchy res = resourceNode.getResourceHierarchy(); + if (((StoreAttribute) resourceNode.getAttribute()).isStored() && noPullTransfer && res.getNumParameters() == 0) { + String resName = langSpec.toVariableName(getComponentName(res, langSpec)); + boolean existsField = false; + for (FieldDeclaration field: parentComponent.getFields()) { + if (resName.equals(field.getName())) { + existsField = true; + break; + } + } + if (!existsField) { + FieldDeclaration stateField = langSpec.newFieldDeclaration(res.getResourceStateType(), resName); + parentComponent.addField(stateField); + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), langSpec.newVariableDeclaration(res.getResourceStateType(), resName))); + } + } + } + } + + + private MethodDeclaration declareStateGetterMethod(ResourceNode resourceNode, TypeDeclaration component, Type resStateType, ILanguageSpecific langSpec) { + // Declare the getter method of the resource state. + MethodDeclaration stateGetter = langSpec.newMethodDeclaration(getterOfResourceState, resStateType); + component.addMethod(stateGetter); + + if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { + fillStateGetterMethod(stateGetter, resourceNode.getResourceHierarchy(), resStateType, langSpec); + } else { + // invocations to other getter methods when at least one incoming data-flow edges is PULL-style. + addOtherGetterInvocationsToStateGatter(stateGetter, resourceNode, langSpec); + } + + return stateGetter; + } + + private MethodDeclaration declareStateGetterMethodInParent(ResourceNode resourceNode, TypeDeclaration parentComponent, Type resStateType, ILanguageSpecific langSpec) { + // Check duplication. + String getterName = getterPrefix + getComponentName(resourceNode.getResourceHierarchy(), langSpec); + for (MethodDeclaration method: parentComponent.getMethods()) { + if (method.getName().equals(getterName)) return null; + } + + // Declare the getter method of the resource state. + 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++; + } + MethodDeclaration stateGetter = null; + if (getterParams.size() == 0) { + stateGetter = new MethodDeclaration(getterName, resStateType); + } else { + stateGetter = new MethodDeclaration(getterName, false, resStateType, getterParams); + } + if (parentComponent != null) { + parentComponent.addMethod(stateGetter); + } + + if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { + fillChildGetterMethod(stateGetter, resourceNode.getResourceHierarchy(), resourceNode.getResourceHierarchy().getParent().getResourceStateType(), langSpec); + } else { + addOtherGetterInvocationsToStateGatter(stateGetter, resourceNode, langSpec); + } + + return stateGetter; + } + + private void addOtherGetterInvocationsToStateGatter(MethodDeclaration stateGetter, ResourceNode resourceNode, + ILanguageSpecific langSpec) { + boolean isContainedPush = false; + DataTransferChannel ch = null; + HashMap inputResourceToStateAccessor = new HashMap<>(); + for (Edge chToRes: resourceNode.getInEdges()) { + DataTransferChannel ch2 = ((ChannelNode) chToRes.getSource()).getChannel(); + for (Edge resToCh: chToRes.getSource().getInEdges()) { + DataFlowEdge dIn = (DataFlowEdge) resToCh; + ChannelMember in = null; + for (ChannelMember cm: ch2.getInputChannelMembers()) { + if (((ResourceNode) dIn.getSource()).getOutSideResources().contains(cm.getResource())) { + in = cm; + break; + } + } + if (((PushPullAttribute) dIn.getAttribute()).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()).getKey().toImplementation(sideEffects); + stateGetter.addStatement(sideEffects[0] + langSpec.getReturnStatement(curState) + langSpec.getStatementDelimiter()); + } else { + // At least one incoming edge is in PUSH-style. + String curState = ch.deriveUpdateExpressionOf(out, getPullAccessor(), inputResourceToStateAccessor).getKey().toImplementation(sideEffects); + stateGetter.addStatement(sideEffects[0] + langSpec.getReturnStatement(curState) + langSpec.getStatementDelimiter()); + } + break; + } + } + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork + | InvalidMessage | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + } + + private void declareChildGetterMethod(ResourceNode resourceNode, TypeDeclaration component, ILanguageSpecific langSpec) { + // Declare the getter methods in this resource to obtain the children resources. + Set children = new HashSet<>(); + for (ResourceNode child: resourceNode.getChildren()) { + if (generatesComponent(child.getResourceHierarchy())) { + // A component for the child is generated. + if (!children.contains(child.getResourceHierarchy())) { + children.add(child.getResourceHierarchy()); + 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 = getComponentName(resourceNode.getResourceHierarchy(), langSpec); + 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 srcResPath = ((ResourceNode) re.getSource()).getOutSideResource(ch); + String srcResComponentName = getComponentName(srcResPath.getResourceHierarchy(), langSpec); + String srcResName = langSpec.toVariableName(srcResComponentName); + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().equals(srcResPath) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + // Check if the output resource is outside of the channel scope. + boolean outsideOutputResource = false; + ResourcePath dstResPath = null; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (resourceNode.getInSideResources().contains(cm.getResource())) { + dstResPath = cm.getResource(); + if (cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + } + if ((((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) { + // for push data transfer + + // Declare an update method in the type of the destination resource. + ArrayList parameters = new ArrayList<>(); + int v = 1; + for (Expression exp: dstResPath.getPathParams()) { + if (exp instanceof Variable) { + Variable pathVar = (Variable) exp; + String varName = "self" + (v > 1 ? v : ""); +// String varName = pathVar.getName(); + VariableDeclaration pathParam = langSpec.newVariableDeclaration(pathVar.getType(), varName); + parameters.add(pathParam); // A path parameter to identify the self resource. + } + v++; + } + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + VariableDeclaration chParam = langSpec.newVariableDeclaration(selVar.getType(), selVar.getName()); + parameters.add(chParam); // A channel parameter to specify the context of the collaboration. + } + } + parameters.add(langSpec.newVariableDeclaration(srcResPath.getResourceStateType(), srcResPath.getLeafResourceName())); // The state of the source resource to carry the data-flow. + // For the refs. + for (ResourcePath ref: ch.getReferenceResources()) { + if (!resourceNode.getInSideResources().contains(ref)) { + parameters.add(langSpec.newVariableDeclaration(ref.getResourceStateType(), ref.getLeafResourceName())); + } + } + MethodDeclaration update = null; + if (component != null) { + for (MethodDeclaration method: component.getMethods()) { + if (method.getName().equals(updateMethodPrefix + from + srcResComponentName)) { + update = method; + break; + } + } + if (update == null) { + update = langSpec.newMethodDeclaration(updateMethodPrefix + from + srcResComponentName, false, null, parameters); + component.addMethod(update); + } + } else if (parentComponent != null) { + for (MethodDeclaration method: parentComponent.getMethods()) { + if (method.getName().equals(updateMethodPrefix + resComponentName + from + srcResComponentName)) { + update = method; + break; + } + } + if (update == null) { + update = langSpec.newMethodDeclaration(updateMethodPrefix + resComponentName + from + srcResComponentName, false, null, parameters); + 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 = null; + if (ch.getReferenceChannelMembers().size() == 0) { + updateExp = ch.deriveUpdateExpressionOf(out, getPushAccessor()).getKey(); + } else { + // if there exists one or more reference channel member. + HashMap inputResourceToStateAccessor = new HashMap<>(); + for (ChannelMember in: ch.getInputChannelMembers()) { + inputResourceToStateAccessor.put(in, getPushAccessor()); + } + for (ChannelMember c: ch.getReferenceChannelMembers()) { + inputResourceToStateAccessor.put(c, getRefAccessor()); + } + updateExp = ch.deriveUpdateExpressionOf(out, getPushAccessor(), inputResourceToStateAccessor).getKey(); + } + // 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 update method. + String[] sideEffects = new String[] {""}; + String newState = updateExp.toImplementation(sideEffects); + int numOfOutResourcesWithTheSameHierarchy = 0; + for (ResourcePath outResPath: ch.getOutputResources()) { + if (outResPath.getResourceHierarchy().equals(outRes)) { + numOfOutResourcesWithTheSameHierarchy++; + } + } + String updateStatement = ""; + if (generatesComponent(outRes)) { + if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { + updateStatement = sideEffects[0]; + } else { + updateStatement = sideEffects[0] + langSpec.getFieldAccessor(fieldOfResourceState) + langSpec.getAssignment() + newState + langSpec.getStatementDelimiter(); // this.value = ... + } + } else { + if (sideEffects[0] != null) { + updateStatement = sideEffects[0]; + String resourceName = langSpec.toVariableName(getComponentName(outRes, langSpec)); + updateStatement = updateStatement.replace(langSpec.getFieldAccessor(fieldOfResourceState), langSpec.getFieldAccessor(resourceName)); + } + if (DataConstraintModel.typeList.isAncestorOf(resourceNode.getParent().getResourceStateType())) { + Term selector = new Term(DataConstraintModel.set); + selector.addChild(new Constant(langSpec.getFieldAccessor(fieldOfResourceState))); + selector.addChild(new Variable(update.getParameters().get(update.getParameters().size() - 2).getName())); + selector.addChild(new Constant(newState)); + String[] sideEffects2 = new String[] {""}; + String newList = selector.toImplementation(sideEffects2); + updateStatement += 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(update.getParameters().get(update.getParameters().size() - 2).getName())); + selector.addChild(new Constant(newState)); + String[] sideEffects2 = new String[] {""}; + String newMap = selector.toImplementation(sideEffects2); + updateStatement += sideEffects2[0]; + } else if (!(updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect())) { + String resourceName = langSpec.toVariableName(getComponentName(outRes, langSpec)); + updateStatement += langSpec.getFieldAccessor(resourceName) + langSpec.getAssignment() + newState + langSpec.getStatementDelimiter(); + } + } + if (numOfOutResourcesWithTheSameHierarchy == 1) { + update.addFirstStatement(updateStatement); + } else { + Term conditions = null; + int i = 1; + Map>> resourcePaths = ch.fillOutsideResourcePaths(out, JerseyCodeGenerator.pushAccessor); + for (Expression pathParam: out.getResource().getPathParams()) { + if (pathParam instanceof Variable) { + String selfParamName = ((Variable) pathParam).getName(); + Expression arg = null; + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + if (selVar.getName().equals(selfParamName)) { + arg = selVar; + break; + } + } + } + if (arg == null) { + ResourcePath filledPath = resourcePaths.get(out).getKey(); + arg = filledPath.getPathParams().get(i - 1); + } + Term condition = new Term(DataConstraintModel.eq, new Expression[] { + new Parameter("self" + (i > 1 ? i : "")), + arg}); + if (conditions == null) { + conditions = condition; + } else { + conditions = new Term(DataConstraintModel.and, new Expression[] { + conditions, + condition}); + } + } + i++; + } + update.addFirstStatement(langSpec.getIfStatement(conditions, 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 + || (resToCh.getDestination().getIndegree() == 1 && ch.getInputChannelMembers().iterator().next().getStateTransition().isRightPartial())) { + // If incoming edges are multiple, or the current state of an input member is needed. + if (langSpec.declareField()) { + // Declare the cache field. + FieldDeclaration cacheField = langSpec.newFieldDeclaration( + srcResPath.getResourceStateType(), + srcResPath.getLeafResourceName(), + langSpec.getFieldInitializer(srcResPath.getResourceStateType(), srcResPath.getResourceHierarchy().getInitialValue())); + if (component != null) { + component.addField(cacheField); + } else if (parentComponent != null){ + parentComponent.addField(cacheField); + } + + } + // Update the cache field. + String cacheStatement = langSpec.getFieldAccessor(langSpec.toVariableName(srcResName)) + langSpec.getAssignment() + langSpec.toVariableName(srcResName) + langSpec.getStatementDelimiter(); + if (update.getBody() == null || !update.getBody().getStatements().contains(cacheStatement)) { + update.addStatement(cacheStatement); + } + } + + 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(srcResPath)) { + // 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; + ChannelMember in = null; + Set outsideInputMembers2 = new HashSet<>(); + for (ChannelMember cm: ch2.getInputChannelMembers()) { + if (resourceNode.getOutSideResources().contains(cm.getResource())) { + if (cm.isOutside()) { + outsideInputResource2 = true; // Regarded as pull transfer. + } + in = cm; + } + if (cm.isOutside()) { + outsideInputMembers2.add(cm); + } + } + for (Edge chToRes2: resToCh2.getDestination().getOutEdges()) { + ResourceNode dstNode = ((ResourceNode) chToRes2.getDestination()); + // Check if the output resource is outside of the channel scope. + boolean outsideOutputResource2 = false; + ChannelMember out = null; + for (ChannelMember cm: ch2.getOutputChannelMembers()) { + if (dstNode.getInSideResources().contains(cm.getResource())) { + out = cm; + if (cm.isOutside()) { + outsideOutputResource2 = true; + break; + } + } + } + if ((((PushPullAttribute) dOut.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource2) || outsideOutputResource2) { + // PUSH transfer + List params = new ArrayList<>(); + // Values of path parameters. + for (Expression pathParam: out.getResource().getPathParams()) { + if (pathParam instanceof Variable) { + Variable pathVar = (Variable) pathParam; + params.add(pathVar.getName()); + } + } + // Values of channel parameters. + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + params.add(selVar.getName()); + } + } + // Value of the source side (input side) resource. + ResourceHierarchy srcRes2 = resourceNode.getResourceHierarchy(); + if (generatesComponent(srcRes2)) { + params.add(langSpec.getFieldAccessor(fieldOfResourceState)); + } else { + params.add(langSpec.getFieldAccessor(langSpec.toVariableName(srcRes2.getResourceName()))); + srcRes2 = srcRes2.getParent(); + } + Map> referredResources = new HashMap<>(); + 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.getLeafResourceName(); + 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.addStatement(sideEffects[0] + langSpec.getVariableDeclaration(refTypeName, refVarName) + langSpec.getAssignment() + refExp + langSpec.getStatementDelimiter()); + } + params.add(refVarName); + } + } + String updateMethodName = null; + ResourceHierarchy dstRes = dstNode.getResourceHierarchy(); + if (!generatesComponent(dstRes)) { + updateMethodName = updateMethodPrefix + getComponentName(dstRes, langSpec) + from + resComponentName; + dstRes = dstRes.getParent(); + } else { + updateMethodName = updateMethodPrefix + from + resComponentName; + } + String dstCompName = langSpec.toVariableName(getComponentName(dstRes, langSpec)); + if (!outsideOutputResource2) { + // The destination resource is not outside. + if (srcRes2 != dstRes) { + update.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, params) + + langSpec.getStatementDelimiter()); // this.dst.updateDstFromSrc(value, refParams); + } else { + update.addStatement(langSpec.getMethodInvocation(updateMethodName, params) + + langSpec.getStatementDelimiter()); // this.updateDstFromSrc(value, refParams); + } + } else { + // Use the reference field to refer to outside destination resource. + update.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, params) + + langSpec.getStatementDelimiter()); // this.dst.updateDstFromSrc(value, refParams); + } + } + } + if (outsideInputMembers2.size() > 0) { + if (!generatesComponent(resourceNode.getResourceHierarchy())) { + // srcRes2 does not have a component. + ResourcePath srcRes2 = resourceNode.getOutSideResource(ch2); + 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.getInputChannels()) { + 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<>(); + // The path parameters are not to be passed to the input method of each resource (resInputParams) + // because they are always equal to either channel selectors or message parameters. + + // Channel parameters to specify the context of the collaboration. + int v = 1; + for (Selector selector: ch.getSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + resInputParams.add(langSpec.newVariableDeclaration(selVar.getType(), selVar.getName())); + mainInputParams.add(langSpec.newVariableDeclaration(selVar.getType(), selVar.getName())); + } else if (selector.getExpression() instanceof Term) { + Term var = (Term) selector.getExpression(); + resInputParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v)); + mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + if (ch.getParent() != null) { + for (Selector selector: ch.getParent().getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + mainInputParams.add(langSpec.newVariableDeclaration(selVar.getType(), selVar.getName())); + } else if (selector.getExpression() instanceof Term) { + Term var = (Term) selector.getExpression(); + mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + } + // Message parameters to carry the data-flows. + for (Map.Entry varEnt: message.getVariables().entrySet()) { + Variable var = varEnt.getValue(); + String refVarName = null; + for (ChannelMember refCm: ((DataTransferChannel) ch).getReferenceChannelMembers()) { + Expression varExp = refCm.getStateTransition().getMessageExpression().getSubTerm(varEnt.getKey()); + if (varExp != null && varExp instanceof Variable) { + if (refCm.getStateTransition().getCurStateExpression().contains(varExp)) { + refVarName = refCm.getResource().getLeafResourceName(); + break; + } + } + } + if (refVarName != null) { + // var has come from a reference resource. + resInputParams.add(langSpec.newVariableDeclaration(var.getType(), refVarName)); + } else { + // var has not come from a reference resource. + resInputParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + boolean bExists = false; + for (VariableDeclaration mainParam: mainInputParams) { + if (mainParam.getName().equals(var.getName()) ) { + bExists = true; + break; + } + } + if (!bExists) { + mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + } + } + } + String inputMethodName = ((Term) message).getSymbol().getImplName(); + if (((DataTransferChannel) ch).getOutputChannelMembers().size() > 1) { + inputMethodName += _for + getComponentName(out.getResource().getResourceHierarchy(), langSpec); + } + if (component != null) { + // A component is created for this resource. + for (MethodDeclaration method: component.getMethods()) { + if (method.getName().equals(inputMethodName)) { + input = method; + break; + } + } + if (input == null) { + input = langSpec.newMethodDeclaration(inputMethodName, false, null, resInputParams); + component.addMethod(input); + } + } else if (parentComponent != null) { + // No component is created for this resource. + for (MethodDeclaration method: parentComponent.getMethods()) { + if (method.getName().equals(inputMethodName)) { + input = method; + break; + } + } + if (input == null) { + input = langSpec.newMethodDeclaration(inputMethodName, false, null, resInputParams); + parentComponent.addMethod(input); + } + } + + // Declare the accessor in the main component to call the input method. + String messageSymbol = ((Term) message).getSymbol().getImplName(); + inputAccessor = getMethod(mainComponent, messageSymbol); + if (inputAccessor == null) { + inputAccessor = langSpec.newMethodDeclaration(messageSymbol, 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++; + } + } + String inputMethodName = ((Variable) message).getName(); + if (((DataTransferChannel) ch).getOutputChannelMembers().size() > 1) { + inputMethodName += _for + getComponentName(out.getResource().getResourceHierarchy(), langSpec); + } + if (component != null) { + // A component is created for this resource. + for (MethodDeclaration method: component.getMethods()) { + if (method.getName().equals(inputMethodName)) { + input = method; + break; + } + } + if (input == null) { + if (resInputParams.size() == 0) { + input = langSpec.newMethodDeclaration(inputMethodName, null); + } else { + input = langSpec.newMethodDeclaration(inputMethodName, false, null, resInputParams); + } + component.addMethod(input); + } + } else if (parentComponent != null) { + // No component is created for this resource. + for (MethodDeclaration method: parentComponent.getMethods()) { + if (method.getName().equals(inputMethodName)) { + input = method; + break; + } + } + if (input == null) { + if (resInputParams.size() == 0) { + input = langSpec.newMethodDeclaration(inputMethodName, null); + } else { + input = langSpec.newMethodDeclaration(inputMethodName, false, null, resInputParams); + } + parentComponent.addMethod(input); + } + } + + // Declare the accessor in the main component to call the input method. + String messageSymbol = ((Variable) message).getName(); + inputAccessor = getMethod(mainComponent, messageSymbol); + if (inputAccessor == null) { + if (mainInputParams.size() == 0) { + inputAccessor = langSpec.newMethodDeclaration(messageSymbol, null); + } else { + inputAccessor = langSpec.newMethodDeclaration(messageSymbol, false, null, mainInputParams); + } + mainComponent.addMethod(inputAccessor); + } + } + + // Add an invocation to the accessor method. + if (inputAccessor != null) { + for (ChannelMember rc: ((DataTransferChannel) ch).getReferenceChannelMembers()) { + // For each reference channel member, get the current state of the reference side resource by pull data transfer. + ResourcePath ref = rc.getResource(); + if (!out.getResource().equals(ref)) { + String refVarName = ref.getLeafResourceName(); + Expression refGetter = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(ref, null); + String[] sideEffects = new String[] {""}; + String refExp = refGetter.toImplementation(sideEffects); + String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); + inputAccessor.addFirstStatement(sideEffects[0] + refTypeName + " " + refVarName + " = " + refExp + ";"); + } + } + 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}); + // Values of channel parameters. + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + if (!args.contains(selVar.getName())) { + args.add(selVar.getName()); + } + } + } + // Values of message parameters. + if (message instanceof Term) { + for (Map.Entry varEnt: message.getVariables().entrySet()) { + String refVarName = null; + for (ChannelMember rc: ((DataTransferChannel) ch).getReferenceChannelMembers()) { + Expression varExp = rc.getStateTransition().getMessageExpression().getSubTerm(varEnt.getKey()); + if (varExp != null && rc.getStateTransition().getCurStateExpression().contains(varExp)) { + refVarName = rc.getResource().getLeafResourceName(); + break; + } + } + if (refVarName != null) { + if (!args.contains(refVarName)) { + args.add(refVarName); + } + } else { + if (!args.contains(varEnt.getValue().getName())) args.add(varEnt.getValue().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, getRefAccessor()).getKey(); + // 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 = ""; + if (sideEffects[0] != null) { + updateStatement = sideEffects[0]; + String resourceName = langSpec.toVariableName(getComponentName(resource, langSpec)); + updateStatement = updateStatement.replace(langSpec.getFieldAccessor(fieldOfResourceState), langSpec.getFieldAccessor(resourceName)); + } + 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 += 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 += sideEffects2[0]; + } else if (!(updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect())) { + String resourceName = langSpec.toVariableName(getComponentName(resource, langSpec)); + updateStatement += 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(); + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource2 = false; + ChannelMember in = null; + Set outsideInputMembers2 = new HashSet<>(); + for (ChannelMember cm: ch2.getInputChannelMembers()) { + if (resourceNode.getOutSideResources().contains(cm.getResource())) { + if (cm.isOutside()) { + outsideInputResource2 = true; // Regarded as pull transfer. + } + in = cm; + } + if (cm.isOutside()) { + outsideInputMembers2.add(cm); + } + } + for (Edge chToRes: resToCh.getDestination().getOutEdges()) { + ResourceNode dstNode = ((ResourceNode) chToRes.getDestination()); + // Check if the output resource is outside of the channel scope. + boolean outsideOutputResource2 = false; + ChannelMember out2 = null; + for (ChannelMember cm: ch2.getOutputChannelMembers()) { + if (dstNode.getInSideResources().contains(cm.getResource())) { + out2 = cm; + if (cm.isOutside()) { + outsideOutputResource2 = true; + break; + } + } + } + if ((((PushPullAttribute) dOut.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource2) || outsideOutputResource2) { + // PUSH transfer + Map> referredResources = new HashMap<>(); + List params = new ArrayList<>(); + // Values of path parameters. + for (Expression pathParam: out2.getResource().getPathParams()) { + if (pathParam instanceof Variable) { + Variable pathVar = (Variable) pathParam; + params.add(pathVar.getName()); + } + } + // Values of channel parameters. + for (Selector selector: ch2.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + params.add(selVar.getName()); + } + } + // Value of the source side (input side) resource. + ResourceHierarchy srcRes = resourceNode.getResourceHierarchy(); + if (generatesComponent(srcRes)) { + params.add(langSpec.getFieldAccessor(fieldOfResourceState)); + } else { + params.add(langSpec.getFieldAccessor(langSpec.toVariableName(srcRes.getResourceName()))); + srcRes = srcRes.getParent(); + } + 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 (!resourceNode.getOutSideResources().contains(ref)) { + String refVarName = ref.getLeafResourceName(); + 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.addStatement(sideEffects[0] + langSpec.getVariableDeclaration(refTypeName, refVarName) + langSpec.getAssignment() + refExp + langSpec.getStatementDelimiter()); + } + params.add(refVarName); + } + } + String updateMethodName = null; + ResourceHierarchy dstRes = dstNode.getResourceHierarchy(); + if (!generatesComponent(dstRes)) { + updateMethodName = updateMethodPrefix + getComponentName(dstRes, langSpec) + from + resComponentName; + dstRes = dstRes.getParent(); + } else { + updateMethodName = updateMethodPrefix + from + resComponentName; + } + String dstCompName = langSpec.toVariableName(getComponentName(dstRes, langSpec)); + if (!outsideOutputResource2) { + // The destination resource is not outside. + if (srcRes != dstRes) { + input.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, params) + + langSpec.getStatementDelimiter()); // this.dst.updateDstFromSrc(value, refParams); + } else { + input.addStatement(langSpec.getMethodInvocation(updateMethodName, params) + + langSpec.getStatementDelimiter()); // this.updateDstFromSrc(value, refParams); + } + } else { + // Use the reference field to refer to outside destination resource. + input.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, params) + + langSpec.getStatementDelimiter()); // this.dst.updateDstFromSrc(value, refParams); + } + } + } + if (outsideInputMembers2.size() > 0) { + if (!generatesComponent(resourceNode.getResourceHierarchy())) { + ResourcePath srcRes2 = resourceNode.getOutSideResource(ch2); + 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..6c86540 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/generators/ILanguageSpecific.java @@ -0,0 +1,48 @@ +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.Term; +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 receiverName, List parameters); + String getMethodInvocation(String receiverName, String methodName); + String getMethodInvocation(String receiverName, String methodName, List parameters); + String getConstructorInvocation(String componentName, List parameters); + String getReturnStatement(String returnValue); + String getIfStatement(Term condition, String block); + 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..e86cc63 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/JavaCodeGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/JavaCodeGenerator.java @@ -1,12 +1,17 @@ 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.Annotation; import code.ast.Block; import code.ast.CompilationUnit; import code.ast.FieldDeclaration; @@ -19,6 +24,7 @@ import models.algebra.Expression; import models.algebra.Field; import models.algebra.Parameter; +import models.algebra.Position; import models.algebra.Symbol; import models.algebra.Term; import models.algebra.Type; @@ -26,12 +32,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 +69,662 @@ 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<>(); + Map> updates = new HashMap<>(); + Map> inputs = new HashMap<>(); + 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 (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))); - } - } - } + // For each resource node. + for (ResourceNode resourceNode: resources) { + TypeDeclaration component = null; + Set depends = new HashSet<>(); 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; + if (generatesComponent(resourceNode.getResourceHierarchy())) { + boolean f = false; + String resourceName = getComponentName(resourceNode.getResourceHierarchy()); + + component = resourceComponents.get(resourceNode.getResourceHierarchy()); + if (component == null) { + // Add compilation unit for each resource. + component = new TypeDeclaration(resourceName); + resourceComponents.put(resourceNode.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. + if (resourceNode.getResourceHierarchy().getParent() == null) { + // For a root resource + String fieldInitializer = "new " + resourceName + "("; + for (Edge resToCh: resourceNode.getOutEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { + for (Edge chToRes: re.getDestination().getOutEdges()) { + ResourceHierarchy dstRes = ((ResourceNode) chToRes.getDestination()).getResourceHierarchy(); + String resName = getComponentName(dstRes); + depends.add(dstRes); + fieldInitializer += toVariableName(resName) + ","; + f = true; + } + } + } + for (Edge chToRes : resourceNode.getInEdges()) { + for (Edge resToCh: chToRes.getSource().getInEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + ResourceHierarchy srcRes = ((ResourceNode) re.getSource()).getResourceHierarchy(); + String resName = getComponentName(srcRes); + if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { + depends.add(srcRes); + fieldInitializer += toVariableName(resName) + ","; + f = true; + } else { + ChannelNode cn = (ChannelNode) re.getDestination(); + if (cn.getIndegree() > 1 + || (cn.getIndegree() == 1 && cn.getChannel().getInputChannelMembers().iterator().next().getStateTransition().isRightPartial())) { + // Declare a field to cache the state of the source resource in the type of the destination resource. + ResourceHierarchy cacheRes = ((ResourceNode) re.getSource()).getResourceHierarchy(); + component.addField(new FieldDeclaration( + cacheRes.getResourceStateType(), cacheRes.getResourceName(), getInitializer(cacheRes))); + } + } + } + } + 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); + String resName = getComponentName(dependedRes); + fieldInitializer += toVariableName(resName) + ","; + f = true; + } + } + } + for (Channel ch : model.getChannels()) { + DataTransferChannel c = (DataTransferChannel) ch; + if (resourceNode.getOutSideResource(c) != null) { + for (ResourcePath res: c.getReferenceResources()) { + if (!refs.contains(res) && !depends.contains(res.getResourceHierarchy())) { + refs.add(res); + String refResName = res.getLeafResourceName(); + fieldInitializer += toVariableName(refResName) + ","; + f = true; + } + } + } + } + if (f) fieldInitializer = fieldInitializer.substring(0, fieldInitializer.length() - 1); + fieldInitializer += ")"; + FieldDeclaration field = new FieldDeclaration(new Type(resourceName, resourceName), resourceNode.getResourceName()); + mainComponent.addField(field); + Block mainConstructorBody = mainConstructor.getBody(); + if (mainConstructorBody == null) { + mainConstructorBody = new Block(); + mainConstructor.setBody(mainConstructorBody); + } + mainConstructorBody.addStatement(resourceNode.getResourceName() + " = " + fieldInitializer + ";"); + } + + // Declare the field to store the state in the type of each resource. + if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { + ResourceHierarchy res = resourceNode.getResourceHierarchy(); + Set children = res.getChildren(); + if (children == null || children.size() == 0) { + // leaf resource. + Type fieldType = getImplStateType(res); + component.addField(new FieldDeclaration(fieldType, "value", getInitializer(res))); + } else { + ResourceHierarchy child = children.iterator().next(); + if (children.size() == 1 && child.getNumParameters() > 0) { + // map or list. + component.addField(new FieldDeclaration(getImplStateType(res), "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 method to obtain the resource state in this component. + MethodDeclaration stateGetter = new MethodDeclaration("getValue", getImplStateType(resourceNode.getResourceHierarchy())); + component.addMethod(stateGetter); + + // Declare the accessor method in the main type to call the getter method. + declareAccessorMethodInMainComponent(resourceNode, mainComponent); + + // Declare the getter methods to obtain the children resources. + Set children = new HashSet<>(); + for (ResourceNode child: resourceNode.getChildren()) { + if (generatesComponent(child.getResourceHierarchy())) { + // A component for the child is generated. + if (!children.contains(child.getResourceHierarchy())) { + children.add(child.getResourceHierarchy()); + 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); + } + } + } + } } - 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())); + // Declare the state field in the parent component. + if (component == null) { + // Declare reference fields for push/pull data transfer. + boolean noPullTransfer = true; + for (Edge chToRes : resourceNode.getInEdges()) { + for (Edge resToCh: chToRes.getSource().getInEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(ch); + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().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 (resourceNode.getInSideResources().contains(cm.getResource()) && cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + if ((((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { + noPullTransfer = false; } } - type.addMethod(new MethodDeclaration("update" + srcResName, false, typeVoid, vars)); + } + // Declare the state field in the parent component. + ResourceHierarchy res = resourceNode.getResourceHierarchy(); + if (((StoreAttribute) resourceNode.getAttribute()).isStored() && noPullTransfer && res.getNumParameters() == 0) { + String resName = getComponentName(res); + FieldDeclaration stateField = new FieldDeclaration(res.getResourceStateType(), toVariableName(resName)); + fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), stateField)); + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), new VariableDeclaration(res.getResourceStateType(), toVariableName(resName)))); + } + } + + // Declare the getter method to obtain the resource state in the parent component. + if (component == null) { + // No component is created for this resource. + String getterName = "get" + getComponentName(resourceNode.getResourceHierarchy()); + boolean bExists = false; + for (Map.Entry entry: getters) { + if (entry.getKey() == resourceNode.getParent().getResourceHierarchy() && entry.getValue().getName().equals(getterName)) { + bExists = true; + break; + } + } + if (!bExists) { + 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++; + } + Type resType = getImplStateType(resourceNode.getResourceHierarchy()); + MethodDeclaration stateGetter = null; + if (getterParams.size() == 0) { + stateGetter = new MethodDeclaration(getterName, resType); + } else { + stateGetter = new MethodDeclaration(getterName, false, resType, getterParams); + } + getters.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), stateGetter)); + + // Declare the accessor method in the main type to call the getter method. + declareAccessorMethodInMainComponent(resourceNode, mainComponent); + } + } + + // Declare reference fields for push data transfer. + for (Edge resToCh : resourceNode.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 (resourceNode.getOutSideResources().contains(cm.getResource()) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + for (Edge chToRes: re.getDestination().getOutEdges()) { + ResourceHierarchy dstRes = ((ResourceNode) chToRes.getDestination()).getResourceHierarchy(); + // Check if the output resource is outside of the channel scope. + boolean outsideOutputResource = false; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (((ResourceNode) chToRes.getDestination()).getInSideResources().contains(cm.getResource()) && cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + if ((((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) { + // Declare a field to refer to the destination resource of push transfer. + if (!generatesComponent(dstRes)) { + dstRes = dstRes.getParent(); + } + String dstResName = getComponentName(dstRes); + depends.add(dstRes); + FieldDeclaration dstRefField = new FieldDeclaration(new Type(dstResName, dstResName), toVariableName(dstResName)); + VariableDeclaration dstRefVar = new VariableDeclaration(new Type(dstResName, dstResName), toVariableName(dstResName)); + if (component != null) { + // A component is created for this resource. + if (resourceNode.getResourceHierarchy() != dstRes) { + component.addField(dstRefField); + if (!outsideOutputResource) { + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy(), dstRefVar)); + } + } + } else { + // No component is created for this resource. + if (resourceNode.getParent().getResourceHierarchy() != dstRes) { + fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), dstRefField)); + if (!outsideOutputResource) { + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), dstRefVar)); + } + } + } + if (outsideOutputResource) { + // When the reference to the destination resource can vary. + if (dstRes.getParent() != null) { + // Reference to root resource. + ResourceHierarchy dstRootRes = dstRes.getRoot(); + String dstRootResName = getComponentName(dstRootRes); + FieldDeclaration dstRootRefField = new FieldDeclaration(new Type(dstRootResName, dstRootResName), toVariableName(dstRootResName)); + VariableDeclaration dstRootRefVar = new VariableDeclaration(new Type(dstRootResName, dstRootResName), toVariableName(dstRootResName)); + if (component != null) { + // A component is created for this resource. + component.addField(dstRootRefField); + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy(), dstRootRefVar)); + } else { + // No component is created for this resource. + fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), dstRootRefField)); + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), dstRootRefVar)); + } + } + } + } + } + } + // Declare update methods for push data transfer and reference fields for pull data transfer. + for (Edge chToRes : resourceNode.getInEdges()) { + for (Edge resToCh: chToRes.getSource().getInEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(ch); + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().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; + ChannelMember out = null; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (resourceNode.getInSideResources().contains(cm.getResource())) { + out = cm; + if (cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + } + String srcResName = getComponentName(srcRes.getResourceHierarchy()); + ResourcePath srcRes2 = srcRes; + String srcResName2 = srcResName; + if (!generatesComponent(srcRes.getResourceHierarchy())) { + srcRes2 = srcRes.getParent(); + srcResName2 = 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()); + FieldDeclaration srcRefField = new FieldDeclaration(new Type(srcResName2, srcResName2), toVariableName(srcResName2)); + VariableDeclaration srcRefVar = new VariableDeclaration(new Type(srcResName2, srcResName2), toVariableName(srcResName2)); + if (component != null) { + // A component is created for this resource. + if (resourceNode.getResourceHierarchy() != srcRes2.getResourceHierarchy()) { + component.addField(srcRefField); + if (!outsideInputResource) { + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy(), srcRefVar)); + } + } + } else { + // No component is created for this resource. + if (resourceNode.getParent().getResourceHierarchy() != srcRes2.getResourceHierarchy()) { + fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), srcRefField)); + if (!outsideInputResource) { + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), srcRefVar)); + } + } + } + if (outsideInputResource) { + // When the reference to the source resource can vary. + if (srcRes2.getParent() != null) { + // Reference to its root resource. + ResourcePath srcRootRes = srcRes2.getRoot(); + String srcRootResName = getComponentName(srcRootRes.getResourceHierarchy()); + FieldDeclaration srcRootRefField = new FieldDeclaration(new Type(srcRootResName, srcRootResName), toVariableName(srcRootResName)); + VariableDeclaration srcRootRefVar = new VariableDeclaration(new Type(srcRootResName, srcRootResName), toVariableName(srcRootResName)); + if (component != null) { + // A component is created for this resource. + component.addField(srcRootRefField); + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy(), srcRootRefVar)); + } else { + // No component is created for this resource. + fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), srcRootRefField)); + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), srcRootRefVar)); + } + } + } + } else { + // Declare an update method in the type of the destination resource. + ArrayList params = new ArrayList<>(); + int v = 1; + for (Expression exp: out.getResource().getPathParams()) { + if (exp instanceof Variable) { + Variable pathVar = (Variable) exp; + String varName = "self" + (v > 1 ? v : ""); +// String varName = pathVar.getName(); + VariableDeclaration pathParam = new VariableDeclaration(pathVar.getType(), varName); + params.add(pathParam); // A path parameter to identify the self resource. + } + v++; + } + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + VariableDeclaration chParam = new VariableDeclaration(selVar.getType(), selVar.getName()); + params.add(chParam); // A channel parameter to specify the context of the collaboration. + } + } + params.add(new VariableDeclaration(srcRes.getResourceStateType(), srcRes.getLeafResourceName())); // The state of the source resource to carry the data-flow. + for (ResourcePath ref: ch.getReferenceResources()) { + if (!ref.equals(resourceNode.getInSideResource(ch))) { + params.add(new VariableDeclaration(ref.getResourceStateType(), ref.getLeafResourceName())); + } + } + MethodDeclaration update = null; + if (component != null) { + // A component is created for this resource. + update = new MethodDeclaration("updateFrom" + srcResName, false, typeVoid, params); + component.addMethod(update); + } else { + // No component is created for this resource. + String resourceName = getComponentName(resourceNode.getResourceHierarchy()); + String updateMethodName = "update" + resourceName + "From" + srcResName; + Map nameToMethod = updates.get(resourceNode.getParent().getResourceHierarchy()); + if (nameToMethod == null) { + nameToMethod = new HashMap<>(); + updates.put(resourceNode.getParent().getResourceHierarchy(), nameToMethod); + } + if (nameToMethod.get(updateMethodName) == null) { + update = new MethodDeclaration(updateMethodName, false, typeVoid, params); + nameToMethod.put(updateMethodName, update); + } + } + } + } + } + // 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); + String resName = getComponentName(dependedRes); + FieldDeclaration refField = new FieldDeclaration(new Type(resName, resName), toVariableName(resName)); + VariableDeclaration refVar = new VariableDeclaration(new Type(resName, resName), toVariableName(resName)); + if (component != null) { + // A component is created for this resource. + component.addField(refField); + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy(), refVar)); + } else { + // No component is created for this resource. + fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), refField)); + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), refVar)); + } + } + } } } // 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() + ";"); + for (Channel ch : model.getChannels()) { + DataTransferChannel c = (DataTransferChannel) ch; + if (resourceNode.getOutSideResource(c) != null) { + for (ResourcePath res: c.getReferenceResources()) { + if (!refs.contains(res) && !depends.contains(res.getResourceHierarchy())) { + refs.add(res); + String refResName = getComponentName(res.getResourceHierarchy()); + FieldDeclaration refResField = new FieldDeclaration(new Type(refResName, refResName), res.getLeafResourceName()); + VariableDeclaration refResVar = new VariableDeclaration(new Type(refResName, refResName), res.getLeafResourceName()); + if (component != null) { + // A component is created for this resource. + component.addField(refResField); + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy(), refResVar)); + } else { + // No component is created for this resource. + fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), refResField)); + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), refResVar)); + } } } } } - 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())) { + + // Declare the input method in this component and the main component. + for (Channel ch : model.getInputChannels()) { + for (ChannelMember cm : ((DataTransferChannel) ch).getOutputChannelMembers()) { + if (resourceNode.getInSideResources().contains(cm.getResource())) { Expression message = cm.getStateTransition().getMessageExpression(); - if (message.getClass() == Term.class) { - ArrayList params = new ArrayList<>(); - for (Variable var: message.getVariables().values()) { - params.add(new VariableDeclaration(var.getType(), var.getName())); + if (message instanceof Term) { + // In each resource. + ArrayList resInputParams = new ArrayList<>(); + ArrayList mainInputParams = new ArrayList<>(); + // The path parameters are not to be passed to the input method of each resource (resInputParams) + // because they are always equal to either channel selectors or message parameters. + + // Channel parameters to specify the context of the collaboration. + int v = 1; + for (Selector selector: ch.getSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + resInputParams.add(new VariableDeclaration(selVar.getType(), selVar.getName())); + mainInputParams.add(new VariableDeclaration(selVar.getType(), selVar.getName())); + } else if (selector.getExpression() instanceof Term) { + Term var = (Term) selector.getExpression(); + resInputParams.add(new VariableDeclaration(var.getType(), "v" + v)); + mainInputParams.add(new VariableDeclaration(var.getType(), "v" + v)); + } + v++; } - MethodDeclaration input = new MethodDeclaration( - ((Term) cm.getStateTransition().getMessageExpression()).getSymbol().getImplName(), - false, typeVoid, params); - type.addMethod(input); - String str = ((Term) cm.getStateTransition().getMessageExpression()).getSymbol().getImplName(); - input = getMethod(mainType, str); + if (ch.getParent() != null) { + for (Selector selector: ch.getParent().getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + mainInputParams.add(new VariableDeclaration(selVar.getType(), selVar.getName())); + } else if (selector.getExpression() instanceof Term) { + Term var = (Term) selector.getExpression(); + mainInputParams.add(new VariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + } + // Message parameters to carry the data-flows. + for (Map.Entry varEnt: message.getVariables().entrySet()) { + Variable var = varEnt.getValue(); + String refVarName = null; + for (ChannelMember refCm: ((DataTransferChannel) ch).getReferenceChannelMembers()) { + Expression varExp = refCm.getStateTransition().getMessageExpression().getSubTerm(varEnt.getKey()); + if (varExp != null && varExp instanceof Variable) { + if (refCm.getStateTransition().getCurStateExpression().contains(varExp)) { + refVarName = refCm.getResource().getLeafResourceName(); + break; + } + } + } + if (refVarName != null) { + // var has come from a reference resource. + resInputParams.add(new VariableDeclaration(var.getType(), refVarName)); + } else { + // var has not come from a reference resource. + resInputParams.add(new VariableDeclaration(var.getType(), var.getName())); + boolean bExists = false; + for (VariableDeclaration mainParam: mainInputParams) { + if (mainParam.getName().equals(var.getName()) ) { + bExists = true; + break; + } + } + if (!bExists) { + mainInputParams.add(new VariableDeclaration(var.getType(), var.getName())); + } + } + } + String inputMethodName = ((Term) message).getSymbol().getImplName(); + if (((DataTransferChannel) ch).getOutputChannelMembers().size() > 1) { + inputMethodName += "For" + getComponentName(cm.getResource().getResourceHierarchy()); + } + MethodDeclaration input = new MethodDeclaration(inputMethodName, false, typeVoid, resInputParams); + if (component != null) { + // A component is created for this resource. + component.addMethod(input); + } else { + // No component is created for this resource. + Map nameToMethod = inputs.get(resourceNode.getParent().getResourceHierarchy()); + if (nameToMethod == null) { + nameToMethod = new HashMap<>(); + inputs.put(resourceNode.getParent().getResourceHierarchy(), nameToMethod); + } + if (nameToMethod.get(inputMethodName) == null) { + nameToMethod.put(inputMethodName, input); + } + } + + // In the main type. + String messageSymbol = ((Term) message).getSymbol().getImplName(); + input = getMethod(mainComponent, messageSymbol, mainInputParams); if (input == null) { - input = new MethodDeclaration(str, false, typeVoid, params); - mainType.addMethod(input); + input = new MethodDeclaration(messageSymbol, 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 +732,121 @@ } } } - } else if (message.getClass() == Variable.class) { - MethodDeclaration input = new MethodDeclaration( - ((Variable) cm.getStateTransition().getMessageExpression()).getName(), - false, typeVoid, null); - type.addMethod(input); - String str = ((Variable) cm.getStateTransition().getMessageExpression()).getName(); - input = getMethod(mainType, str); + } else if (message instanceof Variable) { + // In each resource. + ArrayList resInputParams = new ArrayList<>(); + ArrayList mainInputParams = new ArrayList<>(); + int 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++; + } + } + String inputMethodName = ((Variable) message).getName(); + if (((DataTransferChannel) ch).getOutputChannelMembers().size() > 1) { + inputMethodName += "For" + getComponentName(cm.getResource().getResourceHierarchy()); + } + MethodDeclaration input = null; + if (resInputParams.size() > 0) { + input = new MethodDeclaration(inputMethodName, false, typeVoid, resInputParams); + } else { + input = new MethodDeclaration(inputMethodName, false, typeVoid, null); + } + if (component != null) { + // A component is created for this resource. + component.addMethod(input); + } else { + // No component is created for this resource. + Map nameToMethod = inputs.get(resourceNode.getParent().getResourceHierarchy()); + if (nameToMethod == null) { + nameToMethod = new HashMap<>(); + inputs.put(resourceNode.getParent().getResourceHierarchy(), nameToMethod); + } + if (nameToMethod.get(inputMethodName) == null) { + nameToMethod.put(inputMethodName, input); + } + } + + // In the main type. + String messageSymbol = ((Variable) message).getName(); + input = getMethod(mainComponent, messageSymbol, mainInputParams); if (input == null) { - input = new MethodDeclaration(str, false, typeVoid, null); - mainType.addMethod(input); + if (mainInputParams.size() > 0) { + input = new MethodDeclaration(messageSymbol, false, typeVoid, mainInputParams); + } else { + input = new MethodDeclaration(messageSymbol, 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 update methods to the parent components. + for (Map.Entry> entry: updates.entrySet()) { + for (MethodDeclaration update: entry.getValue().values()) { + resourceComponents.get(entry.getKey()).addMethod(update); } - - // 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); + } + + // Add leaf input methods to the parent components. + for (Map.Entry> entry: inputs.entrySet()) { + for (MethodDeclaration input: entry.getValue().values()) { + resourceComponents.get(entry.getKey()).addMethod(input); + } + } + + // 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; + } + } + 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 +877,194 @@ } } - // 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(); - String initializer = null; - if (resId.getInitialValue() != null) { - initializer = resId.getInitialValue().toImplementation(new String[] {""}); + 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 (curResNode.getOutSideResources().contains(m.getResource())) { + topologicalSort(resNode, graph, dependedRootComponentGraph, visited, orderedList); + } + } + } + } + orderedList.add(0, curResNode); + } + + private static void declareAccessorMethodInMainComponent(ResourceNode resourceNode, TypeDeclaration mainComponent) { + MethodDeclaration getterAccessor = null; + List mainGetterParams = new ArrayList<>(); + int v = 1; + for (Expression param: resourceNode.getPrimaryResourcePath().getPathParams()) { + if (param instanceof Variable) { + Variable var = (Variable) param; + mainGetterParams.add(new VariableDeclaration(var.getType(), var.getName())); + } else if (param instanceof Term) { + Term var = (Term) param; + mainGetterParams.add(new VariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + if (mainGetterParams.size() > 0) { + getterAccessor = new MethodDeclaration("get" + getComponentName(resourceNode.getResourceHierarchy()), + false, + getImplStateType(resourceNode.getResourceHierarchy()), + mainGetterParams); } else { + getterAccessor = new MethodDeclaration("get" + getComponentName(resourceNode.getResourceHierarchy()), + getImplStateType(resourceNode.getResourceHierarchy())); + } + getterAccessor.setBody(new Block()); + Expression getState = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(resourceNode.getPrimaryResourcePath(), null); + getterAccessor.getBody().addStatement("return " + getState.toImplementation(new String[] {null}) + ";"); + mainComponent.addMethod(getterAccessor); + } + + 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(ResourceHierarchy res) { + Type stateType = res.getResourceStateType(); + String initializer = null; + if (res.getInitialValue() != null) { + initializer = res.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 +1115,209 @@ 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() + // use the cached value as the current state + return new Field(targetRes.getLeafResourceName(), + 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.getLeafResourceName(), + 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.getLeafResourceName(), + 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.getLeafResourceName(), 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; + } + } + }; + + static public IResourceStateAccessor refAccessor = 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("value", + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + return new Parameter(targetRes.getLeafResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + return new Parameter(targetRes.getLeafResourceName(), + 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.getLeafResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); } }; } diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java index 86cd28a..08bc6ec 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java @@ -4,18 +4,25 @@ 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.Parameter; 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 +31,16 @@ 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.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 +50,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 +61,640 @@ 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 (src.getOutSideResources().contains(cm.getResource()) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } } - if (update.getBody() == null || !update.getBody().getStatements().contains(updateStatement)) { - 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); - } - } - 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;"); + // Check if the output resource is outside of the channel scope. + boolean outsideOutputResource = out.isOutside(); + if ((pushPull.getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) { + // for push data transfer + MethodDeclaration update = null; + if (dstComponent == null) { + String dstParentResourceName = JavaCodeGenerator.getComponentName(dst.getResourceHierarchy().getParent()); + dstComponent = componentMap.get(dstParentResourceName); + update = getUpdateMethod(dstComponent, dstResourceName, 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);"); + update = getUpdateMethod(dstComponent, null, srcResourceName); } - } - } - // 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 (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 + ";"); + 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, JavaCodeGenerator.pushAccessor).getKey(); + } else { + // if there exists one or more reference channel member. + HashMap inputResourceToStateAccessor = new HashMap<>(); + for (ChannelMember in: ch.getInputChannelMembers()) { + inputResourceToStateAccessor.put(in, JavaCodeGenerator.pushAccessor); + } + for (ChannelMember c: ch.getReferenceChannelMembers()) { + inputResourceToStateAccessor.put(c, JavaCodeGenerator.refAccessor); + } + updateExp = ch.deriveUpdateExpressionOf(out, JavaCodeGenerator.pushAccessor, inputResourceToStateAccessor).getKey(); } - 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 + ";"); + // 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; + // Add statements to the update method. + String[] sideEffects = new String[] {""}; + String newState = updateExp.toImplementation(sideEffects); + int numOfOutResourcesWithTheSameHierarchy = 0; + for (ResourcePath outResPath: ch.getOutputResources()) { + if (outResPath.getResourceHierarchy().equals(outRes)) { + numOfOutResourcesWithTheSameHierarchy++; + } + } + String updateStatement = ""; + if (JavaCodeGenerator.generatesComponent(outRes)) { + if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { + updateStatement = sideEffects[0]; + } else { + updateStatement = sideEffects[0] + "this.value = " + newState + ";"; + } + } else { + if (sideEffects[0] != null) { + updateStatement = sideEffects[0]; + updateStatement = updateStatement.replace(".value", "." + JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(outRes))); + } + if (DataConstraintModel.typeList.isAncestorOf(outRes.getParent().getResourceStateType())) { + Term selector = new Term(DataConstraintModel.set); + selector.addChild(new Field("value")); + selector.addChild(new Variable(update.getParameters().get(update.getParameters().size() - 2).getName())); + selector.addChild(new Constant(newState)); + String[] sideEffects2 = new String[] {""}; + String newList = selector.toImplementation(sideEffects2); + updateStatement += sideEffects2[0]; + } else if (DataConstraintModel.typeMap.isAncestorOf(outRes.getParent().getResourceStateType())) { + Term selector = new Term(DataConstraintModel.insert); + selector.addChild(new Field("value")); + selector.addChild(new Variable(update.getParameters().get(update.getParameters().size() - 2).getName())); + selector.addChild(new Constant(newState)); + String[] sideEffects2 = new String[] {""}; + String newMap = selector.toImplementation(sideEffects2); + updateStatement += sideEffects2[0]; + } else if (!(updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect())) { + updateStatement += "this." + JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(outRes)) + " = " + newState + ";"; + } + } + if (numOfOutResourcesWithTheSameHierarchy == 1) { + update.addFirstStatement(updateStatement); + } else { + Term conditions = null; + int v = 1; + Map>> resourcePaths = ch.fillOutsideResourcePaths(out, JerseyCodeGenerator.pushAccessor); + for (Expression pathParam: out.getResource().getPathParams()) { + if (pathParam instanceof Variable) { + String selfParamName = ((Variable) pathParam).getName(); + Expression arg = null; + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + if (selVar.getName().equals(selfParamName)) { + arg = selVar; + break; + } + } + } + if (arg == null) { + ResourcePath filledPath = resourcePaths.get(out).getKey(); + arg = filledPath.getPathParams().get(v - 1); + } + Term condition = new Term(DataConstraintModel.eq, new Expression[] { + new Parameter("self" + (v > 1 ? v : "")), + arg}); + if (conditions == null) { + conditions = condition; + } else { + conditions = new Term(DataConstraintModel.and, new Expression[] { + conditions, + condition}); + } + } + v++; + } + String ifStatement = "if (" + conditions.toImplementation(new String[] {})+ ") {\n"; + update.addFirstStatement(ifStatement + "\t" + updateStatement.replace("\n", "\n\t") + "\n}"); + } } - } - 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 + || (resToCh.getDestination().getIndegree() == 1 && ch.getInputChannelMembers().iterator().next().getStateTransition().isRightPartial())) { + // update a cache of src side resource (when incoming edges are multiple) + String cacheStatement = "this." + JavaCodeGenerator.toVariableName(srcResourceName) + " = " + JavaCodeGenerator.toVariableName(srcResourceName) + ";"; + if (update.getBody() == null || !update.getBody().getStatements().contains(cacheStatement)) { + update.addStatement(cacheStatement); + } + } + 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 (JavaCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { + // dst has a component. + 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 { + // dst has no component. + String dstResName = JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(dst.getResourceHierarchy())); + if (model.isPrimitiveType(resourceType)) { + getter.addStatement("return " + dstResName + ";"); + } else { + // copy the current state to be returned as a 'value' + String implTypeName = resourceType.getImplementationTypeName(); + getter.addStatement("return new " + implTypeName + "(" + dstResName + ");"); + } + } + } 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 (src.getOutSideResources().contains(cm.getResource())) { + in = cm; + break; + } + } + String srcResName = null; + if (srcComponent == null) { + String srcParentResourceName = JavaCodeGenerator.getComponentName(src.getResourceHierarchy().getParent()); + srcComponent = componentMap.get(srcParentResourceName); + srcResName = srcResourceName; + } + for (MethodDeclaration srcUpdate: getUpdateMethods(srcComponent, srcResName)) { + ResourcePath dstRes = out.getResource(); + // Values of path parameters. + String pathParams = ""; + for (Expression pathParam: dstRes.getPathParams()) { + if (pathParam instanceof Variable) { + Variable pathVar = (Variable) pathParam; + pathParams += pathVar.getName() + ", "; + } + } + // Values of channel parameters. + String chParams = ""; + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + chParams += selVar.getName() + ", "; + } + } + 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.getLeafResourceName(); + 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; + } + } + String updateMethodName = null; + if (JavaCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { + updateMethodName = "updateFrom" + srcResourceName; + } else { + updateMethodName = "update" + dstResourceName + "From" + srcResourceName; + } + // Value of the source side (input side) resource. + String srcFieldName = "value"; + if (!JavaCodeGenerator.generatesComponent(src.getResourceHierarchy())) { + srcFieldName = JavaCodeGenerator.toVariableName(srcResourceName); + } + if (!outsideOutputResource) { + // The destination resource is not outside. + if (srcComponent != dstComponent) { + srcUpdate.addStatement("this." + JavaCodeGenerator.toVariableName(dstComponent.getTypeName()) + "." + updateMethodName + "(" + pathParams + chParams + srcFieldName + refParams + ");"); + } else { + srcUpdate.addStatement("this." + updateMethodName + "(" + pathParams + chParams + srcFieldName + refParams + ");"); + } + } else { + // Use the reference field to refer to outside destination resource. + srcUpdate.addStatement("this." + JavaCodeGenerator.toVariableName(dstComponent.getTypeName()) + "." + updateMethodName + "(" + pathParams + chParams + srcFieldName + refParams + ");"); + } + } + for (MethodDeclaration srcInput: getInputMethods(srcComponent, src, model)) { + ResourcePath dstRes = out.getResource(); + // Values of path parameters. + String pathParams = ""; + for (Expression pathParam: dstRes.getPathParams()) { + if (pathParam instanceof Variable) { + Variable pathVar = (Variable) pathParam; + pathParams += pathVar.getName() + ", "; + } + } + // Values of channel parameters. + String chParams = ""; + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + chParams += selVar.getName() + ", "; + } + } + 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.getLeafResourceName(); + 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; + } + } + String updateMethodName = null; + if (JavaCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { + updateMethodName = "updateFrom" + srcResourceName; + } else { + updateMethodName = "update" + dstResourceName + "From" + srcResourceName; + } + String srcFieldName = "value"; + if (!JavaCodeGenerator.generatesComponent(src.getResourceHierarchy())) { + srcFieldName = JavaCodeGenerator.toVariableName(srcResourceName); + } + if (!outsideOutputResource) { + // The destination resource is not outside. + if (srcComponent != dstComponent) { + srcInput.addStatement("this." + JavaCodeGenerator.toVariableName(dstComponent.getTypeName()) + "." + updateMethodName + "(" + pathParams + chParams + srcFieldName + refParams + ");"); + } else { + srcInput.addStatement("this." + updateMethodName + "(" + pathParams + chParams + srcFieldName + refParams + ");"); + } + } else { + // Use the reference field to refer to outside destination resource. + srcInput.addStatement("this." + JavaCodeGenerator.toVariableName(dstComponent.getTypeName()) + "." + updateMethodName + "(" + pathParams + chParams + srcFieldName + 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 (((ResourceNode) dIn.getSource()).getOutSideResources().contains(cm.getResource())) { + 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).getKey().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).getKey().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()).getOutSideResources().contains(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. + Set outs = ch2.getOutputChannelMembers(); + MethodDeclaration input = getInputMethod(dependingComponent, outs.iterator().next(), outs.size()); + 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 = null; + if (JavaCodeGenerator.generatesComponent(dependingRes.getResourceHierarchy())) { + update = getUpdateMethod(dependingComponent, null, JavaCodeGenerator.getComponentName(dependingResSrc.getResourceHierarchy())); + } else { + String dependingResName = JavaCodeGenerator.getComponentName(dependingRes.getResourceHierarchy()); + update = getUpdateMethod(dependingComponent, dependingResName, 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 = null; + if (JavaCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { + update = getUpdateMethod(dstComponent, null, JavaCodeGenerator.getComponentName(dependingRes.getResourceHierarchy())); + } else { + String dstResName = JavaCodeGenerator.getComponentName(dst.getResourceHierarchy()); + update = getUpdateMethod(dstComponent, dstResName, 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(); + DataTransferChannel ch = entry.getKey(); + for (ChannelMember out: outs) { + MethodDeclaration input = null; + if (JavaCodeGenerator.generatesComponent(resource)) { + // A component is generated for this resource. + input = getInputMethod(component, out, ch.getOutputChannelMembers().size()); + } 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, ch.getOutputChannelMembers().size()); + } + } + if (input != null) { + // In each resource + Expression updateExp = ch.deriveUpdateExpressionOf(out, JavaCodeGenerator.refAccessor).getKey(); + // 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 +704,115 @@ 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 = ", "; + } else { + String updateStatement = ""; + if (sideEffects[0] != null) { + updateStatement = sideEffects[0]; + updateStatement = updateStatement.replace(".value", "." + JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(resource))); + } + 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 += 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 += sideEffects2[0]; + } else if (!(updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect())) { + updateStatement += "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) { + Expression message = out.getStateTransition().getMessageExpression(); + String inputAccessorName = input.getName(); + if (message instanceof Term) { + inputAccessorName = ((Term) message).getSymbol().getImplName(); + } else if (message instanceof Variable) { + inputAccessorName = ((Variable) message).getName(); + } + MethodDeclaration inputAccessor = getMethod(mainComponent, inputAccessorName); + if (inputAccessor != null) { + Set referredSet = referredResources.get(input); + 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(input, referredSet); + } + if (!out.getResource().equals(ref)) { + String refVarName = ref.getLeafResourceName(); + if (!referredSet.contains(ref)) { + referredSet.add(ref); + Expression refGetter = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(ref, null); + sideEffects = new String[] {""}; + String refExp = refGetter.toImplementation(sideEffects); + String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); + inputAccessor.addFirstStatement(sideEffects[0] + refTypeName + " " + refVarName + " = " + refExp + ";"); } } - mainInput.addStatement("this." + resourceName + "." + input.getName() + "(" + args + ");"); } + Expression resExp = JavaCodeGenerator.pullAccessor.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}); + // Values of channel parameters. + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + if (!args.contains(selVar.getName())) { + args.add(selVar.getName()); + } + } + } + // Values of message parameters. + if (message instanceof Term) { + for (Map.Entry varEnt: message.getVariables().entrySet()) { + String refVarName = null; + for (ChannelMember rc: ch.getReferenceChannelMembers()) { + Expression varExp = rc.getStateTransition().getMessageExpression().getSubTerm(varEnt.getKey()); + if (varExp != null && rc.getStateTransition().getCurStateExpression().contains(varExp)) { + refVarName = rc.getResource().getLeafResourceName(); + break; + } + } + if (refVarName != null) { + if (!args.contains(refVarName)) { + args.add(refVarName); + } + } else { + if (!args.contains(varEnt.getValue().getName())) { + args.add(varEnt.getValue().getName()); + } + } + } + } + String argsStr = ""; + String delimiter = ""; + for (String arg: args) { + argsStr += delimiter + arg; + delimiter = ", "; + } + inputAccessor.addStatement(resourceAccess + "." + input.getName() + "(" + argsStr + ");"); } } } @@ -269,37 +826,93 @@ 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) { + if (param instanceof Term) { + if (((Term) param).getType() == null) { + ((Term) param).setType(var.getType()); + } + } else if (param instanceof Variable) { + if (((Variable) param).getType() == null) { + ((Variable) param).setType(var.getType()); + } + } + constructorInvocation = constructorInvocation + delimiter + param.toImplementation(null); + } 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 dstResName, String srcResName) { + for (MethodDeclaration m: component.getMethods()) { + if (dstResName == null) { + if (m.getName().equals("updateFrom" + srcResName)) return m; + } else { + if (m.getName().equals("update" + dstResName + "From" + srcResName)) return m; + } + } + return null; + } + + private static List getUpdateMethods(TypeDeclaration component, String resName) { List updates = new ArrayList<>(); - for (MethodDeclaration m: type.getMethods()) { - if (m.getName().startsWith("update")) { - updates.add(m); + for (MethodDeclaration m: component.getMethods()) { + if (resName == null) { + if (m.getName().startsWith("updateFrom")) { + updates.add(m); + } + } else { + if (m.getName().startsWith("update" + resName + "From")) { + 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()) { + for (Channel c: model.getInputChannels()) { 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 +927,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()) { + for (Channel c: model.getInputChannels()) { 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, channel.getOutputChannelMembers().size()); inputs.add(input); } } @@ -329,20 +942,39 @@ return inputs; } - private static MethodDeclaration getInputMethod(TypeDeclaration type, ChannelMember out) { - 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()); + private static List getInputMethods(TypeDeclaration component, ResourceHierarchy resource, DataTransferModel model) { + List inputs = new ArrayList<>(); + for (Channel c: model.getInputChannels()) { + DataTransferChannel channel = (DataTransferChannel) c; + // I/O channel + for (ChannelMember out: channel.getOutputChannelMembers()) { + if (resource.equals(out.getResource().getResourceHierarchy())) { + MethodDeclaration input = getInputMethod(component, out, channel.getOutputChannelMembers().size()); + inputs.add(input); + } + } } + return inputs; + } + + private static MethodDeclaration getInputMethod(TypeDeclaration component, ChannelMember cm, int outNumber) { + String inputMethodName = null; + if (cm.getStateTransition().getMessageExpression() instanceof Term) { + Term message = (Term) cm.getStateTransition().getMessageExpression(); + inputMethodName =message.getSymbol().getImplName(); + } else if (cm.getStateTransition().getMessageExpression() instanceof Variable) { + Variable message = (Variable) cm.getStateTransition().getMessageExpression(); + inputMethodName = message.getName(); + } + if (outNumber > 1) { + inputMethodName += "For" + JavaCodeGenerator.getComponentName(cm.getResource().getResourceHierarchy()); + } + MethodDeclaration input = getMethod(component, inputMethodName); 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..3070f23 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/generators/JavaSpecific.java @@ -0,0 +1,297 @@ +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 methodName, List parameters) { + if (parameters == null) return getMethodInvocation( methodName); + String invocation = self + "." + 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 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 getIfStatement(Term condition, String block) { + return "if (" + condition.toImplementation(new String[] {})+ ") {\n" + "\t" + block.replace("\n", "\n\t") + "\n}"; + } + + @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..78a04d2 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; @@ -17,6 +23,7 @@ import models.algebra.Expression; import models.algebra.Field; import models.algebra.Parameter; +import models.algebra.Position; import models.algebra.Symbol; import models.algebra.Term; import models.algebra.Type; @@ -24,7 +31,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 +42,7 @@ import models.dataFlowModel.DataFlowEdge; import models.dataFlowModel.DataFlowGraph; import models.dataFlowModel.ResourceNode; +import models.dataFlowModel.ChannelNode; import models.dataFlowModel.StoreAttribute; /** @@ -44,6 +54,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 +70,883 @@ 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<>(); + Map> updates = new HashMap<>(); + Map> inputs = new HashMap<>(); + List> fields = new ArrayList<>(); + Map getterAccessors = new HashMap<>(); + Map inputAccessors = new HashMap<>(); + Map> constructorParams = new HashMap<>(); + Map priorMemberForInputChannel = new HashMap<>(); + // 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 resourceNode = (ResourceNode) n; + TypeDeclaration component = null; + if (generatesComponent(resourceNode.getResourceHierarchy())) { + String resourceName = getComponentName(resourceNode.getResourceHierarchy()); + + component = resourceComponents.get(resourceNode.getResourceHierarchy()); + if (component == null) { + // Add compilation unit for each resource. + component = new TypeDeclaration(resourceName); + if (resourceNode.getResourceHierarchy().getParent() == null) { + // For a root node. + component.addAnnotation(new Annotation("Component")); + component.addAnnotation(new Annotation("Path", "\"/" + resourceNode.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(resourceNode.getResourceHierarchy(), component); + + CompilationUnit cu = new CompilationUnit(component); + cu.addImport(new ImportDeclaration("java.util.*")); + if (resourceNode.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 the field to store the state in the type of each resource. + if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { + ResourceHierarchy res = resourceNode.getResourceHierarchy(); + Set children = res.getChildren(); + if (children == null || children.size() == 0) { + // leaf resource. + Type fieldType = getImplStateType(res); + component.addField(new FieldDeclaration(fieldType, "value", getInitializer(res))); + Map nameToParam = constructorParams.get(res); + if (nameToParam == null) { + nameToParam = new HashMap<>(); + constructorParams.put(resourceNode.getResourceHierarchy(), nameToParam); + } + String varName = toVariableName(resourceName); + if (nameToParam.get(varName) == null) { + nameToParam.put(varName, new VariableDeclaration(fieldType, varName)); + } + } else { + ResourceHierarchy child = children.iterator().next(); + if (children.size() == 1 && child.getNumParameters() > 0) { + // map or list. + component.addField(new FieldDeclaration(getImplStateType(res), "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))); + } + } + } } } - MethodDeclaration update = new MethodDeclaration("update" + srcResName, false, typeVoid, vars); - for (ChannelMember cm: re.getChannel().getOutputChannelMembers()) { - if (cm.getResource() == rn.getResource()) { - if (cm.getStateTransition().isRightUnary()) { + + // Declare the getter method to obtain the resource state in the component of each resource. + MethodDeclaration stateGetter = new MethodDeclaration("getValue", getImplStateType(resourceNode.getResourceHierarchy())); + if (resourceNode.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); + + // Declare the getter methods to obtain the children resources. + Set children = new HashSet<>(); + for (ResourceNode child: resourceNode.getChildren()) { + if (generatesComponent(child.getResourceHierarchy())) { + // The child generates a component. + if (!children.contains(child.getResourceHierarchy())) { + children.add(child.getResourceHierarchy()); + List pathParams = new ArrayList<>(); + int v = 1; + for (Expression pathParam: child.getPrimaryResourcePath().getPathParams()) { + if (pathParam instanceof Variable) { + Variable var = (Variable) pathParam; + pathParams.add(new VariableDeclaration(var.getType(), var.getName())); + } else if (pathParam instanceof Term) { + Term var = (Term) pathParam; + 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. + boolean bDeclareClientField = false; + if (component == null) { + // 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()) { + ResourceHierarchy dstRes = ((ResourceNode) chToRes.getDestination()).getResourceHierarchy(); + // Check if the output resource is outside of the channel scope. + boolean outsideOutputResource = false; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (((ResourceNode) chToRes.getDestination()).getInSideResources().contains(cm.getResource()) && 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. + if (!generatesComponent(dstRes)) { + dstRes = dstRes.getParent(); + } + String dstResName = getComponentName(dstRes); +// if (resourceNode.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<>(resourceNode.getParent().getResourceHierarchy(), clientField)); + bDeclareClientField = true; + } +// } else { +// // Inner-service +// // Declare a field to directly refer to the destination resource of push transfer. +// if (resourceNode.getParent().getResourceHierarchy() != dstRes.getResourceHierarchy()) { +// FieldDeclaration refFieldForPush = new FieldDeclaration(new Type(dstResName, dstResName), toVariableName(dstResName)); +// fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), refFieldForPush)); +// } +// } + } + } + } + for (Edge chToRes : resourceNode.getInEdges()) { + for (Edge resToCh: chToRes.getSource().getInEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(ch); + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().equals(srcRes) && 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. + if (!generatesComponent(srcRes.getResourceHierarchy())) { + srcRes = srcRes.getParent(); + } + String srcResName = getComponentName(srcRes.getResourceHierarchy()); +// if (resourceNode.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<>(resourceNode.getParent().getResourceHierarchy(), clientField)); + bDeclareClientField = true; + } +// } else { +// // Inner-service +// // Declare a field to directly refer to the source resource of pull transfer. +// if (resourceNode.getParent().getResourceHierarchy() != srcRes.getResourceHierarchy()) { +// FieldDeclaration refFieldForPull = new FieldDeclaration(new Type(srcResName, srcResName), toVariableName(srcResName)); +// fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), refFieldForPull)); +// } +// } + noPullTransfer = false; + } + } + } + // Declare the state field in the parent component. + ResourceHierarchy res = resourceNode.getResourceHierarchy(); + if (((StoreAttribute) resourceNode.getAttribute()).isStored() && noPullTransfer && res.getNumParameters() == 0) { + String resName = getComponentName(res); + FieldDeclaration stateField = new FieldDeclaration(res.getResourceStateType(), toVariableName(resName)); + fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), stateField)); + + + Map nameToParam = constructorParams.get(resourceNode.getParent().getResourceHierarchy()); + if (nameToParam == null) { + nameToParam = new HashMap<>(); + constructorParams.put(resourceNode.getParent().getResourceHierarchy(), nameToParam); + } + String varName = toVariableName(resName); + if (nameToParam.get(varName) == null) { + nameToParam.put(varName, new VariableDeclaration(res.getResourceStateType(), varName)); + } + } + } + + // Declare the getter method to obtain the resource state in the parent component. + if (component == null) { + // No component is created for this resource. + String getterName = "get" + getComponentName(resourceNode.getResourceHierarchy()); + boolean bExists = false; + for (Map.Entry entry: getters) { + if (entry.getKey() == resourceNode.getParent().getResourceHierarchy() && entry.getValue().getName().equals(getterName)) { + bExists = true; + break; + } + } + if (!bExists) { + List pathParams = new ArrayList<>(); + int v = 1; + for (Selector pathParam: resourceNode.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++; + } + Type resType = getImplStateType(resourceNode.getResourceHierarchy()); + MethodDeclaration stateGetter = null; + if (pathParams.size() == 0) { + stateGetter = new MethodDeclaration(getterName, resType); + } else { + stateGetter = new MethodDeclaration(getterName, false, resType, pathParams); + } + getters.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), stateGetter)); + } + } + + // Declare the getter accessor in the root resource. + if (resourceNode.getResourceHierarchy().getParent() != null) { + // For a non-root resource + MethodDeclaration getterAccessor = null; + List mainGetterParams = new ArrayList<>(); + String resourcePath = getGetterResourcePathAndPathParams(resourceNode.getPrimaryResourcePath(), mainGetterParams); + if (resourcePath.indexOf('/') > 0) { + resourcePath = resourcePath.substring(resourcePath.indexOf('/')); + } else { + resourcePath = ""; + } + if (mainGetterParams.size() > 0) { + getterAccessor = new MethodDeclaration("get" + getComponentName(resourceNode.getResourceHierarchy()) + "Value", + false, + getImplStateType(resourceNode.getResourceHierarchy()), + mainGetterParams); + } else { + getterAccessor = new MethodDeclaration("get" + getComponentName(resourceNode.getResourceHierarchy()) + "Value", + getImplStateType(resourceNode.getResourceHierarchy())); + } + getterAccessor.setBody(new Block()); + ResourcePath resPath = resourceNode.getPrimaryResourcePath(); + Expression getState = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(resPath, resPath.getRoot()); + getterAccessor.getBody().addStatement("return " + getState.toImplementation(new String[] {null}) + ";"); + + getterAccessor.addAnnotation(new Annotation("Produces", "MediaType.APPLICATION_JSON")); + getterAccessor.addAnnotation(new Annotation("GET")); + if (resourcePath.length() > 0) { + getterAccessor.addAnnotation(new Annotation("Path", "\"" + resourcePath + "\"")); + } + getterAccessors.put(resourceNode.getResourceHierarchy(), getterAccessor); + } + + // Declare a client field and update methods from other resources. + for (Edge resToCh: resourceNode.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().equals(resourceNode.getOutSideResource(ch)) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + for (ChannelMember cm: ch.getOutputChannelMembers()) { + ResourcePath dstRes = cm.getResource(); + // Check if the output resource is outside of the channel scope. + boolean outsideOutputResource = cm.isOutside(); + if (!bDeclareClientField && ((((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource)) { + // For push transfer. + if (!generatesComponent(dstRes.getResourceHierarchy())) { + dstRes = dstRes.getParent(); + } + String dstResName = getComponentName(dstRes.getResourceHierarchy()); + if (outsideOutputResource || (resourceNode.getOutSideResource(ch).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()"); + if (component != null) { + // A component is created for this resource. + component.addField(clientField); + } else { + // No component is created for this resource. + fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), clientField)); + } + bDeclareClientField = true; + } + } else { + // Inner-service + // Declare a field to directly refer to the destination resource of push transfer. + FieldDeclaration dstRefField = new FieldDeclaration(new Type(dstResName, dstResName), toVariableName(dstResName)); + if (component != null) { + // A component is created for this resource. + if (resourceNode.getResourceHierarchy() != dstRes.getResourceHierarchy()) { + component.addField(dstRefField); + } + } else { + // No component is created for this resource. + if (resourceNode.getParent().getResourceHierarchy() != dstRes.getResourceHierarchy()) { + fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), dstRefField)); + } + } + } + } + } + } + for (Edge chToRes : resourceNode.getInEdges()) { + for (Edge resToCh: chToRes.getSource().getInEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(ch); + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().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; + ChannelMember out = null; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (resourceNode.getInSideResources().contains(cm.getResource())) { + out = cm; + if (cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + } + String srcResName = getComponentName(srcRes.getResourceHierarchy()); + Type srcType = srcRes.getResourceStateType(); + if (!generatesComponent(srcRes.getResourceHierarchy())) { + srcRes = srcRes.getParent(); + } + if ((((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { + // For pull transfer. + if (outsideInputResource || (resourceNode.getInSideResource(ch).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()"); + if (component != null) { + // A component is created for this resource. + component.addField(clientField); + } else { + // No component is created for this resource. + fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), clientField)); + } + bDeclareClientField = true; + } + } else { + // Inner-service + // Declare a field to directly refer to the source resource of pull transfer. + FieldDeclaration srcRefField = new FieldDeclaration(new Type(srcResName, srcResName), toVariableName(srcResName)); + if (component != null) { + // A component is created for this resource. + if (resourceNode.getResourceHierarchy() != srcRes.getResourceHierarchy()) { + component.addField(srcRefField); + } + } else { + // No component is created for this resource. + if (resourceNode.getParent().getResourceHierarchy() != srcRes.getResourceHierarchy()) { + fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), srcRefField)); + } + } + } + } else { + // For push transfer. + boolean hasRestAPI = false; + boolean isRestAPI = false; + if (outsideOutputResource || (resourceNode.getInSideResource(ch).getCommonPrefix(srcRes) == null && differentTreesAsDifferentServices)) { + // Inter-service + hasRestAPI = true; + if (resourceNode.getParent() == null) { + // A root resource + isRestAPI = true; + } + } + // Declare an update method in the type of the destination resource. + ArrayList params = new ArrayList<>(); + getUpdateResourcePathAndPathParams(out.getResource(), params, isRestAPI); // Path parameters to identify the self resource. + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + VariableDeclaration chParam = new VariableDeclaration(selVar.getType(), selVar.getName()); + if (isRestAPI) chParam.addAnnotation(new Annotation("FormParam", "\"" + selVar.getName() + "\"")); + params.add(chParam); // A channel parameter to specify the context of the collaboration. + } + } + String srcName = toVariableName(srcResName); + VariableDeclaration param = new VariableDeclaration(srcType, srcName); + if (isRestAPI) param.addAnnotation(new Annotation("FormParam", "\"" + srcName + "\"")); + params.add(param); // The state of the source resource to carry the data-flow. + for (ResourcePath refRes: ch.getReferenceResources()) { + if (!refRes.equals(resourceNode.getInSideResource(ch))) { + param = new VariableDeclaration(refRes.getResourceStateType(), refRes.getLeafResourceName()); + if (isRestAPI) param.addAnnotation(new Annotation("FormParam", "\"" + refRes.getLeafResourceName() + "\"")); + params.add(param); + } + } + MethodDeclaration update = null; + if (component != null) { + // A component is created for this resource. + update = new MethodDeclaration("updateFrom" + srcResName, false, typeVoid, params); + } else { + // No component is created for this resource. + String resourceName = getComponentName(resourceNode.getResourceHierarchy()); + update = new MethodDeclaration("update" + resourceName + "From" + srcResName, false, typeVoid, params); + } + // Determine whether the update method is put or post. + boolean isPut = false; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (resourceNode.getInSideResources().contains(cm.getResource())) { + if (cm.getStateTransition().isRightUnary()) { + isPut = true; + } else { + isPut = false; + } + } + } + if (isRestAPI) { + if (isPut) { update.addAnnotation(new Annotation("PUT")); } else { update.addAnnotation(new Annotation("POST")); } } - } - 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))); - } - 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 input methods in resources. - 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()) { - String paramName = var.getName(); - VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); - param.addAnnotation(new Annotation("FormParam", "\"" + paramName + "\"")); - params.add(param); + // Calculate in-degree of the destination resource. + Set inResources = new HashSet<>(); + for (ResourceNode rn: graph.getResourceNodes(out.getResource().getResourceHierarchy())) { + // ResourceNodes that have the same ResourceHierarchy. + for (Edge chToRes2: rn.getInEdges()) { + for (Edge resToCh2: chToRes2.getSource().getInEdges()) { + inResources.add(((ResourceNode) resToCh2.getSource()).getResourceHierarchy()); + } } - MethodDeclaration input = new MethodDeclaration( - ((Term) cm.getStateTransition().getMessageExpression()).getSymbol().getImplName(), - false, typeVoid, params); - if (cm.getStateTransition().isRightUnary()) { - input.addAnnotation(new Annotation("PUT")); + } + int inDegree = inResources.size(); + if (inDegree > 1 + || (inDegree == 1 && ch.getInputChannelMembers().iterator().next().getStateTransition().isRightPartial())) { + // Declare a field to cache the state of the source resource in the type of the destination resource. + ResourceHierarchy cacheRes = ((ResourceNode) re.getSource()).getResourceHierarchy(); + FieldDeclaration cacheField = new FieldDeclaration(cacheRes.getResourceStateType(), srcName, getInitializer(cacheRes)); + if (component != null) { + // A component is created for this resource. + component.addField(cacheField); } else { - input.addAnnotation(new Annotation("POST")); + // No component is created for this resource. + fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), cacheField)); } - type.addMethod(input); - } else if (message.getClass() == Variable.class) { - MethodDeclaration input = new MethodDeclaration( - ((Variable) cm.getStateTransition().getMessageExpression()).getName(), - false, typeVoid, null); - if (cm.getStateTransition().isRightUnary()) { - input.addAnnotation(new Annotation("PUT")); + if (inDegree > 1) { + // For each source resource, a child resource is defined in the destination resource so that its state can be updated separately. + if (isRestAPI) update.addAnnotation(new Annotation("Path", "\"/" + srcName + "\"")); + } + } + if (component != null) { + // A component is created for this resource. + component.addMethod(update); + } else { + // No component is created for this resource. + String updateMethodName = update.getName(); + Map nameToMethod = updates.get(resourceNode.getParent().getResourceHierarchy()); + if (nameToMethod == null) { + nameToMethod = new HashMap<>(); + updates.put(resourceNode.getParent().getResourceHierarchy(), nameToMethod); + } + if (nameToMethod.get(updateMethodName) == null) { + nameToMethod.put(updateMethodName, update); + } + } + if (hasRestAPI && !isRestAPI) { + // Declare an update accessor method in the type of root resource. + String updateMethodName = update.getName(); + params = new ArrayList<>(); + String resourcePath = getUpdateResourcePathAndPathParams(out.getResource(), params, true); // Path parameters to identify the self resource. + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + VariableDeclaration chParam = new VariableDeclaration(selVar.getType(), selVar.getName()); + chParam.addAnnotation(new Annotation("FormParam", "\"" + selVar.getName() + "\"")); + params.add(chParam); // A channel parameter to specify the context of the collaboration. + } + } + param = new VariableDeclaration(srcType, srcName); + param.addAnnotation(new Annotation("FormParam", "\"" + srcName + "\"")); + params.add(param); // The state of the source resource to carry the data-flow. + for (ResourcePath refRes: ch.getReferenceResources()) { + if (!refRes.equals(resourceNode.getInSideResource(ch))) { + param = new VariableDeclaration(refRes.getResourceStateType(), refRes.getLeafResourceName()); + param.addAnnotation(new Annotation("FormParam", "\"" + refRes.getLeafResourceName() + "\"")); + params.add(param); + } + } + MethodDeclaration updateAccessor = new MethodDeclaration(updateMethodName, false, typeVoid, params); + if (isPut) { + updateAccessor.addAnnotation(new Annotation("PUT")); } else { - input.addAnnotation(new Annotation("POST")); + updateAccessor.addAnnotation(new Annotation("POST")); } - type.addMethod(input); + if (inDegree > 1) { + // For each source resource, a child resource is defined in the destination resource so that its state can be updated separately. + resourcePath += "/" + toVariableName(srcResName); + } + updateAccessor.addAnnotation(new Annotation("Path", "\"" + resourcePath + "\"")); + Map nameToMethod = updates.get(resourceNode.getResourceHierarchy().getRoot()); + if (nameToMethod == null) { + nameToMethod = new HashMap<>(); + updates.put(resourceNode.getResourceHierarchy().getRoot(), nameToMethod); + } + if (nameToMethod.get(updateMethodName) == null) { + nameToMethod.put(updateMethodName, updateAccessor); + } } } } } - // 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))); + // Declare the input method in each resource and the root resource. + for (Channel ch : model.getInputChannels()) { + for (ChannelMember cm : ((DataTransferChannel) ch).getOutputChannelMembers()) { + if (!cm.isOutside()) { + if (priorMemberForInputChannel.get(ch) == null) { + priorMemberForInputChannel.put(ch, cm); // The receiver of the input event when multiple output resources are defined for the channel. + } + } + } + for (ChannelMember cm : ((DataTransferChannel) ch).getOutputChannelMembers()) { + if (resourceNode.getInSideResources().contains(cm.getResource())) { + Expression message = cm.getStateTransition().getMessageExpression(); + if (message instanceof Term) { + // In each resource. + ArrayList resInputParams = new ArrayList<>(); + ArrayList rootInputParams = new ArrayList<>(); + String resourcePath = getInputMethodResourcePathAndPathParams(cm.getResource(), resInputParams, rootInputParams); // Path parameters for the input REST API. + if (resourcePath.indexOf('/') > 0) { + resourcePath = resourcePath.substring(resourcePath.indexOf('/')); + } else { + resourcePath = ""; + } + // The path parameters are not to be passed to the input method of each resource (resInputParams) + // because they are always equal to either channel selectors or message parameters. + + // Channel parameters to specify the context of the collaboration. + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + VariableDeclaration chParam = new VariableDeclaration(selVar.getType(), selVar.getName()); + resInputParams.add(chParam); + } + } + // Message parameters to carry the data-flows. + for (Map.Entry varEnt: message.getVariables().entrySet()) { + Variable var = varEnt.getValue(); + String refVarName = null; + for (ChannelMember refCm: ((DataTransferChannel) ch).getReferenceChannelMembers()) { + Expression varExp = refCm.getStateTransition().getMessageExpression().getSubTerm(varEnt.getKey()); + if (varExp != null && varExp instanceof Variable) { + if (refCm.getStateTransition().getCurStateExpression().contains(varExp)) { + refVarName = refCm.getResource().getLeafResourceName(); + break; + } + } + } + if (refVarName != null) { + // var has come from a reference resource. + VariableDeclaration param = new VariableDeclaration(var.getType(), refVarName); + resInputParams.add(param); + } else { + // var has not come from reference resource. + String paramName = var.getName(); + VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); + resInputParams.add(param); + if (!resourcePath.contains("{" + paramName+ "}")) { + param = new VariableDeclaration(var.getType(), paramName); + param.addAnnotation(new Annotation("FormParam", "\"" + paramName + "\"")); + rootInputParams.add(param); + } + } + } + + if (resourceNode.getResourceHierarchy().getParent() != null && resourceNode.getResourceHierarchy().getParent().getParent() != null) { + String inputMethodName = ((Term) message).getSymbol().getImplName(); + if (((DataTransferChannel) ch).getOutputChannelMembers().size() > 1) { + inputMethodName += "For" + getComponentName(cm.getResource().getResourceHierarchy()); + } + MethodDeclaration input = new MethodDeclaration(inputMethodName, false, typeVoid, resInputParams); + if (component != null) { + // A component is created for this resource. + component.addMethod(input); + } else { + // No component is created for this resource. + Map nameToMethod = inputs.get(resourceNode.getParent().getResourceHierarchy()); + if (nameToMethod == null) { + nameToMethod = new HashMap<>(); + inputs.put(resourceNode.getParent().getResourceHierarchy(), nameToMethod); + } + if (nameToMethod.get(inputMethodName) == null) { + nameToMethod.put(inputMethodName, input); + } + } + } + + // For the root resource. + if (priorMemberForInputChannel.get(ch) ==null || cm == priorMemberForInputChannel.get(ch)) { + // If cm is the receiver of the input event. + priorMemberForInputChannel.put(ch, cm); + String messageSymbol = ((Term) message).getSymbol().getImplName(); + MethodDeclaration inputAccessor = new MethodDeclaration(messageSymbol, false, typeVoid, rootInputParams); + if (cm.getStateTransition().isRightUnary()) { + inputAccessor.addAnnotation(new Annotation("PUT")); + } else { + inputAccessor.addAnnotation(new Annotation("POST")); + } + if (resourcePath.length() > 0) { + inputAccessor.addAnnotation(new Annotation("Path", "\"" + resourcePath + "\"")); + } + inputAccessors.put(resourceNode.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) { + String inputMethodName = ((Variable) message).getName(); + if (((DataTransferChannel) ch).getOutputChannelMembers().size() > 1) { + inputMethodName += "For" + getComponentName(cm.getResource().getResourceHierarchy()); + } + MethodDeclaration input = new MethodDeclaration(inputMethodName, false, typeVoid, null); + if (component != null) { + // A component is created for this resource. + component.addMethod(input); + } else { + // No component is created for this resource. + Map nameToMethod = inputs.get(cm.getResource().getParent().getResourceHierarchy()); + if (nameToMethod == null) { + nameToMethod = new HashMap<>(); + inputs.put(cm.getResource().getParent().getResourceHierarchy(), nameToMethod); + } + if (nameToMethod.get(inputMethodName) == null) { + nameToMethod.put(inputMethodName, input); + } + } + } + + // For the root resource. + if (priorMemberForInputChannel.get(ch) ==null || cm == priorMemberForInputChannel.get(ch)) { + // If cm is the receiver of the input event. + priorMemberForInputChannel.put(ch, cm); + ArrayList rootInputParams = new ArrayList<>(); + String resourcePath = getGetterResourcePathAndPathParams(cm.getResource(), rootInputParams); + if (resourcePath.indexOf('/') > 0) { + resourcePath = resourcePath.substring(resourcePath.indexOf('/')); + } else { + resourcePath = ""; + } + String messageSymbol = ((Variable) message).getName(); + MethodDeclaration inputAccessor = new MethodDeclaration(messageSymbol, false, typeVoid, rootInputParams); + if (cm.getStateTransition().isRightUnary()) { + inputAccessor.addAnnotation(new Annotation("PUT")); + } else { + inputAccessor.addAnnotation(new Annotation("POST")); + } + if (resourcePath.length() > 0) { + inputAccessor.addAnnotation(new Annotation("Path", "\"" + resourcePath + "\"")); + } + inputAccessors.put(resourceNode.getResourceHierarchy(), inputAccessor); + } + } + } + } } - - // 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); + } + + // Add leaf getter methods to the parent components. + for (Map.Entry entry: getters) { + resourceComponents.get(entry.getKey()).addMethod(entry.getValue()); + } + + // Add leaf update methods to the parent components. + for (Map.Entry> entry: updates.entrySet()) { + for (MethodDeclaration update: entry.getValue().values()) { + resourceComponents.get(entry.getKey()).addMethod(update); + } + } + + // Add leaf input methods to the parent components. + for (Map.Entry> entry: inputs.entrySet()) { + for (MethodDeclaration input: entry.getValue().values()) { + resourceComponents.get(entry.getKey()).addMethod(input); + } + } + + // Add leaf reference fields to the parent components. + for (Map.Entry entry: fields) { + ResourceHierarchy resource = entry.getKey(); + FieldDeclaration field = entry.getValue(); + TypeDeclaration component = resourceComponents.get(resource); + boolean existsField = false; + for (FieldDeclaration fld: component.getFields()) { + if (fld.getName().equals(field.getName())) { + existsField = true; + break; + } + } + if (!existsField) { + component.addField(field); + if (field.getType().equals(typeClient)) { + for (CompilationUnit cu: codes) { + if (cu.types().contains(component)) { + cu.addImport(new ImportDeclaration("javax.ws.rs.client.*")); + break; + } + } + } + } + } + + // 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 Pair class. @@ -214,7 +954,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,17 +993,156 @@ return codes; } + + private static List addConstructorParameters(ResourceHierarchy resource, + Map resourceComponents, + Map resourceConstructors, + Map> constructorParams) { + List params = new ArrayList<>(); + for (ResourceHierarchy child: resource.getChildren()) { + params.addAll(addConstructorParameters(child, resourceComponents, resourceConstructors, constructorParams)); + } + if (constructorParams.get(resource) != null) { + for (VariableDeclaration param: constructorParams.get(resource).values()) { + params.add(param); + } + } + 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 getInitializer(ResourcePath resId) { - Type stateType = resId.getResourceStateType(); + 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 getUpdateResourcePathAndPathParams(ResourcePath resPath, ArrayList rootParams, boolean isRestAPI) { + int v = 1; + List params = new ArrayList<>(); + for (Expression pathParam: resPath.getPathParams()) { + if (pathParam instanceof Variable) { + Variable var = (Variable) pathParam; + String paramName = null; + if (isRestAPI) { + paramName = var.getName(); + } else { + paramName = "self" + (v > 1 ? v : ""); + } + params.add("{" + paramName + "}"); + VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); + if (isRestAPI) param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\"")); + rootParams.add(param); + } else if (pathParam instanceof Term) { + Term var = (Term) pathParam; + String paramName = null; + if (isRestAPI) { + paramName = "v" + v; + } else { + paramName = "self" + (v > 1 ? v : ""); + } + params.add("{" + paramName + "}"); + VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); + if (isRestAPI) param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\"")); + rootParams.add(param); + } + v++; + } + return resPath.getResourceHierarchy().toResourcePath(params); + } + + private static String getInputMethodResourcePathAndPathParams(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); + 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++; + } + 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(ResourceHierarchy res) { + Type stateType = res.getResourceStateType(); String initializer = null; - if (resId.getInitialValue() != null) { - initializer = resId.getInitialValue().toImplementation(new String[] {""}); + if (res.getInitialValue() != null) { + initializer = res.getInitialValue().toImplementation(new String[] {""}); } else { 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; @@ -316,41 +1195,161 @@ 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); + } + // use the cached value as the current state + return new Field(targetRes.getLeafResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + return new Parameter(targetRes.getLeafResourceName(), + 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); } 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); + 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); + } } // for reference channel member - return new Parameter(target.getResourceName(), - target.getResourceStateType() != null ? target.getResourceStateType() + return new Parameter(targetRes.getLeafResourceName(), + 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(); + if (from != null && !target.isOutside()) { + ResourcePath fromRes = from.getResource(); + if (targetRes.getCommonPrefix(fromRes) != null) { + return getDirectStateAccessorFor(targetRes, fromRes); + } + } + return new Parameter(targetRes.getLeafResourceName(), + 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.getLeafResourceName(), + 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; + } + } + }; + static public IResourceStateAccessor refAccessor = 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("value", + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + return new Parameter(targetRes.getLeafResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + return new Parameter(targetRes.getLeafResourceName(), + 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); + } + return null; + } }; } diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java index cdaf898..4efb2dd 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,14 @@ 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.Parameter; 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 +33,16 @@ 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.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 +54,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 +66,791 @@ 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; + ChannelMember in = null; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (src.getOutSideResources().contains(cm.getResource())) { + in = cm; + if (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 = out.isOutside(); + if ((pushPull.getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) { + // for push data transfer + MethodDeclaration update = null; + if (dstComponent == null) { + String dstParentResourceName = JerseyCodeGenerator.getComponentName(dst.getResourceHierarchy().getParent()); + dstComponent = componentMap.get(dstParentResourceName); + update = getUpdateMethod(dstComponent, dstResourceName, srcResourceName); + } else { + update = getUpdateMethod(dstComponent, null, srcResourceName); } - 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 (((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).getKey(); + } 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 in2 = null; + for (ChannelMember cm: ch2.getInputChannelMembers()) { + if (((ResourceNode) dIn.getSource()).getOutSideResources().contains(cm.getResource())) { + in2 = cm; + break; + } + } + inputResourceToStateAccessor.put(in2, JerseyCodeGenerator.pushAccessor); + } + } + for (ChannelMember c: ch.getReferenceChannelMembers()) { + inputResourceToStateAccessor.put(c, JerseyCodeGenerator.refAccessor); + } + updateExp = ch.deriveUpdateExpressionOf(out, JerseyCodeGenerator.pushAccessor, inputResourceToStateAccessor).getKey(); + } + // 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 newState = updateExp.toImplementation(sideEffects); + int numOfOutResourcesWithTheSameHierarchy = 0; + for (ResourcePath outResPath: ch.getOutputResources()) { + if (outResPath.getResourceHierarchy().equals(outRes)) { + numOfOutResourcesWithTheSameHierarchy++; + } + } + String updateStatement = ""; + if (JerseyCodeGenerator.generatesComponent(outRes)) { + if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { + updateStatement = sideEffects[0]; + } else { + updateStatement = sideEffects[0] + "this.value = " + newState + ";"; + } + } else { + if (sideEffects[0] != null) { + updateStatement = sideEffects[0]; + updateStatement = updateStatement.replace(".value", "." + JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(outRes))); + } + if (DataConstraintModel.typeList.isAncestorOf(outRes.getParent().getResourceStateType())) { + Term selector = new Term(DataConstraintModel.set); + selector.addChild(new Field("value")); + selector.addChild(new Variable(update.getParameters().get(update.getParameters().size() - 2).getName())); + selector.addChild(new Constant(newState)); + String[] sideEffects2 = new String[] {""}; + String newList = selector.toImplementation(sideEffects2); + updateStatement += sideEffects2[0]; + } else if (DataConstraintModel.typeMap.isAncestorOf(outRes.getParent().getResourceStateType())) { + Term selector = new Term(DataConstraintModel.insert); + selector.addChild(new Field("value")); + selector.addChild(new Variable(update.getParameters().get(update.getParameters().size() - 2).getName())); + selector.addChild(new Constant(newState)); + String[] sideEffects2 = new String[] {""}; + String newMap = selector.toImplementation(sideEffects2); + updateStatement += sideEffects2[0]; + } else if (!(updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect())) { + updateStatement += "this." + JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(outRes)) + " = " + newState + ";"; + } + } + // add an update statement of the state of dst side resource. + if (numOfOutResourcesWithTheSameHierarchy == 1) { + update.addFirstStatement(updateStatement); + } else { + Term conditions = null; + int v = 1; + Map>> resourcePaths = ch.fillOutsideResourcePaths(out, JerseyCodeGenerator.pushAccessor); + for (Expression pathParam: out.getResource().getPathParams()) { + if (pathParam instanceof Variable) { + String selfParamName = ((Variable) pathParam).getName(); + Expression arg = null; + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + if (selVar.getName().equals(selfParamName)) { + arg = selVar; + break; + } + } + } + if (arg == null) { + ResourcePath filledPath = resourcePaths.get(out).getKey(); + arg = filledPath.getPathParams().get(v - 1); + } + Term condition = new Term(DataConstraintModel.eq, new Expression[] { + new Parameter("self" + (v > 1 ? v : "")), + arg}); + if (conditions == null) { + conditions = condition; + } else { + conditions = new Term(DataConstraintModel.and, new Expression[] { + conditions, + condition}); + } + } + v++; + } + String ifStatement = "if (" + conditions.toImplementation(new String[] {})+ ") {\n"; + update.addFirstStatement(ifStatement + "\t" + updateStatement.replace("\n", "\n\t") + "\n}"); + } } - } 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()) { + // Calculate in-degree of the destination resource. + Set inResources = new HashSet<>(); + for (ResourceNode rn: graph.getResourceNodes(out.getResource().getResourceHierarchy())) { + // ResourceNodes that have the same ResourceHierarchy. + for (Edge chToRes2: rn.getInEdges()) { + for (Edge resToCh2: chToRes2.getSource().getInEdges()) { + inResources.add(((ResourceNode) resToCh2.getSource()).getResourceHierarchy()); + } + } + } + int inDegree = inResources.size(); + if (inDegree > 1 + || (inDegree == 1 && ch.getInputChannelMembers().iterator().next().getStateTransition().isRightPartial())) { + // update a cache of src side resource (when incoming edges are multiple) + String cacheStatement = "this." + JerseyCodeGenerator.toVariableName(srcResourceName) + " = " + JerseyCodeGenerator.toVariableName(srcResourceName) + ";"; + if (update.getBody() == null || !update.getBody().getStatements().contains(cacheStatement)) { + update.addStatement(cacheStatement); + } + } + // For a post/put REST API. + if (outsideOutputResource + || (in.getResource().getCommonPrefix(out.getResource()) == null && JerseyCodeGenerator.differentTreesAsDifferentServices)) { + // Inter-services + if (dst.getResourceHierarchy().getParent() != null) { + // If not a root resource. + TypeDeclaration rootComponent = componentMap.get(JerseyCodeGenerator.getComponentName(dst.getResourceHierarchy().getRoot())); + MethodDeclaration update2 = update; + update = getMethod(rootComponent, update2.getName()); // get the accessor to the update method. + // To make the accessor call the update method. + 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}); + int v = 0; + for (VariableDeclaration var: update2.getParameters()) { + if (v < out.getResource().getPathParams().size()) { + args += delimiter + ((Variable) out.getResource().getPathParams().get(v)).getName(); + } else { + args += delimiter + var.getName(); + } + delimiter = ", "; + v++; + } + update.addStatement(resourceAccess + "." + update2.getName() + "(" + args + ");"); + } + // 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.getBody().getStatements().contains(paramConverter)) { + update.addFirstStatement(paramConverter); + } + } + } + if (((StoreAttribute) dst.getAttribute()).isStored()) { + // returns the state stored in a field. + 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) { + if (JerseyCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { + // dst has a component. + getter.addStatement("return value;"); + } else { + // dst has no component. + String dstResName = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(dst.getResourceHierarchy())); + getter.addStatement("return " + dstResName + ";"); + } + } 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"; + } + String srcName = null; + if (srcComponent == null) { + String srcParentResourceName = JerseyCodeGenerator.getComponentName(src.getResourceHierarchy().getParent()); + srcComponent = componentMap.get(srcParentResourceName); + srcName = srcResourceName; + } + for (MethodDeclaration srcUpdate: getUpdateMethods(srcComponent, srcName)) { + if (srcUpdate != null) { + List>> params = new ArrayList<>(); + ResourcePath dstRes = out.getResource(); + // Values of channel parameters. + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + params.add(new AbstractMap.SimpleEntry<>(selVar.getType(), + new AbstractMap.SimpleEntry<>(selVar.getName(), selVar.getName()))); + } + } + // Value of the source side (input side) resource. + String srcFieldName = "this.value"; + if (!JerseyCodeGenerator.generatesComponent(src.getResourceHierarchy())) { + srcFieldName = JerseyCodeGenerator.toVariableName(srcResourceName); + } + params.add(new AbstractMap.SimpleEntry<>(src.getResourceStateType(), + new AbstractMap.SimpleEntry<>(JerseyCodeGenerator.toVariableName(srcResourceName), srcFieldName))); + 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.getLeafResourceName(); + Type refResourceType = ref.getResourceStateType(); + if (!referredSet.contains(ref)) { + referredSet.add(ref); + String[] sideEffects = new String[] {""}; + if (rc.isOutside()) { + List pathParams = new ArrayList<>(); + for (Expression pathExp: ref.getPathParams()) { + pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); + } + generatePullDataTransfer(srcUpdate, refResourceName, ref.getResourceHierarchy().toResourcePath(pathParams), refResourceType); + } else { + ResourcePath srcRes = in.getResource(); + if (!JerseyCodeGenerator.generatesComponent(srcRes.getResourceHierarchy())) { + srcRes = srcRes.getParent(); + } + Expression refGetter = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(ref, srcRes); + String refExp = refGetter.toImplementation(sideEffects); + String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); + srcUpdate.addFirstStatement(sideEffects[0] + refTypeName + " " + refResourceName + " = " + refExp + ";"); + } + } + // Value of a reference side resource. + params.add(new AbstractMap.SimpleEntry<>(refResourceType, new AbstractMap.SimpleEntry<>(refResourceName, refResourceName))); + } + } + } + if (outsideOutputResource || (in.getResource().getCommonPrefix(dstRes) == null && JerseyCodeGenerator.differentTreesAsDifferentServices)) { + // Inter-servces + String[] sideEffects = new String[] {""}; + List pathParams = new ArrayList<>(); + for (Expression pathExp: dstRes.getPathParams()) { + pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); + } + String srcResName = JerseyCodeGenerator.toVariableName(srcResourceName); + if (inDegree <= 1) { + srcResName = null; + } + if (!chainedCalls.contains(srcUpdate)) { + // The first call to an update method in this method + srcUpdate.addStatement(getHttpMethodParamsStatement(srcComponent.getTypeName(), params, true)); + srcUpdate.addStatement("String result = " + getHttpMethodCallStatement(baseURL, + dstRes.getResourceHierarchy().toResourcePath(pathParams), + srcResName, + httpMethod)); + chainedCalls.add(srcUpdate); + } else { + // After the second time of call to update methods in this method + srcUpdate.addStatement(getHttpMethodParamsStatement(srcComponent.getTypeName(), params, false)); + srcUpdate.addStatement("result = " + getHttpMethodCallStatement(baseURL, + dstRes.getResourceHierarchy().toResourcePath(pathParams), + srcResName, + httpMethod)); + } + srcUpdate.addThrow("JsonProcessingException"); + } else { + // Inner-service + String updateMethodName = null; + if (JerseyCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { + updateMethodName = "updateFrom" + srcResourceName; + } else { + updateMethodName = "update" + dstResourceName + "From" + srcResourceName; + } + String callParams = ""; + String delimiter = ""; + // Values of path parameters. + for (Expression pathParam: dstRes.getPathParams()) { + if (pathParam instanceof Variable) { + Variable pathVar = (Variable) pathParam; + callParams += delimiter + pathVar.getName(); + delimiter = ", "; + } + } + // Values of other parameters. + for (Map.Entry> paramEnt: params) { + callParams += delimiter + paramEnt.getValue().getValue(); + delimiter = ", "; + } + if (srcComponent != dstComponent) { + srcUpdate.addStatement("this." + JerseyCodeGenerator.toVariableName(dstResourceName) + "." + updateMethodName + "(" + callParams + ");"); + } else { + srcUpdate.addStatement("this." + updateMethodName + "(" + callParams + ");"); + } + if (update != null && update.getThrows() != null && update.getThrows().getExceptions().contains("JsonProcessingException")) { + srcUpdate.addThrow("JsonProcessingException"); + } + } + } + } + for (MethodDeclaration srcInput: getInputMethods(srcComponent, src, model)) { + List>> params = new ArrayList<>(); + ResourcePath dstRes = out.getResource(); + // Values of channel parameters. + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + params.add(new AbstractMap.SimpleEntry<>(selVar.getType(), + new AbstractMap.SimpleEntry<>(selVar.getName(), selVar.getName()))); + } + } + // Value of the source side (input side) resource. + String srcFieldName = "this.value"; + if (!JerseyCodeGenerator.generatesComponent(src.getResourceHierarchy())) { + srcFieldName = JerseyCodeGenerator.toVariableName(srcResourceName); + } + params.add(new AbstractMap.SimpleEntry<>(src.getResourceStateType(), + new AbstractMap.SimpleEntry<>(JerseyCodeGenerator.toVariableName(srcResourceName), srcFieldName))); + 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()) { - String refResourceName = ref.getResourceName(); + if (!dst.getInSideResources().contains(ref)) { + String refResourceName = ref.getLeafResourceName(); Type refResourceType = ref.getResourceStateType(); if (!referredSet.contains(ref)) { referredSet.add(ref); - generatePullDataTransfer(srcUpdate, refResourceName, refResourceType); + String[] sideEffects = new String[] {""}; + if (rc.isOutside()) { + List pathParams = new ArrayList<>(); + for (Expression pathExp: ref.getPathParams()) { + pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); + } + generatePullDataTransfer(srcInput, refResourceName, ref.getResourceHierarchy().toResourcePath(pathParams), refResourceType); + } else { + ResourcePath srcRes = in.getResource(); + if (!JerseyCodeGenerator.generatesComponent(srcRes.getResourceHierarchy())) { + srcRes = srcRes.getParent(); + } + Expression refGetter = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(ref, srcRes); + String refExp = refGetter.toImplementation(sideEffects); + String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); + srcInput.addFirstStatement(sideEffects[0] + refTypeName + " " + ref.getLeafResourceName() + " = " + refExp + ";"); + } } // Value of a reference side resource. params.add(new AbstractMap.SimpleEntry<>(refResourceType, new AbstractMap.SimpleEntry<>(refResourceName, refResourceName))); + } + } + if (outsideOutputResource || (in.getResource().getCommonPrefix(dstRes) == null && JerseyCodeGenerator.differentTreesAsDifferentServices)) { + // Inter-services + String[] sideEffects = new String[] {""}; + List pathParams = new ArrayList<>(); + for (Expression pathExp: dstRes.getPathParams()) { + pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); + } + String srcResName = JerseyCodeGenerator.toVariableName(srcResourceName); + if (inDegree <= 1) { + srcResName = null; + } + if (!chainedCalls.contains(srcInput)) { + // First call to an update method in this method + srcInput.addStatement(getHttpMethodParamsStatement(srcComponent.getTypeName(), params, true)); + srcInput.addStatement("String result = " + getHttpMethodCallStatement(baseURL, + dstRes.getResourceHierarchy().toResourcePath(pathParams), + srcResName, + httpMethod)); + chainedCalls.add(srcInput); + } else { + // After the second time of call to update methods in this method + srcInput.addStatement(getHttpMethodParamsStatement(srcComponent.getTypeName(), params, false)); + srcInput.addStatement("result = " + getHttpMethodCallStatement(baseURL, + dstRes.getResourceHierarchy().toResourcePath(pathParams), + srcResName, + httpMethod)); + } + srcInput.addThrow("JsonProcessingException"); + } else { + // Inner-service + String updateMethodName = null; + if (JerseyCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { + updateMethodName = "updateFrom" + srcResourceName; + } else { + updateMethodName = "update" + dstResourceName + "From" + srcResourceName; + } + String callParams = ""; + String delimiter = ""; + // Values of path parameters. + for (Expression pathParam: dstRes.getPathParams()) { + if (pathParam instanceof Variable) { + Variable pathVar = (Variable) pathParam; + callParams += delimiter + pathVar.getName(); + delimiter = ", "; + } + } + // Values of other parameters. + for (Map.Entry> paramEnt: params) { + callParams += delimiter + paramEnt.getValue().getValue(); + delimiter = ", "; + } + if (srcComponent != dstComponent) { + srcInput.addStatement("this." + JerseyCodeGenerator.toVariableName(dstResourceName) + "." + updateMethodName + "(" + callParams + ");"); + } else { + srcInput.addStatement("this." + updateMethodName + "(" + callParams + ");"); + } + if (update != null && update.getThrows() != null && update.getThrows().getExceptions().contains("JsonProcessingException")) { + srcInput.addThrow("JsonProcessingException"); } } } - String srcResName = null; - if (dst.getIndegree() > 1) { - srcResName = srcResourceName; + } 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); } - 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); + MethodDeclaration getter = null; + if (JerseyCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { + getter = getMethod(dstComponent, "getValue"); } 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)); + getter = getGetterMethod(dstComponent, dstResourceName); } - 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); + if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { + // generate a return statement. + Expression curExp = ch.deriveUpdateExpressionOf(out, JerseyCodeGenerator.pullAccessor).getKey(); // no pull data transfer is included. + Map>> resourcePaths = ch.fillOutsideResourcePaths(out, JerseyCodeGenerator.pullAccessor); + String[] sideEffects = new String[] {""}; + String curState = curExp.toImplementation(sideEffects); + getter.addStatement(sideEffects[0] + "return " + curState + ";"); + // For each reference channel member, get the current state of the reference side resource by pull data transfer. + for (ChannelMember rc: ch.getReferenceChannelMembers()) { + ResourcePath refRes = rc.getResource(); + String refResourceName = refRes.getLeafResourceName(); + Type refResourceType = refRes.getResourceStateType(); + if (rc.isOutside()) { + List pathParams = new ArrayList<>(); + for (Expression pathExp: refRes.getPathParams()) { + pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); + } + generatePullDataTransfer(getter, refResourceName, refRes.getResourceHierarchy().toResourcePath(pathParams), refResourceType); + } else { + ResourcePath dstRes = out.getResource(); + if (!JerseyCodeGenerator.generatesComponent(dstRes.getResourceHierarchy())) { + dstRes = dstRes.getParent(); + } + Expression refGetter = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(refRes, dstRes); + String refExp = refGetter.toImplementation(sideEffects); + String refTypeName = refResourceType.getInterfaceTypeName(); + getter.addFirstStatement(sideEffects[0] + refTypeName + " " + refResourceName + " = " + refExp + ";"); + } } - // 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"); + for (Entry>> pathEnt: resourcePaths.entrySet()) { + ChannelMember cm = pathEnt.getKey(); + ResourcePath src2 = pathEnt.getValue().getKey(); + // get outside src resource state by pull data transfer. + if (cm.isOutside() || src2.getCommonPrefix(dst.getInSideResource(ch)) == null) { + Type srcResourceType = src2.getResourceStateType(); + List pathParams = new ArrayList<>(); + for (Expression pathExp: src2.getPathParams()) { + pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); + } + generatePullDataTransfer(getter, src2.getLeafResourceName(), src2.getResourceHierarchy().toResourcePath(pathParams), srcResourceType); + } + } + } + // get src resource state by pull data transfer. + if (src.getNumberOfParameters() == 0 && src.getOutSideResource(ch).getCommonPrefix(dst.getInSideResource(ch)) == null) { + Type srcResourceType = src.getResourceStateType(); + List pathParams = new ArrayList<>(); + generatePullDataTransfer(getter, src.getResourceName(), src.getResourceHierarchy().toResourcePath(pathParams), srcResourceType); + } + } } - } 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()) { + DataTransferChannel ch = entry.getKey(); + 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, ch.getOutputChannelMembers().size()); + } 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, ch.getOutputChannelMembers().size()); + } + } + if (input != null) { + // In each resource + Set referredSet = referredResources.get(input); + 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(input, referredSet); + } + if (!out.getResource().equals(ref)) { + String refResourceName = ref.getLeafResourceName(); + Type refResourceType = ref.getResourceStateType(); + if (!referredSet.contains(ref)) { + referredSet.add(ref); + String[] sideEffects = new String[] {""}; + if (rc.isOutside()) { + List pathParams = new ArrayList<>(); + for (Expression pathExp: ref.getPathParams()) { + pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); + } + generatePullDataTransfer(input, refResourceName, ref.getResourceHierarchy().toResourcePath(pathParams), refResourceType); + } else { + ResourcePath dstRes = out.getResource(); + if (!JerseyCodeGenerator.generatesComponent(dstRes.getResourceHierarchy())) { + dstRes = dstRes.getParent(); + } + Expression refGetter = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(ref, dstRes); + String refExp = refGetter.toImplementation(sideEffects); + String refTypeName = refResourceType.getInterfaceTypeName(); + input.addFirstStatement(sideEffects[0] + refTypeName + " " + refResourceName + " = " + refExp + ";"); + } + } + } + } + Expression updateExp = ch.deriveUpdateExpressionOf(out, JerseyCodeGenerator.refAccessor).getKey(); + // 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]; @@ -320,7 +858,81 @@ updateStatement = sideEffects[0] + "this.value = " + newState + ";"; } if (input.getBody() == null || !input.getBody().getStatements().contains(updateStatement)) { - input.addFirstStatement(updateStatement); + input.addStatement(updateStatement); + } + } else { + String updateStatement = ""; + if (sideEffects[0] != null) { + updateStatement = sideEffects[0]; + updateStatement = updateStatement.replace(".value", "." + JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(resource))); + } + 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 += 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 += sideEffects2[0]; + } else if (!(updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect())) { + updateStatement += "this." + JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(resource)) + " = " + newState + ";"; + } + if (updateStatement != null && (input.getBody() == null || !input.getBody().getStatements().contains(updateStatement))) { + input.addStatement(updateStatement); + } + } + + if (out.getResource().getParent() != null && out.getResource().getParent().getParent() != null) { + // In the root resource + Expression message = out.getStateTransition().getMessageExpression(); + String inputAccessorName = input.getName(); + if (message instanceof Term) { + inputAccessorName = ((Term) message).getSymbol().getImplName(); + } else if (message instanceof Variable) { + inputAccessorName = ((Variable) message).getName(); + } + MethodDeclaration inputAccessor = getMethod(componentMap.get(JerseyCodeGenerator.getComponentName(resource.getRoot())), inputAccessorName); + if (inputAccessor != null) { + // The expression of the receiver (resource) of the input method. + 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}); + // Values of channel parameters. + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + args += delimiter + selVar.getName(); + delimiter = ", "; + } + } + // Values of message parameters. + if (message instanceof Term) { + for (Variable mesVar: message.getVariables().values()) { + args += delimiter + mesVar.getName(); + delimiter = ", "; + } + } + inputAccessor.addStatement(resourceAccess + "." + input.getName() + "(" + args + ");"); + if (input != null && input.getThrows() != null && input.getThrows().getExceptions().contains("JsonProcessingException")) { + inputAccessor.addThrow("JsonProcessingException"); + } } } } @@ -334,7 +946,46 @@ 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) { + if (param instanceof Term) { + if (((Term) param).getType() == null) { + ((Term) param).setType(var.getType()); + } + } else if (param instanceof Variable) { + if (((Variable) param).getType() == null) { + ((Variable) param).setType(var.getType()); + } + } + constructorInvocation = constructorInvocation + delimiter + param.toImplementation(null); + } 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 +1024,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 +1167,54 @@ return "client.target(\"" + baseURL + "\").path(\"/" + resourceName + "\").request()." + httpMethod + "(" + responseShortTypeName + ".class);"; } - private static MethodDeclaration getUpdateMethod(TypeDeclaration type, TypeDeclaration from) { - for (MethodDeclaration m: type.getMethods()) { - if (m.getName().equals("update" + from.getTypeName())) return m; + private static MethodDeclaration getConstructor(TypeDeclaration component) { + for (MethodDeclaration m: component.getMethods()) { + if (m.isConstructor()) return m; + } + return null; + } + + private static MethodDeclaration getUpdateMethod(TypeDeclaration component, String dstResName, String srcResName) { + for (MethodDeclaration m: component.getMethods()) { + if (dstResName == null) { + if (m.getName().equals("updateFrom" + srcResName)) return m; + } else { + if (m.getName().equals("update" + dstResName + "From" + srcResName)) 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, String resName) { List updates = new ArrayList<>(); - for (MethodDeclaration m: type.getMethods()) { - if (m.getName().startsWith("update")) { - updates.add(m); + for (MethodDeclaration m: component.getMethods()) { + if (resName == null) { + if (m.getName().startsWith("updateFrom")) { + updates.add(m); + } + } else { + if (m.getName().startsWith("update" + resName + "From")) { + 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()) { + for (Channel c: model.getInputChannels()) { 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 +1229,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()) { + for (Channel c: model.getInputChannels()) { 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, channel.getOutputChannelMembers().size()); inputs.add(input); } } @@ -576,20 +1244,24 @@ return inputs; } - private static MethodDeclaration getInputMethod(TypeDeclaration type, ChannelMember out) { - 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()); + private static MethodDeclaration getInputMethod(TypeDeclaration component, ChannelMember cm, int outNumber) { + String inputMethodName = null; + if (cm.getStateTransition().getMessageExpression() instanceof Term) { + Term message = (Term) cm.getStateTransition().getMessageExpression(); + inputMethodName = message.getSymbol().getImplName(); + } else if (cm.getStateTransition().getMessageExpression() instanceof Variable) { + Variable message = (Variable) cm.getStateTransition().getMessageExpression(); + inputMethodName = message.getName(); } + if (outNumber > 1) { + inputMethodName += "For" + JerseyCodeGenerator.getComponentName(cm.getResource().getResourceHierarchy()); + } + MethodDeclaration input = getMethod(component, inputMethodName); 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..5f533a7 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 Type[] {}, 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..20c3cf2 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/algebra/Symbol.java +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Symbol.java @@ -1,15 +1,18 @@ package models.algebra; +import java.util.List; + 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; + protected ICalculator calculator = null; public Symbol(String name) { this.name = name; @@ -66,6 +69,37 @@ } } + public Symbol(String name, int arity, ICalculator calculator) { + this.name = name; + this.arity = arity; + this.calculator = calculator; + } + + public Symbol(String name, int arity, Type operatorType, ICalculator calculator) { + this.name = name; + this.arity = arity; + this.operatorType = operatorType; + this.calculator = calculator; + } + + public Symbol(String name, int arity, Type operatorType, IImplGenerator generator, ICalculator calculator) { + this.name = name; + this.arity = arity; + this.operatorType = operatorType; + this.generator = generator; + this.implOperatorType = Type.GENERATIVE; + this.calculator = calculator; + } + + public Symbol(String name, int arity, Type operatorType, String implName, Type implOperatorType, ICalculator calculator) { + this.name = name; + this.implName = implName; + this.arity = arity; + this.operatorType = operatorType; + this.implOperatorType = implOperatorType; + this.calculator = calculator; + } + public void setArity(int arity) { this.arity = arity; } @@ -89,6 +123,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; @@ -155,14 +193,26 @@ /** * Generate the implementation of this symbol * @param type the type of this symbol + * @param childrenTypes * @param childrenImpl the implementations of the children * @param childrenSideEffects (input) an array of the side effects of the children * @param sideEffect (output) an array of the side effect of this symbol * @return the implementation */ - public String generate(models.algebra.Type type, String[] childrenImpl, String[] childrenSideEffects, String[] sideEffect) { + public String generate(models.algebra.Type type, models.algebra.Type[] childrenTypes, String[] childrenImpl, String[] childrenSideEffects, String[] sideEffect) { if (generator != null) { - return generator.generate(type, childrenImpl, childrenSideEffects, sideEffect); + return generator.generate(type, childrenTypes, childrenImpl, childrenSideEffects, sideEffect); + } + return null; + } + + public boolean isCalculatable() { + return (calculator != null); + } + + public Expression calculate(List args) { + if (calculator != null) { + return calculator.calculate(args); } return null; } @@ -219,11 +269,16 @@ /** * Generate the implementation * @param type the type of this expression + * @param childrenTypes * @param children the implementations of the children * @param childrenSideEffects (input) an array of the side effects of the children * @param sideEffect (output) an array of the side effect of this generator * @return the generated implementation */ - public String generate(models.algebra.Type type, String children[], String[] childrenSideEffects, String[] sideEffect); + public String generate(models.algebra.Type type, models.algebra.Type[] childrenTypes, String children[], String[] childrenSideEffects, String[] sideEffect); + } + + public interface ICalculator { + public Expression calculate(List args); } } diff --git a/AlgebraicDataflowArchitectureModel/src/models/algebra/Term.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/Term.java index b7cad09..1b5a6ac 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; } @@ -45,6 +53,12 @@ children.add(child); return true; } + + public boolean setChild(int n, Expression child) { + if (getArity() != -1 && n >= getArity()) return false; + children.set(n, child); + return true; + } public void addChild(Expression child, boolean bForced) { if (!bForced && getArity() != -1 && children.size() >= getArity()) return; @@ -55,7 +69,7 @@ return children.get(n); } - public ArrayList getChildren() { + public List getChildren() { return children; } @@ -123,6 +137,66 @@ } } + 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 if (symbol.isCalculatable()) { + List newChildren = new ArrayList<>(); + for (Expression child: children) { + if (child instanceof Term) { + child = ((Term) child).reduce(); + } + newChildren.add(child); + } + Expression newTerm = symbol.calculate(newChildren); + if (newTerm == null) return this; + return newTerm; + } 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 +259,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++) { @@ -233,15 +307,22 @@ return exp; } if (symbol.isImplGenerative()) { + Type childrenTypes[] = new Type[children.size()]; String childrenImpl[] = new String[children.size()]; String childrenSideEffects[] = new String[children.size()]; if (implParamOrder == null) { for (int i = 0; i < children.size(); i++) { + Expression child = children.get(i); + if (child instanceof Variable) { + childrenTypes[i] = ((Variable) child).getType(); + } else if (child instanceof Term) { + childrenTypes[i] = ((Term) child).getType(); + } String childSideEffect[] = new String[] {""}; - childrenImpl[i] = children.get(i).toImplementation(childSideEffect); + childrenImpl[i] = child.toImplementation(childSideEffect); childrenSideEffects[i] = childSideEffect[0]; } - String exp = symbol.generate(getType(), childrenImpl, childrenSideEffects, sideEffects); + String exp = symbol.generate(getType(), childrenTypes, childrenImpl, childrenSideEffects, sideEffects); if (symbol.isImplWithSideEffect()) { sideEffects[0] = sideEffects[0] + exp; exp = children.get(0).toImplementation(new String[] {""}); // the value of this term @@ -249,11 +330,17 @@ return exp; } else { for (int i = 0; i < children.size(); i++) { + Expression child = children.get(implParamOrder[i]); + if (child instanceof Variable) { + childrenTypes[i] = ((Variable) child).getType(); + } else if (child instanceof Term) { + childrenTypes[i] = ((Term) child).getType(); + } String childSideEffect[] = new String[] {""}; - childrenImpl[i] = children.get(implParamOrder[i]).toImplementation(childSideEffect); + childrenImpl[i] = child.toImplementation(childSideEffect); childrenSideEffects[i] = childSideEffect[0]; } - String exp = symbol.generate(getType(), childrenImpl, childrenSideEffects, sideEffects); + String exp = symbol.generate(getType(), childrenTypes, childrenImpl, childrenSideEffects, sideEffects); if (symbol.isImplWithSideEffect()) { sideEffects[0] = sideEffects[0] + exp; exp = children.get(implParamOrder[0]).toImplementation(new String[] {""}); // the value of this term @@ -268,9 +355,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 +372,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..4e71af2 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Channel.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Channel.java @@ -1,25 +1,44 @@ 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; + protected boolean isNative = false; 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 +49,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 +91,30 @@ 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); + } + } else { + if (channelMember.getResource().getPathParams().size() == 0) { + channelMember.setOutside(false); + } else { + 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; } @@ -93,4 +143,12 @@ } return sourceText; } + + public boolean isNative() { + return isNative; + } + + public void setNative(boolean isNative) { + this.isNative = isNative; + } } diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ChannelMember.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ChannelMember.java index 190a49f..9cf0280 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,16 +30,24 @@ 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) { - return resourcePath.getResourceName() + "(" + return resourcePath.getLeafResourceName() + "(" + stateTransition.getCurStateExpression() + "," + stateTransition.getMessageExpression() + ")"; } - return resourcePath.getResourceName() + "(" + return resourcePath.getLeafResourceName() + "(" + stateTransition.getCurStateExpression() + "," + stateTransition.getMessageExpression() + ")" - + " == " + stateTransition.getNextStateExpression(); + + " = " + stateTransition.getNextStateExpression(); } } diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/DataConstraintModel.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/DataConstraintModel.java index 35b2aa7..eb5e29e 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/DataConstraintModel.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/DataConstraintModel.java @@ -1,16 +1,24 @@ package models.dataConstraintModel; +import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.List; +import models.algebra.Constant; +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 inputChannels = null; protected HashMap types = null; protected HashMap symbols = null; public static final Type typeInt = new Type("Int", "int"); @@ -27,22 +35,482 @@ 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 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 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); + 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, new Symbol.ICalculator() { + @Override + public Expression calculate(List args) { + if (!(args.get(0) instanceof Constant)) { + return null; + } + if (!(args.get(1) instanceof Constant)) { + return null; + } + Constant arg0 = (Constant) args.get(0); + Constant arg1 = (Constant) args.get(1); + String sArg0 = arg0.getSymbol().toString(); + String sArg1 = arg1.getSymbol().toString(); + if (arg0.getType().equals(typeDouble) || arg1.getType().equals(typeDouble)) { + return new Constant(Double.toString(Double.parseDouble(sArg0) + Double.parseDouble(sArg1)), typeDouble); + } else if (arg0.getType().equals(typeFloat) || arg1.getType().equals(typeFloat)) { + return new Constant(Float.toString(Float.parseFloat(sArg0) + Float.parseFloat(sArg1)), typeFloat); + } else if (arg0.getType().equals(typeLong) || arg1.getType().equals(typeLong)) { + return new Constant(Long.toString(Long.parseLong(sArg0) + Long.parseLong(sArg1)), typeLong); + } else if (arg0.getType().equals(typeInt) || arg1.getType().equals(typeInt)) { + return new Constant(Integer.toString(Integer.parseInt(sArg0) + Integer.parseInt(sArg1)), typeInt); + } + return new Constant(Integer.toString(Integer.parseInt(sArg0) + Integer.parseInt(sArg1))); + } + }); + public static final Symbol mul = new Symbol(Parser.MUL, 2, Symbol.Type.INFIX, new Symbol.ICalculator() { + @Override + public Expression calculate(List args) { + if (!(args.get(0) instanceof Constant)) { + return null; + } + if (!(args.get(1) instanceof Constant)) { + return null; + } + Constant arg0 = (Constant) args.get(0); + Constant arg1 = (Constant) args.get(1); + String sArg0 = arg0.getSymbol().toString(); + String sArg1 = arg1.getSymbol().toString(); + if (arg0.getType().equals(typeDouble) || arg1.getType().equals(typeDouble)) { + return new Constant(Double.toString(Double.parseDouble(sArg0) * Double.parseDouble(sArg1)), typeDouble); + } else if (arg0.getType().equals(typeFloat) || arg1.getType().equals(typeFloat)) { + return new Constant(Float.toString(Float.parseFloat(sArg0) * Float.parseFloat(sArg1)), typeFloat); + } else if (arg0.getType().equals(typeLong) || arg1.getType().equals(typeLong)) { + return new Constant(Long.toString(Long.parseLong(sArg0) * Long.parseLong(sArg1)), typeLong); + } else if (arg0.getType().equals(typeInt) || arg1.getType().equals(typeInt)) { + return new Constant(Integer.toString(Integer.parseInt(sArg0) * Integer.parseInt(sArg1)), typeInt); + } + return new Constant(Integer.toString(Integer.parseInt(sArg0) * Integer.parseInt(sArg1))); + } + }); + public static final Symbol sub = new Symbol(Parser.SUB, 2, Symbol.Type.INFIX, new Symbol.ICalculator() { + @Override + public Expression calculate(List args) { + if (!(args.get(0) instanceof Constant)) { + return null; + } + if (!(args.get(1) instanceof Constant)) { + return null; + } + Constant arg0 = (Constant) args.get(0); + Constant arg1 = (Constant) args.get(1); + String sArg0 = arg0.getSymbol().toString(); + String sArg1 = arg1.getSymbol().toString(); + if (arg0.getType().equals(typeDouble) || arg1.getType().equals(typeDouble)) { + return new Constant(Double.toString(Double.parseDouble(sArg0) - Double.parseDouble(sArg1)), typeDouble); + } else if (arg0.getType().equals(typeFloat) || arg1.getType().equals(typeFloat)) { + return new Constant(Float.toString(Float.parseFloat(sArg0) - Float.parseFloat(sArg1)), typeFloat); + } else if (arg0.getType().equals(typeLong) || arg1.getType().equals(typeLong)) { + return new Constant(Long.toString(Long.parseLong(sArg0) - Long.parseLong(sArg1)), typeLong); + } else if (arg0.getType().equals(typeInt) || arg1.getType().equals(typeInt)) { + return new Constant(Integer.toString(Integer.parseInt(sArg0) - Integer.parseInt(sArg1)), typeInt); + } + return new Constant(Integer.toString(Integer.parseInt(sArg0) - Integer.parseInt(sArg1))); + } + }); + public static final Symbol div = new Symbol(Parser.DIV, 2, Symbol.Type.INFIX, new Symbol.ICalculator() { + @Override + public Expression calculate(List args) { + if (!(args.get(0) instanceof Constant)) { + return null; + } + if (!(args.get(1) instanceof Constant)) { + return null; + } + Constant arg0 = (Constant) args.get(0); + Constant arg1 = (Constant) args.get(1); + String sArg0 = arg0.getSymbol().toString(); + String sArg1 = arg1.getSymbol().toString(); + if (arg0.getType().equals(typeDouble) || arg1.getType().equals(typeDouble)) { + return new Constant(Double.toString(Double.parseDouble(sArg0) / Double.parseDouble(sArg1)), typeDouble); + } else if (arg0.getType().equals(typeFloat) || arg1.getType().equals(typeFloat)) { + return new Constant(Float.toString(Float.parseFloat(sArg0) / Float.parseFloat(sArg1)), typeFloat); + } else if (arg0.getType().equals(typeLong) || arg1.getType().equals(typeLong)) { + return new Constant(Long.toString(Long.parseLong(sArg0) / Long.parseLong(sArg1)), typeLong); + } else if (arg0.getType().equals(typeInt) || arg1.getType().equals(typeInt)) { + return new Constant(Integer.toString(Integer.parseInt(sArg0) / Integer.parseInt(sArg1)), typeInt); + } + return new Constant(Integer.toString(Integer.parseInt(sArg0) / Integer.parseInt(sArg1))); + } + }); + public static final Symbol mod = new Symbol(Parser.MOD, 2, Symbol.Type.INFIX, "%", Symbol.Type.INFIX, new Symbol.ICalculator() { + @Override + public Expression calculate(List args) { + if (!(args.get(0) instanceof Constant)) { + return null; + } + if (!(args.get(1) instanceof Constant)) { + return null; + } + Constant arg0 = (Constant) args.get(0); + Constant arg1 = (Constant) args.get(1); + String sArg0 = arg0.getSymbol().toString(); + String sArg1 = arg1.getSymbol().toString(); + if (arg0.getType().equals(typeLong) || arg1.getType().equals(typeLong)) { + return new Constant(Long.toString(Long.parseLong(sArg0) % Long.parseLong(sArg1)), typeLong); + } else if (arg0.getType().equals(typeInt) || arg1.getType().equals(typeInt)) { + return new Constant(Integer.toString(Integer.parseInt(sArg0) % Integer.parseInt(sArg1)), typeInt); + } + return new Constant(Integer.toString(Integer.parseInt(sArg0) % Integer.parseInt(sArg1))); + } + }); + public static final Symbol minus = new Symbol(Parser.MINUS, 1, new Symbol.ICalculator() { + @Override + public Expression calculate(List args) { + if (!(args.get(0) instanceof Constant)) { + return null; + } + Constant arg = (Constant) args.get(0); + String sArg = arg.getSymbol().toString(); + if (arg.getType().equals(typeDouble)) { + return new Constant(Double.toString(-Double.parseDouble(sArg)), typeDouble); + } else if (arg.getType().equals(typeFloat)) { + return new Constant(Float.toString(-Float.parseFloat(sArg)), typeFloat); + } else if (arg.getType().equals(typeLong)) { + return new Constant(Long.toString(-Long.parseLong(sArg)), typeLong); + } else if (arg.getType().equals(typeInt)) { + return new Constant(Integer.toString(-Integer.parseInt(sArg)), typeInt); + } + return new Constant(Integer.toString(-Integer.parseInt(sArg))); + } + }); + public static final Symbol eq = new Symbol(Parser.EQ, 2, Symbol.Type.INFIX, "==", Symbol.Type.INFIX, new Symbol.ICalculator() { + @Override + public Expression calculate(List args) { + if (!(args.get(0) instanceof Constant)) { + return null; + } + if (!(args.get(1) instanceof Constant)) { + return null; + } + Constant arg0 = (Constant) args.get(0); + Constant arg1 = (Constant) args.get(1); + String sArg0 = arg0.getSymbol().toString(); + String sArg1 = arg1.getSymbol().toString(); + boolean result = false; + if (arg0.getType().equals(typeDouble) || arg1.getType().equals(typeDouble)) { + result = (Double.parseDouble(sArg0) == Double.parseDouble(sArg1)); + } else if (arg0.getType().equals(typeFloat) || arg1.getType().equals(typeFloat)) { + result = (Float.parseFloat(sArg0) == Float.parseFloat(sArg1)); + } else if (arg0.getType().equals(typeLong) || arg1.getType().equals(typeLong)) { + result = (Long.parseLong(sArg0) == Long.parseLong(sArg1)); + } else if (arg0.getType().equals(typeInt) || arg1.getType().equals(typeInt)) { + result = (Integer.parseInt(sArg0) == Integer.parseInt(sArg1)); + } + if (result) { + return new Constant(true_); + } else { + return new Constant(false_); + } + } + }); + public static final Symbol neq = new Symbol(Parser.NEQ, 2, Symbol.Type.INFIX, "!=", Symbol.Type.INFIX, new Symbol.ICalculator() { + @Override + public Expression calculate(List args) { + if (!(args.get(0) instanceof Constant)) { + return null; + } + if (!(args.get(1) instanceof Constant)) { + return null; + } + Constant arg0 = (Constant) args.get(0); + Constant arg1 = (Constant) args.get(1); + String sArg0 = arg0.getSymbol().toString(); + String sArg1 = arg1.getSymbol().toString(); + boolean result = false; + if (arg0.getType().equals(typeDouble) || arg1.getType().equals(typeDouble)) { + result = (Double.parseDouble(sArg0) != Double.parseDouble(sArg1)); + } else if (arg0.getType().equals(typeFloat) || arg1.getType().equals(typeFloat)) { + result = (Float.parseFloat(sArg0) != Float.parseFloat(sArg1)); + } else if (arg0.getType().equals(typeLong) || arg1.getType().equals(typeLong)) { + result = (Long.parseLong(sArg0) != Long.parseLong(sArg1)); + } else if (arg0.getType().equals(typeInt) || arg1.getType().equals(typeInt)) { + result = (Integer.parseInt(sArg0) != Integer.parseInt(sArg1)); + } + if (result) { + return new Constant(true_); + } else { + return new Constant(false_); + } + } + }); + public static final Symbol gt = new Symbol(Parser.GT, 2, Symbol.Type.INFIX, ">", Symbol.Type.INFIX, new Symbol.ICalculator() { + @Override + public Expression calculate(List args) { + if (!(args.get(0) instanceof Constant)) { + return null; + } + if (!(args.get(1) instanceof Constant)) { + return null; + } + Constant arg0 = (Constant) args.get(0); + Constant arg1 = (Constant) args.get(1); + String sArg0 = arg0.getSymbol().toString(); + String sArg1 = arg1.getSymbol().toString(); + boolean result = false; + if (arg0.getType().equals(typeDouble) || arg1.getType().equals(typeDouble)) { + result = (Double.parseDouble(sArg0) > Double.parseDouble(sArg1)); + } else if (arg0.getType().equals(typeFloat) || arg1.getType().equals(typeFloat)) { + result = (Float.parseFloat(sArg0) > Float.parseFloat(sArg1)); + } else if (arg0.getType().equals(typeLong) || arg1.getType().equals(typeLong)) { + result = (Long.parseLong(sArg0) > Long.parseLong(sArg1)); + } else if (arg0.getType().equals(typeInt) || arg1.getType().equals(typeInt)) { + result = (Integer.parseInt(sArg0) > Integer.parseInt(sArg1)); + } + if (result) { + return new Constant(true_); + } else { + return new Constant(false_); + } + } + }); + public static final Symbol lt = new Symbol(Parser.LT, 2, Symbol.Type.INFIX, "<", Symbol.Type.INFIX, new Symbol.ICalculator() { + @Override + public Expression calculate(List args) { + if (!(args.get(0) instanceof Constant)) { + return null; + } + if (!(args.get(1) instanceof Constant)) { + return null; + } + Constant arg0 = (Constant) args.get(0); + Constant arg1 = (Constant) args.get(1); + String sArg0 = arg0.getSymbol().toString(); + String sArg1 = arg1.getSymbol().toString(); + boolean result = false; + if (arg0.getType().equals(typeDouble) || arg1.getType().equals(typeDouble)) { + result = (Double.parseDouble(sArg0) < Double.parseDouble(sArg1)); + } else if (arg0.getType().equals(typeFloat) || arg1.getType().equals(typeFloat)) { + result = (Float.parseFloat(sArg0) < Float.parseFloat(sArg1)); + } else if (arg0.getType().equals(typeLong) || arg1.getType().equals(typeLong)) { + result = (Long.parseLong(sArg0) < Long.parseLong(sArg1)); + } else if (arg0.getType().equals(typeInt) || arg1.getType().equals(typeInt)) { + result = (Integer.parseInt(sArg0) < Integer.parseInt(sArg1)); + } + if (result) { + return new Constant(true_); + } else { + return new Constant(false_); + } + } + }); + public static final Symbol ge = new Symbol(Parser.GE, 2, Symbol.Type.INFIX, ">=", Symbol.Type.INFIX, new Symbol.ICalculator() { + @Override + public Expression calculate(List args) { + if (!(args.get(0) instanceof Constant)) { + return null; + } + if (!(args.get(1) instanceof Constant)) { + return null; + } + Constant arg0 = (Constant) args.get(0); + Constant arg1 = (Constant) args.get(1); + String sArg0 = arg0.getSymbol().toString(); + String sArg1 = arg1.getSymbol().toString(); + boolean result = false; + if (arg0.getType().equals(typeDouble) || arg1.getType().equals(typeDouble)) { + result = (Double.parseDouble(sArg0) >= Double.parseDouble(sArg1)); + } else if (arg0.getType().equals(typeFloat) || arg1.getType().equals(typeFloat)) { + result = (Float.parseFloat(sArg0) >= Float.parseFloat(sArg1)); + } else if (arg0.getType().equals(typeLong) || arg1.getType().equals(typeLong)) { + result = (Long.parseLong(sArg0) >= Long.parseLong(sArg1)); + } else if (arg0.getType().equals(typeInt) || arg1.getType().equals(typeInt)) { + result = (Integer.parseInt(sArg0) >= Integer.parseInt(sArg1)); + } + if (result) { + return new Constant(true_); + } else { + return new Constant(false_); + } + } + }); + public static final Symbol le = new Symbol(Parser.LE, 2, Symbol.Type.INFIX, "<=", Symbol.Type.INFIX, new Symbol.ICalculator() { + @Override + public Expression calculate(List args) { + if (!(args.get(0) instanceof Constant)) { + return null; + } + if (!(args.get(1) instanceof Constant)) { + return null; + } + Constant arg0 = (Constant) args.get(0); + Constant arg1 = (Constant) args.get(1); + String sArg0 = arg0.getSymbol().toString(); + String sArg1 = arg1.getSymbol().toString(); + boolean result = false; + if (arg0.getType().equals(typeDouble) || arg1.getType().equals(typeDouble)) { + result = (Double.parseDouble(sArg0) <= Double.parseDouble(sArg1)); + } else if (arg0.getType().equals(typeFloat) || arg1.getType().equals(typeFloat)) { + result = (Float.parseFloat(sArg0) <= Float.parseFloat(sArg1)); + } else if (arg0.getType().equals(typeLong) || arg1.getType().equals(typeLong)) { + result = (Long.parseLong(sArg0) <= Long.parseLong(sArg1)); + } else if (arg0.getType().equals(typeInt) || arg1.getType().equals(typeInt)) { + result = (Integer.parseInt(sArg0) <= Integer.parseInt(sArg1)); + } + if (result) { + return new Constant(true_); + } else { + return new Constant(false_); + } + } + }); + public static final Symbol and = new Symbol(Parser.AND, 2, Symbol.Type.INFIX, "&&", Symbol.Type.INFIX, new Symbol.ICalculator() { + @Override + public Expression calculate(List args) { + if (!(args.get(0) instanceof Constant)) { + return null; + } + if (!(args.get(1) instanceof Constant)) { + return null; + } + Constant arg0 = (Constant) args.get(0); + Constant arg1 = (Constant) args.get(1); + String sArg0 = arg0.getSymbol().toString(); + String sArg1 = arg1.getSymbol().toString(); + boolean result = false; + if (arg0.getType().equals(typeBoolean) || arg1.getType().equals(typeBoolean)) { + result = (Boolean.parseBoolean(sArg0) && Boolean.parseBoolean(sArg1)); + } + if (result) { + return new Constant(true_); + } else { + return new Constant(false_); + } + } + }); + public static final Symbol or = new Symbol(Parser.OR, 2, Symbol.Type.INFIX, "||", Symbol.Type.INFIX, new Symbol.ICalculator() { + @Override + public Expression calculate(List args) { + if (!(args.get(0) instanceof Constant)) { + return null; + } + if (!(args.get(1) instanceof Constant)) { + return null; + } + Constant arg0 = (Constant) args.get(0); + Constant arg1 = (Constant) args.get(1); + String sArg0 = arg0.getSymbol().toString(); + String sArg1 = arg1.getSymbol().toString(); + boolean result = false; + if (arg0.getType().equals(typeBoolean) || arg1.getType().equals(typeBoolean)) { + result = (Boolean.parseBoolean(sArg0) || Boolean.parseBoolean(sArg1)); + } + if (result) { + return new Constant(true_); + } else { + return new Constant(false_); + } + } + }); + public static final Symbol neg = new Symbol(Parser.NEG, 1, Symbol.Type.PREFIX, "!", Symbol.Type.PREFIX, new Symbol.ICalculator() { + @Override + public Expression calculate(List args) { + if (!(args.get(0) instanceof Constant)) { + return null; + } + Constant arg0 = (Constant) args.get(0); + String sArg0 = arg0.getSymbol().toString(); + boolean result = false; + if (arg0.getType().equals(typeBoolean)) { + result = !Boolean.parseBoolean(sArg0); + } + if (result) { + return new Constant(true_); + } else { + return new Constant(false_); + } + } + }); public static final Symbol cons = new Symbol("cons", 2, Symbol.Type.PREFIX, "($x,$y)->$x.add(0, $y)", Symbol.Type.LAMBDA_WITH_SIDE_EFFECT, new int[] {1, 0}); + public static final Symbol append = new Symbol("append", 2, Symbol.Type.PREFIX, "add", Symbol.Type.METHOD_WITH_SIDE_EFFECT); + public static final Symbol remove = new Symbol("remove", 2, Symbol.Type.PREFIX, "remove", Symbol.Type.METHOD_WITH_SIDE_EFFECT); public static final Symbol head = new Symbol("head", 1, Symbol.Type.PREFIX, "($x)->$x.get(0)", Symbol.Type.LAMBDA); public static final Symbol tail = new Symbol("tail", 1, Symbol.Type.PREFIX, "($x)->$x.subList(1, $x.size())", Symbol.Type.LAMBDA); public static final Symbol length = new Symbol("length", 1, Symbol.Type.PREFIX, "($x)->$x.size()", Symbol.Type.LAMBDA); public static final Symbol get = new Symbol("get", 2, Symbol.Type.PREFIX, "get", Symbol.Type.METHOD); public static final Symbol set = new Symbol("set", 3, Symbol.Type.PREFIX, "set", Symbol.Type.METHOD_WITH_SIDE_EFFECT); - public static final Symbol contains = new Symbol("contains", 2, Symbol.Type.PREFIX, "contains", Symbol.Type.METHOD); + public static final Symbol contains = new Symbol("contains", 2, Symbol.Type.PREFIX, new Symbol.IImplGenerator() { + @Override + public String generate(Type type, Type[] childrenTypes, String[] childrenImpl, String[] childrenSideEffects, String[] sideEffect) { + for (String s: childrenSideEffects) { + sideEffect[0] += s; + } + if (childrenTypes[0] != null && (typeMap.isAncestorOf(childrenTypes[0]) || typeJson.isAncestorOf(childrenTypes[0]))) { + return childrenImpl[0] + "." + "containsKey(" + childrenImpl[1] + ")"; + } + return childrenImpl[0] + "." + "contains(" + childrenImpl[1] + ")"; + } + }, new Symbol.ICalculator() { + @Override + public Expression calculate(List args) { + if (args.get(0) instanceof Constant && ((Constant) args.get(0)).getSymbol().equals(nil)) { + return new Constant(false_); + } + if (args.get(0) instanceof Term) { + Term term = (Term) args.get(0); + Type type = term.getType(); + if (typeList.isAncestorOf(type)) { + while (term.getSymbol().equals(DataConstraintModel.append)) { + if (term.getChild(1).equals(args.get(1))) { + return new Constant(true_); + } + if (!(term.getChild(0) instanceof Term)) { + return new Constant(false_); + } + term = (Term) term.getChild(0); + } + if (term instanceof ListTerm) { + for (Expression element: term.getChildren()) { + if (element.equals(args.get(1))) { + return new Constant(true_); + } + } + return new Constant(false_); + } + } else if (typeMap.isAncestorOf(type)) { + while (term.getSymbol().equals(DataConstraintModel.insert)) { + if (term.getChild(1).equals(args.get(1))) { + return new Constant(true_); + } + if (!(term.getChild(0) instanceof Term)) { + return new Constant(false_); + } + term = (Term) term.getChild(0); + } + if (term instanceof MapTerm) { + if (((MapTerm) term).keySet().contains(args.get(1).toString())) { + return new Constant(true_); + } + return new Constant(false_); + } + } else if (typeJson.isAncestorOf(type)) { + while (term.getSymbol().equals(DataConstraintModel.addMember)) { + if (term.getChild(1).equals(args.get(1))) { + return new Constant(true_); + } + if (!(term.getChild(0) instanceof Term)) { + return new Constant(false_); + } + term = (Term) term.getChild(0); + } + if (term instanceof JsonTerm) { + if (((JsonTerm) term).keySet().contains(args.get(1).toString())) { + return new Constant(true_); + } + return new Constant(false_); + } + } + } + return new Constant(false_); + } + }); public static final Symbol nil = new Symbol("nil", 0, Symbol.Type.PREFIX, new Symbol.IImplGenerator() { @Override - public String generate(Type type, String[] children, String[] childrenSideEffects, String[] sideEffect) { + public String generate(Type type, Type[] childrenTypes, String[] children, String[] childrenSideEffects, String[] sideEffect) { String compType = ""; if (type != null) { String interfaceType = type.getInterfaceTypeName(); @@ -53,27 +521,35 @@ if (implType.indexOf('<') >= 0) { implType = implType.substring(0, implType.indexOf('<')); } - return "new " + implType + "<" + compType + ">()"; + if (sideEffect == null) { + return "new " + implType + "<>()"; + } else { + String temp = "temp_nil"; + sideEffect[0] = interfaceType + " " + temp + " = " + "new " + implType + "<" + compType + ">();\n"; + return temp; + } } return "new ArrayList<" + compType + ">()"; } }); public static final Symbol null_ = new Symbol("null", 0, Symbol.Type.PREFIX, "null", Symbol.Type.PREFIX); + public static final Symbol true_ = new Symbol("true", 0, Symbol.Type.PREFIX, "true", Symbol.Type.PREFIX); + public static final Symbol false_ = new Symbol("false", 0, Symbol.Type.PREFIX, "false", Symbol.Type.PREFIX); public static final Symbol cond = new Symbol("if", 3, Symbol.Type.PREFIX, new Symbol.IImplGenerator() { final int count[] = {0}; @Override - public String generate(Type type, String[] children, String[] childrenSideEffects, String[] sideEffect) { + public String generate(Type type, Type[] childrenTypes, String[] childrenImpl, String[] childrenSideEffects, String[] sideEffect) { String temp = "temp_if" + count[0]; String impl = ""; impl += type.getInterfaceTypeName() + " " + temp + ";\n"; if (childrenSideEffects[0] != null && childrenSideEffects[0].length() > 0) impl += childrenSideEffects[0]; - impl += "if (" + children[0] + ") {\n"; + impl += "if (" + childrenImpl[0] + ") {\n"; if (childrenSideEffects[1] != null && childrenSideEffects[1].length() > 0) impl += "\t" + childrenSideEffects[1]; - impl += "\t" + temp + " = " + children[1] + ";\n"; + impl += "\t" + temp + " = " + childrenImpl[1] + ";\n"; impl += "} else {\n"; if (childrenSideEffects[2] != null && childrenSideEffects[2].length() > 0) impl += "\t" + childrenSideEffects[2]; - impl += "\t" + temp + " = " + children[2] + ";\n"; + impl += "\t" + temp + " = " + childrenImpl[2] + ";\n"; impl += "}\n"; sideEffect[0] += impl; @@ -81,24 +557,21 @@ count[0]++; return temp; } + }, new Symbol.ICalculator() { + @Override + public Expression calculate(List args) { + if (!(args.get(0) instanceof Constant)) return null; + if (((Constant) args.get(0)).getSymbol().equals(true_)) { + return args.get(1); + } else if (((Constant) args.get(0)).getSymbol().equals(false_)) { + return args.get(2); + } + return null; + } }); - - - public static final Symbol mod = new Symbol("mod", 2, Symbol.Type.PREFIX, "%", Symbol.Type.INFIX); - public static final Symbol eq = new Symbol("eq", 2, Symbol.Type.PREFIX, "==", Symbol.Type.INFIX); - public static final Symbol neq = new Symbol("neq", 2, Symbol.Type.PREFIX, "!=", Symbol.Type.INFIX); - public static final Symbol gt = new Symbol("gt", 2, Symbol.Type.PREFIX, ">", Symbol.Type.INFIX); - public static final Symbol lt = new Symbol("lt", 2, Symbol.Type.PREFIX, "<", Symbol.Type.INFIX); - public static final Symbol ge = new Symbol("ge", 2, Symbol.Type.PREFIX, ">=", Symbol.Type.INFIX); - public static final Symbol le = new Symbol("le", 2, Symbol.Type.PREFIX, "<=", Symbol.Type.INFIX); - public static final Symbol and = new Symbol("and", 2, Symbol.Type.PREFIX, "&&", Symbol.Type.INFIX); - public static final Symbol or = new Symbol("or", 2, Symbol.Type.PREFIX, "||", Symbol.Type.INFIX); - public static final Symbol neg = new Symbol("neg", 1, Symbol.Type.PREFIX, "!", Symbol.Type.PREFIX); - public static final Symbol true_ = new Symbol("true", 0, Symbol.Type.PREFIX, "true", Symbol.Type.PREFIX); - public static final Symbol false_ = new Symbol("false", 0, Symbol.Type.PREFIX, "false", Symbol.Type.PREFIX); public static final Symbol pair = new Symbol("pair", -1, Symbol.Type.PREFIX, new Symbol.IImplGenerator() { @Override - public String generate(Type type, String[] childrenImpl, String[] childrenSideEffects, String[] sideEffect) { + public String generate(Type type, Type[] childrenTypes, String[] childrenImpl, String[] childrenSideEffects, String[] sideEffect) { for (String s: childrenSideEffects) { sideEffect[0] += s; } @@ -108,7 +581,7 @@ }); public static final Symbol tuple = new Symbol("tuple", -1, Symbol.Type.PREFIX, new Symbol.IImplGenerator() { @Override - public String generate(Type type, String[] childrenImpl, String[] childrenSideEffects, String[] sideEffect) { + public String generate(Type type, Type[] childrenTypes, String[] childrenImpl, String[] childrenSideEffects, String[] sideEffect) { for (String s: childrenSideEffects) { sideEffect[0] += s; } @@ -125,24 +598,41 @@ 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 delete = new Symbol("delete", 2, Symbol.Type.PREFIX, "remove", 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 += "}"; +// 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}); @@ -151,12 +641,6 @@ div.setInverses(new Symbol[] {mul}); minus.setInverses(new Symbol[] {minus}); mod.setSignature(new Type[] {typeInt, null, null}); - cons.setInverses(new Symbol[] {head, tail}); - cons.setSignature(new Type[] {typeList, null, typeList}); - contains.setSignature(new Type[] {typeBoolean, typeList, null}); - length.setSignature(new Type[] {typeInt, null}); - get.setSignature(new Type[] {null, typeList, typeInt}); - set.setSignature(new Type[] {typeList, typeList, typeInt, null}); eq.setSignature(new Type[] {typeBoolean, null, null}); neq.setSignature(new Type[] {typeBoolean, null, null}); gt.setSignature(new Type[] {typeBoolean, null, null}); @@ -166,9 +650,19 @@ and.setSignature(new Type[] {typeBoolean, typeBoolean, typeBoolean}); or.setSignature(new Type[] {typeBoolean, typeBoolean, typeBoolean}); neg.setSignature(new Type[] {typeBoolean, typeBoolean}); + cons.setInverses(new Symbol[] {head, tail}); + cons.setSignature(new Type[] {typeList, null, typeList}); + append.setSignature(new Type[] {typeList, typeList, null}); + remove.setSignature(new Type[] {typeList, typeList, typeInt}); + head.setSignature(new Type[] {null, typeList}); + tail.setSignature(new Type[] {typeList, typeList}); + contains.setSignature(new Type[] {typeBoolean, null, null}); + length.setSignature(new Type[] {typeInt, null}); + get.setSignature(new Type[] {null, typeList, typeInt}); + set.setSignature(new Type[] {typeList, typeList, typeInt, null}); + null_.setSignature(new Type[] {null}); true_.setSignature(new Type[] {typeBoolean}); false_.setSignature(new Type[] {typeBoolean}); - null_.setSignature(new Type[] {null}); pair.setSignature(new Type[] {typePair,null,null}); pair.setInverses(new Symbol[] {left, right}); left.setSignature(new Type[] {null, typePair}); @@ -176,15 +670,40 @@ 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}); + delete.setSignature(new Type[] {typeMap, typeMap, 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<>(); + inputChannels = new HashMap<>(); types = new HashMap<>(); addType(typeInt); addType(typeLong); @@ -196,6 +715,7 @@ addType(typePair); addType(typeTuple); addType(typeMap); + addType(typeJson); symbols = new HashMap<>(); addSymbol(add); addSymbol(mul); @@ -203,15 +723,6 @@ addSymbol(div); addSymbol(minus); addSymbol(mod); - addSymbol(cons); - addSymbol(head); - addSymbol(tail); - addSymbol(length); - addSymbol(contains); - addSymbol(get); - addSymbol(set); - addSymbol(nil); - addSymbol(cond); addSymbol(eq); addSymbol(neq); addSymbol(gt); @@ -221,9 +732,20 @@ addSymbol(and); addSymbol(or); addSymbol(neg); + addSymbol(cons); + addSymbol(append); + addSymbol(remove); + addSymbol(head); + addSymbol(tail); + addSymbol(length); + addSymbol(contains); + addSymbol(get); + addSymbol(set); + addSymbol(cond); + addSymbol(nil); + addSymbol(null_); addSymbol(true_); addSymbol(false_); - addSymbol(null_); addSymbol(pair); addSymbol(left); addSymbol(right); @@ -231,33 +753,78 @@ addSymbol(fst); addSymbol(snd); addSymbol(insert); + addSymbol(delete); 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); - for (Channel ch: channels.values()) { - ch.removeChannelMember(id); + public ResourcePath getResourcePath(String path) { + for (ResourcePath resourcePath: resourcePaths) { + if (resourcePath.toString().equals(path)) return resourcePath; } - for (Channel ch: ioChannels.values()) { - ch.removeChannelMember(id); + 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(resourcePath); + } + for (Channel ch: inputChannels.values()) { + ch.removeChannelMember(resourcePath); } } @@ -271,50 +838,34 @@ 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) { channels.remove(channelName); } - public Collection getIOChannels() { - return ioChannels.values(); + public Collection getInputChannels() { + return inputChannels.values(); } - public Channel getIOChannel(String channelName) { - return ioChannels.get(channelName); + public Channel getInputChannel(String channelName) { + return inputChannels.get(channelName); } - 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 setInputChannels(HashMap inputChannels) { + this.inputChannels = inputChannels; } - public void addIOChannel(Channel ioChannel) { - ioChannels.put(ioChannel.getChannelName(), ioChannel); - for (ResourcePath id: ioChannel.getResources()) { - resourcePaths.put(id.getResourceName(), id); - } + public void addInputChannel(Channel inputChannel) { + inputChannels.put(inputChannel.getChannelName(), inputChannel); } - public void removeIOChannel(String ioChannelName) { - ioChannels.remove(ioChannelName); + public void removeInputChannel(String inputChannelName) { + inputChannels.remove(inputChannelName); } public void addType(Type type) { @@ -383,7 +934,7 @@ @Override public String toString() { String out = ""; - for (Channel channel: ioChannels.values()) { + for (Channel channel: inputChannels.values()) { out += channel.toString(); } for (Channel channel: channels.values()) { @@ -395,7 +946,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; @@ -404,7 +955,7 @@ if (init.length() > 0) { out += "init {\n" + init + "}\n"; } - for (Channel channel: ioChannels.values()) { + for (Channel channel: inputChannels.values()) { out += channel.getSourceText(); } for (Channel channel: channels.values()) { diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonAccessor.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonAccessor.java new file mode 100644 index 0000000..85994f2 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonAccessor.java @@ -0,0 +1,180 @@ +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 instanceof JsonTerm) { + return ((JsonTerm) json).get(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/JsonTerm.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonTerm.java new file mode 100644 index 0000000..6c5d1eb --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonTerm.java @@ -0,0 +1,50 @@ +package models.dataConstraintModel; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Symbol; +import models.algebra.Term; + +public class JsonTerm extends Term { + private Map keyToIndex = new HashMap<>(); + + public JsonTerm() { + super(new Symbol("json", -1)); + setType(DataConstraintModel.typeJson); + } + + public void addMember(String key, Expression value) { + if (keyToIndex.containsKey(key)) { + setChild(keyToIndex.get(key), value); + } else { + keyToIndex.put(key, getChildren().size()); + addChild(value); + } + } + + public Set keySet() { + return keyToIndex.keySet(); + } + + public Expression get(String key) { + return getChild(keyToIndex.get(key)); + } + + public Expression get(Constant key) { + return getChild(keyToIndex.get(key.toString())); + } + + public String toString() { + String jsonStr = "{"; + String delim = ""; + for (String key: keyToIndex.keySet()) { + jsonStr += delim + key + ": " + getChild(keyToIndex.get(key)).toString(); + delim = ", "; + } + return jsonStr + "}"; + } +} 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/ListTerm.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ListTerm.java new file mode 100644 index 0000000..d70d476 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ListTerm.java @@ -0,0 +1,31 @@ +package models.dataConstraintModel; + +import models.algebra.Expression; +import models.algebra.Symbol; +import models.algebra.Term; + +public class ListTerm extends Term { + + public ListTerm() { + super(new Symbol("list", -1)); + setType(DataConstraintModel.typeList); + } + + public void append(Expression value) { + addChild(value); + } + + public Expression get(int index) { + return getChild(index); + } + + public String toString() { + String listStr = "["; + String delim = ""; + for (Expression child: getChildren()) { + listStr += delim + child.toString(); + delim = ", "; + } + return listStr + "]"; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/MapTerm.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/MapTerm.java new file mode 100644 index 0000000..34792f6 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/MapTerm.java @@ -0,0 +1,50 @@ +package models.dataConstraintModel; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Symbol; +import models.algebra.Term; + +public class MapTerm extends Term { + private Map keyToIndex = new HashMap<>(); + + public MapTerm() { + super(new Symbol("map", -1)); + setType(DataConstraintModel.typeMap); + } + + public void insert(String key, Expression value) { + if (keyToIndex.containsKey(key)) { + setChild(keyToIndex.get(key), value); + } else { + keyToIndex.put(key, getChildren().size()); + addChild(value); + } + } + + public Set keySet() { + return keyToIndex.keySet(); + } + + public Expression get(String key) { + return getChild(keyToIndex.get(key)); + } + + public Expression get(Constant key) { + return getChild(keyToIndex.get(key.toString())); + } + + public String toString() { + String mapStr = "{"; + String delim = ""; + for (String key: keyToIndex.keySet()) { + mapStr += delim + key + ": " + getChild(keyToIndex.get(key)).toString(); + delim = ", "; + } + return mapStr + "}"; + } +} 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..6cadc2d 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourcePath.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourcePath.java @@ -1,70 +1,143 @@ 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.Term; import models.algebra.Type; +import models.algebra.Symbol; +import models.algebra.Term; -public class ResourcePath { - private String resourceName = null; - private Type resourceStateType = null; - private int numParameters = 0; - private Expression initialValue = null; - protected String initText = null; +public class ResourcePath extends Symbol { + protected ResourcePath parent = null; + protected ResourceHierarchy resourceHierarchy = null; + protected List pathParams = null; + + public ResourcePath(String fullResourceName) { + super(fullResourceName); + this.parent = null; + this.resourceHierarchy = new ResourceHierarchy(null, fullResourceName); + this.pathParams = new ArrayList<>(); + } - public ResourcePath(String resourceName, int numParameters) { - this.resourceName = resourceName; - this.numParameters = numParameters; + public ResourcePath(String fullResourceName, ResourceHierarchy resourceHierarchy) { + super(fullResourceName); + this.parent = null; + this.resourceHierarchy = resourceHierarchy; + this.pathParams = new ArrayList<>(); + } + + public ResourcePath(ResourcePath parent, String leafResourceName) { + super(parent.toString() + "." + leafResourceName); + this.parent = parent; + this.resourceHierarchy = new ResourceHierarchy(parent.getResourceHierarchy(), leafResourceName); + this.pathParams = parent.getPathParams(); + } + + public ResourcePath(ResourcePath parent, String leafResourceName, ResourceHierarchy resourceHierarchy) { + super(parent.toString() + "." + leafResourceName); + this.parent = parent; + this.resourceHierarchy = resourceHierarchy; + 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 parent, Expression exp, ResourceHierarchy resourceHierarchy) { + super(parent.toString() + ".{" + exp + "}"); + this.parent = parent; + this.resourceHierarchy = resourceHierarchy; + 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 String getResourceName() { - return resourceName; + public void setResourceHierarchy(ResourceHierarchy resourceHierarchy) { + this.resourceHierarchy = resourceHierarchy; + } + + public String getLeafResourceName() { + 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..997d1f6 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; @@ -42,13 +44,24 @@ } public boolean isRightUnary() { - for (Position pos : curStateExpression.getVariables().keySet()) { - if (nextStateExpression.contains(curStateExpression.getVariables().get(pos))) return false; + for (Variable var: curStateExpression.getVariables().values()) { + if (nextStateExpression.contains(var)) return false; } return true; } + + public boolean isRightPartial() { + for (Variable var: curStateExpression.getVariables().values()) { + if (messageExpression.contains(var)) return true; + } + if (isRightUnary()) return false; + for (Variable var: messageExpression.getVariables().values()) { + if (nextStateExpression.contains(var)) return true; + } + return false; + } - 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 +71,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 +103,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 +116,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 +129,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 +142,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 +178,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 +209,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..a86dc97 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataFlowGraph.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataFlowGraph.java @@ -1,40 +1,173 @@ package models.dataFlowModel; +import java.util.ArrayList; +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.ResourceHierarchy; 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, + ResourcePath outSideResource, + DataTransferChannel outSideChannel) { + ResourceNode node = null; + if (outSideResource.getNumberOfParameters() == 0) { + // a global (possibly non-root) resource node + Collection nodes = getResourceNodes(outSideResource.getResourceHierarchy()); + if (nodes.size() > 0) { + node = nodes.iterator().next(); + if (outSideChannel != null) { + // not a terminal node + node.addOutSideResource(outSideChannel, outSideResource); + } + } + } else { + // a channel local resource node + node = resourceNodeMap.get(System.identityHashCode(outSideResource)); } + if (node != null) return node; + node = new ResourceNode(parent, outSideResource, outSideChannel); + addNode(node); + if (parent == null) { + rootResourceNodes.add(node); + } else { + parent.addChild(node); + } + resourceNodeMap.put(System.identityHashCode(outSideResource), node); + return node; + } + + public ResourceNode addTerminalResourceNode(ResourceNode parent, + DataTransferChannel inSideChannel, + ResourcePath inSideResource) { + ResourceNode node = null; + if (inSideResource.getNumberOfParameters() == 0) { + // a global (possibly non-root) terminal resource node + Collection nodes = getResourceNodes(inSideResource.getResourceHierarchy()); + if (nodes.size() > 0) { + node = nodes.iterator().next(); + } + } else { + // a channel local terminal resource node + node = resourceNodeMap.get(System.identityHashCode(inSideResource)); + } + if (node != null) return node; + node = new ResourceNode(parent, inSideResource, inSideChannel, true); + addNode(node); + if (parent == null) { + rootResourceNodes.add(node); + } else { + parent.addChild(node); + } + resourceNodeMap.put(System.identityHashCode(inSideResource), node); + return node; + } + + public ResourceNode addResourceNode(ResourceNode parent, + Map inSide, + Map outSide) { + ResourceNode node = null; + for (ResourcePath outRes: outSide.values()) { + node = resourceNodeMap.get(System.identityHashCode(outRes)); + if (node != null) return node; + } + for (ResourcePath inRes: inSide.values()) { + node = resourceNodeMap.get(System.identityHashCode(inRes)); + if (node != null) return node; + } + node = new ResourceNode(parent, inSide, outSide); + addNode(node); + if (parent == null) { + rootResourceNodes.add(node); + } else { + parent.addChild(node); + } + for (ResourcePath outRes: outSide.values()) { + resourceNodeMap.put(System.identityHashCode(outRes), node); + } + for (ResourcePath inRes: inSide.values()) { + resourceNodeMap.put(System.identityHashCode(inRes), 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 resPath) { + return resourceNodeMap.get(System.identityHashCode(resPath)); + } + + public ChannelNode getChannelNode(DataTransferChannel channel) { + return channelNodeMap.get(channel); + } + + public Collection getResourceNodes() { + HashSet result = new HashSet<>(resourceNodeMap.values()); + return result; + } + + public Collection getResourceNodes(ResourceHierarchy resourceHierarchy) { + Collection resourceNodes = new ArrayList<>(); + for (ResourceNode rn: resourceNodeMap.values()) { + if (rn.getResourceHierarchy() == resourceHierarchy) { + resourceNodes.add(rn); + } + } + return resourceNodes; } - 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 Collection getChannelNodes() { + return channelNodeMap.values(); + } + + 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..15e3194 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().getLeafResourceName(); Parameter curStateParam = curStateParams.get(resource); if (curStateParam == null) { curStateParam = new Parameter("cur" + resource); @@ -153,72 +173,254 @@ } @Override - public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { - String resource = target.getResourceName(); + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + String resource = target.getResource().getLeafResourceName(); 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.getLeafResourceName(); + Parameter curStateParam = curStateParams.get(resource); + if (curStateParam == null) { + curStateParam = new Parameter("cur" + resource); + curStateParams.put(resource, curStateParam); + } + return curStateParam; + } }; - return deriveUpdateExpressionOf(targetMember, defaultStateAccessor); + return deriveUpdateExpressionOf(targetMember, defaultStateAccessor).getKey(); } /** - * 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 and the unified message * @throws ParameterizedIdentifierIsFutureWork * @throws ResolvingMultipleDefinitionIsFutureWork * @throws InvalidMessage * @throws UnificationFailed * @throws ValueUndefined */ - public Expression deriveUpdateExpressionOf(ChannelMember targetMember, IResourceStateAccessor stateAccessor) + public Map.Entry deriveUpdateExpressionOf(ChannelMember targetMember, IResourceStateAccessor stateAccessor) throws ParameterizedIdentifierIsFutureWork, ResolvingMultipleDefinitionIsFutureWork, InvalidMessage, UnificationFailed, ValueUndefined { return deriveUpdateExpressionOf(targetMember, stateAccessor, null); } - public Expression deriveUpdateExpressionOf(ChannelMember targetMember, IResourceStateAccessor stateAccessor, HashMap inputResourceToStateAccessor) + public Map.Entry 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 new AbstractMap.SimpleEntry<>(targetMember.getStateTransition().deriveNextStateExpressionFor(curOutputStateAccessor, unifiedMessage), unifiedMessage); + } + + /** + * Derive the state update calculation of the target channel member with a message and a given resource push/pull state accessor. + * @param targetMember a channel member whose state is to be updated + * @param message a message on the channel + * @param stateAccessor a push/pull resource state accessor + * @return the derived update calculation + * @throws ParameterizedIdentifierIsFutureWork + * @throws ResolvingMultipleDefinitionIsFutureWork + * @throws InvalidMessage + * @throws UnificationFailed + * @throws ValueUndefined + */ + public Expression deriveUpdateExpressionOf(ChannelMember targetMember, Term message, IResourceStateAccessor stateAccessor) + throws ParameterizedIdentifierIsFutureWork, ResolvingMultipleDefinitionIsFutureWork, InvalidMessage, UnificationFailed, ValueUndefined { + if (!getOutputChannelMembers().contains(targetMember)) return null; + + // Calculate the next state of target resource from the unified message and the current resource state + Expression curOutputStateAccessor = stateAccessor.getCurrentStateAccessorFor(targetMember, targetMember); + return targetMember.getStateTransition().deriveNextStateExpressionFor(curOutputStateAccessor, message); + } + + /** + * Fill outside resource paths with a given resource push/pull state accessor. + * @param targetMember a channel member whose state is to be updated + * @param stateAccessor a push/pull resource state accessor + * @return map from a depending channel member to the pair of the filled resource path and the set of the depended channel members + * @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> substitutedPositionsInMessageFromChannels = new HashMap<>(); + Term unifiedMessage = calcUnifiedMessage(targetMember, stateAccessor, inputResourceToStateAccessor, substitutedPositionsInMessageFromChannels); + + // Fill outside resource paths + if (unifiedMessage != null) { + for (ChannelMember cm: getInputChannelMembers()) { + if (cm.isOutside()) { + Set dependingVarPosInMessage = new HashSet<>(); + ResourcePath filledResPath = fillOutsideResourcePath(cm.getResource(), unifiedMessage, targetMember.getStateTransition().getMessageExpression(), dependingVarPosInMessage); + Set dependingChannelMembers = new HashSet<>(); + for (ChannelMember otherCm: substitutedPositionsInMessageFromChannels.keySet()) { + for (Position otherPos: substitutedPositionsInMessageFromChannels.get(otherCm)) { + for (Position thisPos: dependingVarPosInMessage) { + 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 dependingVarPosInMessage = new HashSet<>(); + ResourcePath filledResPath = fillOutsideResourcePath(cm.getResource(), unifiedMessage, targetMember.getStateTransition().getMessageExpression(), dependingVarPosInMessage); + Set dependingChannelMembers = new HashSet<>(); + for (ChannelMember otherCm: substitutedPositionsInMessageFromChannels.keySet()) { + for (Position otherPos: substitutedPositionsInMessageFromChannels.get(otherCm)) { + for (Position thisPos: dependingVarPosInMessage) { + 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 dependingVarPosInMessage = new HashSet<>(); + ResourcePath filledResPath = fillOutsideResourcePath(cm.getResource(), unifiedMessage, targetMember.getStateTransition().getMessageExpression(), dependingVarPosInMessage); + Set dependingChannelMembers = new HashSet<>(); + for (ChannelMember otherCm: substitutedPositionsInMessageFromChannels.keySet()) { + for (Position otherPos: substitutedPositionsInMessageFromChannels.get(otherCm)) { + for (Position thisPos: dependingVarPosInMessage) { + if (thisPos.isAncestorOf(otherPos)) { + dependingChannelMembers.add(otherCm); + break; + } + } + if (dependingChannelMembers.contains(otherCm)) break; + } + } + resourcePaths.put(cm, new AbstractMap.SimpleEntry<>(filledResPath, dependingChannelMembers)); + } + } + } + return resourcePaths; + } + + public Map> getMemberDependency() { + Map> dependency = new HashMap<>(); + + // Collect depended channel members and their positions in the message + Map> substitutedPositionsInMessage = getDependedChannelMembersAndTheirPositionsInMessage(); + + // Resolve dependency for each outside resource path + if (substitutedPositionsInMessage != null) { + for (ChannelMember cm: getInputChannelMembers()) { + if (cm.isOutside()) { + Set dependingVarPosInMessage = getPossitionsInMessageThatChannelMemberDependsOn(cm.getResource(), cm.getStateTransition().getMessageExpression()); + Set dependingChannelMembers = new HashSet<>(); + for (ChannelMember otherCm: substitutedPositionsInMessage.keySet()) { + for (Position otherPos: substitutedPositionsInMessage.get(otherCm)) { + for (Position thisPos: dependingVarPosInMessage) { + if (thisPos.isAncestorOf(otherPos)) { + dependingChannelMembers.add(otherCm); + break; + } + } + if (dependingChannelMembers.contains(otherCm)) break; + } + } + dependency.put(cm, dependingChannelMembers); + } + } + for (ChannelMember cm: getReferenceChannelMembers()) { + if (cm.isOutside()) { + Set dependingVarPosInMessage = getPossitionsInMessageThatChannelMemberDependsOn(cm.getResource(), cm.getStateTransition().getMessageExpression()); + Set dependingChannelMembers = new HashSet<>(); + for (ChannelMember otherCm: substitutedPositionsInMessage.keySet()) { + for (Position otherPos: substitutedPositionsInMessage.get(otherCm)) { + for (Position thisPos: dependingVarPosInMessage) { + if (thisPos.isAncestorOf(otherPos)) { + dependingChannelMembers.add(otherCm); + break; + } + } + if (dependingChannelMembers.contains(otherCm)) break; + } + } + dependency.put(cm, dependingChannelMembers); + } + } + for (ChannelMember cm: getOutputChannelMembers()) { + if (cm.isOutside()) { + Set dependingVarPosInMessage = getPossitionsInMessageThatChannelMemberDependsOn(cm.getResource(), cm.getStateTransition().getMessageExpression()); + Set dependingChannelMembers = new HashSet<>(); + for (ChannelMember otherCm: substitutedPositionsInMessage.keySet()) { + for (Position otherPos: substitutedPositionsInMessage.get(otherCm)) { + for (Position thisPos: dependingVarPosInMessage) { + if (thisPos.isAncestorOf(otherPos)) { + dependingChannelMembers.add(otherCm); + break; + } + } + if (dependingChannelMembers.contains(otherCm)) break; + } + } + dependency.put(cm, dependingChannelMembers); + } + } + } + return dependency; + } + + private Term calcUnifiedMessage(ChannelMember targetMember, IResourceStateAccessor stateAccessor, + Map inputResourceToStateAccessor, Map> substitutedPositionsInMessageFromChannels) + 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()); - } else { - curInputStateAccessor = inputResourceToStateAccessor.get(inputResource).getCurrentStateAccessorFor(inputResource, targetMember.getResource()); - nextInputStateAccessor = inputResourceToStateAccessor.get(inputResource).getNextStateAccessorFor(inputResource, targetMember.getResource()); - } - Expression messageConstraintByInput = inputMember.getStateTransition().deriveMessageConstraintFor(curInputStateAccessor, nextInputStateAccessor); + Expression messageConstraintByInput = calcMessageConstraintForInputMember(inputMember, targetMember, stateAccessor, inputResourceToStateAccessor, substitutedPositionsInMessageFromChannels); 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()); - } else { - curInputStateAccessor = inputResourceToStateAccessor.get(referenceResource).getCurrentStateAccessorFor(referenceResource, targetMember.getResource()); - } - Expression messageConstraintByReference = referenceMember.getStateTransition().deriveMessageConstraintFor(curInputStateAccessor); + Expression messageConstraintByReference = calcMessageConstraintForReferenceMember(referenceMember, targetMember, stateAccessor, inputResourceToStateAccessor, substitutedPositionsInMessageFromChannels); messageConstraints.add((Term) messageConstraintByReference); } @@ -234,20 +436,158 @@ } } } - - // 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(); + return unifiedMessage; + } + + public Expression calcMessageConstraintForInputMember(ChannelMember inputMember, ChannelMember targetMember, IResourceStateAccessor stateAccessor, + Map inputResourceToStateAccessor, Map> substitutedPositionsInMessageFromChannels) + throws InvalidMessage, ResolvingMultipleDefinitionIsFutureWork { + Expression curInputStateAccessor = null; + Expression nextInputStateAccessor = null; + if (inputResourceToStateAccessor == null) { + curInputStateAccessor = stateAccessor.getCurrentStateAccessorFor(inputMember, targetMember); + nextInputStateAccessor = stateAccessor.getNextStateAccessorFor(inputMember, targetMember); + } else { + curInputStateAccessor = inputResourceToStateAccessor.get(inputMember).getCurrentStateAccessorFor(inputMember, targetMember); + nextInputStateAccessor = inputResourceToStateAccessor.get(inputMember).getNextStateAccessorFor(inputMember, targetMember); } - Expression curOutputStateAccessor = stateAccessor.getCurrentStateAccessorFor(targetResource, targetResource); - if (unifiedMessage == null) { - // for IOChannel - if (targetMember.getStateTransition().getMessageExpression() instanceof Term) { - unifiedMessage = (Term) targetMember.getStateTransition().getMessageExpression(); + Set substitutedPositionsInMessage = new HashSet<>(); + Expression messageConstraintByInput = inputMember.getStateTransition().deriveMessageConstraintFor(curInputStateAccessor, nextInputStateAccessor, substitutedPositionsInMessage); + if (substitutedPositionsInMessage.size() > 0) substitutedPositionsInMessageFromChannels.put(inputMember, substitutedPositionsInMessage); + return messageConstraintByInput; + } + + public Expression calcMessageConstraintForReferenceMember(ChannelMember referenceMember, ChannelMember targetMember, IResourceStateAccessor stateAccessor, + Map inputResourceToStateAccessor, Map> substitutedPositionsInMessageFromChannels) + throws InvalidMessage, ResolvingMultipleDefinitionIsFutureWork { + Expression curInputStateAccessor = null; + if (inputResourceToStateAccessor == null) { + curInputStateAccessor = stateAccessor.getCurrentStateAccessorFor(referenceMember, targetMember); + } else { + curInputStateAccessor = inputResourceToStateAccessor.get(referenceMember).getCurrentStateAccessorFor(referenceMember, targetMember); + } + Set substitutedPositions = new HashSet<>(); + Expression messageConstraintByReference = referenceMember.getStateTransition().deriveMessageConstraintFor(curInputStateAccessor, substitutedPositions); + if (substitutedPositions.size() > 0) substitutedPositionsInMessageFromChannels.put(referenceMember, substitutedPositions); + return messageConstraintByReference; + } + + /** + * Get depended channel members and their positions in the message. + * + * @return depended channel members and their positions in the message + */ + private Map> getDependedChannelMembersAndTheirPositionsInMessage() { + Map> channelMembersMessageDependsOn = new HashMap<>(); + // Collect channel members that the message depends on from input state transitions + for (ChannelMember inputMember: getInputChannelMembers()) { + Set substitutedPositionsInMessage = new HashSet<>(); + Expression messageTerm = inputMember.getStateTransition().getMessageExpression(); + Expression curStateTerm = inputMember.getStateTransition().getCurStateExpression(); + Expression nextStateTerm = inputMember.getStateTransition().getNextStateExpression(); + HashMap messageVars = messageTerm.getVariables(); + for (Entry varEnt: messageVars.entrySet()) { + Variable var = varEnt.getValue(); + if (curStateTerm.getVariables().values().contains(var) || nextStateTerm.getVariables().values().contains(var)) { + if (messageTerm instanceof Term) { + substitutedPositionsInMessage.add(varEnt.getKey()); + } else if (messageTerm instanceof Variable) { + if (messageTerm.equals(var)) { + substitutedPositionsInMessage.add(new Position()); + } + } + } + } + if (substitutedPositionsInMessage.size() > 0) channelMembersMessageDependsOn.put(inputMember, substitutedPositionsInMessage); + } + + // Collect channel members that the message depends on from reference state transitions + for (ChannelMember referenceMember: getReferenceChannelMembers()) { + Set substitutedPositionsInMessage = new HashSet<>(); + Expression messageTerm = referenceMember.getStateTransition().getMessageExpression(); + Expression curStateTerm = referenceMember.getStateTransition().getCurStateExpression(); + Expression nextStateTerm = referenceMember.getStateTransition().getNextStateExpression(); + HashMap messageVars = messageTerm.getVariables(); + for (Entry varEnt: messageVars.entrySet()) { + Variable var = varEnt.getValue(); + if (curStateTerm.getVariables().values().contains(var) || nextStateTerm.getVariables().values().contains(var)) { + if (messageTerm instanceof Term) { + substitutedPositionsInMessage.add(varEnt.getKey()); + } else if (messageTerm instanceof Variable) { + if (messageTerm.equals(var)) { + substitutedPositionsInMessage.add(new Position()); + } + } + } + } + if (substitutedPositionsInMessage.size() > 0) channelMembersMessageDependsOn.put(referenceMember, substitutedPositionsInMessage); + } + return channelMembersMessageDependsOn; + } + + public ResourcePath fillOutsideResourcePath(ResourcePath resource, Term unifiedMessage, Expression messageTerm, Set dependingVarPosInMessage) + throws ResolvingMultipleDefinitionIsFutureWork { + ResourcePath filledResourcePath = new ResourcePath(resource); + + 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()); // Replace a path parameter with a value in the unified message. + dependingVarPosInMessage.add(bindings.get((Variable) pathParam).getKey()); // The position of the replaced variable in the message. + } else if (pathParam instanceof Term) { + Map pathParamVars = ((Term) pathParam).getVariables(); + for (Variable var: bindings.keySet()) { + if (pathParamVars.values().contains(var)) { // var is a subterm of a path parameter + pathParam = ((Term) pathParam).substitute(var, bindings.get(var).getValue()); // Substitute a value in the unified message to var. + dependingVarPosInMessage.add(bindings.get((Variable) var).getKey()); // The position of the replaced variable in the message. + } + } + if (!(pathParam instanceof Constant)) { + pathParam = ((Term) pathParam).reduce(); + } + dstParams.set(i, pathParam); // Replace a path parameter with the substituted term. + } + } + return filledResourcePath; + } + + private Set getPossitionsInMessageThatChannelMemberDependsOn(ResourcePath resourcePath, Expression messageTerm) { + Set dependingVarPosInMessage = new HashSet<>(); + Map messageVars = messageTerm.getVariables(); + for (Expression pathParam: resourcePath.getPathParams()) { + if (pathParam instanceof Variable) { + for (Entry messageVarEnt: messageVars.entrySet()) { + Variable var = messageVarEnt.getValue(); + Position varPos = messageVarEnt.getKey(); + if (pathParam.equals(var)) { + dependingVarPosInMessage.add(varPos); + } + } + } else if (pathParam instanceof Term) { + Map pathParamVars = ((Term) pathParam).getVariables(); + for (Entry messageVarEnt: messageVars.entrySet()) { + Variable var = messageVarEnt.getValue(); + Position varPos = messageVarEnt.getKey(); + if (pathParamVars.values().contains(var)) { + dependingVarPosInMessage.add(varPos); + } + } + } + } + return dependingVarPosInMessage; } @Override @@ -267,7 +607,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..b27156b 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferModel.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferModel.java @@ -1,31 +1,263 @@ 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>> channelLocalResMap = 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, channelLocalResMap); } - for (Channel channel: getIOChannels()) { - DataTransferChannel dfChannel = (DataTransferChannel)channel; - Set outputResources = dfChannel.getOutputResources(); - for (ResourcePath out: outputResources) { - dataFlowGraph.addNode(out); - } + for (Channel channel: getChannels()) { + addChannelToResourceEdges(dataFlowGraph, channel, null, channelLocalResMap); + } + for (Channel channel: getInputChannels()) { + addChannelToResourceEdges(dataFlowGraph, channel, null, channelLocalResMap); } return dataFlowGraph; } + + private void addResourceToChannelEdges(DataFlowGraph dataFlowGraph, Channel dstChannel, ChannelNode parentChannelNode, + Map>> channelLocalResMap) { + DataTransferChannel dstDfChannel = (DataTransferChannel) dstChannel; + ChannelNode dstChannelNode = dataFlowGraph.addChannelNode(parentChannelNode, dstDfChannel); + for (ResourcePath srcRes: dstDfChannel.getInputResources()) { + Map> chLocalResNodes = channelLocalResMap.get(srcRes.getResourceHierarchy()); + if (srcRes.getNumberOfParameters() == 0) { + // ResourcePath without parameter corresponds to a global ResourceNode. + ResourceNode srcResNode = addResourceNodes(dataFlowGraph, srcRes, dstDfChannel, channelLocalResMap, false); + dataFlowGraph.addEdge(srcResNode, dstChannelNode); + } else { + if (chLocalResNodes == null || chLocalResNodes.get(dstChannel) == null) { + // There is no channel-local ResourcePath. + // Then, create a new channel-local ResourceNode. + ResourceNode srcResNode = addResourceNodes(dataFlowGraph, srcRes, dstDfChannel, channelLocalResMap, false); + dataFlowGraph.addEdge(srcResNode, dstChannelNode); + } else { + // There already has been a channel-local ResourceNode. + Set nodes = chLocalResNodes.get(dstChannel); // channel-local ResourceNodes. + boolean bExists = false; + for (ResourceNode node: nodes) { + ResourcePath r = node.getOutSideResource(dstDfChannel); + if (r.toString().equals(srcRes.toString())) { + bExists = true; // There exists a textually identical ResourecPath within the same channel. + break; + } + } + if (!bExists) { + // Create a new channel-local ResourceNode. + ResourceNode srcResNode = addResourceNodes(dataFlowGraph, srcRes, dstDfChannel, channelLocalResMap, false); + dataFlowGraph.addEdge(srcResNode, dstChannelNode); + } + } + } + } + for (Channel childChannel: dstDfChannel.getChildren()) { + addResourceToChannelEdges(dataFlowGraph, childChannel, dstChannelNode, channelLocalResMap); + } + } + + private void addChannelToResourceEdges(DataFlowGraph dataFlowGraph, Channel srcChannel, ChannelNode parentChannelNode, + Map>> channelLocalResMap) { + DataTransferChannel srcDfChannel = (DataTransferChannel) srcChannel; + ChannelNode srcChannelNode = dataFlowGraph.addChannelNode(parentChannelNode, srcDfChannel); + for (ResourcePath dstRes: srcDfChannel.getOutputResources()) { + Map> chLocalResNodes = channelLocalResMap.get(dstRes.getResourceHierarchy()); // ResourceNodes that have the same ResourceHierarchy. + Set dstResSet = new HashSet<>(); + if (dstRes.getNumberOfParameters() == 0) { + // ResourcePath without parameter corresponds to a global ResourceNode. + if (chLocalResNodes == null) { + // Create a new global terminal ResourceNode. + ResourceNode dstResNode = addResourceNodes(dataFlowGraph, dstRes, srcDfChannel, channelLocalResMap, true); + dstResSet.add(dstResNode); + } else { + // Select all (usually one) global ResourceNodes. + for (Set resSet: chLocalResNodes.values()) { + dstResSet.addAll(resSet); + } + } + } else { + if (chLocalResNodes == null) { + // There is no corresponding ResourceNode. + // Create a new channel-local terminal ResourceNode. + ResourceNode dstResNode = addResourceNodes(dataFlowGraph, dstRes, srcDfChannel, channelLocalResMap, true); + dstResSet.add(dstResNode); + } else { + if (chLocalResNodes.get(srcDfChannel) == null) { + // Select all non-terminal ResourceNodes. + for (Set resSet: chLocalResNodes.values()) { + dstResSet.addAll(resSet); + } + } else { + // There already has been a channel-local terminal ResourceNode. + if (chLocalResNodes.size() > 0) { + // There already has been a channel-local ResourceNode. + for (Set resNodes: chLocalResNodes.values()) { + for (ResourceNode localResNode: resNodes) { + for (ResourcePath localResPath: localResNode.getInSideResources()) { + if (localResPath.toString().equals(dstRes.toString())) { + // Channel-local ResourcePath should be identical, and the identical ResourcePath is selected on top priority. + dstResSet.add(localResNode); + } + } + } + } + } + // Search a common channel-local ancestor. + if (dstResSet.size() == 0) { + ResourcePath dstParent = dstRes.getParent(); + while (dstParent != null && dstParent.getNumberOfParameters() > 0) { + Map> chToLocalDstNodes = channelLocalResMap.get(dstParent.getResourceHierarchy()); + if (chToLocalDstNodes != null) { + for (Channel dstCh: chToLocalDstNodes.keySet()) { + for (ResourceNode localParentNode: chToLocalDstNodes.get(dstCh)) { + for (ResourcePath localParentPath: localParentNode.getInSideResources()) { + if (localParentPath.toString().equals(dstParent.toString())) { + // There already has been a common channel-local ancestor. + ResourceNode dstResNode = addResourceNodes(dataFlowGraph, dstRes, (DataTransferChannel)dstCh, channelLocalResMap, false); + dstResSet.add(dstResNode); + break; + } + } + if (dstResSet.size() > 0) break; + } + if (dstResSet.size() > 0) break; + } + if (dstResSet.size() > 0) break; + } + dstParent = dstParent.getParent(); + } + } + if (dstResSet.size() == 0) { + for (Set nodes: chLocalResNodes.values()) { + // Select all corresponding ResourceNodes. + dstResSet.addAll(nodes); + } + } + if (dstResSet.size() == 0) { + // Otherwise create a new ResourceNode. + ResourceNode dstResNode = addResourceNodes(dataFlowGraph, dstRes, srcDfChannel, channelLocalResMap, false); + 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, channelLocalResMap); + } + } + + private ResourceNode addResourceNodes(DataFlowGraph dataFlowGraph, ResourcePath resPath, DataTransferChannel dfChannel, + Map>> channelLocalResMap, boolean isTerminal) { + ResourceNode resNode = null; + if (resPath.getParent() == null) { + if (!isTerminal) { + resNode = dataFlowGraph.addResourceNode(null, resPath, dfChannel); + } else { + resNode = dataFlowGraph.addTerminalResourceNode(null, dfChannel, resPath); + } + } else { + // Search an identical parent ResourceNode. + ResourceNode parent = null; + DataTransferChannel parentDfChannel = dfChannel; + if (resPath.getResourceHierarchy().getParent().getNumParameters() == 0) { + parentDfChannel = null; + } + if (channelLocalResMap.get(resPath.getResourceHierarchy().getParent()) != null) { + Set chLocalNodes = channelLocalResMap.get(resPath.getResourceHierarchy().getParent()).get(parentDfChannel); + if (chLocalNodes != null) { + for (ResourceNode node: chLocalNodes) { + for (ResourcePath r: node.getOutSideResources()) { + if (r.toString().equals(resPath.getParent().toString())) { + parent = node; + break; + } + } + if (parent != null) break; + for (ResourcePath r: node.getInSideResources()) { + if (r.toString().equals(resPath.getParent().toString())) { + parent = node; + break; + } + } + if (parent != null) break; + } + } + } + if (parent == null) { + parent = addResourceNodes(dataFlowGraph, resPath.getParent(), parentDfChannel, channelLocalResMap, false); + } + if (!isTerminal) { + resNode = dataFlowGraph.addResourceNode(parent, resPath, dfChannel); + } else { + resNode = dataFlowGraph.addTerminalResourceNode(parent, dfChannel, resPath); + } + } + Map> chToLocalNodes = channelLocalResMap.get(resPath.getResourceHierarchy()); + if (chToLocalNodes == null) { + chToLocalNodes = new HashMap<>(); + channelLocalResMap.put(resPath.getResourceHierarchy(), chToLocalNodes); + } + Set chLocalNodes = chToLocalNodes.get(dfChannel); + if (chLocalNodes == null) { + chLocalNodes = new HashSet<>(); + chToLocalNodes.put(dfChannel, chLocalNodes); + } + chLocalNodes.add(resNode); + return resNode; + } + +// private ResourceNode addResourceNodes(DataFlowGraph dataFlowGraph, ResourcePath resPath, DataTransferChannel dfChannel, +// Map>> resourceMap) { +// if (resPath.getNumberOfParameters() == 0) { +// // ResourcePath without parameter corresponds to a global ResourceNode. +// dfChannel = null; +// } +// if (resourceMap.get(resPath.getResourceHierarchy()) != null && resourceMap.get(resPath.getResourceHierarchy()).get(dfChannel) != null) { +// Set nodes = resourceMap.get(resPath.getResourceHierarchy()).get(dfChannel); +// for (ResourceNode node: nodes) { +// if (node.getOutSideResource().toString().equals(resPath.toString())) { +// return node; +// } +// for (ResourcePath res: node.getInSideResources()) { +// if (res.toString().toString().equals(resPath.toString())) { +// return node; +// } +// } +// } +// } +// ResourceNode resNode = null; +// if (resPath.getParent() == null) { +// resNode = dataFlowGraph.addResourceNode(null, dfChannel, resPath); +// } else { +// ResourceNode parent = addResourceNodes(dataFlowGraph, resPath.getParent(), dfChannel, resourceMap); +// resNode = dataFlowGraph.addResourceNode(parent, dfChannel, resPath); +// } +// Map> chToNodes = resourceMap.get(resPath.getResourceHierarchy()); +// if (chToNodes == null) { +// chToNodes = new HashMap<>(); +// resourceMap.put(resPath.getResourceHierarchy(), chToNodes); +// } +// Set nodes = chToNodes.get(dfChannel); +// if (nodes == null) { +// nodes = new HashSet<>(); +// chToNodes.put(dfChannel, nodes); +// } +// nodes.add(resNode); +// return resNode; +// } } diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ModelExtension.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ModelExtension.java index d8aa4af..bff95f8 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ModelExtension.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ModelExtension.java @@ -26,7 +26,7 @@ final int[] count = new int[] {0}; sum.setGenerator(new Symbol.IImplGenerator() { @Override - public String generate(Type type, String[] children, String[] childrenSideEffects, String[] sideEffect) { + public String generate(Type type, Type[] childrenTypes, String[] children, String[] childrenSideEffects, String[] sideEffect) { String compType = "Integer"; if (type != null) { String interfaceType = type.getInterfaceTypeName(); @@ -56,8 +56,8 @@ final int[] count = new int[] {0}; merge.setGenerator(new Symbol.IImplGenerator() { @Override - public String generate(Type type, String[] childrenImpl, String[] childrenSideEffects, String[] sideEffect) { - String implType = "Arrayist<>"; + public String generate(Type type, Type[] childrenTypes, String[] childrenImpl, String[] childrenSideEffects, String[] sideEffect) { + String implType = "ArrayList<>"; String interfaceType = "List"; String compType = "Integer"; if (type != null) { @@ -109,7 +109,7 @@ extractFaceDown.setArity(1); extractFaceDown.setGenerator(new Symbol.IImplGenerator() { @Override - public String generate(Type type, String[] children, String[] childrenSideEffects, String[] sideEffect) { + public String generate(Type type, Type[] childrenTypes, String[] children, String[] childrenSideEffects, String[] sideEffect) { return children[0]+".stream().filter(item -> item.getValue()==false).collect(Collectors.toList())"; } }); @@ -124,7 +124,7 @@ sortByKey.setArity(1); sortByKey.setGenerator(new Symbol.IImplGenerator() { @Override - public String generate(Type type, String[] children, String[] childrenSideEffects, String[] sideEffect) { + public String generate(Type type, Type[] childrenTypes, String[] children, String[] childrenSideEffects, String[] sideEffect) { String compType = ""; String temp_sort="temp_sort"; if (type != null) { 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..6c2073c 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ResourceNode.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ResourceNode.java @@ -1,30 +1,185 @@ 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 outSide = null; + protected List selectors = null; - public ResourceNode(ResourcePath resourcePath) { - this.resourcePath = resourcePath; + public ResourceNode(ResourceNode parent, + ResourcePath outSideResource, + DataTransferChannel outSideChannel) { + this.parent = parent; + this.children = new HashSet<>(); + this.inSide = new HashMap<>(); + this.outSide = new HashMap<>(); + this.outSide.put(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 ResourceNode(ResourceNode parent, + ResourcePath resource, + DataTransferChannel channel, boolean isInside) { + this.parent = parent; + this.children = new HashSet<>(); + this.inSide = new HashMap<>(); + this.outSide = new HashMap<>(); + if (isInside) { + this.inSide.put(channel, resource); + } else { + this.outSide.put(channel, resource); + } + this.resourceHierarchy = resource.getResourceHierarchy(); + this.selectors = new ArrayList<>(); + if (resourceHierarchy.getNumParameters() > 0) { + if (channel != null) { + selectors.addAll(channel.getSelectors()); + } else { + List pathParams = resource.getPathParams(); + selectors.add(new Selector(pathParams.get(pathParams.size() - 1))); + } + } } - public ResourcePath getResource() { - return resourcePath; + public ResourceNode(ResourceNode parent, + Map outSide, + Map inSide) { + this.parent = parent; + this.children = new HashSet<>(); + this.inSide = inSide; + this.outSide = outSide; + this.selectors = new ArrayList<>(); + for (Map.Entry outsideEnt: outSide.entrySet()) { + DataTransferChannel outSideChannel = outsideEnt.getKey(); + ResourcePath outSideResource = outsideEnt.getValue(); + if (this.resourceHierarchy == null) { + this.resourceHierarchy = outSideResource.getResourceHierarchy(); + } + 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 ResourcePath getPrimaryResourcePath() { + if (outSide.size() > 0) return outSide.values().iterator().next(); + return inSide.values().iterator().next(); + } + + public Collection getInSideResources() { + return inSide.values(); + } + + public ResourcePath getInSideResource(DataTransferChannel channel) { + return inSide.get(channel); + } + + public Collection getOutSideResources() { + return outSide.values(); + } + + public ResourcePath getOutSideResource(DataTransferChannel channel) { + return outSide.get(channel); + } + + public Set getOutSideChannel() { + return outSide.keySet(); + } + + public void addInSideResource(DataTransferChannel channel, ResourcePath inResource) { + inSide.put(channel, inResource); + } + + public void addOutSideResource(DataTransferChannel channel, ResourcePath outResource) { + outSide.put(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/models/visualModel/FormulaChannel.java b/AlgebraicDataflowArchitectureModel/src/models/visualModel/FormulaChannel.java index 581d31e..2fb57ee 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/visualModel/FormulaChannel.java +++ b/AlgebraicDataflowArchitectureModel/src/models/visualModel/FormulaChannel.java @@ -32,24 +32,24 @@ // channelMember.setStateTransition(st); super.addChannelMemberAsInput(channelMember); if (formula != null && getInputChannelMembers().size() > 1) { - formula += " " + defaultOperator + " " + channelMember.getResource().getResourceName(); + formula += " " + defaultOperator + " " + channelMember.getResource().getLeafResourceName(); if (formulaRhs != null) { if (formulaRhs instanceof Variable) { Term newTerm = new Term(defaultOperator); newTerm.addChild(formulaRhs); - newTerm.addChild(new Variable(channelMember.getResource().getResourceName()), true); + newTerm.addChild(new Variable(channelMember.getResource().getLeafResourceName()), true); formulaRhs = newTerm; } else if (formulaRhs instanceof Term) { Term newTerm = new Term(defaultOperator); newTerm.addChild(formulaRhs); - newTerm.addChild(new Variable(channelMember.getResource().getResourceName())); + newTerm.addChild(new Variable(channelMember.getResource().getLeafResourceName())); formulaRhs = newTerm; } } } else { if (formula == null) formula = ""; - formula += channelMember.getResource().getResourceName(); - formulaRhs = new Variable(channelMember.getResource().getResourceName()); + formula += channelMember.getResource().getLeafResourceName(); + formulaRhs = new Variable(channelMember.getResource().getLeafResourceName()); } if (formulaRhs != null) { setFormulaTerm(formulaRhs); @@ -63,8 +63,8 @@ super.addChannelMemberAsOutput(channelMember); if (getOutputChannelMembers().size() == 1) { if (formula == null) formula = ""; - if (!formula.contains("==")) { - formula = channelMember.getResource().getResourceName() + " == " + formula; + if (!formula.contains("=")) { + formula = channelMember.getResource().getLeafResourceName() + " = " + formula; } } if (formulaRhs != null) { @@ -96,7 +96,7 @@ Map resToNextVar = new HashMap<>(); for (ChannelMember cm: this.getInputChannelMembers()) { ResourcePath id = cm.getResource(); - String resName = id.getResourceName(); + String resName = id.getLeafResourceName(); Variable curVar = new Variable(resName + "1"); Variable nextVar = new Variable(resName + "2"); curStates.put(id, curVar); @@ -134,7 +134,7 @@ for (ChannelMember cm: getOutputChannelMembers()) { ResourcePath id = cm.getResource(); StateTransition st = new StateTransition(); - String resName = id.getResourceName(); + String resName = id.getLeafResourceName(); Variable curVar = new Variable(resName + "1"); st.setCurStateExpression(curVar); st.setNextStateExpression(rhs); diff --git a/AlgebraicDataflowArchitectureModel/src/parser/Parser.java b/AlgebraicDataflowArchitectureModel/src/parser/Parser.java index 876ee6f..a137bf7 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,11 @@ 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 NATIVE = "native"; public static final String LEFT_CURLY_BRACKET = "{"; public static final String RIGHT_CURLY_BRACKET = "}"; public static final String LEFT_CURLY_BRACKET_REGX = "\\{"; @@ -43,23 +55,36 @@ 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 = "-"; public static final String DIV = "/"; + public static final String MOD = "%"; public static final String MINUS = "-"; + public static final String EQ = "=="; + public static final String NEQ = "!="; + public static final String GT = ">"; + public static final String LT = "<"; + public static final String GE = ">="; + public static final String LE = "<="; + public static final String AND = "&&"; + public static final String OR = "||"; + public static final String NEG = "!"; public static final String ADD_REGX = "\\+"; 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 OR_REGX = "\\|\\|"; + 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,17 +104,21 @@ } 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) { if (channel.getInputChannelMembers().size() == 0) { - model.addIOChannel(channel); + model.addInputChannel(channel); } else { model.addChannel(channel); } @@ -98,17 +127,21 @@ } 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; + boolean isNative = false; String channelOrInitKeyword = stream.next(); - if (!channelOrInitKeyword.equals(CHANNEL)) { + if (channelOrInitKeyword.equals(NATIVE)) { + // A native channel + isNative = true; + channelOrInitKeyword = stream.next(); + } + if (!channelOrInitKeyword.equals(CHANNEL) && !channelOrInitKeyword.equals(SUB_CHANNEL)) { if (!channelOrInitKeyword.equals(INIT)) throw new ExpectedChannel(stream.getLine()); parseInit(model); channelOrInitKeyword = stream.next(); @@ -120,50 +153,65 @@ int fromLine = stream.getLine(); DataTransferChannel channel = new DataTransferChannel(channelName); + if (isNative) { + channel.setNative(true); + } 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 +226,17 @@ 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)); } + if (stream.hasNext()) stream.next(); } 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 +250,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,27 +281,38 @@ 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; for (;;) { - String leftBracketOrMinus = stream.next(); - if (leftBracketOrMinus.equals(LEFT_BRACKET)) { + String leftBracketOrMinusOrNeg = stream.next(); + if (leftBracketOrMinusOrNeg.equals(LEFT_BRACKET)) { Expression exp = parseTerm(stream, model); String rightBracket = stream.next(); if (!rightBracket.equals(RIGHT_BRACKET)) throw new ExpectedRightBracket(stream.getLine()); expressions.add(exp); + } else if (leftBracketOrMinusOrNeg.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 (leftBracketOrMinusOrNeg.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; + Symbol minusOrNeg = null; String symbolName = null; - if (leftBracketOrMinus.equals(MINUS)) { - minus = DataTransferModel.minus; // not sub + if (leftBracketOrMinusOrNeg.equals(MINUS)) { + minusOrNeg = DataTransferModel.minus; // not sub + symbolName = stream.next(); + } else if (leftBracketOrMinusOrNeg.equals(NEG)) { + minusOrNeg = DataTransferModel.neg; symbolName = stream.next(); } else { - symbolName = leftBracketOrMinus; + symbolName = leftBracketOrMinusOrNeg; } Expression exp = null; if (stream.checkNext() != null && stream.checkNext().equals(LEFT_BRACKET)) { @@ -283,38 +336,39 @@ 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 if (symbolName.startsWith("\"") && symbolName.endsWith("\"")) { + // a string value + exp = new Constant(symbolName, DataTransferModel.typeString); } else { - exp = new Variable(symbolName); + // a variable + exp = parseVariable(stream, model, symbolName); } } } - if (minus != null) { - Term minusTerm = new Term(minus); - minusTerm.addChild(exp); - expressions.add(minusTerm); + if (minusOrNeg != null) { + Term minusOrNegTerm = new Term(minusOrNeg); + minusOrNegTerm.addChild(exp); + expressions.add(minusOrNegTerm); } else { expressions.add(exp); } @@ -324,16 +378,79 @@ 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(MOD)) { + operators.add(DataTransferModel.mod); + stream.next(); + } else if (operator.equals(EQ)) { + operators.add(DataTransferModel.eq); + stream.next(); + } else if (operator.equals(NEQ)) { + operators.add(DataTransferModel.neq); + stream.next(); + } else if (operator.equals(GT)) { + operators.add(DataTransferModel.gt); + stream.next(); + } else if (operator.equals(LT)) { + operators.add(DataTransferModel.lt); + stream.next(); + } else if (operator.equals(GE)) { + operators.add(DataTransferModel.ge); + stream.next(); + } else if (operator.equals(LE)) { + operators.add(DataTransferModel.le); + stream.next(); + } else if (operator.equals(AND)) { + operators.add(DataTransferModel.and); + stream.next(); + } else if (operator.equals(OR)) { + operators.add(DataTransferModel.or); + 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 @@ -343,13 +460,41 @@ ArrayList addSubs = new ArrayList<>(); Expression first = expressions.get(0); int i = 1; + Term rootTerm = null; for (Symbol op: operators) { Expression second = expressions.get(i); - if (op.getName().equals(MUL) || op.getName().equals(DIV)) { + if (op.getName().equals(MUL) || op.getName().equals(DIV) || op.getName().equals(MOD)) { + // higher priority than add and sub Term term = new Term(op); term.addChild(first); term.addChild(second); first = term; + } else if (op.getName().equals(EQ) || op.getName().equals(NEQ) || op.getName().equals(GT) || op.getName().equals(LT) + || op.getName().equals(GE) || op.getName().equals(LE) || op.getName().equals(AND) || op.getName().equals(OR)) { + // lower priority than add and sub + if (first != null) monomials.add(first); + Expression firstMonomial = monomials.get(0); + int j = 1; + for (Symbol op2: addSubs) { + Expression secondMonomial = monomials.get(j); + Term term = new Term(op2); + term.addChild(firstMonomial); + term.addChild(secondMonomial); + firstMonomial = term; + j++; + } + if (rootTerm == null) { + rootTerm = new Term(op); + rootTerm.addChild(firstMonomial); + } else { + rootTerm.addChild(firstMonomial); + firstMonomial = rootTerm; + rootTerm = new Term(op); + rootTerm.addChild(firstMonomial); + } + monomials.clear(); + addSubs.clear(); + first = second; } else { // add or sub ==> new monomial monomials.add(first); @@ -369,29 +514,140 @@ firstMonomial = term; i++; } - return firstMonomial; + if (rootTerm == null) { + return firstMonomial; + } else { + rootTerm.addChild(firstMonomial); + return rootTerm; + } } - /**-------------------------------------------------------------------------------- - * [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; + if (stream.checkNext().equals(COLON)) 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> tokens = new ArrayList<>(); private ArrayList lines = new ArrayList<>(); private int line = 0; private int n = 0; @@ -416,48 +672,99 @@ 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( + splitBy( + splitBy( + splitBy( + splitBy( + splitBy( + splitBy( + splitBy( + splitBy( + splitBy( + splitBy( + line.split("[ \t]"), + ADD, + ADD_REGX), + MUL, + MUL_REGX), + SUB, + SUB_REGX), + DIV, + DIV_REGX), + MOD, + MOD), + EQ, + EQ), + NEQ, + NEQ), + GE, + GE), + LE, + LE), + GT, + GT), + LT, + LT), + AND, + AND), + OR, + OR_REGX), + NEG, + NEG), + 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(String[] tokens, final String delimiter, final String delimiterRegx) { + ArrayList newTokens = new ArrayList<>(); + for (String token: tokens) { + newTokens.add(new Token(token)); + } + return splitBy(newTokens, delimiter, delimiterRegx); } - private ArrayList splitBy(final List tokens, final String delimiter, final String delimiterRegx) { - ArrayList newTokens = new ArrayList<>(); - for (String token: tokens) { - String[] splitTokens = token.split(delimiterRegx); - boolean fFirstToken = true; - for (String t: splitTokens) { - if (!fFirstToken) { - newTokens.add(delimiter); + private ArrayList splitBy(final List tokens, final String delimiter, final String delimiterRegx) { + ArrayList newTokens = new ArrayList<>(); + for (Token token: tokens) { + if (token.isAtomic()) { + newTokens.add(token); + } else { + String[] splitTokens = token.split(delimiterRegx); + boolean fFirstToken = true; + for (String t: splitTokens) { + if (!fFirstToken) { + newTokens.add(new Token(delimiter, true)); + } + if (t.length() > 0) { + newTokens.add(new Token(t)); + } + fFirstToken = false; } - if (t.length() > 0) { - newTokens.add(t); + while (token.endsWith(delimiter)) { + newTokens.add(new Token(delimiter, true)); + token = token.substring(0, token.length() - 1); } - fFirstToken = false; - } - while (token.endsWith(delimiter)) { - newTokens.add(delimiter); - token = token.substring(0, token.length() - 1); } } return newTokens; @@ -470,7 +777,7 @@ n = 0; if (line >= tokens.size()) return null; } - String token = tokens.get(line).get(n); + String token = tokens.get(line).get(n).getTokenStr(); n++; return token; } @@ -482,7 +789,7 @@ n = 0; if (line >= tokens.size()) return null; } - return tokens.get(line).get(n); + return tokens.get(line).get(n).getTokenStr(); } public boolean hasNext() { @@ -507,4 +814,42 @@ return text; } } + + public static class Token { + String token; + boolean isAtomic = false; + + public Token(String token) { + this.token = token; + } + + public Token(String token, boolean isAtomic) { + this.token = token; + this.isAtomic = isAtomic; + } + + public String getTokenStr() { + return token; + } + + public boolean isAtomic() { + return isAtomic; + } + + public String[] split(String delimiterRegx) { + return token.split(delimiterRegx); + } + + public boolean endsWith(String delimiter) { + return token.endsWith(delimiter); + } + + public int length() { + return token.length(); + } + + public Token substring(int beginIdx, int endIdx) { + return new Token(token.substring(beginIdx, endIdx)); + } + } } 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/simulator/ChannelState.java b/AlgebraicDataflowArchitectureModel/src/simulator/ChannelState.java new file mode 100644 index 0000000..9fd4993 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/ChannelState.java @@ -0,0 +1,134 @@ +package simulator; + +import java.util.Map; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.dataConstraintModel.Selector; +import models.dataFlowModel.DataTransferChannel; + +public class ChannelState { + private DataTransferChannel channel; + private ReferenceStructure referenceStructure; + + public ChannelState(DataTransferChannel channel) { + this.channel = channel; + } + + public DataTransferChannel getChannel() { + return channel; + } + + public Map getDependingParamAndValues(List channelValues) { + if (referenceStructure == null) { + return null; + } + return referenceStructure.getDependingParamAndValues(channelValues); + } + + public List> getDependedChannelSelectorValues(Expression dependingVariable, Expression itsValue) { + if (referenceStructure == null) { + return null; + } + Map dependingVarToVal = new HashMap<>(); + dependingVarToVal.put(dependingVariable, itsValue); + return referenceStructure.getDependedChannelSelectorValues(dependingVarToVal); + } + + public List> getDependedChannelSelectorValues(Map dependingVarToVal) { + if (referenceStructure == null) { + return null; + } + return referenceStructure.getDependedChannelSelectorValues(dependingVarToVal); + } + + public void addDependingParamAndValue(List channelValues, Expression dependingVariable, Expression itsValue) { + if (referenceStructure == null) { + referenceStructure = new ReferenceStructure(); + } + referenceStructure.addDependingParamAndValue(channelValues, dependingVariable, itsValue); + } + + private static class ReferenceStructure { + private Map dependingParamAndValues = null; + private Map referenceStructure; + + public Map getDependentParamAndValues() { + return dependingParamAndValues; + } + + public Map getDependingParamAndValues(List channelValues) { + ReferenceStructure subStructure = this; + for (Constant chVal: channelValues) { + if (subStructure.getDependentParamAndValues() == null) { + subStructure = subStructure.getReferenceStructure(chVal); + } else { + return null; + } + } + return subStructure.getDependentParamAndValues(); + } + + public List> getDependedChannelSelectorValues(Map dependingVarToVal) { + List> channelValuesList = new ArrayList<>(); + if (dependingParamAndValues != null) { + boolean doesMatch = true; + for (Expression dependingVariable: dependingVarToVal.keySet()) { + if (dependingParamAndValues.keySet().contains(dependingVariable)) { + if (!dependingParamAndValues.get(dependingVariable).equals(dependingVarToVal.get(dependingVariable))) { + doesMatch = false; + } + } + } + if (doesMatch) { + List> chValsList = new ArrayList<>(); + chValsList.add(new ArrayList<>()); + return chValsList; + } else { + return null; + } + } else { + for (Constant chVal: referenceStructure.keySet()) { + List> chValsList = referenceStructure.get(chVal).getDependedChannelSelectorValues(dependingVarToVal); + if (chValsList != null) { + for (List chVals: chValsList) { + chVals.add(0, chVal); + channelValuesList.add(chVals); + } + } + } + } + return channelValuesList; + } + + public void addDependingParamAndValue(Expression dependingVariable, Expression itsValue) { + if (dependingParamAndValues == null) { + dependingParamAndValues = new HashMap<>(); + } + dependingParamAndValues.put(dependingVariable, itsValue); + } + + public void addDependingParamAndValue(List channelValues, Expression dependingVariable, Expression itsValue) { + ReferenceStructure subStructure = this; + for (Constant chVal: channelValues) { + if (subStructure.referenceStructure == null) { + subStructure.referenceStructure = new HashMap<>(); + } + ReferenceStructure nextStructure = subStructure.referenceStructure.get(chVal); + if (nextStructure == null) { + nextStructure = new ReferenceStructure(); + subStructure.referenceStructure.put(chVal, nextStructure); + } + subStructure = nextStructure; + } + subStructure.addDependingParamAndValue(dependingVariable, itsValue); + } + + public ReferenceStructure getReferenceStructure(Constant channelParam) { + return referenceStructure.get(channelParam); + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/Event.java b/AlgebraicDataflowArchitectureModel/src/simulator/Event.java new file mode 100644 index 0000000..b0bce8c --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/Event.java @@ -0,0 +1,413 @@ +package simulator; + +import java.util.AbstractMap; +import java.util.ArrayList; +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.InvalidMessage; +import models.algebra.Position; +import models.algebra.Term; +import models.algebra.UnificationFailed; +import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.ResourcePath; +import models.dataConstraintModel.Selector; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor; +import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork; + +public class Event { + private DataTransferChannel channel; + private Expression message; + private boolean isInput = false; + private ResourcePath inputResourcePath = null; + private Resource inputResource = null; + + private List> channelSelectorAndValues = new ArrayList<>(); + private Map dependingParameters = new HashMap<>(); + private Set outputResources = new HashSet<>(); + private Set succEvents = new HashSet<>(); + private Map> channelSelectorToInputResourcePathParam = new HashMap<>(); + private Map> channelSelectorToOutputResourcePathParam = new HashMap<>(); + + public Event(DataTransferChannel channel, Expression message, ResourcePath outputResPath, Resource outputResource) { + this.channel = channel; + this.message = message; + this.isInput = true; + this.outputResources.add(outputResource); + connectChannelSelectorAndPathParameters(); + + // Extract channel parameters from the output resource. + List channelSelectors = channel.getAllSelectors(); + for (Selector sel: channelSelectors) { + int paramIdx = channelSelectorToOutputResourcePathParam.get(sel).get(outputResPath); + Resource ancestor = outputResource; + int idx = outputResPath.getPathParams().size(); + while (ancestor != null) { + if (ancestor.getResourceHierarchy().getNumParameters() > 0) { + idx--; + if (idx == paramIdx) break; + } + ancestor = ancestor.getParent(); + } + channelSelectorAndValues.add(new AbstractMap.SimpleEntry<>(sel, ancestor.getParameter())); + } + } + + public Event(DataTransferChannel channel, ResourcePath inputResPath, Resource inputResource) { + this.channel = channel; + this.isInput = false; + this.inputResourcePath = inputResPath; + this.inputResource = inputResource; + connectChannelSelectorAndPathParameters(); + + // Extract channel parameters from the input resource. + List channelSelectors = channel.getAllSelectors(); + for (Selector sel: channelSelectors) { + if (inputResPath != null) { + Integer paramIdx = channelSelectorToInputResourcePathParam.get(sel).get(inputResPath); + if (paramIdx != null) { + Resource ancestor = inputResource; + int idx = inputResPath.getPathParams().size(); + while (ancestor != null) { + if (ancestor.getResourceHierarchy().getNumParameters() > 0) { + idx--; + if (idx == paramIdx) break; + } + ancestor = ancestor.getParent(); + } + channelSelectorAndValues.add(new AbstractMap.SimpleEntry<>(sel, ancestor.getParameter())); + } + } + } + } + + public Event(DataTransferChannel channel, ResourcePath inputResPath, Resource inputResource, List channelSelectorValues, Map dependingVarToVal) { + this.channel = channel; + this.isInput = false; + this.inputResourcePath = inputResPath; + this.inputResource = inputResource; + connectChannelSelectorAndPathParameters(); + + // Extract channel parameters from the input resource. + List channelSelectors = channel.getAllSelectors(); + for (int i = 0; i < channelSelectors.size(); i++) { + Selector sel = channelSelectors.get(i); + channelSelectorAndValues.add(new AbstractMap.SimpleEntry<>(sel, channelSelectorValues.get(i))); + } + this.dependingParameters = dependingVarToVal; + } + + private void connectChannelSelectorAndPathParameters() { + List channelSelectors = channel.getAllSelectors(); + for (Selector sel: channelSelectors) { + for (ResourcePath resPath: channel.getInputResources()) { + int paramIdx = resPath.getPathParams().indexOf(sel.getExpression()); + if (paramIdx >= 0) { + Map pathToIdx = channelSelectorToInputResourcePathParam.get(sel); + if (pathToIdx == null) { + pathToIdx = new HashMap<>(); + channelSelectorToInputResourcePathParam.put(sel, pathToIdx); + } + pathToIdx.put(resPath, paramIdx); + } + } + for (ResourcePath resPath: channel.getOutputResources()) { + int paramIdx = resPath.getPathParams().indexOf(sel.getExpression()); + if (paramIdx >= 0) { + Map pathToIdx = channelSelectorToOutputResourcePathParam.get(sel); + if (pathToIdx == null) { + pathToIdx = new HashMap<>(); + channelSelectorToOutputResourcePathParam.put(sel, pathToIdx); + } + pathToIdx.put(resPath, paramIdx); + } + } + } + } + + public DataTransferChannel getChannel() { + return channel; + } + + public Expression getMessage() { + return message; + } + + public void setMessage(Expression message) { + this.message = message; + } + + public boolean isInput() { + return isInput; + } + + public List> getChannelSelectorAndValues() { + return channelSelectorAndValues; + } + + public List getChannelSelectorValues() { + List channelValues = new ArrayList<>(); + for (Map.Entry chEnt: channelSelectorAndValues) { + channelValues.add(chEnt.getValue()); + } + return channelValues; + } + + public void updateChannelSelectorValues(List channelSelectorValues) { + for (int i = 0; i < channelSelectorValues.size(); i++) { + Map.Entry chEnt = channelSelectorAndValues.get(i); + chEnt.setValue(channelSelectorValues.get(i)); + } + } + + public Map getDependingParameters() { + return dependingParameters; + } + + + public Term updateDependingParameters(IResourceStateValueProvider resourceStateValueProvider) { + IResourceStateAccessor resouceStateAccessor = new IResourceStateAccessor() { + @Override + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourceIdentifier resId = getInputResourceIdentifier(target.getResource()); + return resourceStateValueProvider.getCurrentStateValueOf(resId); + } + + @Override + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourceIdentifier resId = getInputResourceIdentifier(target.getResource()); + return resourceStateValueProvider.getNextStateValueOf(resId); + } + + @Override + public Expression getDirectStateAccessorFor(ResourcePath target, ResourcePath from) { + ResourceIdentifier resId = getInputResourceIdentifier(target); + return resourceStateValueProvider.getCurrentStateValueOf(resId); + } + }; + + try { + Map> substitutedPositionsInMessageFromChannels = new HashMap<>(); + Term unifiedMessage = null; + + // Calculate message constraints from leaf channel members on the channel member dependency graph. + Map> dependency = channel.getMemberDependency(); + if (dependency.size() == 0) { + // No channel member dependency. + Expression messageConstraint = null; + for (ChannelMember channelMember: channel.getInputChannelMembers()) { + // Calculate message constraint from an input state transition + messageConstraint = channel.calcMessageConstraintForInputMember(channelMember, null, resouceStateAccessor, null, substitutedPositionsInMessageFromChannels); + if (unifiedMessage == null) { + unifiedMessage = (Term) messageConstraint; + } else { + unifiedMessage = (Term) unifiedMessage.unify(messageConstraint); + if (unifiedMessage == null) { + throw new UnificationFailed(); + } + } + } + for (ChannelMember channelMember: channel.getReferenceChannelMembers()) { + // Calculate message constraint from a reference state transition + messageConstraint = channel.calcMessageConstraintForReferenceMember(channelMember, null, resouceStateAccessor, null, substitutedPositionsInMessageFromChannels); + if (unifiedMessage == null) { + unifiedMessage = (Term) messageConstraint; + } else { + unifiedMessage = (Term) unifiedMessage.unify(messageConstraint); + if (unifiedMessage == null) { + throw new UnificationFailed(); + } + } + } + return unifiedMessage; + } + Set toResolve = new HashSet<>(); + Set resolved = new HashSet<>(); + for (Set depended: dependency.values()) { + toResolve.addAll(depended); + } + for (ChannelMember depending: dependency.keySet()) { + toResolve.remove(depending); + } + for (ChannelMember leafMember: toResolve) { + Expression messageConstraint = null; + if (channel.getInputChannelMembers().contains(leafMember)) { + // Calculate message constraint from an input state transition + messageConstraint = channel.calcMessageConstraintForInputMember(leafMember, null, resouceStateAccessor, null, substitutedPositionsInMessageFromChannels); + } else if (channel.getReferenceChannelMembers().contains(leafMember)) { + // Calculate message constraint from a reference state transition + messageConstraint = channel.calcMessageConstraintForReferenceMember(leafMember, null, resouceStateAccessor, null, substitutedPositionsInMessageFromChannels); + } + if (unifiedMessage == null) { + unifiedMessage = (Term) messageConstraint; + } else { + unifiedMessage = (Term) unifiedMessage.unify(messageConstraint); + if (unifiedMessage == null) { + throw new UnificationFailed(); + } + } + } + resolved.addAll(toResolve); + toResolve.clear(); + + for (;;) { + for (Map.Entry> dependEnt: dependency.entrySet()) { + ChannelMember dependingMem = dependEnt.getKey(); + Set dependedMems = dependEnt.getValue(); + if (!resolved.contains(dependingMem) && resolved.containsAll(dependedMems)) { + toResolve.add(dependingMem); + } + } + if (toResolve.size() == 0) break; + for (ChannelMember dependingMem: toResolve) { + // Fill the path parameters of the resource path of a depending channel member. + Set dependingVarPosInMessage = new HashSet<>(); + ResourcePath filledResPath = channel.fillOutsideResourcePath(dependingMem.getResource(), unifiedMessage, dependingMem.getStateTransition().getMessageExpression(), dependingVarPosInMessage); + ResourcePath unfilledResPath = dependingMem.getResource(); + for (int i = 0; i < unfilledResPath.getPathParams().size(); i++) { + Expression var = unfilledResPath.getPathParams().get(i); + Expression val = filledResPath.getPathParams().get(i); + boolean isSelector = false; + for (Selector sel: channel.getAllSelectors()) { + if (sel.getExpression().equals(var)) { + isSelector = true; + break; + } + } + if (!isSelector) { + // A depending channel parameter + updateDependingParameter(var, val); + } + } + + // Calculate message constraint + Expression messageConstraint = null; + if (channel.getInputChannelMembers().contains(dependingMem)) { + // Calculate message constraint from an input state transition + messageConstraint = channel.calcMessageConstraintForInputMember(dependingMem, null, resouceStateAccessor, null, substitutedPositionsInMessageFromChannels); + } else if (channel.getReferenceChannelMembers().contains(dependingMem)) { + // Calculate message constraint from a reference state transition + messageConstraint = channel.calcMessageConstraintForReferenceMember(dependingMem, null, resouceStateAccessor, null, substitutedPositionsInMessageFromChannels); + } + if (unifiedMessage == null) { + unifiedMessage = (Term) messageConstraint; + } else { + unifiedMessage = (Term) unifiedMessage.unify(messageConstraint); + if (unifiedMessage == null) { + throw new UnificationFailed(); + } + } + } + resolved.addAll(toResolve); + toResolve.clear(); + } + return unifiedMessage; + } catch (ResolvingMultipleDefinitionIsFutureWork | InvalidMessage | UnificationFailed e) { + e.printStackTrace(); + } + return null; + } + + public void updateDependingParameter(Expression variable, Expression value) { + dependingParameters.put(variable, value); + } + + public ResourcePath getInputResourcePath() { + return inputResourcePath; + } + + public Resource getInputResource() { + return inputResource; + } + + public Set getOutputResources() { + return outputResources; + } + + public Set getSuccessors() { + return succEvents; + } + + public void addSuccessor(Event succEvt) { + succEvents.add(succEvt); + } + + public ResourceIdentifier getResourceIdentifier(ResourcePath resPath) { + ResourceIdentifier resId = ResourceIdentifier.createFrom(resPath); + for (Map.Entry chParamEnt: channelSelectorAndValues) { + Selector sel = chParamEnt.getKey(); + Map inputPathParamEnt = channelSelectorToInputResourcePathParam.get(sel); + if (inputPathParamEnt != null) { + Integer paramIdx = inputPathParamEnt.get(resPath); + if (paramIdx != null) { + resId.setPathParam(paramIdx, chParamEnt.getValue()); + } + } + } + for (Map.Entry chParamEnt: channelSelectorAndValues) { + Selector sel = chParamEnt.getKey(); + Map outputPathParamEnt = channelSelectorToOutputResourcePathParam.get(sel); + if (outputPathParamEnt != null) { + Integer paramIdx = outputPathParamEnt.get(resPath); + if (paramIdx != null) { + resId.setPathParam(paramIdx, chParamEnt.getValue()); + } + } + } + for (Expression var: dependingParameters.keySet()) { + int paramIdx = resId.getPathParams().indexOf(var); + if (paramIdx >= 0) { + resId.setPathParam(paramIdx, (Constant) dependingParameters.get(var)); + } + } + return resId; + } + + public ResourceIdentifier getInputResourceIdentifier(ResourcePath inputResPath) { + ResourceIdentifier resId = ResourceIdentifier.createFrom(inputResPath); + for (Map.Entry chParamEnt: channelSelectorAndValues) { + Selector sel = chParamEnt.getKey(); + Integer paramIdx = channelSelectorToInputResourcePathParam.get(sel).get(inputResPath); + if (paramIdx != null) { + resId.setPathParam(paramIdx, chParamEnt.getValue()); + } + } + for (Expression var: dependingParameters.keySet()) { + int paramIdx = resId.getPathParams().indexOf(var); + if (paramIdx >= 0) { + resId.setPathParam(paramIdx, (Constant) dependingParameters.get(var)); + } + } + return resId; + } + + public ResourceIdentifier getOutputResourceIdentifier(ResourcePath outputResPath) { + ResourceIdentifier resId = ResourceIdentifier.createFrom(outputResPath); + for (Map.Entry chParamEnt: channelSelectorAndValues) { + Selector sel = chParamEnt.getKey(); + Integer paramIdx = channelSelectorToOutputResourcePathParam.get(sel).get(outputResPath); + if (paramIdx != null) { + resId.setPathParam(paramIdx, chParamEnt.getValue()); + } + } + for (Expression var: dependingParameters.keySet()) { + int paramIdx = resId.getPathParams().indexOf(var); + if (paramIdx >= 0) { + resId.setPathParam(paramIdx, (Constant) dependingParameters.get(var)); + } + } + return resId; + } + + + public interface IResourceStateValueProvider { + Expression getCurrentStateValueOf(ResourceIdentifier resId); + Expression getNextStateValueOf(ResourceIdentifier resId); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/Resource.java b/AlgebraicDataflowArchitectureModel/src/simulator/Resource.java new file mode 100644 index 0000000..c88b684 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/Resource.java @@ -0,0 +1,178 @@ +package simulator; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import models.algebra.Constant; +import models.algebra.Type; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.ResourceHierarchy; +import models.dataConstraintModel.ResourcePath; +import simulator.states.*; + +public class Resource { + private Resource parent = null; + private Map children = null; + private ResourceHierarchy resourceHierarchy = null; + private ResourceState state = null; + private Constant parameter = null; + + public Resource(ResourceHierarchy resourceHierarchy) { + this.resourceHierarchy = resourceHierarchy; + Type resType = resourceHierarchy.getResourceStateType(); + if (resourceHierarchy.getChildren().size() > 0 + || DataConstraintModel.typeList.isAncestorOf(resType) + || DataConstraintModel.typeMap.isAncestorOf(resType) + || DataConstraintModel.typeJson.isAncestorOf(resType)) { + if (DataConstraintModel.typeList.isAncestorOf(resType)) { + // List resource + state = new ListResourceState(); + } else if (DataConstraintModel.typeMap.isAncestorOf(resType)) { + // Map resource + state = new MapResourceState(); + } else { + // Json resource + state = new JsonResourceState(); + children = new LinkedHashMap<>(); + for (ResourceHierarchy child: resourceHierarchy.getChildren()) { + Resource cRes = new Resource(child, this); + ((JsonResourceState) state).addChildState(child.getResourceName(), cRes.getState()); + children.put(child.getResourceName(), cRes); + } + } + } else { + state = new PrimitiveResourceState((Constant) resourceHierarchy.getInitialValue()); + } + } + + public Resource(ResourceHierarchy resourceHierarchy, Resource parent) { + this(resourceHierarchy); + this.parent = parent; + } + + public Resource(ResourceHierarchy resourceHierarchy, Resource parent, ResourceState state) { + this.resourceHierarchy = resourceHierarchy; + this.parent = parent; + this.state = state; + } + + public Resource(ResourceHierarchy resourceHierarchy, Resource parent, Constant parameter, ResourceState state) { + this.resourceHierarchy = resourceHierarchy; + this.parent = parent; + this.parameter = parameter; + this.state = state; + } + + public Resource getParent() { + return parent; + } + + public void setParent(Resource parent) { + this.parent = parent; + } + + public ResourceHierarchy getResourceHierarchy() { + return resourceHierarchy; + } + + public ResourceIdentifier getResourceIdentifier() { + ResourceIdentifier resId = null; + if (parent != null) { + ResourceIdentifier parentResId = parent.getResourceIdentifier(); + if (resourceHierarchy.getNumParameters() == 0) { + resId = new ResourceIdentifier(parentResId, resourceHierarchy.getResourceName(), resourceHierarchy); + } else { + resId = new ResourceIdentifier(parentResId, parameter, resourceHierarchy); + } + } else { + resId = new ResourceIdentifier(resourceHierarchy.getResourceName(), resourceHierarchy); + } + return resId; + } + + public ResourceState getState() { + return state; + } + + public void changeState(ResourceState state) { + this.state = state; + } + + public Constant getParameter() { + return parameter; + } + + public Collection getChildren() { + return getChildrenMap().values(); + } + + public Map getChildrenMap() { + Map children = null; + if (resourceHierarchy.getChildren().size() > 0) { + children = new LinkedHashMap<>(); + ResourceHierarchy childRes = resourceHierarchy.getChildren().iterator().next(); + if (childRes.getNumParameters() > 0) { + // List or Map type. + if (state instanceof CompositeResourceState) { + // If the list or map is not nil. + Map childStates = ((CompositeResourceState) state).getChildStates(); + for (Map.Entry childEnt: childStates.entrySet()) { + String childParam = childEnt.getKey(); + ResourceState childState = childEnt.getValue(); + Type thisType = resourceHierarchy.getResourceStateType(); + if (DataConstraintModel.typeList.isAncestorOf(thisType)) { + children.put(childParam, new Resource(childRes, this, new Constant(childParam, DataConstraintModel.typeInt), childState)); + } else if (DataConstraintModel.typeMap.isAncestorOf(thisType)) { + children.put(childParam, new Resource(childRes, this, new Constant(childParam, DataConstraintModel.typeString), childState)); + } + } + } + } else { + // Json type. + Map childStates = ((CompositeResourceState) state).getChildStates(); + if (this.children == null || this.children.size() < childStates.size()) { + for (Map.Entry childEnt: childStates.entrySet()) { + String childParam = childEnt.getKey(); + ResourceState childState = childEnt.getValue(); + Type thisType = resourceHierarchy.getResourceStateType(); + for (ResourceHierarchy c: resourceHierarchy.getChildren()) { + if (c.getResourceName().equals(childParam.replace("\"", ""))) { + childRes = c; + break; + } + } + if (DataConstraintModel.typeJson.isAncestorOf(thisType)) { + children.put(childParam, new Resource(childRes, this, new Constant(childParam, DataConstraintModel.typeString), childState)); + } + } + this.children = children; + } + return this.children; + } + } + return children; + } + + public Resource getDescendant(ResourceIdentifier resId) { + if (this.getResourceIdentifier().equals(resId)) return this; + if (!resId.startsWith(this.getResourceIdentifier())) return null; + for (Resource child: getChildren()) { + Resource res = child.getDescendant(resId); + if (res != null) return res; + } + return null; + } + + public Resource getDescendant(String resId) { + if (this.getResourceIdentifier().toString().equals(resId)) return this; + if (!resId.startsWith(this.getResourceIdentifier().toString())) return null; + for (Resource child: getChildren()) { + Resource res = child.getDescendant(resId); + if (res != null) return res; + } + return null; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/ResourceIdentifier.java b/AlgebraicDataflowArchitectureModel/src/simulator/ResourceIdentifier.java new file mode 100644 index 0000000..d454391 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/ResourceIdentifier.java @@ -0,0 +1,126 @@ +package simulator; + +import java.util.HashMap; +import java.util.Map; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.dataConstraintModel.ResourceHierarchy; +import models.dataConstraintModel.ResourcePath; +import models.dataConstraintModel.Selector; + +public class ResourceIdentifier extends ResourcePath { + + public ResourceIdentifier(String fullResourceName, ResourceHierarchy resourceHierarchy) { + super(fullResourceName, resourceHierarchy); + } + + public ResourceIdentifier(ResourceIdentifier parentResId, String leafResourceName, ResourceHierarchy resourceHierarchy) { + super(parentResId, leafResourceName, resourceHierarchy); + } + + public ResourceIdentifier(ResourceIdentifier parentResId, Expression exp, ResourceHierarchy resourceHierarchy) { + super(parentResId, exp, resourceHierarchy); + } + + public void setPathParam(int paramIdx, Constant param) { + pathParams.set(paramIdx, param); + } + + public boolean equals(ResourceIdentifier another) { + if (!this.getResourceHierarchy().toString().equals(another.getResourceHierarchy().toString())) return false; + ResourceIdentifier resId = this; + while (resId != null && another != null) { + if (resId.getResourceHierarchy().getNumParameters() == 0) { + if (!another.getResourceHierarchy().getResourceName().equals(resId.getResourceHierarchy().getResourceName())) { + return false; + } + } else { + if (!another.getLastParam().equals(resId.getLastParam())) { + return false; + } + } + resId = (ResourceIdentifier) resId.getParent(); + another = (ResourceIdentifier) another.getParent(); + } + return true; + } + + public boolean startsWith(ResourceIdentifier another) { + if (this.length() > another.length()) { + return ((ResourceIdentifier) parent).startsWith(another); + } + return this.equals(another); + } + + public int length() { + if (parent == null) { + return 1; + } + return ((ResourceIdentifier) parent).length() + 1; + } + + public String toString() { + String resId = ""; + if (parent != null) { + resId += parent.toString() + "."; + } + if (!this.endsWithParam()) { + resId += getLeafResourceName(); + } else { + resId += getLastParam().toString().replace("\"", ""); + } + return resId; + } + + public boolean isInstanceOf(ResourcePath resPath) { + ResourcePath resId = this; + while (resId != null && resPath != null) { + if (resId.getResourceHierarchy().getNumParameters() == 0) { + if (!resPath.getResourceHierarchy().getResourceName().equals(resId.getResourceHierarchy().getResourceName())) { + return false; + } + } else if (resId.getResourceHierarchy().getNumParameters() != resPath.getResourceHierarchy().getNumParameters()) { + return false; + } + resId = resId.getParent(); + resPath = resPath.getParent(); + } + return true; + } + + public Map extractParameters(ResourcePath resPath) { + ResourcePath resId = this; + Map paramMap = new HashMap<>(); + while (resId != null && resPath != null) { + if (resId.getResourceHierarchy().getNumParameters() > 0) { + paramMap.put(resPath.getLastParam(), (Constant) resId.getLastParam()); + } else { + if (!resPath.getName().equals(resId.getName())) { + return null; + } + } + resId = resId.getParent(); + resPath = resPath.getParent(); + } + return paramMap; + } + + public static ResourceIdentifier createFrom(ResourcePath resPath) { + ResourceIdentifier parent = null; + if (resPath.getParent() != null) { + parent = createFrom(resPath.getParent()); + } + ResourceHierarchy res = resPath.getResourceHierarchy(); + if (res.getNumParameters() == 0) { + if (parent == null) { + return new ResourceIdentifier(resPath.getLeafResourceName(), res); + } else { + return new ResourceIdentifier(parent, resPath.getLeafResourceName(), res); + } + } else { + return new ResourceIdentifier(parent, resPath.getLastParam(), res); + } + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/Simulator.java b/AlgebraicDataflowArchitectureModel/src/simulator/Simulator.java new file mode 100644 index 0000000..2338858 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/Simulator.java @@ -0,0 +1,289 @@ +package simulator; + +import java.util.AbstractMap; +import java.util.ArrayList; +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.InvalidMessage; +import models.algebra.ParameterizedIdentifierIsFutureWork; +import models.algebra.Term; +import models.algebra.UnificationFailed; +import models.algebra.ValueUndefined; +import models.algebra.Variable; +import models.dataConstraintModel.Channel; +import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.ResourceHierarchy; +import models.dataConstraintModel.ResourcePath; +import models.dataConstraintModel.Selector; +import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor; +import simulator.Event.IResourceStateValueProvider; +import simulator.interfaces.INativeReceiver; + +public class Simulator { + private DataTransferModel model; + private SystemState curState; + private Map> nativeReceivers = new HashMap<>(); + private Map nativeChannelReceivers = new HashMap<>(); + + public Simulator(DataTransferModel model) { + this.model = model; + init(); + } + + public DataTransferModel getModel() { + return model; + } + + public SystemState init() { + curState = new SystemState(); + for (ResourceHierarchy res: model.getResourceHierarchies()) { + if (res.getParent() == null) { + // root resource + curState.addResource(new Resource(res)); + } + } + for (Channel channel: model.getChannels()) { + curState.addChannel((DataTransferChannel) channel); + } + return curState; + } + + public SystemState getCurState() { + return curState; + } + + /** + * Change the state of the system for a given input event. + * + * @param inputEvent an input event + * @return the next system state + * @throws ParameterizedIdentifierIsFutureWork + * @throws ResolvingMultipleDefinitionIsFutureWork + * @throws InvalidMessage + * @throws UnificationFailed + * @throws ValueUndefined + */ + public SystemState transition(Event inputEvent) + throws ParameterizedIdentifierIsFutureWork, ResolvingMultipleDefinitionIsFutureWork, InvalidMessage, UnificationFailed, ValueUndefined { + SystemState nextSystemState = new SystemState(curState); + nextSystemState.addEvent(inputEvent); + + fireEvent(inputEvent, curState, nextSystemState); + + curState = nextSystemState; + return nextSystemState; + } + + public void addNativeReceiver(INativeReceiver receiver, DataTransferChannel channel) { + nativeChannelReceivers.put(channel, receiver); + } + + public void addNativeReceiver(INativeReceiver receiver, DataTransferChannel channel, Resource resource) { + Map receivers = nativeReceivers.get(channel); + if (receivers == null) { + receivers = new HashMap<>(); + nativeReceivers.put(channel, receivers); + } + receivers.put(resource, receiver); + } + + public void removeNativeReceiver(DataTransferChannel channel) { + nativeChannelReceivers.remove(channel); + } + + public void removeNativeReceiver(DataTransferChannel channel, Resource resource) { + Map receivers = nativeReceivers.get(channel); + if (receivers != null) { + receivers.remove(resource); + if (receivers.size() == 0) { + nativeReceivers.remove(channel); + } + } + } + + /** + * Fire an given event and construct the next system state from the current system state. + * + * @param event an event + * @param curSystemState the current state of the system + * @param nextSystemState the next state of the system to be constructed + * @throws ParameterizedIdentifierIsFutureWork + * @throws ResolvingMultipleDefinitionIsFutureWork + * @throws InvalidMessage + * @throws UnificationFailed + * @throws ValueUndefined + */ + private void fireEvent(final Event event, final SystemState curSystemState, final SystemState nextSystemState) + throws ParameterizedIdentifierIsFutureWork, ResolvingMultipleDefinitionIsFutureWork, InvalidMessage, UnificationFailed, ValueUndefined { + IResourceStateAccessor resouceStateAccessor = new IResourceStateAccessor() { + @Override + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourceIdentifier resId = event.getResourceIdentifier(target.getResource()); + Resource res = curSystemState.getResource(resId); + if (res == null) return null; + return res.getState().getValue(); + } + + @Override + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourceIdentifier resId = event.getResourceIdentifier(target.getResource()); + Resource res = nextSystemState.getResource(resId); + if (res == null) return null; + return res.getState().getValue(); + } + + @Override + public Expression getDirectStateAccessorFor(ResourcePath target, ResourcePath from) { + ResourceIdentifier resId = event.getResourceIdentifier(target); + Resource res = curSystemState.getResource(resId); + if (res == null) return null; + return res.getState().getValue(); + } + }; + + DataTransferChannel channel = event.getChannel(); + if (channel.getOutputResources().size() > 0) { + // For each output resource, calculate the next state. + for (ChannelMember out: channel.getOutputChannelMembers()) { + Expression nextResState = null; + if (!event.isInput()) { + nextResState = channel.deriveUpdateExpressionOf(out, resouceStateAccessor).getKey(); + } else { + nextResState = channel.deriveUpdateExpressionOf(out, (Term) event.getMessage(), resouceStateAccessor); + } + ResourceIdentifier outResId = event.getOutputResourceIdentifier(out.getResource()); + if (nextResState instanceof Term) { + nextResState = ((Term) nextResState).reduce(); + } + ResourceIdentifier updatedOutResId = nextSystemState.updateResourceState(outResId, nextResState); + while (updatedOutResId != null) { // In addition to the target state, its descendants' states are also changed. + for (Event nextEvent: getNextEvents(updatedOutResId, curSystemState, nextSystemState)) { + fireEvent(nextEvent, curSystemState, nextSystemState); + } + updatedOutResId = (ResourceIdentifier) updatedOutResId.getParent(); + } + } + } else if (channel.isNative()) { + // A native output event channel + INativeReceiver receiver = nativeChannelReceivers.get(channel); // receiver for the channel + if (receiver != null) receiver.onReceiveFromModel(event); + if (nativeReceivers.get(channel) != null) { + receiver = nativeReceivers.get(channel).get(event.getInputResource()); // receiver for the channel and resource + if (receiver != null) receiver.onReceiveFromModel(event); + } + } + } + + private Set getNextEvents(ResourceIdentifier inResId, final SystemState curSystemState, final SystemState nextSystemState) + throws ParameterizedIdentifierIsFutureWork, ResolvingMultipleDefinitionIsFutureWork, InvalidMessage, UnificationFailed, ValueUndefined { + Set nextEvents = new HashSet<>(); + IResourceStateValueProvider resourceStateValueProvider = new IResourceStateValueProvider() { + @Override + public Expression getCurrentStateValueOf(ResourceIdentifier resId) { + return curSystemState.getResource(resId).getState().getValue(); + } + + @Override + public Expression getNextStateValueOf(ResourceIdentifier resId) { + return nextSystemState.getResource(resId).getState().getValue(); + } + }; + for (Map.Entry chEntry: nextSystemState.getChannelStates().entrySet()) { + DataTransferChannel channel = chEntry.getKey(); + ChannelState nextChState = chEntry.getValue(); + Map> dependency = channel.getMemberDependency(); + Map> invDependency = new HashMap<>(); + for (ChannelMember dependingMem: dependency.keySet()) { + for (ChannelMember dependedMem: dependency.get(dependingMem)) { + Set dependings = invDependency.get(dependedMem); + if (dependings == null) { + dependings = new HashSet<>(); + invDependency.put(dependedMem, dependings); + } + dependings.add(dependingMem); + } + } + for (ResourcePath inResPath: channel.getInputResources()) { + if (inResId.isInstanceOf(inResPath)) { + // Update the channel state and resource identifiers by the update of the input resource. + for (ChannelMember dependedMem: invDependency.keySet()) { + if (inResPath == dependedMem.getResource()) { + // If some depending resources are to be updated by the update of an depended input resource. + Event nextEvent = new Event(channel, inResPath, nextSystemState.getResource(inResId)); + nextEvent.updateDependingParameters(resourceStateValueProvider); + if (nextChState == null) { + nextChState = new ChannelState(channel); + nextSystemState.updateChannelState(channel, nextChState); + } + List channelSelValues = nextEvent.getChannelSelectorValues(); + for (Map.Entry paramEnt: nextEvent.getDependingParameters().entrySet()) { + nextChState.addDependingParamAndValue(channelSelValues, paramEnt.getKey(), paramEnt.getValue()); + } + nextEvents.add(nextEvent); + } + } + if (invDependency.size() == 0) { + Event nextEvent = new Event(channel, inResPath, nextSystemState.getResource(inResId)); + nextEvent.setMessage(nextEvent.updateDependingParameters(resourceStateValueProvider)); + nextEvents.add(nextEvent); + } + if (nextChState != null) { + for (ChannelMember dependingMem: dependency.keySet()) { + if (inResPath == dependingMem.getResource()) { + // If a depending resource is directly updated. + ResourcePath filledResPath = inResId; + ResourcePath unfilledResPath = inResPath; + Map selectorVarToVal = new HashMap<>(); + Map dependingVarToVal = new HashMap<>(); + for (int i = 0; i < unfilledResPath.getPathParams().size(); i++) { + Expression var = unfilledResPath.getPathParams().get(i); + Expression val = filledResPath.getPathParams().get(i); + boolean isSelector = false; + for (Selector sel: channel.getAllSelectors()) { + if (sel.getExpression().equals(var)) { + isSelector = true; + break; + } + } + if (isSelector) { + selectorVarToVal.put(var, val); + } else { + dependingVarToVal.put(var, val); + } + } + for (List channelSelectorValues: nextChState.getDependedChannelSelectorValues(dependingVarToVal)) { + // Guess every tuple of channel selector values that may affects the updated resource. + boolean doesMatch = true; + for (Expression var: selectorVarToVal.keySet()) { + for (int i = 0; i < channel.getAllSelectors().size(); i++) { + if (channel.getAllSelectors().get(i).getExpression().equals(var)) { + if (!channelSelectorValues.get(i).equals(selectorVarToVal.get(var))) { + // If the value of a selector in the updated resource path does not matches a guessed channel selector value. + doesMatch = false; + } + } + } + } + if (doesMatch) { + Event nextEvent = new Event(channel, inResPath, nextSystemState.getResource(inResId), channelSelectorValues, dependingVarToVal); + nextEvents.add(nextEvent); + } + } + } + } + } + } + } + } + return nextEvents; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/SystemState.java b/AlgebraicDataflowArchitectureModel/src/simulator/SystemState.java new file mode 100644 index 0000000..8f5037e --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/SystemState.java @@ -0,0 +1,304 @@ +package simulator; + +import java.util.AbstractMap; +import java.util.ArrayList; +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.Term; +import models.algebra.Type; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.ResourceHierarchy; +import models.dataFlowModel.DataTransferChannel; +import simulator.states.CompositeResourceState; +import simulator.states.JsonResourceState; +import simulator.states.ListResourceState; +import simulator.states.MapResourceState; +import simulator.states.PrimitiveResourceState; +import simulator.states.ResourceState; + +public class SystemState { + private Set rootResources = new HashSet<>(); + private Map channelStates = new HashMap<>(); + private List events = new ArrayList<>(); + + public SystemState() { + } + + public SystemState(SystemState prevState) { + rootResources = new HashSet<>(prevState.getRootResources()); + channelStates = new HashMap<>(prevState.getChannelStates()); + } + + public Set getRootResources() { + return rootResources; + } + + public void addResource(Resource rootResource) { + rootResources.add(rootResource); + } + + public Resource getResource(ResourceIdentifier resourceIdentifier) { + for (Resource root: rootResources) { + Resource descendant = root.getDescendant(resourceIdentifier); + if (descendant != null) return descendant; + } + return null; + } + + public Resource getResource(String resourceIdentifier) { + for (Resource root: rootResources) { + Resource descendant = root.getDescendant(resourceIdentifier); + if (descendant != null) return descendant; + } + return null; + } + + /** + * update the state of a specified resource + * + * @param resourceIdentifier a resource identifier to identify the resource + * @param resStateValue a new state of the resource + * @return + */ + public ResourceIdentifier updateResourceState(ResourceIdentifier resourceIdentifier, Expression resStateValue) { + Type resType = resourceIdentifier.getResourceStateType(); + if (resType != null && DataConstraintModel.typeList.isAncestorOf(resType)) { + if (resStateValue instanceof Constant) { + } else if (resStateValue instanceof Term) { + Term listValue = (Term) resStateValue; + if (listValue.getSymbol().equals(DataConstraintModel.append)) { + Resource res = getResource(resourceIdentifier); + ResourceState state = res.getState(); + Expression childExp = null; + if (state instanceof ListResourceState) { + childExp = new Constant(Integer.toString(((ListResourceState) state).getChildStates().size())); + } else if (state instanceof PrimitiveResourceState && ((PrimitiveResourceState) state).getValue().getSymbol().equals(DataConstraintModel.nil)) { + // If the value of state is nil. + childExp = new Constant("0"); + ResourceState parentState = res.getParent().getState(); + ResourceState newState = new ListResourceState(); + if (parentState instanceof CompositeResourceState) { + ((CompositeResourceState) parentState).replaceChildState(state, newState); + } + state = newState; + res.changeState(state); + } + ResourceHierarchy childResourceHierarchy = null; + if (resourceIdentifier.getResourceHierarchy().getChildren() != null && resourceIdentifier.getResourceHierarchy().getChildren().size() > 0) { + childResourceHierarchy = resourceIdentifier.getResourceHierarchy().getChildren().iterator().next(); + } else { + childResourceHierarchy = new ResourceHierarchy(resourceIdentifier.getResourceHierarchy(), 1); + } + ResourceIdentifier childResourceIdentifier = new ResourceIdentifier(resourceIdentifier, childExp, childResourceHierarchy); + Map.Entry childInfo = createResourceState(childResourceIdentifier, listValue.getChild(1)); + ((ListResourceState) state).addChildState(childInfo.getKey()); + return childInfo.getValue(); + } + } + } else if (resType != null && DataConstraintModel.typeMap.isAncestorOf(resType)) { + if (resStateValue instanceof Constant) { + } else if (resStateValue instanceof Term) { + Term mapValue = (Term) resStateValue; + if (mapValue.getSymbol().equals(DataConstraintModel.insert)) { + Expression childExp = mapValue.getChild(1); + if (childExp instanceof Constant) { + Resource res = getResource(resourceIdentifier); + ResourceState state = res.getState(); + if (state instanceof PrimitiveResourceState && ((PrimitiveResourceState) state).getValue().getSymbol().equals(DataConstraintModel.nil)) { + // If the value of state is nil. + ResourceState parentState = res.getParent().getState(); + ResourceState newState = new MapResourceState(); + if (parentState instanceof CompositeResourceState) { + ((CompositeResourceState) parentState).replaceChildState(state, newState); + } + state = newState; + res.changeState(state); + } + ResourceHierarchy childResourceHierarchy = null; + if (resourceIdentifier.getResourceHierarchy().getChildren() != null && resourceIdentifier.getResourceHierarchy().getChildren().size() > 0) { + childResourceHierarchy = resourceIdentifier.getResourceHierarchy().getChildren().iterator().next(); + } else { + childResourceHierarchy = new ResourceHierarchy(resourceIdentifier.getResourceHierarchy(), 1); + } + ResourceIdentifier childResourceIdentifier = new ResourceIdentifier(resourceIdentifier, childExp, childResourceHierarchy); + String childId = ((Constant) childExp).toString(); + Map.Entry childInfo = createResourceState(childResourceIdentifier, mapValue.getChild(2)); + ((MapResourceState) state).addChildState(childId, childInfo.getKey()); + return childInfo.getValue(); + } + } + } + } else if (resType != null && DataConstraintModel.typeJson.isAncestorOf(resType)) { + if (resStateValue instanceof Constant) { + } else if (resStateValue instanceof Term) { + Term jsonValue = (Term) resStateValue; + Resource res = getResource(resourceIdentifier); + ResourceState state = res.getState(); + ResourceIdentifier createdResource = null; + while (jsonValue.getSymbol().equals(DataConstraintModel.addMember)) { + Expression childExp = jsonValue.getChild(1); + if (childExp instanceof Constant) { + String memberName = ((Constant) childExp).getSymbol().getName().replace("\"", ""); + ResourceHierarchy childResourceHierarchy = null; + for (ResourceHierarchy childRes: resourceIdentifier.getResourceHierarchy().getChildren()) { + if (childRes.getResourceName().equals(memberName)) { + childResourceHierarchy = childRes; + break; + } + } + if (childResourceHierarchy == null) { + childResourceHierarchy = new ResourceHierarchy(resourceIdentifier.getResourceHierarchy(), memberName); + } + ResourceIdentifier childResourceIdentifier = new ResourceIdentifier(resourceIdentifier, memberName, childResourceHierarchy); + String childId = ((Constant) childExp).toString(); + Map.Entry childInfo = createResourceState(childResourceIdentifier, jsonValue.getChild(2)); + ((JsonResourceState) state).addChildState(childId, childInfo.getKey()); + createdResource = childInfo.getValue(); + } + if (!(jsonValue.getChild(0) instanceof Term)) break; + jsonValue = (Term) jsonValue.getChild(0); + } + return createdResource; + } + } else { + if (resStateValue instanceof Constant) { + Resource res = getResource(resourceIdentifier); + ResourceState state = null; + if (res != null) { + state = res.getState(); + ((PrimitiveResourceState) state).setValue((Constant) resStateValue); + } else { + ResourceIdentifier parentResId = (ResourceIdentifier) resourceIdentifier.getParent(); + Type parentResType = parentResId.getResourceStateType(); + if (parentResType != null && DataConstraintModel.typeList.isAncestorOf(parentResType)) { + } else if (parentResType != null && DataConstraintModel.typeMap.isAncestorOf(parentResType)) { + JsonResourceState parentState = (JsonResourceState) getResource(parentResId).getState(); + parentState.addChildState(((Constant) resourceIdentifier.getLastParam()).toString(), new PrimitiveResourceState((Constant) resStateValue)); + } else if (parentResType != null && DataConstraintModel.typeJson.isAncestorOf(parentResType)) { + JsonResourceState parentState = (JsonResourceState) getResource(parentResId).getState(); + parentState.addChildState("\"" + resourceIdentifier.getLeafResourceName() + "\"", new PrimitiveResourceState((Constant) resStateValue)); + } + } + return resourceIdentifier; + } + } + return resourceIdentifier; + } + + public Map.Entry createResourceState(ResourceIdentifier resourceIdentifier, Expression resStateValue) { + Type resType = null; + if (resStateValue instanceof Term) { + resType = ((Term) resStateValue).getType(); + resStateValue = ((Term) resStateValue).reduce(); + } + if (resType != null) { + if (DataConstraintModel.typeList.isAncestorOf(resType)) { + if (resStateValue instanceof Constant) { + } else if (resStateValue instanceof Term) { + Term listValue = (Term) resStateValue; + if (listValue.getSymbol().equals(DataConstraintModel.append)) { + ListResourceState state = new ListResourceState(); + Expression childExp = new Constant(Integer.toString(((ListResourceState) state).getChildStates().size())); + ResourceIdentifier childResourceIdentifier = new ResourceIdentifier(resourceIdentifier, + childExp, + resourceIdentifier.getResourceHierarchy().getChildren().iterator().next()); + Map.Entry childInfo = createResourceState(childResourceIdentifier, listValue.getChild(1)); + state.addChildState(childInfo.getKey()); + return new AbstractMap.SimpleEntry<>(state, childInfo.getValue()); + } + } + } else if (DataConstraintModel.typeMap.isAncestorOf(resType)) { + if (resStateValue instanceof Constant) { + } else if (resStateValue instanceof Term) { + Term mapValue = (Term) resStateValue; + if (mapValue.getSymbol().equals(DataConstraintModel.insert)) { + Expression childExp = mapValue.getChild(1); + if (childExp instanceof Constant) { + MapResourceState state = new MapResourceState(); + ResourceIdentifier childResourceIdentifier = new ResourceIdentifier(resourceIdentifier, + childExp, + resourceIdentifier.getResourceHierarchy().getChildren().iterator().next()); + String childId = ((Constant) childExp).toString(); + Map.Entry childInfo = createResourceState(childResourceIdentifier, mapValue.getChild(2)); + state.addChildState(childId, childInfo.getKey()); + return new AbstractMap.SimpleEntry<>(state, childInfo.getValue()); + } + } + } + } else if (DataConstraintModel.typeJson.isAncestorOf(resType)) { + if (resStateValue instanceof Constant) { + } else if (resStateValue instanceof Term) { + Term jsonValue = (Term) resStateValue; + JsonResourceState state = new JsonResourceState(); + Map.Entry createInfo = null; + while (jsonValue.getSymbol().equals(DataConstraintModel.addMember)) { + Expression childExp = jsonValue.getChild(1); + if (childExp instanceof Constant) { + String memberName = ((Constant) childExp).getSymbol().getName().replace("\"", ""); + ResourceHierarchy childResourceHierarchy = null; + for (ResourceHierarchy childRes: resourceIdentifier.getResourceHierarchy().getChildren()) { + if (childRes.getResourceName().equals(memberName)) { + childResourceHierarchy = childRes; + break; + } + } + if (childResourceHierarchy == null) { + childResourceHierarchy = new ResourceHierarchy(resourceIdentifier.getResourceHierarchy(), memberName); + } + ResourceIdentifier childResourceIdentifier = new ResourceIdentifier(resourceIdentifier, memberName, childResourceHierarchy); + String childId = ((Constant) childExp).toString(); + Map.Entry childInfo = createResourceState(childResourceIdentifier, jsonValue.getChild(2)); + state.addChildState(childId, childInfo.getKey()); + createInfo = new AbstractMap.SimpleEntry<>(state, childInfo.getValue()); + } + if (!(jsonValue.getChild(0) instanceof Term)) break; + jsonValue = (Term) jsonValue.getChild(0); + } + return createInfo; + } + } + } + if (resStateValue instanceof Constant) { + Resource res = getResource(resourceIdentifier); + ResourceState state = null; + if (res != null) { + state = res.getState(); + ((PrimitiveResourceState) state).setValue((Constant) resStateValue); + } else { + state = new PrimitiveResourceState((Constant) resStateValue); + } + return new AbstractMap.SimpleEntry<>(state, resourceIdentifier); + } + return null; + } + + public Map getChannelStates() { + return channelStates; + } + + public ChannelState getChannelState(DataTransferChannel channel) { + return channelStates.get(channel); + } + + public void addChannel(DataTransferChannel channel) { + channelStates.put(channel, null); + } + + public void updateChannelState(DataTransferChannel channel, ChannelState channelState) { + channelStates.put(channel, channelState); + } + + public List getEvents() { + return events; + } + + public void addEvent(Event event) { + events.add(event); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/INativeReceiver.java b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/INativeReceiver.java new file mode 100644 index 0000000..50e651e --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/INativeReceiver.java @@ -0,0 +1,7 @@ +package simulator.interfaces; + +import simulator.Event; + +public interface INativeReceiver { + public void onReceiveFromModel(Event event); +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/NativeSender.java b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/NativeSender.java new file mode 100644 index 0000000..a286401 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/NativeSender.java @@ -0,0 +1,40 @@ +package simulator.interfaces; + +import java.util.HashSet; +import java.util.Set; + +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.ResourcePath; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork; +import simulator.Event; +import simulator.Resource; +import simulator.Simulator; + +abstract public class NativeSender { + protected Simulator simulator; + protected DataTransferChannel channel; + protected ResourcePath resourcePath; + protected Resource resource; + + public NativeSender(Simulator simulator, DataTransferChannel channel, ResourcePath resourcePath, Resource resource) { + this.simulator = simulator; + this.channel = channel; + this.resourcePath = resourcePath; + this.resource = resource; + } + + public void sendToModel(Expression message) { + try { + Event event = new Event(channel, message, resourcePath, resource); + simulator.transition(event); + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentMouseSender.java b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentMouseSender.java new file mode 100644 index 0000000..f90d46b --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentMouseSender.java @@ -0,0 +1,47 @@ +package simulator.interfaces.swing; + +import java.awt.Component; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Term; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.ResourcePath; +import models.dataFlowModel.DataTransferChannel; +import simulator.Resource; +import simulator.Simulator; +import simulator.interfaces.NativeSender; + +public class ComponentMouseSender extends NativeSender implements MouseListener { + + public ComponentMouseSender(Simulator simulator, DataTransferChannel channel, ResourcePath resourcePath, Resource resource) { + super(simulator, channel, resourcePath, resource); + } + + public void mouseClicked(MouseEvent e) { + } + + public void mousePressed(MouseEvent e) { + Constant one = new Constant("1", DataConstraintModel.typeInt); + Expression message = channel.getOutputChannelMembers().iterator().next().getStateTransition().getMessageExpression(); + message = (Term) message.clone(); + ((Term) message).setChild(0, one); + sendToModel(message); + } + + public void mouseReleased(MouseEvent e) { + Constant zero = new Constant("0", DataConstraintModel.typeInt); + Expression message = channel.getOutputChannelMembers().iterator().next().getStateTransition().getMessageExpression(); + message = (Term) message.clone(); + ((Term) message).setChild(0, zero); + sendToModel(message); + } + + public void mouseEntered(MouseEvent e) { + } + + public void mouseExited(MouseEvent e) { + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentTextReceiver.java b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentTextReceiver.java new file mode 100644 index 0000000..e110a67 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentTextReceiver.java @@ -0,0 +1,39 @@ +package simulator.interfaces.swing; + +import java.awt.Component; + +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.text.JTextComponent; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Term; +import simulator.Event; +import simulator.interfaces.INativeReceiver; + +public class ComponentTextReceiver implements INativeReceiver { + protected Component component; + + public ComponentTextReceiver(Component component) { + this.component = component; + } + + @Override + public void onReceiveFromModel(Event event) { + Expression message = event.getMessage(); + if (message instanceof Term) { + Expression text = ((Term) message).getChild(0); + if (text instanceof Constant) { + if (component instanceof JTextComponent) { + ((JTextComponent) component).setText(((Constant) text).getSymbol().getName()); + } else if (component instanceof JLabel) { + ((JLabel) component).setText(((Constant) text).getSymbol().getName()); + } else if (component instanceof JButton) { + ((JButton) component).setText(((Constant) text).getSymbol().getName()); + } + } + } + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentTextSender.java b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentTextSender.java new file mode 100644 index 0000000..4e13f41 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentTextSender.java @@ -0,0 +1,36 @@ +package simulator.interfaces.swing; + +import java.awt.TextField; +import java.awt.event.InputMethodEvent; +import java.awt.event.InputMethodListener; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Term; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.ResourcePath; +import models.dataFlowModel.DataTransferChannel; +import simulator.Resource; +import simulator.Simulator; +import simulator.interfaces.NativeSender; + +public class ComponentTextSender extends NativeSender implements InputMethodListener { + + public ComponentTextSender(Simulator simulator, DataTransferChannel channel, ResourcePath resourcePath, Resource resource) { + super(simulator, channel, resourcePath, resource); + } + + @Override + public void inputMethodTextChanged(InputMethodEvent event) { + Constant text = new Constant(((TextField) event.getSource()).getText(), DataConstraintModel.typeString); + Expression message = channel.getOutputChannelMembers().iterator().next().getStateTransition().getMessageExpression(); + message = (Term) message.clone(); + ((Term) message).setChild(0, text); + sendToModel(message); + } + + @Override + public void caretPositionChanged(InputMethodEvent event) { + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentVisibilityReceiver.java b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentVisibilityReceiver.java new file mode 100644 index 0000000..2527a52 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentVisibilityReceiver.java @@ -0,0 +1,34 @@ +package simulator.interfaces.swing; + +import java.awt.Component; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Term; +import models.dataConstraintModel.DataConstraintModel; +import simulator.Event; +import simulator.interfaces.INativeReceiver; + +public class ComponentVisibilityReceiver implements INativeReceiver { + protected Component component; + + public ComponentVisibilityReceiver(Component component) { + this.component = component; + } + + @Override + public void onReceiveFromModel(Event event) { + Expression message = event.getMessage(); + if (message instanceof Term) { + Expression visible = ((Term) message).getChild(0); + if (visible instanceof Constant) { + if (((Constant) visible).getSymbol() == DataConstraintModel.true_) { + component.setVisible(true); + } else { + component.setVisible(false); + } + } + } + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/SwingPresenter.java b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/SwingPresenter.java new file mode 100644 index 0000000..f8b3b2e --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/SwingPresenter.java @@ -0,0 +1,128 @@ +package simulator.interfaces.swing; + +import java.awt.Component; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; + +import models.algebra.Expression; +import models.dataConstraintModel.JsonTerm; +import models.dataConstraintModel.MapTerm; +import models.dataConstraintModel.ResourcePath; +import models.dataFlowModel.DataTransferChannel; +import simulator.Event; +import simulator.Resource; +import simulator.Simulator; +import simulator.interfaces.INativeReceiver; + +public class SwingPresenter implements INativeReceiver { + public final String screenUpdateChannelName = "ScreenUpdate"; + public final String setVisibleChannelName = "SetVisible"; + public final String mouseEventChannelName = "MouseEvent"; + public final String textEventChannelName = "TextEvent"; + + protected JPanel mainPanel; + protected Simulator simulator; + protected Map components; + protected DataTransferChannel screenUpdateChannel; + protected DataTransferChannel setVisibleChannel; + protected DataTransferChannel mouseEventChannel; + protected DataTransferChannel textEventChannel; + protected Map> channelAndResourcesForReceiving = new HashMap<>(); + + public SwingPresenter(JPanel mainPanel, Simulator simulator) { + this.mainPanel = mainPanel; + this.simulator = simulator; + screenUpdateChannel = (DataTransferChannel) simulator.getModel().getChannel(screenUpdateChannelName); + setVisibleChannel = (DataTransferChannel) simulator.getModel().getChannel(setVisibleChannelName); + mouseEventChannel = (DataTransferChannel) simulator.getModel().getChannel(mouseEventChannelName); + textEventChannel = (DataTransferChannel) simulator.getModel().getChannel(textEventChannelName); + simulator.addNativeReceiver(this, screenUpdateChannel); + } + + @Override + public void onReceiveFromModel(Event event) { + Expression message = event.getMessage(); + if (message instanceof JsonTerm) { + // Remove old native receivers. + for (DataTransferChannel channel: channelAndResourcesForReceiving.keySet()) { + for (Resource resource: channelAndResourcesForReceiving.get(channel)) { + simulator.removeNativeReceiver(channel, resource); + } + } + channelAndResourcesForReceiving.clear(); + + // Reconstruct swing components. + JsonTerm screenContent = (JsonTerm) message; + Resource screenResource = simulator.getCurState().getResource(event.getInputResource().getResourceIdentifier()); + Expression widgets = screenContent.get("\"widgets\""); + Resource widgetsResource = screenResource.getChildrenMap().get("\"widgets\""); + if (widgets instanceof MapTerm) { + for (String key: ((MapTerm) widgets).keySet()) { + Expression value = ((MapTerm) widgets).get(key); + if (value instanceof JsonTerm) { + JsonTerm widget = (JsonTerm) value; + Resource widgetResource = widgetsResource.getChildrenMap().get(key); + Expression type = widget.get("\"type\""); + if (type.toString().equals("button")) { + // Add a button component. + Expression text = widget.get("\"text\""); + JButton button = new JButton(text.toString().toString()); + mainPanel.add(button); + components.put(key, button); + // Connect swing component and model. + ResourcePath resPath = mouseEventChannel.getOutputResources().iterator().next(); + button.addMouseListener(new ComponentMouseSender(simulator, mouseEventChannel, resPath, widgetResource)); // button => widgetResource + ComponentVisibilityReceiver nativeReceiver = new ComponentVisibilityReceiver(button); // widgetResource => button + simulator.addNativeReceiver(nativeReceiver, setVisibleChannel, widgetResource); + Set resources = channelAndResourcesForReceiving.get(setVisibleChannel); + if (resources == null) { + resources = new HashSet<>(); + channelAndResourcesForReceiving.put(setVisibleChannel, resources); + } + resources.add(widgetsResource); + } else if (type.toString().equals("label")) { + // Add a label component. + Expression text = widget.get("\"text\""); + JLabel label = new JLabel(text.toString().toString()); + mainPanel.add(label); + components.put(key, label); + // Connect swing component and model. + ComponentVisibilityReceiver nativeReceiver = new ComponentVisibilityReceiver(label); + simulator.addNativeReceiver(nativeReceiver, setVisibleChannel, widgetResource); + Set resources = channelAndResourcesForReceiving.get(setVisibleChannel); + if (resources == null) { + resources = new HashSet<>(); + channelAndResourcesForReceiving.put(setVisibleChannel, resources); + } + resources.add(widgetsResource); + } else if (type.toString().equals("inputText")) { + // Add a text input component. + JTextField textField = new JTextField(); + mainPanel.add(textField); + components.put(key, textField); + // Connect swing component and model. + ResourcePath resPath = textEventChannel.getOutputResources().iterator().next(); + textField.addInputMethodListener(new ComponentTextSender(simulator, textEventChannel, resPath, widgetResource)); // textField => widgetResource + ComponentVisibilityReceiver nativeReceiver = new ComponentVisibilityReceiver(textField); // widgetResource => textField + simulator.addNativeReceiver(nativeReceiver, setVisibleChannel, widgetResource); + Set resources = channelAndResourcesForReceiving.get(setVisibleChannel); + if (resources == null) { + resources = new HashSet<>(); + channelAndResourcesForReceiving.put(setVisibleChannel, resources); + } + resources.add(widgetsResource); + } + } + } + } + } + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/states/CompositeResourceState.java b/AlgebraicDataflowArchitectureModel/src/simulator/states/CompositeResourceState.java new file mode 100644 index 0000000..5e6311f --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/states/CompositeResourceState.java @@ -0,0 +1,14 @@ +package simulator.states; + +import java.util.Map; + +public abstract class CompositeResourceState extends ResourceState { + @Override + boolean hasChildren() { + return true; + } + + public abstract Map getChildStates(); + + public abstract void replaceChildState(ResourceState state, ResourceState newState); +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/states/JsonResourceState.java b/AlgebraicDataflowArchitectureModel/src/simulator/states/JsonResourceState.java new file mode 100644 index 0000000..cf4ce2b --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/states/JsonResourceState.java @@ -0,0 +1,42 @@ +package simulator.states; + +import java.util.HashMap; +import java.util.Map; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Term; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.JsonTerm; + +public class JsonResourceState extends CompositeResourceState { + private Map children = new HashMap<>(); + + @Override + public Expression getValue() { + JsonTerm value = new JsonTerm(); + value.setType(DataConstraintModel.typeJson); + for (Map.Entry childEnt: children.entrySet()) { + value.addMember(childEnt.getKey(), childEnt.getValue().getValue()); + } + return value; + } + + @Override + public Map getChildStates() { + return children; + } + + public void addChildState(String param, ResourceState childState) { + children.put(param, childState); + } + + @Override + public void replaceChildState(ResourceState state, ResourceState newState) { + for (Map.Entry childEnt: children.entrySet()) { + if (childEnt.getValue().equals(state)) { + children.put(childEnt.getKey(), newState); + } + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/states/ListResourceState.java b/AlgebraicDataflowArchitectureModel/src/simulator/states/ListResourceState.java new file mode 100644 index 0000000..022a32f --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/states/ListResourceState.java @@ -0,0 +1,51 @@ +package simulator.states; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Term; +import models.dataConstraintModel.DataConstraintModel; + +public class ListResourceState extends CompositeResourceState { + private List children = new ArrayList<>(); + + @Override + public Expression getValue() { + Term value = new Constant(DataConstraintModel.nil); + value.setType(DataConstraintModel.typeList); + for (ResourceState child: children) { + Term newTerm = new Term(DataConstraintModel.append); + newTerm.addChild(value); + newTerm.addChild(child.getValue()); + value = newTerm; + } + return value; + } + + @Override + public Map getChildStates() { + Map childParams = new TreeMap<>(); + for (int i = 0; i < children.size(); i++) { + childParams.put(Integer.toString(i), children.get(i)); + } + return childParams; + } + + public void addChildState(ResourceState childState) { + children.add(childState); + } + + + @Override + public void replaceChildState(ResourceState state, ResourceState newState) { + for (int i = 0; i < children.size(); i++) { + if (children.get(i).equals(state)) { + children.set(i, newState); + } + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/states/MapResourceState.java b/AlgebraicDataflowArchitectureModel/src/simulator/states/MapResourceState.java new file mode 100644 index 0000000..5bd2fc7 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/states/MapResourceState.java @@ -0,0 +1,45 @@ +package simulator.states; + +import java.util.HashMap; +import java.util.Map; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Term; +import models.dataConstraintModel.DataConstraintModel; + +public class MapResourceState extends CompositeResourceState { + private Map children = new HashMap<>(); + + @Override + public Expression getValue() { + Term value = new Constant(DataConstraintModel.nil); + value.setType(DataConstraintModel.typeMap); + for (Map.Entry childEnt: children.entrySet()) { + Term newTerm = new Term(DataConstraintModel.insert); + newTerm.addChild(value); + newTerm.addChild(new Constant(childEnt.getKey(), DataConstraintModel.typeString)); + newTerm.addChild(childEnt.getValue().getValue()); + value = newTerm; + } + return value; + } + + @Override + public Map getChildStates() { + return children; + } + + public void addChildState(String param, ResourceState childState) { + children.put(param, childState); + } + + @Override + public void replaceChildState(ResourceState state, ResourceState newState) { + for (Map.Entry childEnt: children.entrySet()) { + if (childEnt.getValue().equals(state)) { + children.put(childEnt.getKey(), newState); + } + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/states/PrimitiveResourceState.java b/AlgebraicDataflowArchitectureModel/src/simulator/states/PrimitiveResourceState.java new file mode 100644 index 0000000..d5aba63 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/states/PrimitiveResourceState.java @@ -0,0 +1,27 @@ +package simulator.states; + +import models.algebra.Constant; +import models.dataConstraintModel.DataConstraintModel; + +public class PrimitiveResourceState extends ResourceState { + private Constant value; + + public PrimitiveResourceState(Constant initialValue) { + value = initialValue; + } + + @Override + public Constant getValue() { + if (value == null) return new Constant(DataConstraintModel.null_); + return value; + } + + public void setValue(Constant newValue) { + value = newValue; + } + + @Override + boolean hasChildren() { + return false; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/states/ResourceState.java b/AlgebraicDataflowArchitectureModel/src/simulator/states/ResourceState.java new file mode 100644 index 0000000..7eba6c7 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/states/ResourceState.java @@ -0,0 +1,5 @@ +package simulator.states; + +abstract public class ResourceState extends State { + abstract boolean hasChildren(); +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/states/State.java b/AlgebraicDataflowArchitectureModel/src/simulator/states/State.java new file mode 100644 index 0000000..f9286dd --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/states/State.java @@ -0,0 +1,7 @@ +package simulator.states; + +import models.algebra.Expression; + +abstract public class State { + abstract public Expression getValue(); +} 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..b84e563 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,47 +13,47 @@ 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.addInputChannel(cio_setCustomerOff); + model.addInputChannel(cio_setCompanyAdd); + model.addChannel(c); // Check the model. assertEquals(3, model.getResourcePaths().size()); - assertEquals(2, model.getIOChannels().size()); + assertEquals(7, model.getResourceHierarchies().size()); + assertEquals(2, model.getInputChannels().size()); assertEquals(1, model.getChannels().size()); } diff --git a/AlgebraicDataflowArchitectureModel/src/tests/DataFlowModelTest.java b/AlgebraicDataflowArchitectureModel/src/tests/DataFlowModelTest.java deleted file mode 100644 index 605b006..0000000 --- a/AlgebraicDataflowArchitectureModel/src/tests/DataFlowModelTest.java +++ /dev/null @@ -1,88 +0,0 @@ -package tests; - -import static org.junit.Assert.*; - -import org.junit.Test; - -import models.*; -import models.dataConstraintModel.*; -import models.dataFlowModel.*; - -public class DataFlowModelTest { - - @Test - 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 === - // - // customers.{x1}.off(c, set(x)) == x - // customers.{x1}.off(c, e) == c - // - DataTransferChannel gin_1 = new DataTransferChannel("gin_1"); // set customer's office (an input channel) - GroupSelector x1 = new GroupSelector(); - 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()); - - // === gin_2 === - // - // companies.{x2}.add(a, set(y)) == y - // companies.{x2}.add(a, e) == a - // - DataTransferChannel gin_2 = new DataTransferChannel("gin_2"); // set companie's address (an input channel) - GroupSelector x2 = new GroupSelector(); - 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()); - - // === g === - // - // customers.{x3}.off( c, update(y, z)) == y - // companies.{y}.add( a1, update(y, z)) == z - // customers.{x3}.add(a2, update(y, z)) == z - // - DataTransferChannel g = new DataTransferChannel("g"); // update customer's address - GroupSelector x3 = new GroupSelector(); - ChannelSelector y = new ChannelSelector(); - 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()); - - // Construct a data-flow architecture model. - model.addIOChannel(gin_1); - model.addIOChannel(gin_2); - model.addChannel(g); - - // Check the model. - assertEquals(3, model.getResourcePaths().size()); - assertEquals(2, model.getIOChannels().size()); - assertEquals(1, model.getChannels().size()); - - // Extract the resource dependency graph. - DataFlowGraph resourceDependencyGraph = model.getDataFlowGraph(); - - // Check the graph. - assertEquals(3, resourceDependencyGraph.getNodes().size()); - assertEquals(2, 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/DataTransferModelTest.java b/AlgebraicDataflowArchitectureModel/src/tests/DataTransferModelTest.java new file mode 100644 index 0000000..0f4575b --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/tests/DataTransferModelTest.java @@ -0,0 +1,88 @@ +package tests; + +import static org.junit.Assert.*; + +import org.junit.Test; + +import models.*; +import models.algebra.Variable; +import models.dataConstraintModel.*; +import models.dataFlowModel.*; + +public class DataTransferModelTest { + + @Test + public void test() { + // Construct a data-flow architecture model. + DataTransferModel model = new DataTransferModel(); + + 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) === + // + // out customers.{uid}.off(c, set(x)) = x + // + 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); + cio_setCustomerOff.addChannelMemberAsOutput(customer_off_1); + assertEquals(customer_off.getPathParams().get(0), cio_setCustomerOff.getSelectors().iterator().next().getExpression()); + + // === cio_setCompanyAdd(cid) === + // + // out companies.{cid}.add(a, set(y)) = y + // + 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); + cio_setCompanyAdd.addChannelMemberAsOutput(company_add_1); + assertEquals(company_add.getPathParams().get(0), cio_setCompanyAdd.getSelectors().iterator().next().getExpression()); + + // === c === + // + // 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 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); + 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.addInputChannel(cio_setCustomerOff); + model.addInputChannel(cio_setCompanyAdd); + model.addChannel(c); + + // Check the model. + assertEquals(3, model.getResourcePaths().size()); + assertEquals(7, model.getResourceHierarchies().size()); + assertEquals(2, model.getInputChannels().size()); + assertEquals(1, model.getChannels().size()); + + // Extract the resource dependency graph. + DataFlowGraph resourceDependencyGraph = model.getDataFlowGraph(); + + // Check the graph. + 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/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/NativeAccessTest.java b/AlgebraicDataflowArchitectureModel/src/tests/NativeAccessTest.java new file mode 100644 index 0000000..d9598b8 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/tests/NativeAccessTest.java @@ -0,0 +1,101 @@ +package tests; + +import org.junit.Test; + +import algorithms.TypeInference; +import models.algebra.Expression; +import models.algebra.InvalidMessage; +import models.algebra.ParameterizedIdentifierIsFutureWork; +import models.algebra.UnificationFailed; +import models.algebra.ValueUndefined; +import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.ResourcePath; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork; +import parser.Parser; +import parser.Parser.TokenStream; +import parser.exceptions.ExpectedColon; +import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.WrongJsonExpression; +import simulator.Event; +import simulator.Resource; +import simulator.Simulator; +import simulator.SystemState; +import simulator.interfaces.INativeReceiver; +import simulator.interfaces.NativeSender; + +public class NativeAccessTest { + + @Test + public void test() { + try { + TokenStream stream = new Parser.TokenStream(); + Parser parser = new Parser(stream); + + // Construct a data transfer architecture model. + DataTransferModel model = new DataTransferModel(); + ResourcePath sampleText = new ResourcePath("sampleText"); + model.addResourcePath(sampleText); + + DataTransferChannel nativeInputEventChannel = new DataTransferChannel("NativeInput"); + nativeInputEventChannel.setNative(true); + ChannelMember textIn = new ChannelMember(sampleText); + stream.addLine("curText:Str"); + Expression curStateIn = parser.parseTerm(stream, model); + stream.addLine("input(nextText)"); + Expression messageIn = parser.parseTerm(stream, model); + stream.addLine("nextText"); + Expression nextStateIn = parser.parseTerm(stream, model); + textIn.getStateTransition().setCurStateExpression(curStateIn); + textIn.getStateTransition().setMessageExpression(messageIn); + textIn.getStateTransition().setNextStateExpression(nextStateIn); + nativeInputEventChannel.addChannelMemberAsOutput(textIn); + model.addInputChannel(nativeInputEventChannel); + + DataTransferChannel nativeOutputEventChannel = new DataTransferChannel("NativeOutput"); + nativeOutputEventChannel.setNative(true); + ChannelMember textOut = new ChannelMember(sampleText); + stream.addLine("curText:Str"); + Expression curStateOut = parser.parseTerm(stream, model); + stream.addLine("output(nextText)"); + Expression messageOut = parser.parseTerm(stream, model); + stream.addLine("nextText"); + Expression nextStateOut = parser.parseTerm(stream, model); + textOut.getStateTransition().setCurStateExpression(curStateOut); + textOut.getStateTransition().setMessageExpression(messageOut); + textOut.getStateTransition().setNextStateExpression(nextStateOut); + nativeOutputEventChannel.addChannelMemberAsInput(textOut); + model.addChannel(nativeOutputEventChannel); + TypeInference.infer(model); + + // Create simulator + Simulator simulator = new Simulator(model); + + // Initial state + SystemState initialState = simulator.init(); + Resource ioResource = initialState.getResource("sampleText"); + + // Connect the simulator and native code + NativeSender textSender = new NativeSender(simulator, nativeInputEventChannel, sampleText, ioResource) { // Native code => model + }; + INativeReceiver textReceiver = new INativeReceiver() { // Model to native code + @Override + public void onReceiveFromModel(Event event) { // Receive a message from the model + Expression message = event.getMessage(); + System.out.println(message); + } + }; + simulator.addNativeReceiver(textReceiver, nativeOutputEventChannel); + + stream.addLine("input(\"HelloWorld\")"); + Expression sendMessage = parser.parseTerm(stream, model); + textSender.sendToModel(sendMessage); // Send a message to the model + + } catch (ExpectedRightBracket | WrongJsonExpression | ExpectedColon e) { + e.printStackTrace(); + } + + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/tests/SimplifiedDataFlowModelTest.java b/AlgebraicDataflowArchitectureModel/src/tests/SimplifiedDataFlowModelTest.java index acb5333..099c6a6 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); @@ -71,22 +75,23 @@ assertEquals(c3.getOutputChannelMembers().size(), 1); // Construct a data-flow architecture model. - model.addIOChannel(cin); + model.addInputChannel(cin); model.addChannel(c1); model.addChannel(c2); model.addChannel(c3); // Check the model. assertEquals(4, model.getResourcePaths().size()); - assertEquals(1, model.getIOChannels().size()); + assertEquals(1, model.getInputChannels().size()); assertEquals(3, model.getChannels().size()); // Extract the resource dependency graph. 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/SimulatorTest.java b/AlgebraicDataflowArchitectureModel/src/tests/SimulatorTest.java new file mode 100644 index 0000000..ca760bb --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/tests/SimulatorTest.java @@ -0,0 +1,224 @@ +package tests; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import models.algebra.Expression; +import models.algebra.InvalidMessage; +import models.algebra.ParameterizedIdentifierIsFutureWork; +import models.algebra.UnificationFailed; +import models.algebra.ValueUndefined; +import models.algebra.Variable; +import models.algebra.Constant; +import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.ResourcePath; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork; +import parser.Parser; +import parser.Parser.TokenStream; +import parser.exceptions.ExpectedColon; +import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.WrongJsonExpression; +import simulator.Event; +import simulator.Resource; +import simulator.Simulator; +import simulator.SystemState; +import simulator.ResourceIdentifier; +import simulator.states.MapResourceState; + +public class SimulatorTest { + @Test + public void test() { + try { + TokenStream stream = new Parser.TokenStream(); + Parser parser = new Parser(stream); + + // Construct a data transfer architecture model. + DataTransferModel model = new DataTransferModel(); + + ResourcePath customers = new ResourcePath("customers"); // "customers" + ResourcePath customer = new ResourcePath(customers, + new Variable("uid", DataConstraintModel.typeString)); // "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", DataConstraintModel.typeString)); // "companies.{cid}" + ResourcePath company_add = new ResourcePath(company, "add"); // "companies.{cid}.add" + ResourcePath company2 = new ResourcePath(companies, + new Variable("cid2", DataConstraintModel.typeString)); // "companies.{cid2}" + ResourcePath company2_add = new ResourcePath(company2, "add"); // "companies.{cid2}.add" + ResourcePath companyA = new ResourcePath(companies, + new Constant("\"A\"", DataConstraintModel.typeString)); // "companies.A" + ResourcePath companyA_add = new ResourcePath(companyA, "add"); // "companies.{cid2}.add" + model.addResourcePath(customer_off); + model.addResourcePath(customer_add); + model.addResourcePath(company_add); + model.addResourcePath(company2_add); + model.addResourcePath(customers); + model.addResourcePath(companies); + + // For channel CIO_AddCustomer + DataTransferChannel cio_addCustomer = new DataTransferChannel("CIO_AddCustomer"); // add a customer + ChannelMember customers1 = new ChannelMember(customers); + stream.addLine("db:Map"); + Expression curStateExp0 = parser.parseTerm(stream, model); + customers1.getStateTransition().setCurStateExpression(curStateExp0); + stream.addLine("addCustomer(uid:Str, off:Str)"); + Expression messageExp0 = parser.parseTerm(stream, model); + customers1.getStateTransition().setMessageExpression(messageExp0); + stream.addLine("insert(db, uid, {\"off\": off})"); + Expression nextStateExp0 = parser.parseTerm(stream, model); + customers1.getStateTransition().setNextStateExpression(nextStateExp0); + cio_addCustomer.addChannelMemberAsOutput(customers1); + + // For channel CIO_AddCompany + DataTransferChannel cio_addCompany = new DataTransferChannel("CIO_AddCompany"); // add a company + ChannelMember companies1 = new ChannelMember(companies); + stream.addLine("db:Map"); + Expression curStateExp1 = parser.parseTerm(stream, model); + companies1.getStateTransition().setCurStateExpression(curStateExp1); + stream.addLine("addCampany(cid:Str, add:Str)"); + Expression messageExp1 = parser.parseTerm(stream, model); + companies1.getStateTransition().setMessageExpression(messageExp1); + stream.addLine("insert(db, cid, {\"add\": add})"); + Expression nextStateExp1 = parser.parseTerm(stream, model); + companies1.getStateTransition().setNextStateExpression(nextStateExp1); + cio_addCompany.addChannelMemberAsOutput(companies1); + + // For channel CIO_SetCustomerOff + DataTransferChannel cio_setCustomerOff = new DataTransferChannel("CIO_SetCustomerOff", new Variable("uid")); // set customer's office + ChannelMember customer_off_1 = new ChannelMember(customer_off); + stream.addLine("cid:Str"); + Expression curStateExp2 = parser.parseTerm(stream, model); + customer_off_1.getStateTransition().setCurStateExpression(curStateExp2); + stream.addLine("setOff(cid2:Str)"); + Expression messageExp2 = parser.parseTerm(stream, model); + customer_off_1.getStateTransition().setMessageExpression(messageExp2); + stream.addLine("cid2"); + Expression nextStateExp2 = parser.parseTerm(stream, model); + customer_off_1.getStateTransition().setNextStateExpression(nextStateExp2); + cio_setCustomerOff.addChannelMemberAsOutput(customer_off_1); + + // For channel CIO_SetCompanyAdd + DataTransferChannel cio_setCompanyAdd = new DataTransferChannel("CIO_SetCompanyAdd", new Variable("cid")); // set companie's address + ChannelMember company_add_1 = new ChannelMember(company_add); + stream.addLine("a1:Str"); + Expression curStateExp3 = parser.parseTerm(stream, model); + company_add_1.getStateTransition().setCurStateExpression(curStateExp3); + stream.addLine("setAdd(a2:Str)"); + Expression messageExp3 = parser.parseTerm(stream, model); + company_add_1.getStateTransition().setMessageExpression(messageExp3); + stream.addLine("a2"); + Expression nextStateExp3 = parser.parseTerm(stream, model); + company_add_1.getStateTransition().setNextStateExpression(nextStateExp3); + cio_setCompanyAdd.addChannelMemberAsOutput(company_add_1); + + // For channel C + DataTransferChannel c = new DataTransferChannel("C", new Variable("uid")); // update customer's address + ChannelMember customer_off_2 = new ChannelMember(customer_off); + stream.addLine("cid:Str"); + Expression curStateExp4 = parser.parseTerm(stream, model); + customer_off_2.getStateTransition().setCurStateExpression(curStateExp4); + stream.addLine("sync(cid2:Str, add2:Str)"); + Expression messageExp4 = parser.parseTerm(stream, model); + customer_off_2.getStateTransition().setMessageExpression(messageExp4); + stream.addLine("cid2"); + Expression nextStateExp4 = parser.parseTerm(stream, model); + customer_off_2.getStateTransition().setNextStateExpression(nextStateExp4); + c.addChannelMemberAsInput(customer_off_2); + + ChannelMember company_add_2 = new ChannelMember(company2_add); + stream.addLine("a1:Str"); + Expression curStateExp5 = parser.parseTerm(stream, model); + company_add_2.getStateTransition().setCurStateExpression(curStateExp5); + stream.addLine("sync(cid2:Str, add2:Str)"); + Expression messageExp5 = parser.parseTerm(stream, model); + company_add_2.getStateTransition().setMessageExpression(messageExp5); + stream.addLine("add2"); + Expression nextStateExp5 = parser.parseTerm(stream, model); + company_add_2.getStateTransition().setNextStateExpression(nextStateExp5); + c.addChannelMemberAsInput(company_add_2); + + ChannelMember customer_add_2 = new ChannelMember(customer_add); + stream.addLine("a3:Str"); + Expression curStateExp6 = parser.parseTerm(stream, model); + customer_add_2.getStateTransition().setCurStateExpression(curStateExp6); + stream.addLine("sync(cid2:Str, add2:Str)"); + Expression messageExp6 = parser.parseTerm(stream, model); + customer_add_2.getStateTransition().setMessageExpression(messageExp6); + stream.addLine("add2"); + Expression nextStateExp6 = parser.parseTerm(stream, model); + customer_add_2.getStateTransition().setNextStateExpression(nextStateExp6); + c.addChannelMemberAsOutput(customer_add_2); + + model.addInputChannel(cio_setCustomerOff); + model.addInputChannel(cio_setCompanyAdd); + model.addChannel(c); + + Simulator simulator = new Simulator(model); + + // Initial state + SystemState initialState = simulator.init(); + + assertEquals(2, initialState.getRootResources().size()); + for (Resource rootRes: initialState.getRootResources()) { + assertTrue(rootRes.getState() instanceof MapResourceState); + assertEquals(0, rootRes.getChildren().size()); + assertEquals(0, ((MapResourceState) rootRes.getState()).getChildStates().size()); + } + System.out.println("companies:" + initialState.getResource(ResourceIdentifier.createFrom(companies)).getState().getValue()); + System.out.println("customers:" + initialState.getResource(ResourceIdentifier.createFrom(customers)).getState().getValue()); + + // Next state (companies.addCompany("A", "Osaka")) + stream.addLine("addCompany(\"A\", \"Osaka\")"); + Expression messageAddComp = parser.parseTerm(stream, model); + Event addCompany = new Event(cio_addCompany, messageAddComp, companies, initialState.getResource(ResourceIdentifier.createFrom(companies))); + simulator.transition(addCompany); + SystemState nextState = simulator.getCurState(); + + assertEquals(2, nextState.getRootResources().size()); + Resource companiesRes = nextState.getResource(ResourceIdentifier.createFrom(companies)); + assertTrue(companiesRes.getState() instanceof MapResourceState); + assertEquals(1, companiesRes.getChildren().size()); + assertEquals(1, ((MapResourceState) companiesRes.getState()).getChildStates().size()); + System.out.println("companies:" + nextState.getResource(ResourceIdentifier.createFrom(companies)).getState().getValue()); + System.out.println("customers:" + nextState.getResource(ResourceIdentifier.createFrom(customers)).getState().getValue()); + + // After the next state (customers.addCustomer("1", "A")) + stream.addLine("addCustomer(\"1\", \"A\")"); + Expression messageAddCust = parser.parseTerm(stream, model); + Event addCustomer = new Event(cio_addCustomer, messageAddCust, customers, nextState.getResource(ResourceIdentifier.createFrom(customers))); + simulator.transition(addCustomer); + SystemState nextNextState = simulator.getCurState(); + + assertEquals(2, nextState.getRootResources().size()); + Resource customersRes = nextNextState.getResource(ResourceIdentifier.createFrom(customers)); + assertTrue(customersRes.getState() instanceof MapResourceState); + assertEquals(1, customersRes.getChildren().size()); + assertEquals(1, ((MapResourceState) customersRes.getState()).getChildStates().size()); + System.out.println("companies:" + nextNextState.getResource(ResourceIdentifier.createFrom(companies)).getState().getValue()); + System.out.println("customers:" + nextNextState.getResource(ResourceIdentifier.createFrom(customers)).getState().getValue()); + + // After the next next state (companies.A.setAdd("Tokyo")) + stream.addLine("setAdd(\"Tokyo\")"); + Expression messageSetAdd = parser.parseTerm(stream, model); + Event setAdd = new Event(cio_setCompanyAdd, messageSetAdd, company_add, nextNextState.getResource(ResourceIdentifier.createFrom(companyA_add))); + simulator.transition(setAdd); + SystemState nextNextNextState = simulator.getCurState(); + Resource customersRes2 = nextNextNextState.getResource(ResourceIdentifier.createFrom(customers)); + assertTrue(customersRes2.getState() instanceof MapResourceState); + assertEquals(1, customersRes2.getChildren().size()); + assertEquals(1, ((MapResourceState) customersRes2.getState()).getChildStates().size()); + System.out.println("companies:" + nextNextNextState.getResource(ResourceIdentifier.createFrom(companies)).getState().getValue()); + System.out.println("customers:" + nextNextNextState.getResource(ResourceIdentifier.createFrom(customers)).getState().getValue()); + + } catch (ExpectedRightBracket | WrongJsonExpression | ExpectedColon | ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/tests/UpdateCodeGenerationTest.java b/AlgebraicDataflowArchitectureModel/src/tests/UpdateCodeGenerationTest.java index 69a5b1f..bc1db89 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) + // payment(p1, update1(y)) = y + // 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); @@ -147,21 +165,21 @@ System.out.println(c1.deriveUpdateExpressionOf(c1_loyalty).toImplementation(sideEffects)); System.out.println("-- PUSH --"); - Expression loyaltyPushUpdate = c1.deriveUpdateExpressionOf(c1_loyalty, pushAccessor); + Expression loyaltyPushUpdate = c1.deriveUpdateExpressionOf(c1_loyalty, pushAccessor).getKey(); 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("\t return " + c1.deriveUpdateExpressionOf(c1_loyalty, pullAccessor).toImplementation(sideEffects) + ";"); + System.out.println(points.getResourceStateType().getImplementationTypeName() + " " + pointsGetter.toImplementation() + "() {"); + System.out.println("\t return " + c1.deriveUpdateExpressionOf(c1_loyalty, pullAccessor).getKey().toImplementation(sideEffects) + ";"); System.out.println("}"); } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage | UnificationFailed | ValueUndefined e) { @@ -172,8 +190,8 @@ // === c2 === // - // payment(p1, update2(z)) == z - // history(h, update2(z)) == cons(z, h) + // payment(p1, update2(z)) = z + // history(h, update2(z)) = cons(z, h) // DataTransferChannel c2 = new DataTransferChannel("c2"); ChannelMember c2_payment = new ChannelMember(payment); @@ -210,10 +228,10 @@ System.out.println(c2.deriveUpdateExpressionOf(c2_history).toImplementation(sideEffects)); System.out.println("-- PUSH --"); - Expression historyPushUpdate = c2.deriveUpdateExpressionOf(c2_history, pushAccessor); + Expression historyPushUpdate = c2.deriveUpdateExpressionOf(c2_history, pushAccessor).getKey(); 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; } @@ -224,7 +242,7 @@ System.out.println("-- PULL --"); System.out.println(history.getResourceStateType().getImplementationTypeName() + " " + historyGetter.toImplementation() + "() {"); - System.out.println("\t return " + c2.deriveUpdateExpressionOf(c2_history, pullAccessor).toImplementation(sideEffects) + ";"); + System.out.println("\t return " + c2.deriveUpdateExpressionOf(c2_history, pullAccessor).getKey().toImplementation(sideEffects) + ";"); System.out.println("}"); } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage | UnificationFailed | ValueUndefined e) { @@ -273,10 +291,10 @@ System.out.println(c3.deriveUpdateExpressionOf(c3_total).toImplementation(sideEffects)); System.out.println("-- PUSH --"); - Expression totalPushUpdate = c3.deriveUpdateExpressionOf(c3_total, pushAccessor); + Expression totalPushUpdate = c3.deriveUpdateExpressionOf(c3_total, pushAccessor).getKey(); 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; } @@ -287,7 +305,7 @@ System.out.println("-- PULL --"); System.out.println(total.getResourceStateType().getImplementationTypeName() + " " + totalGetter.toImplementation() + "() {"); - System.out.println("\t return " + c3.deriveUpdateExpressionOf(c3_total, pullAccessor).toImplementation(sideEffects) + ";"); + System.out.println("\t return " + c3.deriveUpdateExpressionOf(c3_total, pullAccessor).getKey().toImplementation(sideEffects) + ";"); System.out.println("}"); } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage | UnificationFailed | ValueUndefined e) { 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..6234ba9 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 { @@ -40,7 +44,7 @@ for (Channel c: model.getChannels()) { for (ChannelMember out: ((DataTransferChannel) c).getOutputChannelMembers()) { String[] sideEffects = new String[] {""}; - System.out.println("next" + out.getResource().getResourceName() + " = " + ((DataTransferChannel) c).deriveUpdateExpressionOf(out).toImplementation(sideEffects)); + System.out.println("next" + out.getResource().getLeafResourceName() + " = " + ((DataTransferChannel) c).deriveUpdateExpressionOf(out).toImplementation(sideEffects)); } } @@ -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) {