diff --git a/AlgebraicDataflowArchitectureModel/.settings/org.eclipse.jdt.core.prefs b/AlgebraicDataflowArchitectureModel/.settings/org.eclipse.jdt.core.prefs index 263a512..d0dc81b 100644 --- a/AlgebraicDataflowArchitectureModel/.settings/org.eclipse.jdt.core.prefs +++ b/AlgebraicDataflowArchitectureModel/.settings/org.eclipse.jdt.core.prefs @@ -1,9 +1,9 @@ eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate -org.eclipse.jdt.core.compiler.codegen.targetPlatform=13 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=14 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=13 +org.eclipse.jdt.core.compiler.compliance=14 org.eclipse.jdt.core.compiler.debug.lineNumber=generate org.eclipse.jdt.core.compiler.debug.localVariable=generate org.eclipse.jdt.core.compiler.debug.sourceFile=generate @@ -12,4 +12,4 @@ org.eclipse.jdt.core.compiler.problem.enumIdentifier=error org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning org.eclipse.jdt.core.compiler.release=disabled -org.eclipse.jdt.core.compiler.source=13 +org.eclipse.jdt.core.compiler.source=14 diff --git a/AlgebraicDataflowArchitectureModel/models/Accounts.model b/AlgebraicDataflowArchitectureModel/models/Accounts.model new file mode 100644 index 0000000..24710ee --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/Accounts.model @@ -0,0 +1,7 @@ +channel CIO1 { + out accounts(l:List, signup(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/Citrus.model b/AlgebraicDataflowArchitectureModel/models/Citrus.model new file mode 100644 index 0000000..356405a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/Citrus.model @@ -0,0 +1,75 @@ +channel Signup { + out accounts(accDB:Map, signup(aid:Str)) = insert(accDB, aid, {"favorites": nil, "books": nil}) +} + +channel CreateBook(aid:Str) { + out accounts.{aid}.books(bookList:List, createBook(title:Str)) = append(bookList, {"title": title, "todos": nil, "favorited": nil}) +} + +channel DeleteAccount { + out accounts(accDB:Map, deleteAccount(aid:Str)) = if(contains(accDB, aid), delete(accDB, aid), accDB) +} + +channel ChangeAccountName { + out accounts(accDB:Map, changeAccountId(aid:Str, newAid:Str)) = if(aid == newAid, accDB, delete(insert(accDB, newAid, lookup(accDB, aid)), aid)) +} + +channel ChangeBookName(aid:Str, bid:Int) { + out accounts.{aid}.books.{bid}.title(title:Str, changeBookName(newTitle)) = newTitle +} + +channel DeleteBook(aid:Str) { + out accounts.{aid}.books(bookList:List, deleteBook(bid:Int)) = if(bid < length(bookList), remove(bookList, bid), bookList) +} + +channel CreateToDo(aid:Str, bid:Int) { + out accounts.{aid}.books.{bid}.todos(toDoDB:Map, createtodo(year:Str, month:Str, day:Str, title:Str)) = + if( + contains(toDoDB,year), + if( + contains(lookup(toDoDB,year),month), + if( + contains(lookup(lookup(toDoDB,year),month),day), + insert(toDoDB,year,insert(lookup(toDoDB,year),month,insert(lookup(lookup(toDoDB,year),month),day,append(lookup(lookup(lookup(toDoDB,year),month),day),{"title":title,"check":false})))), + insert(toDoDB,year,insert(lookup(toDoDB,year),month,insert(lookup(lookup(toDoDB,year),month),day,append(nil,{"title":title,"check":false})))) + ), + insert(toDoDB,year,insert(lookup(toDoDB,year),month,insert(nil,day,append(nil,{"title":title,"check":false})))) + ), + insert(toDoDB,year,insert(nil,month,insert(nil,day,append(nil,{"title":title,"check":false})))) + ) +} + +channel ChangeToDoName(aid:Str, bid:Int, year:Str, month:Str, day:Str, tid:Int) { + out accounts.{aid}.books.{bid}.todos.{year}.{month}.{day}.{tid}.title(title:Str, changeToDoName(newTitle)) = newTitle +} + +channel ChangeCheck(aid:Str, bid:Int, year:Str, month:Str, day:Str, tid:Int) { + out accounts.{aid}.books.{bid}.todos.{year}.{month}.{day}.{tid}.check(check:Bool, changeCheck(newCheck)) = newCheck +} + +channel DeleteToDo(aid:Str, bid:Int) { + out accounts.{aid}.books.{bid}.todos(toDoDB:Map, deleteToDo(year:Str, month:Str, day:Str, tid:Int)) = insert(toDoDB, + year, + insert(lookup(toDoDB, year), + month, + insert(lookup(lookup(toDoDB, year), month), + day, + remove(lookup(lookup(lookup(toDoDB, year), month), day), + tid)))) +} + +channel AddFavorited(aid:Str, bid:Int) { + out accounts.{aid}.books.{bid}.favorited(faList:List, addFavorited(o_aid:Str)) = if(aid==o_aid, + faList, + if(contains(faList, o_aid), + remove(faList, indexOf(faList, o_aid)), + append(faList, o_aid))) + out accounts.{o_aid}.favorites(aDB:Map, addFavorited(o_aid:Str)) + = if(aid==o_aid, + aDB, + if(contains(aDB, aid), + if(contains(lookup(aDB, aid), bid), + insert(aDB, aid, remove(lookup(aDB, aid), indexOf(lookup(aDB, aid), bid))), + insert(aDB, aid, append(lookup(aDB, aid), bid))), + insert(aDB, aid, append(nil, bid)))) +} 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/CustomerManagement.model b/AlgebraicDataflowArchitectureModel/models/CustomerManagement.model new file mode 100644 index 0000000..5277f32 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/CustomerManagement.model @@ -0,0 +1,21 @@ +channel AddCustomer { + out customers(csDB:Map, addCustomer(uid:Str, org:Str)) = insert(csDB, uid, {"organization": org}) +} + +channel AddCampany { + out companies(cmDB:Map, addCampany(cid:Str, address:Str)) = insert(cmDB, cid, {"address": address}) +} + +channel SetCustomerOrganization(uid:Str) { + out customers.{uid}.organization(prevCid:Str, setOrganization(cid)) = cid +} + +channel SetCompanyAddress(cid:Str) { + out companies.{cid}.address(prevAdd:Str, setAddress(add)) = add +} + +channel UpdateCustomerAddress(uid:Str) { + in customers.{uid}.organization(prevCid, updateCustomerAddress(cid, add)) = cid + in companies.{cid}.address(prevAdd, updateCustomerAddress(cid, add)) = add + out customers.{uid}.address(prevAdd, updateCustomerAddress(cid, add)) = add +} diff --git a/AlgebraicDataflowArchitectureModel/models/CustomerOffice.model b/AlgebraicDataflowArchitectureModel/models/CustomerOffice.model deleted file mode 100644 index f140bff..0000000 --- a/AlgebraicDataflowArchitectureModel/models/CustomerOffice.model +++ /dev/null @@ -1,33 +0,0 @@ -channel C_CustomerAOff_In { - out customerA_off(c:String, setOff(x)) == x - out customerA_off(c, e) == c -} - -channel C_CustomerBOff_In { - out customerB_off(c:String, setOff(x)) == x - out customerB_off(c, e) == c -} - -channel C_CompanyC1Add_In { - out companyC1_add(a:String, setAdd(y)) == y - out companyC1_add(a, e) == a -} - -channel C_CompanyC2Add_In { - out companyC2_add(a:String, setAdd(y)) == y - out companyC2_add(a, e) == a -} - -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)) -} 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/GroupChat.model b/AlgebraicDataflowArchitectureModel/models/GroupChat.model new file mode 100644 index 0000000..1203e1a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/GroupChat.model @@ -0,0 +1,27 @@ +channel Signup { + out accounts(acDB:Map, signUp(aid:Str)) = insert(acDB, aid, {"notifications": nil}) +} + +channel HasRead(aid:Str) { + out accounts.{aid}.notifications(ntMap:Map, hasRead(gid:Str)) = delete(ntMap, gid) +} + +channel CreateGroup { + out groups(grDB:Map, createGroup(gid:Str)) = insert(grDB, gid, {"members": nil, "messages": nil}) +} + +channel AddGroupMember(gid:Str) { + out groups.{gid}.members(memList:List, addGroupMember(aid:Str)) = append(memList, aid) +} + +channel PostMessage(gid:Str) { + out groups.{gid}.messages(mesList:List, postMessage(message:Str)) = append(mesList, message) +} + +channel Notify(gid:Str) { + in groups.{gid}.messages(prevMesList, notify(m)) = mesList + for EachMember(mno:Int) { + ref groups.{gid}.members.{mno}(m.{mno}:Str, notify(m)) + out accounts.{m.{mno}:Str}.notifications(prevNtMap:Map, notify(m)) = insert(prevNtMap, gid, true) + } +} 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..0158d26 --- /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(contains(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..d91b144 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/OnlineBattleGame.model @@ -0,0 +1,31 @@ +channel Signup { + out accounts(acDB:Map, signUp(aid:Str, name:Str)) = insert(acDB, aid, {"name": name}) +} + +channel ChangeName(aid:Str) { + out accounts.{aid}.name(prevName:Str, changeName(name)) = name +} + +channel CreateRoom { + out rooms(rmDB:Map, createRoom(rid:Str, blueId:Str, redId:Str)) = insert(rmDB, rid, {"blue_id": blueId, "red_id": redId}) +} + +channel ChangeRedId(rid:Str) { + out rooms.{rid}.red_id(prevRedId:Str, changeRedId(redId)) = redId +} + +channel ChangeBlueId(rid:Str) { + out rooms.{rid}.blue_id(prevBlueId:Str, changeBlueId(blueId)) = blueId +} + +channel UpdateRedName(rid:Str) { + in rooms.{rid}.red_id(prevAid:Str, updateRedName(aid, name)) = aid + in accounts.{aid}.name(prevName:Str, updateRedName(aid, name)) = name + out rooms.{rid}.red_name(prevName:Str, updateRedName(aid, name)) = name +} + +channel UpdateBlueName(rid:Str) { + in rooms.{rid}.blue_id(prevAid:Str, updateBlueName(aid, name)) = aid + in accounts.{aid}.name(prevName:Str, updateBlueName(aid, name)) = name + out rooms.{rid}.blue_name(prevName:Str, updateBlueName(aid, name)) = name +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/models/OnlineBattleGame2.model b/AlgebraicDataflowArchitectureModel/models/OnlineBattleGame2.model new file mode 100644 index 0000000..ccd15a7 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/OnlineBattleGame2.model @@ -0,0 +1,33 @@ +channel Signup { + out accounts(acDB:Map, signUp(aid:Str, name:Str)) = insert(acDB, aid, {"name": name, "point": 0}) +} + +channel ChangeName(aid:Str) { + out accounts.{aid}.name(prevName:Str, changeName(name)) = name +} + +channel CreateRoom { + out rooms(rmDB:Map, createRoom(rid:Str)) = insert(rmDB, rid, {"members": nil, "battle": false}) +} + +channel AddRoomMember(rid:Str) { + out rooms.{rid}.members(mList:List, addRoomMember(id:Str)) = append(mList, {"id": id}) +} + +channel Battle(rid:Str) { + out rooms.{rid}.battle(prevHasWon, battle(hasWon:Bool)) = hasWon +} + +channel UpdateName(rid:Str, mno:Int) { + in rooms.{rid}.members.{mno}.id(prevMid:Str, updateName(mid, name)) = mid + in accounts.{mid}.name(prevName:Str, updateName(mid, name)) = name + out rooms.{rid}.members.{mno}.name(prevName:Str, updateName(mid, name)) = name +} + +channel UpdatePoint(rid:Str) { + in rooms.{rid}.battle(prevState, updatePoint(hasWon, mid)) = hasWon + for EachMember(mno:Int) { + ref rooms.{rid}.members.{mno}.id(mid:Str, updatePoint(hasWon, mid)) + out accounts.{mid}.point(prevPoint:Int, updatePoint(hasWon, mid)) = if(hasWon, prevPoint + 1, prevPoint) + } +} \ 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/RefBug.model b/AlgebraicDataflowArchitectureModel/models/RefBug.model new file mode 100644 index 0000000..e421f6b --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/RefBug.model @@ -0,0 +1,19 @@ +init { + constant := 10 + variable := 10 + ans := 20 +} + +channel changeVar { + out variable(cur: Int, change(next: Int)) = next +} + +channel changeConst { + out constant(const: Int, change(next: Int)) = next +} + +channel calcAns { + in variable(curVar: Int, calc(const: Int, nextVar: Int)) = nextVar + ref constant(const: Int, calc(const: Int, var: Int)) + out ans(curAns: Int, calc(const: Int, var: Int)) = var + const +} 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..6d4cb43 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/SimpleUI.model @@ -0,0 +1,112 @@ +init { + screenTemplates := { + "000": {"widgets": {"001": {"type": "textInput", "text": "", "state": 0, "visible": true}, + "002": {"type": "button", "text": "Next", "state": 0, "visible": true}}, + "layout": true}, + "001": {"widgets": {"003": {"type": "label", "text": "label", "state": 0, "visible": true}, + "004": {"type": "button", "text": "Back", "state": 0, "visible": true}}, + "layout": true} + } +} + +native channel ScreenUpdate { + in screen(curSc: Json, update(curSc, nextSc)) = nextSc +} + +native channel SetLayout { + in screen.layout(curLayout: Bool, setLayout(nextLayout)) = nextLayout +} + +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 SetX(wid: Str) { + in screen.widgets.{wid}.x(curX: Int, setX(nextX)) = nextX +} + +native channel SetY(wid: Str) { + in screen.widgets.{wid}.y(curY: Int, setY(nextY)) = nextY +} + +native channel SetWidth(wid: Str) { + in screen.widgets.{wid}.width(curWidth: Int, setWidth(nextWidth)) = nextWidth +} + +native channel SetHeight(wid: Str) { + in screen.widgets.{wid}.height(curHeight: Int, setHeight(nextHeight)) = nextHeight +} + +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 +} + +channel ChangeCurScreen { + out curScreen(curScId: Str, changeCurScreen(nextScId)) = nextScId +} + +channel ScreenTransition { + in curScreen(curScId: Str, transScreen(nextScId, screen)) = nextScId + ref screenTemplates.{nextScId}(screen, transScreen(nextScId, screen)) + out screen(curS, transScreen(nextScId, screen)) = screen +} + +channel EventDispatch(wid: Str) { + in screen.widgets.{wid}.state(curState: Int, dispatchEvent(curScId, wid, nextState)) = nextState + ref curScreen(curScId: Str, dispatchEvent(curScId, wid, nextState)) + out screenTemplates.{curScId}.widgets.{wid}.state(curState: Int, dispatchEvent(curScId, wid, nextState)) = nextState +} + +channel EventHandler1(scId: Str, wid: Str) { + in screenTemplates.{scId="000"}.widgets.{wid="002"}.state(curState: Int, handleEvent1(nextState)) = nextState + out curScreen(curScId: Str, handleEvent1(nextState)) = if(nextState == 0, "001", curScId) +} + +channel EventHandler2(scId: Str, wid: Str) { + in screenTemplates.{scId="001"}.widgets.{wid="004"}.state(curState: Int, handleEvent2(nextState)) = nextState + out curScreen(curScId: Str, handleEvent2(nextState)) = if(nextState == 0, "000", curScId) +} + +channel ChangeLayout { + out screen.layout(curLayout: Bool, changeLayout(layout)) = layout +} + +channel ChangeX(wid: Str) { + out screen.widgets.{wid}.x(curX: Int, changeX(x)) = x +} + +channel ChangeY(wid: Str) { + out screen.widgets.{wid}.y(curY: Int, changeY(y)) = y +} + +channel AddButton { + out screen.widgets(widgets: Map, addButton(wid: Str, text: Str)) = insert(widgets, wid, {"type": "button", "text": text, "state": 0}) +} + +channel AddLabel { + out screen.widgets(widgets: Map, addLabel(wid: Str, text: Str)) = insert(widgets, wid, {"type": "label", "text": text, "state": 0}) +} + +channel AddTextInput { + out screen.widgets(widgets: Map, addTextInput(wid: Str)) = insert(widgets, wid, {"type": "textInput", "text": "", "state": 0}) +} + +channel AddMovableButton { + out screen.widgets(widgets: Map, addMovableButton(wid: Str, text: Str, x: Int, y: Int, width: Int, height: Int)) = insert(widgets, wid, {"type": "button", "text": text, "x": x, "y": y, "width": width, "height": height, "state": 0}) +} + +channel AddMovableLabel { + out screen.widgets(widgets: Map, addMovableLabel(wid: Str, text: Str, x: Int, y: Int, width: Int, height: Int)) = insert(widgets, wid, {"type": "label", "text": text, "x": x, "y": y, "width": width, "height": height, "state": 0}) +} + +channel AddMovableTextInput { + out screen.widgets(widgets: Map, addMovableTextInput(wid: Str, x: Int, y: Int, width: Int, height: Int)) = insert(widgets, wid, {"type": "textInput", "text": "", "x": x, "y": y, "width": width, "height": height, "state": 0}) +} \ No newline at end of file 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/TableUI.model b/AlgebraicDataflowArchitectureModel/models/TableUI.model new file mode 100644 index 0000000..defb520 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/TableUI.model @@ -0,0 +1,114 @@ +init { + screenTemplates := { + "000": { + "widgets": { + "001": {"type": "textInput", "text": "", "state": 0, "visible": true}, + "002": {"type": "table", "text": "testTeble", "state": 0, "visible": true, + "data": {"_": {"name": "_", "age": 0}}, + "rowHeight": 40, + "colWidth": 100, + "columns": append(append(nil, "name"), "age"), + "primaryKeyName": "id", + } + }, + "layout": true + } + } + screen1 := "000" + w002 := "002" +} + +native channel ScreenUpdate { + in screen(curSc: Json, update(curSc, nextSc)) = nextSc +} + +native channel SetLayout { + in screen.layout(curLayout: Bool, setLayout(nextLayout)) = nextLayout +} + +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 SetX(wid: Str) { + in screen.widgets.{wid}.x(curX: Int, setX(nextX)) = nextX +} + +native channel SetY(wid: Str) { + in screen.widgets.{wid}.y(curY: Int, setY(nextY)) = nextY +} + +native channel SetWidth(wid: Str) { + in screen.widgets.{wid}.width(curWidth: Int, setWidth(nextWidth)) = nextWidth +} + +native channel SetHeight(wid: Str) { + in screen.widgets.{wid}.height(curHeight: Int, setHeight(nextHeight)) = nextHeight +} + +native channel MouseEvent(wid: Str) { + out screen.widgets.{wid}.state(curState: Int, mouseEvent(nextState)) = nextState +} + +native channel OnTableChanged(wid: Str) { + in screen.widgets.{wid}.data(curData: Map, tableChanged(nextData)) = nextData +} + +native channel TextEvent(wid: Str) { + out screen.widgets.{wid}.text(curText: Str, textEvent(nextText)) = nextText +} + +channel ChangeCurScreen { + out curScreen(curScId: Str, changeCurScreen(nextScId)) = nextScId +} + +channel ScreenTransition { + in curScreen(curScId: Str, transScreen(nextScId, screen)) = nextScId + ref screenTemplates.{nextScId}(screen, transScreen(nextScId, screen)) + out screen(curS, transScreen(nextScId, screen)) = screen +} + +channel EventDispatch(wid: Str) { + in screen.widgets.{wid}.state(curState: Int, dispatchEvent(curScId, wid, nextState)) = nextState + ref curScreen(curScId: Str, dispatchEvent(curScId, wid, nextState)) + out screenTemplates.{curScId}.widgets.{wid}.state(curState: Int, dispatchEvent(curScId, wid, nextState)) = nextState +} + + +channel EventHandler1(scId: Str, wid: Str) { + in screenTemplates.{scId="000"}.widgets.{wid="002"}.state(curState: Int, handleEvent1(nextState)) = nextState + out curScreen(curScId: Str, handleEvent1(nextState)) = if(nextState == 0, "001", curScId) +} + +channel EventHandler2(scId: Str, wid: Str) { + in screenTemplates.{scId="001"}.widgets.{wid="004"}.state(curState: Int, handleEvent2(nextState)) = nextState + out curScreen(curScId: Str, handleEvent2(nextState)) = if(nextState == 0, "000", curScId) +} + +channel EventHandler3(curScId: Str, wid: Str) { + in screenTemplates.{curScId="000"}.widgets.{wid="002"}.data(curData: Map, handleEvent3(nextData, wid)) = nextData + out screen.widgets.{wid="002"}.data(curData: Map, handleEvent3(nextData, wid)) = nextData +} + +channel addAccount { + out accounts(accounts: Map, addAccount(id: Str, name: Str, age: Int)) = insert(accounts, id, {"name": name, "age": age}) +} + +channel changeNameOfAccount(id: Str) { + out accounts.{id}.name(curName: Str, changeName(nextName: Str)) = nextName +} + +channel changeNameOfAccount(id: Str) { + out accounts.{id}.age(curAge: Int, changeAge(nextAge: Int)) = nextAge +} + +channel sendAccountToTable { + in accounts(cur: Map, sendAccount(next: Map, scId:Str, wid:Str)) = next + ref screen1(scId:Str, sendAccount(next, scId, wid)) + ref w002(wid: Str, sendAccount(next, scId, wid)) + out screenTemplates.{scId}.widgets.{wid}.data(cur: Map, sendAccount(next, scId, wid)) = next +} diff --git a/AlgebraicDataflowArchitectureModel/models/Test.model b/AlgebraicDataflowArchitectureModel/models/Test.model index a8a1348..e5f79c4 100644 --- a/AlgebraicDataflowArchitectureModel/models/Test.model +++ b/AlgebraicDataflowArchitectureModel/models/Test.model @@ -1,9 +1,9 @@ -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 +channel AddUser { + out users(userMap: Map, addUser(userId: Str, name: Str)) = insert(userMap, userId, {"name": name}) +} + +channel PostFriendRequest { + ref users(usersMap: Map, postFriendRequest(senderId: Str, receiverId: Str)) + out friendRequests(friendRequestList: List, postFriendRequest(senderId: Str, receiverId: Str)) = + if(senderId != null && receiverId != null, append(friendRequestList, {"senderId": senderId, "receiverId": receiverId}), friendRequestList) } \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/models/Timer.model b/AlgebraicDataflowArchitectureModel/models/Timer.model new file mode 100644 index 0000000..83a66d5 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/Timer.model @@ -0,0 +1,15 @@ +native channel TimersUpdated { + in timers(curTimers: Map, update(curTimers, nextTimers)) = nextTimers +} + +native channel TimerEvent(tid: Str) { + out timers.{tid}.count(count: Long, tick()) = count + 1 +} + +channel StartTimer { + out timers(timers: Map, startTimer(tid: Str, interval: Long)) = insert(timers, tid, {"interval": interval, "count": 0}) +} + +channel ClearTimer { + out timers(timers: Map, clearTimer(tid: Str)) = delete(timers, tid) +} 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..e5c46a8 100644 --- a/AlgebraicDataflowArchitectureModel/models/Twitter.model +++ b/AlgebraicDataflowArchitectureModel/models/Twitter.model @@ -1,47 +1,20 @@ -channel a_Tweet { - out a_tweets(l:List, a_tweet(t:Str, time:Long)) == cons(tuple(time, t), l) +channel SignUp { + out accounts(acDB:Map, signup(id:Str, name:Str)) = insert(acDB, id, {"name": name, "tweets": nil, "followees": nil, "timeline": nil}) } -channel a_Follow { - out a_following(f:List, a_follow(u:Int)) == cons(u, f) +channel Tweet(id:Str) { + out accounts.{id}.tweets(twList:List, tweet(text:Str, time:Long)) = append(twList, {"time": time, "text": text}) } -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 AddFollowee(id:Str) { + out accounts.{id}.followees(fwList:List, addFollowee(flwId:Str)) = append(fwList, flwId) } -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 UpdateTimeline(myId:Str) { + in accounts.{myId}.tweets(t1:List, m) = m.myTweets + for EachFollowee(no:Int) { + in accounts.{myId}.followees.{no}(id:Str, m) = m.flw.{no}.id + in accounts.{m.flw.{no}.id}.tweets(t2:List, m) = m.flw.{no}.tweets + } + out accounts.{myId}.timeline(l:List, m) = merge(m.myTweets, m.flw) +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/models/VotingSystem.model b/AlgebraicDataflowArchitectureModel/models/VotingSystem.model new file mode 100644 index 0000000..ff425db --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/VotingSystem.model @@ -0,0 +1,14 @@ +channel Signup { + out accounts(acDB:Map, signUp(aid:Str, name:Str)) = insert(acDB, aid, {"name": name, "vote": null}) +} + +channel Cast(aid:Str) { + out accounts.{aid}.vote(preV, cast(v:Str)) = v +} + +channel Collect { + for EachAccount(aid:Str) { + in accounts.{aid}.vote(preV:Str, collect(m)) = m.{aid} + } + out counts(preCnts:Json, collect(m)) = m +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/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/prototypes/JAX-RS/Accounts/Account.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Accounts/Account.java new file mode 100644 index 0000000..423e265 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Accounts/Account.java @@ -0,0 +1,19 @@ +import java.util.*; + +public class Account { + private String name; + public Map getValue() { + Map temp_nil1 = new HashMap<>(); + temp_nil1.put("name",this.getName()); + return temp_nil1; + } + public String getName() { + return this.name; + } + public void changeName(int uid, String name) { + this.name = name; + } + public Account(String name) { + this.name = name; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Accounts/Accounts.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Accounts/Accounts.java new file mode 100644 index 0000000..d851471 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Accounts/Accounts.java @@ -0,0 +1,42 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/accounts") +@Component +public class Accounts { + private List value = new ArrayList<>(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public List getValue() { + return new ArrayList<>(value); + } + public Account getAccount(int uid) { + return this.value.get(uid); + } + @Path("/{uid}") + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getAccountValue(@PathParam("uid") int uid) { + return getAccount(uid).getValue(); + } + @Path("/{uid}/name") + @Produces(MediaType.APPLICATION_JSON) + @GET + public String getNameValue(@PathParam("uid") int uid) { + return getAccount(uid).getName(); + } + @POST + public void signup(@FormParam("name") String name) { + this.value.add(new Account(name)); + } + @Path("/{uid}/name") + @PUT + public void changeName(@PathParam("uid") int uid, @FormParam("name") String name) { + getAccount(uid).changeName(uid, name); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PULL-first/Hour.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PULL-first/Hour.java new file mode 100644 index 0000000..a7acf75 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PULL-first/Hour.java @@ -0,0 +1,28 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/hour") +@Component +public class Hour { + private int value = 0; + @Produces(MediaType.APPLICATION_JSON) + @GET + public int getValue() { + return value; + } + @POST + public void updateFromMin(@FormParam("min") int min) { + int temp_if0; + if ((min==0)) { + temp_if0 = ((this.value+1)%24); + } else { + temp_if0 = this.value; + } + this.value = temp_if0; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PULL-first/Hour_ang.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PULL-first/Hour_ang.java new file mode 100644 index 0000000..9c14018 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PULL-first/Hour_ang.java @@ -0,0 +1,19 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/hour_ang") +@Component +public class Hour_ang { + private Client client = ClientBuilder.newClient(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public double getValue() { + int hour = client.target("http://localhost:8080").path("/hour").request().get(int.class); + return ((hour/6)*Math.PI); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PULL-first/Min.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PULL-first/Min.java new file mode 100644 index 0000000..db4cea5 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PULL-first/Min.java @@ -0,0 +1,27 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/min") +@Component +public class Min { + private int value = 0; + private Client client = ClientBuilder.newClient(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public int getValue() { + return value; + } + @POST + public void tick() throws JsonProcessingException { + Form form = new Form(); + form.param("min", Integer.toString(this.value)); + Entity
entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED); + String result = client.target("http://localhost:8080").path("/hour").request().post(entity, String.class); + this.value = ((this.value+1)%60); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PULL-first/Min_ang.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PULL-first/Min_ang.java new file mode 100644 index 0000000..96ae12b --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PULL-first/Min_ang.java @@ -0,0 +1,19 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/min_ang") +@Component +public class Min_ang { + private Client client = ClientBuilder.newClient(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public double getValue() { + int min = client.target("http://localhost:8080").path("/min").request().get(int.class); + return ((min/30)*Math.PI); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PUSH-first/Hour.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PUSH-first/Hour.java new file mode 100644 index 0000000..12f10e1 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PUSH-first/Hour.java @@ -0,0 +1,33 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/hour") +@Component +public class Hour { + private double value = 0.0; + private Client client = ClientBuilder.newClient(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public double getValue() { + return value; + } + @POST + public void updateFromMin(@FormParam("min") double min) throws JsonProcessingException { + double temp_if1; + if ((min==0)) { + temp_if1 = ((this.value+1)%24); + } else { + temp_if1 = this.value; + } + this.value = temp_if1; + Form form = new Form(); + form.param("hour", Double.toString(this.value)); + Entity entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED); + String result = client.target("http://localhost:8080").path("/hour_ang").request().put(entity, String.class); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PUSH-first/Hour_ang.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PUSH-first/Hour_ang.java new file mode 100644 index 0000000..5412a9a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PUSH-first/Hour_ang.java @@ -0,0 +1,22 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/hour_ang") +@Component +public class Hour_ang { + private double value = 0.0; + @Produces(MediaType.APPLICATION_JSON) + @GET + public double getValue() { + return value; + } + @PUT + public void updateFromHour(@FormParam("hour") double hour) { + this.value = ((hour/6)*Math.PI); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PUSH-first/Min.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PUSH-first/Min.java new file mode 100644 index 0000000..f068cb6 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PUSH-first/Min.java @@ -0,0 +1,31 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/min") +@Component +public class Min { + private double value = 0.0; + private Client client = ClientBuilder.newClient(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public double getValue() { + return value; + } + @POST + public void tick() throws JsonProcessingException { + Form form = new Form(); + form.param("min", Double.toString(this.value)); + Entity entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED); + String result = client.target("http://localhost:8080").path("/hour").request().post(entity, String.class); + form = new Form(); + form.param("min", Double.toString(this.value)); + entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED); + result = client.target("http://localhost:8080").path("/min_ang").request().put(entity, String.class); + this.value = ((this.value+1)%60); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PUSH-first/Min_ang.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PUSH-first/Min_ang.java new file mode 100644 index 0000000..b467b97 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PUSH-first/Min_ang.java @@ -0,0 +1,22 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/min_ang") +@Component +public class Min_ang { + private double value = 0.0; + @Produces(MediaType.APPLICATION_JSON) + @GET + public double getValue() { + return value; + } + @PUT + public void updateFromMin(@FormParam("min") double min) { + this.value = ((min/30)*Math.PI); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/CustomerManagement/Companies.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/CustomerManagement/Companies.java new file mode 100644 index 0000000..a67aea8 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/CustomerManagement/Companies.java @@ -0,0 +1,42 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/companies") +@Component +public class Companies { + private Map value = new HashMap<>(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getValue() { + return new HashMap<>(value); + } + public Company getCompany(String cid) { + return this.value.get(cid); + } + @Path("/{cid}/address") + @Produces(MediaType.APPLICATION_JSON) + @GET + public String getAddressValue(@PathParam("cid") String cid) { + return getCompany(cid).getAddress(); + } + @Path("/{cid}") + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getCompanyValue(@PathParam("cid") String cid) { + return getCompany(cid).getValue(); + } + @Path("/{cid}/address") + @PUT + public void setAddress(@PathParam("cid") String cid, @FormParam("add") String add) { + getCompany(cid).setAddress(cid, add); + } + @POST + public void addCampany(@FormParam("address") String address, @FormParam("cid") String cid) { + this.value.put(cid,new Company(address)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/CustomerManagement/Company.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/CustomerManagement/Company.java new file mode 100644 index 0000000..24e1769 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/CustomerManagement/Company.java @@ -0,0 +1,19 @@ +import java.util.*; + +public class Company { + private String address; + public Map getValue() { + Map temp_nil2 = new HashMap<>(); + temp_nil2.put("address",this.getAddress()); + return temp_nil2; + } + public String getAddress() { + return this.address; + } + public void setAddress(String cid, String add) { + this.address = add; + } + public Company(String address) { + this.address = address; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/CustomerManagement/Customer.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/CustomerManagement/Customer.java new file mode 100644 index 0000000..96f2373 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/CustomerManagement/Customer.java @@ -0,0 +1,26 @@ +import java.util.*; +import javax.ws.rs.client.*; + +public class Customer { + private Client client = ClientBuilder.newClient(); + private String organization; + public Map getValue() { + Map temp_nil3 = new HashMap<>(); + temp_nil3.put("address",this.getAddress()); + temp_nil3.put("organization",this.getOrganization()); + return temp_nil3; + } + public String getAddress() { + String address = client.target("http://localhost:8080").path("/companies/" + organization + "/address").request().get(String.class); + return address; + } + public String getOrganization() { + return this.organization; + } + public void setOrganization(String uid, String cid) { + this.organization = cid; + } + public Customer(String organization) { + this.organization = organization; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/CustomerManagement/Customers.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/CustomerManagement/Customers.java new file mode 100644 index 0000000..22358f4 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/CustomerManagement/Customers.java @@ -0,0 +1,48 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/customers") +@Component +public class Customers { + private Map value = new HashMap<>(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getValue() { + return new HashMap<>(value); + } + public Customer getCustomer(String uid) { + return this.value.get(uid); + } + @Path("/{uid}") + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getCustomerValue(@PathParam("uid") String uid) { + return getCustomer(uid).getValue(); + } + @Path("/{uid}/address") + @Produces(MediaType.APPLICATION_JSON) + @GET + public String getAddressValue(@PathParam("uid") String uid) { + return getCustomer(uid).getAddress(); + } + @Path("/{uid}/organization") + @Produces(MediaType.APPLICATION_JSON) + @GET + public String getOrganizationValue(@PathParam("uid") String uid) { + return getCustomer(uid).getOrganization(); + } + @POST + public void addCustomer(@FormParam("org") String org, @FormParam("uid") String uid) { + this.value.put(uid,new Customer(org)); + } + @Path("/{uid}/organization") + @PUT + public void setOrganization(@PathParam("uid") String uid, @FormParam("cid") String cid) { + getCustomer(uid).setOrganization(uid, cid); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Account.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Account.java new file mode 100644 index 0000000..5f97d07 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Account.java @@ -0,0 +1,22 @@ +import java.util.*; + +public class Account { + private Map notifications; + public Map getValue() { + Map temp_nil3 = new HashMap<>(); + temp_nil3.put("notifications",this.getNotifications()); + return temp_nil3; + } + public Map getNotifications() { + return notifications; + } + public void updateNotificationsFromMessages(String self, String gid, int mno, List messages, String member) { + this.notifications.put(gid,true); + } + public void hasRead(String aid, String gid) { + this.notifications.remove(gid); + } + public Account(Map notifications) { + this.notifications = notifications; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Accounts.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Accounts.java new file mode 100644 index 0000000..c18d220 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Accounts.java @@ -0,0 +1,47 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/accounts") +@Component +public class Accounts { + private Map value = new HashMap<>(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getValue() { + return new HashMap<>(value); + } + public Account getAccount(String v1) { + return this.value.get(v1); + } + @Path("accounts/{v1}/notifications") + @POST + public void updateNotificationsFromMessages(@PathParam("v1") String v1, @FormParam("gid") String gid, @FormParam("mno") int mno, @FormParam("messages") List messages, @FormParam("member") String member) { + getAccount(v1).updateNotificationsFromMessages(v1, gid, mno, messages, member); + } + @Path("/{v1}") + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getAccountValue(@PathParam("v1") String v1) { + return getAccount(v1).getValue(); + } + @Path("/{aid}/notifications") + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getNotificationsValue(@PathParam("aid") String aid) { + return getAccount(aid).getNotifications(); + } + @POST + public void signUp(@FormParam("aid") String aid) { + this.value.put(aid,new Account(new HashMap<>())); + } + @Path("/{aid}/notifications") + @DELETE + public void hasRead(@PathParam("aid") String aid, @FormParam("gid") String gid) { + getAccount(aid).hasRead(aid, gid); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Group.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Group.java new file mode 100644 index 0000000..bb06954 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Group.java @@ -0,0 +1,45 @@ +import java.util.*; +import javax.ws.rs.client.*; + +public class Group { + private List members; + private List messages; + private Client client = ClientBuilder.newClient(); + public Map getValue() { + Map temp_nil2 = new HashMap<>(); + temp_nil2.put("members",this.getMembers()); + temp_nil2.put("messages",this.getMessages()); + return temp_nil2; + } + public List getMembers() { + return this.members; + } + public String getMember(int mno) { + return this.members.get(mno); + } + public List getMessages() { + return this.messages; + } + public void postMessage(String gid, String message) throws JsonProcessingException { + for (int mno = 0; mno < members.size(); mno++) { + String member = getMember(mno); + Form form = new Form(); + form.param("gid", gid.toString()); + form.param("mno", Integer.toString(mno)); + for (String i: messages) { + form.param("messages", i.toString()); + } + form.param("member", member.toString()); + Entity entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED); + String result = client.target("http://localhost:8080").path("/accounts/"+member+"/notifications").request().post(entity, String.class); + } + this.messages.add(message); + } + public void addGroupMember(String gid, String aid) { + this.members.add(aid); + } + public Group(List members, List messages) { + this.members = members; + this.messages = messages; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Groups.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Groups.java new file mode 100644 index 0000000..19b1f42 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Groups.java @@ -0,0 +1,59 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/groups") +@Component +public class Groups { + private Map value = new HashMap<>(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getValue() { + return new HashMap<>(value); + } + public Group getGroup(String gid) { + return this.value.get(gid); + } + @Path("/{gid}") + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getGroupValue(@PathParam("gid") String gid) { + return getGroup(gid).getValue(); + } + @Path("/{gid}/members") + @Produces(MediaType.APPLICATION_JSON) + @GET + public List getMembersValue(@PathParam("gid") String gid) { + return getGroup(gid).getMembers(); + } + @Path("/{gid}/messages") + @Produces(MediaType.APPLICATION_JSON) + @GET + public List getMessagesValue(@PathParam("gid") String gid) { + return getGroup(gid).getMessages(); + } + @Path("/{gid}/members/{mno}") + @Produces(MediaType.APPLICATION_JSON) + @GET + public String getMemberValue(@PathParam("gid") String gid, @PathParam("mno") int mno) { + return getGroup(gid).getMember(mno); + } + @Path("/{gid}/members") + @POST + public void addGroupMember(@PathParam("gid") String gid, @FormParam("aid") String aid) { + getGroup(gid).addGroupMember(gid, aid); + } + @Path("/{gid}/messages") + @POST + public void postMessage(@PathParam("gid") String gid, @FormParam("message") String message) throws JsonProcessingException { + getGroup(gid).postMessage(gid, message); + } + @POST + public void createGroup(@FormParam("gid") String gid) { + this.value.put(gid,new Group(new ArrayList<>(), new ArrayList<>())); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/InventoryManagement/Inventory.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/InventoryManagement/Inventory.java new file mode 100644 index 0000000..de0f8fc --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/InventoryManagement/Inventory.java @@ -0,0 +1,42 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/inventory") +@Component +public class Inventory { + private Map value = new HashMap<>(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getValue() { + return new HashMap<>(value); + } + public InventoryElement getInventoryElement(String itemId) { + return this.value.get(itemId); + } + @Path("/{itemId}/count") + @Produces(MediaType.APPLICATION_JSON) + @GET + public int getCountValue(@PathParam("itemId") String itemId) { + return getInventoryElement(itemId).getCount(); + } + @Path("/{itemId}") + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getInventoryElementValue(@PathParam("itemId") String itemId) { + return getInventoryElement(itemId).getValue(); + } + @Path("/{itemId}/count") + @POST + public void receiveOrShip(@PathParam("itemId") String itemId, @FormParam("quantity") int quantity) { + getInventoryElement(itemId).receiveOrShip(itemId, quantity); + } + @POST + public void registerItem(@FormParam("itemName") String itemName, @FormParam("quantity") int quantity, @FormParam("itemId") String itemId) { + this.value.put(itemId,new InventoryElement(quantity)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/InventoryManagement/InventoryElement.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/InventoryManagement/InventoryElement.java new file mode 100644 index 0000000..8928640 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/InventoryManagement/InventoryElement.java @@ -0,0 +1,19 @@ +import java.util.*; + +public class InventoryElement { + private int count; + public Map getValue() { + Map temp_nil16 = new HashMap<>(); + temp_nil16.put("count",this.getCount()); + return temp_nil16; + } + public int getCount() { + return this.count; + } + public void receiveOrShip(String itemId, int quantity) { + this.count = (this.value+quantity); + } + public InventoryElement(int count) { + this.count = count; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame/Account.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame/Account.java new file mode 100644 index 0000000..1f2a118 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame/Account.java @@ -0,0 +1,19 @@ +import java.util.*; + +public class Account { + private String name; + public Map getValue() { + Map temp_nil19 = new HashMap<>(); + temp_nil19.put("name",this.getName()); + return temp_nil19; + } + public String getName() { + return this.name; + } + public void changeName(String aid, String name) { + this.name = name; + } + public Account(String name) { + this.name = name; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame/Accounts.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame/Accounts.java new file mode 100644 index 0000000..e4c2155 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame/Accounts.java @@ -0,0 +1,42 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/accounts") +@Component +public class Accounts { + private Map value = new HashMap<>(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getValue() { + return new HashMap<>(value); + } + public Account getAccount(String aid) { + return this.value.get(aid); + } + @Path("/{aid}/name") + @Produces(MediaType.APPLICATION_JSON) + @GET + public String getNameValue(@PathParam("aid") String aid) { + return getAccount(aid).getName(); + } + @Path("/{aid}") + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getAccountValue(@PathParam("aid") String aid) { + return getAccount(aid).getValue(); + } + @POST + public void signUp(@FormParam("name") String name, @FormParam("aid") String aid) { + this.value.put(aid,new Account(name)); + } + @Path("/{aid}/name") + @PUT + public void changeName(@PathParam("aid") String aid, @FormParam("name") String name) { + getAccount(aid).changeName(aid, name); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame/Room.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame/Room.java new file mode 100644 index 0000000..c5ce166 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame/Room.java @@ -0,0 +1,40 @@ +import java.util.*; +import javax.ws.rs.client.*; + +public class Room { + private Client client = ClientBuilder.newClient(); + private String blue_id; + private String red_id; + public Map getValue() { + Map temp_nil18 = new HashMap<>(); + temp_nil18.put("blue_id",this.getBlue_id()); + temp_nil18.put("red_name",this.getRed_name()); + temp_nil18.put("blue_name",this.getBlue_name()); + temp_nil18.put("red_id",this.getRed_id()); + return temp_nil18; + } + public String getRed_name() { + String name = client.target("http://localhost:8080").path("/accounts/" + red_id + "/name").request().get(String.class); + return name; + } + public String getBlue_id() { + return this.blue_id; + } + public String getBlue_name() { + String name = client.target("http://localhost:8080").path("/accounts/" + blue_id + "/name").request().get(String.class); + return name; + } + public String getRed_id() { + return this.red_id; + } + public void changeRedId(String rid, String redId) { + this.red_id = redId; + } + public void changeBlueId(String rid, String blueId) { + this.blue_id = blueId; + } + public Room(String blue_id, String red_id) { + this.blue_id = blue_id; + this.red_id = red_id; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame/Rooms.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame/Rooms.java new file mode 100644 index 0000000..0124b30 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame/Rooms.java @@ -0,0 +1,65 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/rooms") +@Component +public class Rooms { + private Map value = new HashMap<>(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getValue() { + return new HashMap<>(value); + } + public Room getRoom(String rid) { + return this.value.get(rid); + } + @Path("/{rid}/blue_id") + @Produces(MediaType.APPLICATION_JSON) + @GET + public String getBlue_idValue(@PathParam("rid") String rid) { + return getRoom(rid).getBlue_id(); + } + @Path("/{rid}/red_name") + @Produces(MediaType.APPLICATION_JSON) + @GET + public String getRed_nameValue(@PathParam("rid") String rid) { + return getRoom(rid).getRed_name(); + } + @Path("/{rid}/blue_name") + @Produces(MediaType.APPLICATION_JSON) + @GET + public String getBlue_nameValue(@PathParam("rid") String rid) { + return getRoom(rid).getBlue_name(); + } + @Path("/{rid}") + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getRoomValue(@PathParam("rid") String rid) { + return getRoom(rid).getValue(); + } + @Path("/{rid}/red_id") + @Produces(MediaType.APPLICATION_JSON) + @GET + public String getRed_idValue(@PathParam("rid") String rid) { + return getRoom(rid).getRed_id(); + } + @Path("/{rid}/blue_id") + @PUT + public void changeBlueId(@PathParam("rid") String rid, @FormParam("blueId") String blueId) { + getRoom(rid).changeBlueId(rid, blueId); + } + @Path("/{rid}/red_id") + @PUT + public void changeRedId(@PathParam("rid") String rid, @FormParam("redId") String redId) { + getRoom(rid).changeRedId(rid, redId); + } + @POST + public void createRoom(@FormParam("blueId") String blueId, @FormParam("redId") String redId, @FormParam("rid") String rid) { + this.value.put(rid,new Room(blueId, redId)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Account.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Account.java new file mode 100644 index 0000000..31bc4bd --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Account.java @@ -0,0 +1,33 @@ +import java.util.*; + +public class Account { + private String name; + private int point; + public Map getValue() { + Map temp_nil5 = new HashMap<>(); + temp_nil5.put("point",this.getPoint()); + temp_nil5.put("name",this.getName()); + return temp_nil5; + } + public String getName() { + return this.name; + } + public int getPoint() { + return point; + } + public void updatePointFromBattle(String self, String rid, int mno, boolean battle, String id) { + int temp_if0; + if (battle) { + temp_if0 = (this.point+1); + } else { + temp_if0 = this.point; + }this.point = temp_if0; + } + public void changeName(String aid, String name) { + this.name = name; + } + public Account(String name, int point) { + this.name = name; + this.point = point; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Accounts.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Accounts.java new file mode 100644 index 0000000..fc4d20e --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Accounts.java @@ -0,0 +1,53 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/accounts") +@Component +public class Accounts { + private Map value = new HashMap<>(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getValue() { + return new HashMap<>(value); + } + public Account getAccount(String mid) { + return this.value.get(mid); + } + @Path("accounts/{mid}/point") + @POST + public void updatePointFromBattle(@PathParam("mid") String mid, @FormParam("rid") String rid, @FormParam("mno") int mno, @FormParam("battle") boolean battle, @FormParam("id") String id) { + getAccount(mid).updatePointFromBattle(mid, rid, mno, battle, id); + } + @Path("/{mid}/point") + @Produces(MediaType.APPLICATION_JSON) + @GET + public int getPointValue(@PathParam("mid") String mid) { + return getAccount(mid).getPoint(); + } + @Path("/{mid}") + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getAccountValue(@PathParam("mid") String mid) { + return getAccount(mid).getValue(); + } + @Path("/{mid}/name") + @Produces(MediaType.APPLICATION_JSON) + @GET + public String getNameValue(@PathParam("mid") String mid) { + return getAccount(mid).getName(); + } + @POST + public void signUp(@FormParam("name") String name, @FormParam("aid") String aid) { + this.value.put(aid,new Account(name, 0)); + } + @Path("/{aid}/name") + @PUT + public void changeName(@PathParam("aid") String aid, @FormParam("name") String name) { + getAccount(aid).changeName(aid, name); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Member.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Member.java new file mode 100644 index 0000000..d12c2cf --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Member.java @@ -0,0 +1,23 @@ +import java.util.*; +import javax.ws.rs.client.*; + +public class Member { + private String id; + private Client client = ClientBuilder.newClient(); + public Map getValue() { + Map temp_nil6 = new HashMap<>(); + temp_nil6.put("id",this.getId()); + temp_nil6.put("name",this.getName()); + return temp_nil6; + } + public String getId() { + return this.id; + } + public String getName() { + String name = client.target("http://localhost:8080").path("/accounts/"+id+"/name").request().get(String.class); + return name; + } + public Member(String id) { + this.id = id; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Members.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Members.java new file mode 100644 index 0000000..0dd75c8 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Members.java @@ -0,0 +1,14 @@ +import java.util.*; + +public class Members { + private List value = new ArrayList<>(); + public List getValue() { + return new ArrayList<>(value); + } + public Member getMember(int mno) { + return this.value.get(mno); + } + public void addRoomMember(String rid, String id) { + this.value.add(new Member(id)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Room.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Room.java new file mode 100644 index 0000000..edbdd93 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Room.java @@ -0,0 +1,36 @@ +import java.util.*; +import javax.ws.rs.client.*; + +public class Room { + private Members members = new Members(); + private boolean battle; + private Client client = ClientBuilder.newClient(); + public Map getValue() { + Map temp_nil4 = new HashMap<>(); + temp_nil4.put("battle",this.getBattle()); + temp_nil4.put("members",this.members.getValue()); + return temp_nil4; + } + public Members getMembers() { + return this.members; + } + public boolean getBattle() { + return this.battle; + } + public void battle(String rid, boolean hasWon) throws JsonProcessingException { + for (int mno = 0; mno < members.getValue().size(); mno++) { + String id = getMembers().getMember(mno).getId(); + Form form = new Form(); + form.param("rid", rid.toString()); + form.param("mno", Integer.toString(mno)); + form.param("battle", Boolean.toString(battle)); + form.param("id", id.toString()); + Entity entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED); + String result = client.target("http://localhost:8080").path("/accounts/"+id+"/point").request().post(entity, String.class); + } + this.battle = hasWon; + } + public Room(boolean battle) { + this.battle = battle; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Rooms.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Rooms.java new file mode 100644 index 0000000..818eebd --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Rooms.java @@ -0,0 +1,71 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/rooms") +@Component +public class Rooms { + private Map value = new HashMap<>(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getValue() { + return new HashMap<>(value); + } + public Room getRoom(String rid) { + return this.value.get(rid); + } + @Path("/{rid}") + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getRoomValue(@PathParam("rid") String rid) { + return getRoom(rid).getValue(); + } + @Path("/{rid}/members/{mno}/id") + @Produces(MediaType.APPLICATION_JSON) + @GET + public String getIdValue(@PathParam("rid") String rid, @PathParam("mno") int mno) { + return getRoom(rid).getMembers().getMember(mno).getId(); + } + @Path("/{rid}/members/{mno}") + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getMemberValue(@PathParam("rid") String rid, @PathParam("mno") int mno) { + return getRoom(rid).getMembers().getMember(mno).getValue(); + } + @Path("/{rid}/battle") + @Produces(MediaType.APPLICATION_JSON) + @GET + public boolean getBattleValue(@PathParam("rid") String rid) { + return getRoom(rid).getBattle(); + } + @Path("/{rid}/members/{mno}/name") + @Produces(MediaType.APPLICATION_JSON) + @GET + public String getNameValue(@PathParam("rid") String rid, @PathParam("mno") int mno) { + return getRoom(rid).getMembers().getMember(mno).getName(); + } + @Path("/{rid}/members") + @Produces(MediaType.APPLICATION_JSON) + @GET + public List getMembersValue(@PathParam("rid") String rid) { + return getRoom(rid).getMembers().getValue(); + } + @POST + public void createRoom(@FormParam("rid") String rid) { + this.value.put(rid,new Room(false)); + } + @Path("/{rid}/battle") + @PUT + public void battle(@PathParam("rid") String rid, @FormParam("hasWon") boolean hasWon) throws JsonProcessingException { + getRoom(rid).battle(rid, hasWon); + } + @Path("/{rid}/members") + @POST + public void addRoomMember(@PathParam("rid") String rid, @FormParam("id") String id) { + getRoom(rid).getMembers().addRoomMember(rid, id); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PULL-first/History.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PULL-first/History.java new file mode 100644 index 0000000..92776d5 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PULL-first/History.java @@ -0,0 +1,22 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/history") +@Component +public class History { + private List value = new ArrayList<>(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public List getValue() { + return value; + } + @POST + public void updateFromPayment(@FormParam("payment") int payment) { + this.value.add(0, payment); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PULL-first/Payment.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PULL-first/Payment.java new file mode 100644 index 0000000..a694ab6 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PULL-first/Payment.java @@ -0,0 +1,27 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/payment") +@Component +public class Payment { + private int value = 0; + private Client client = ClientBuilder.newClient(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public int getValue() { + return value; + } + @PUT + public void purchase(@FormParam("x") int x) throws JsonProcessingException { + Form form = new Form(); + form.param("payment", Integer.toString(this.value)); + Entity entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED); + String result = client.target("http://localhost:8080").path("/history").request().post(entity, String.class); + this.value = x; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PULL-first/Points.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PULL-first/Points.java new file mode 100644 index 0000000..f7ac799 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PULL-first/Points.java @@ -0,0 +1,19 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/points") +@Component +public class Points { + private Client client = ClientBuilder.newClient(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public int getValue() { + int payment = client.target("http://localhost:8080").path("/payment").request().get(int.class); + return (int)Math.floor((payment*0.05)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PULL-first/Total.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PULL-first/Total.java new file mode 100644 index 0000000..87b8845 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PULL-first/Total.java @@ -0,0 +1,23 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/total") +@Component +public class Total { + private Client client = ClientBuilder.newClient(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public int getValue() { + List history = client.target("http://localhost:8080").path("/history").request().get(ArrayList.class); + Integer temp_sum1 = 0; + for (Integer x: history) { + temp_sum1 += x; + } + return temp_sum1; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PUSH-first/History.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PUSH-first/History.java new file mode 100644 index 0000000..e6c5c7f --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PUSH-first/History.java @@ -0,0 +1,29 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/history") +@Component +public class History { + private List value = new ArrayList<>(); + private Client client = ClientBuilder.newClient(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public List getValue() { + return value; + } + @POST + public void updateFromPayment(@FormParam("payment") double payment) throws JsonProcessingException { + this.value.add(0, payment); + Form form = new Form(); + for (Double i: this.value) { + form.param("history", i.toString()); + } + Entity entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED); + String result = client.target("http://localhost:8080").path("/total").request().put(entity, String.class); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PUSH-first/Payment.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PUSH-first/Payment.java new file mode 100644 index 0000000..5d65395 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PUSH-first/Payment.java @@ -0,0 +1,31 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/payment") +@Component +public class Payment { + private double value = 0.0; + private Client client = ClientBuilder.newClient(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public double getValue() { + return value; + } + @PUT + public void purchase(@FormParam("x") double x) throws JsonProcessingException { + Form form = new Form(); + form.param("payment", Double.toString(this.value)); + Entity entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED); + String result = client.target("http://localhost:8080").path("/points").request().put(entity, String.class); + form = new Form(); + form.param("payment", Double.toString(this.value)); + entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED); + result = client.target("http://localhost:8080").path("/history").request().post(entity, String.class); + this.value = x; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PUSH-first/Points.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PUSH-first/Points.java new file mode 100644 index 0000000..7071bf0 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PUSH-first/Points.java @@ -0,0 +1,22 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/points") +@Component +public class Points { + private int value = 0; + @Produces(MediaType.APPLICATION_JSON) + @GET + public int getValue() { + return value; + } + @PUT + public void updateFromPayment(@FormParam("payment") double payment) { + this.value = (int)Math.floor((payment*0.05)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PUSH-first/Total.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PUSH-first/Total.java new file mode 100644 index 0000000..0fdd957 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PUSH-first/Total.java @@ -0,0 +1,26 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/total") +@Component +public class Total { + private int value = 0; + @Produces(MediaType.APPLICATION_JSON) + @GET + public int getValue() { + return value; + } + @PUT + public void updateFromHistory(@FormParam("history") List history) { + Integer temp_sum1 = 0; + for (Integer x: history) { + temp_sum1 += x; + } + this.value = temp_sum1; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/SimpleTwitter/Account.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/SimpleTwitter/Account.java new file mode 100644 index 0000000..fe3d260 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/SimpleTwitter/Account.java @@ -0,0 +1,19 @@ +import java.util.*; + +public class Account { + private List tweets; + public Map getValue() { + Map temp_nil26 = new HashMap<>(); + temp_nil26.put("tweets",this.getTweets()); + return temp_nil26; + } + public List getTweets() { + return this.tweets; + } + public void tweet(String accountId, String contents) { + this.tweets.add(contents); + } + public Account(List tweets) { + this.tweets = tweets; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/SimpleTwitter/Accounts.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/SimpleTwitter/Accounts.java new file mode 100644 index 0000000..8797c5d --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/SimpleTwitter/Accounts.java @@ -0,0 +1,42 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/accounts") +@Component +public class Accounts { + private Map value = new HashMap<>(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getValue() { + return new HashMap<>(value); + } + public Account getAccount(String accountId) { + return this.value.get(accountId); + } + @Path("/{accountId}/tweets") + @Produces(MediaType.APPLICATION_JSON) + @GET + public List getTweetsValue(@PathParam("accountId") String accountId) { + return getAccount(accountId).getTweets(); + } + @Path("/{accountId}") + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getAccountValue(@PathParam("accountId") String accountId) { + return getAccount(accountId).getValue(); + } + @POST + public void signUp(@FormParam("name") String name, @FormParam("accountId") String accountId) { + this.value.put(accountId,new Account(new ArrayList<>())); + } + @Path("/{accountId}/tweets") + @POST + public void tweet(@PathParam("accountId") String accountId, @FormParam("contents") String contents) { + getAccount(accountId).tweet(accountId, contents); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/VotingSystem/Account.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/VotingSystem/Account.java new file mode 100644 index 0000000..5bdc922 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/VotingSystem/Account.java @@ -0,0 +1,19 @@ +import java.util.*; + +public class Account { + private String vote; + public Map getValue() { + Map temp_nil7 = new HashMap<>(); + temp_nil7.put("vote",this.getVote()); + return temp_nil7; + } + public String getVote() { + return this.vote; + } + public void cast(String aid, String v) { + this.vote = v; + } + public Account(String vote) { + this.vote = vote; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/VotingSystem/Accounts.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/VotingSystem/Accounts.java new file mode 100644 index 0000000..ab6b793 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/VotingSystem/Accounts.java @@ -0,0 +1,42 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/accounts") +@Component +public class Accounts { + private Map value = new HashMap<>(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getValue() { + return new HashMap<>(value); + } + public Account getAccount(String aid) { + return this.value.get(aid); + } + @Path("/{aid}/vote") + @Produces(MediaType.APPLICATION_JSON) + @GET + public String getVoteValue(@PathParam("aid") String aid) { + return getAccount(aid).getVote(); + } + @Path("/{aid}") + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getAccountValue(@PathParam("aid") String aid) { + return getAccount(aid).getValue(); + } + @Path("/{aid}/vote") + @PUT + public void cast(@PathParam("aid") String aid, @FormParam("v") String v) { + getAccount(aid).cast(aid, v); + } + @POST + public void signUp(@FormParam("name") String name, @FormParam("aid") String aid) { + this.value.put(aid,new Account(null)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/VotingSystem/Counts.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/VotingSystem/Counts.java new file mode 100644 index 0000000..06026ed --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/VotingSystem/Counts.java @@ -0,0 +1,27 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/counts") +@Component +public class Counts { + private Map value = new HashMap<>(); + private Client client = ClientBuilder.newClient(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getValue() { + Map v0 = new HashMap<>(); + Map> accounts_json = client.target("http://localhost:8080").path("/accounts").request().get(HashMap.class); + Map> accounts = new HashMap<>(); + accounts = accounts_json; + for (String aid: accounts.keySet()) { + String vote = client.target("http://localhost:8080").path("/accounts/"+aid+"/vote").request().get(String.class); + v0.put(aid,vote); + } + return v0; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/WeatherObservationSystem/PULL-first/Highest.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/WeatherObservationSystem/PULL-first/Highest.java new file mode 100644 index 0000000..0b95991 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/WeatherObservationSystem/PULL-first/Highest.java @@ -0,0 +1,32 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/highest") +@Component +public class Highest { + private double value = 0.0; + @Produces(MediaType.APPLICATION_JSON) + @GET + public double getValue() { + return value; + } + @POST + public void updateFromTemp_f(@FormParam("temp_f") double temp_f) { + double temp_if2; + if ((temp_f>=this.value)) { + temp_if2 = temp_f; + } else { + temp_if2 = this.value; + } + this.value = temp_if2; + } + @PUT + public void reset(@FormParam("v") double v) { + this.value = v; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/WeatherObservationSystem/PULL-first/Temp_c.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/WeatherObservationSystem/PULL-first/Temp_c.java new file mode 100644 index 0000000..3492c58 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/WeatherObservationSystem/PULL-first/Temp_c.java @@ -0,0 +1,19 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/temp_c") +@Component +public class Temp_c { + private Client client = ClientBuilder.newClient(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public double getValue() { + double temp_f = client.target("http://localhost:8080").path("/temp_f").request().get(double.class); + return ((temp_f-32)/1.8); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/WeatherObservationSystem/PULL-first/Temp_f.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/WeatherObservationSystem/PULL-first/Temp_f.java new file mode 100644 index 0000000..a23f614 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/WeatherObservationSystem/PULL-first/Temp_f.java @@ -0,0 +1,27 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/temp_f") +@Component +public class Temp_f { + private double value = 0.0; + private Client client = ClientBuilder.newClient(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public double getValue() { + return value; + } + @PUT + public void observe(@FormParam("x") double x) throws JsonProcessingException { + Form form = new Form(); + form.param("temp_f", Double.toString(this.value)); + Entity entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED); + String result = client.target("http://localhost:8080").path("/highest").request().post(entity, String.class); + this.value = x; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/WeatherObservationSystem/PUSH-first/Highest.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/WeatherObservationSystem/PUSH-first/Highest.java new file mode 100644 index 0000000..7196436 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/WeatherObservationSystem/PUSH-first/Highest.java @@ -0,0 +1,32 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/highest") +@Component +public class Highest { + private double value = 0.0; + @Produces(MediaType.APPLICATION_JSON) + @GET + public double getValue() { + return value; + } + @POST + public void updateFromTemp_f(@FormParam("temp_f") double temp_f) { + double temp_if3; + if ((temp_f>=this.value)) { + temp_if3 = temp_f; + } else { + temp_if3 = this.value; + } + this.value = temp_if3; + } + @PUT + public void reset(@FormParam("v") double v) { + this.value = v; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/WeatherObservationSystem/PUSH-first/Temp_c.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/WeatherObservationSystem/PUSH-first/Temp_c.java new file mode 100644 index 0000000..ee3d10a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/WeatherObservationSystem/PUSH-first/Temp_c.java @@ -0,0 +1,22 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/temp_c") +@Component +public class Temp_c { + private double value = 0.0; + @Produces(MediaType.APPLICATION_JSON) + @GET + public double getValue() { + return value; + } + @PUT + public void updateFromTemp_f(@FormParam("temp_f") double temp_f) { + this.value = ((temp_f-32)/1.8); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/WeatherObservationSystem/PUSH-first/Temp_f.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/WeatherObservationSystem/PUSH-first/Temp_f.java new file mode 100644 index 0000000..a589b35 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/WeatherObservationSystem/PUSH-first/Temp_f.java @@ -0,0 +1,31 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/temp_f") +@Component +public class Temp_f { + private double value = 0.0; + private Client client = ClientBuilder.newClient(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public double getValue() { + return value; + } + @PUT + public void observe(@FormParam("x") double x) throws JsonProcessingException { + Form form = new Form(); + form.param("temp_f", Double.toString(this.value)); + Entity entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED); + String result = client.target("http://localhost:8080").path("/temp_c").request().put(entity, String.class); + form = new Form(); + form.param("temp_f", Double.toString(this.value)); + entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED); + result = client.target("http://localhost:8080").path("/highest").request().post(entity, String.class); + this.value = x; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/Accounts/Account.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/Accounts/Account.java new file mode 100644 index 0000000..40c6dfb --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/Accounts/Account.java @@ -0,0 +1,19 @@ +import java.util.*; + +public class Account { + private String name; + public Map getValue() { + Map temp_nil0 = new HashMap<>(); + temp_nil0.put("name",this.getName()); + return temp_nil0; + } + public String getName() { + return this.name; + } + public void changeName(int uid, String name) { + this.name = name; + } + public Account(String name) { + this.name = name; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/Accounts/Accounts.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/Accounts/Accounts.java new file mode 100644 index 0000000..9053c32 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/Accounts/Accounts.java @@ -0,0 +1,14 @@ +import java.util.*; + +public class Accounts { + private List value = new ArrayList<>(); + public List getValue() { + return new ArrayList<>(value); + } + public Account getAccount(int uid) { + return this.value.get(uid); + } + public void signup(String name) { + this.value.add(new Account(name)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/Accounts/Main.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/Accounts/Main.java new file mode 100644 index 0000000..1340414 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/Accounts/Main.java @@ -0,0 +1,23 @@ +import java.util.*; + +public class Main { + private Accounts accounts; + public Main() { + accounts = new Accounts(); + } + public String getName(int uid) { + return this.accounts.getAccount(uid).getName(); + } + public void changeName(int uid, String name) { + this.accounts.getAccount(uid).changeName(uid, name); + } + public Map getAccount(int uid) { + return this.accounts.getAccount(uid).getValue(); + } + public List getAccounts() { + return this.accounts.getValue(); + } + public void signup(String name) { + this.accounts.signup(name); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PULL-first/Clock.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PULL-first/Clock.java new file mode 100644 index 0000000..a8b488a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PULL-first/Clock.java @@ -0,0 +1,29 @@ +import java.util.*; + +public class Clock { + private Hour hour; + private Hour_ang hour_ang; + private Min min; + private Min_ang min_ang; + public Clock() { + hour = new Hour(); + hour_ang = new Hour_ang(hour); + min = new Min(hour); + min_ang = new Min_ang(min); + } + public double getHour() { + return this.hour.getValue(); + } + public double getHour_ang() { + return this.hour_ang.getValue(); + } + public double getMin() { + return this.min.getValue(); + } + public void tick() { + this.min.tick(); + } + public double getMin_ang() { + return this.min_ang.getValue(); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PULL-first/Hour.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PULL-first/Hour.java new file mode 100644 index 0000000..0e389ac --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PULL-first/Hour.java @@ -0,0 +1,17 @@ +import java.util.*; + +public class Hour { + private double value = 0.0; + public double getValue() { + return value; + } + public void updateFromMin(double min) { + double temp_if1; + if ((min==0)) { + temp_if1 = ((this.value+1)%24); + } else { + temp_if1 = this.value; + } + this.value = temp_if1; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PULL-first/Hour_ang.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PULL-first/Hour_ang.java new file mode 100644 index 0000000..2758d12 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PULL-first/Hour_ang.java @@ -0,0 +1,11 @@ +import java.util.*; + +public class Hour_ang { + private Hour hour; + public double getValue() { + return ((this.hour.getValue()/6)*Math.PI); + } + public Hour_ang(Hour hour) { + this.hour = hour; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PULL-first/Min.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PULL-first/Min.java new file mode 100644 index 0000000..f0d82da --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PULL-first/Min.java @@ -0,0 +1,16 @@ +import java.util.*; + +public class Min { + private double value = 0.0; + private Hour hour; + public double getValue() { + return value; + } + public void tick() { + this.value = ((this.value+1)%60); + this.hour.updateFromMin(value); + } + public Min(Hour hour) { + this.hour = hour; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PULL-first/Min_ang.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PULL-first/Min_ang.java new file mode 100644 index 0000000..d91aefa --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PULL-first/Min_ang.java @@ -0,0 +1,11 @@ +import java.util.*; + +public class Min_ang { + private Min min; + public double getValue() { + return ((this.min.getValue()/30)*Math.PI); + } + public Min_ang(Min min) { + this.min = min; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PUSH-first/Clock.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PUSH-first/Clock.java new file mode 100644 index 0000000..2710bd1 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PUSH-first/Clock.java @@ -0,0 +1,29 @@ +import java.util.*; + +public class Clock { + private Hour_ang hour_ang; + private Min_ang min_ang; + private Hour hour; + private Min min; + public Clock() { + hour_ang = new Hour_ang(); + min_ang = new Min_ang(); + hour = new Hour(hour_ang); + min = new Min(min_ang,hour); + } + public double getHour_ang() { + return this.hour_ang.getValue(); + } + public double getMin_ang() { + return this.min_ang.getValue(); + } + public int getHour() { + return this.hour.getValue(); + } + public int getMin() { + return this.min.getValue(); + } + public void tick() { + this.min.tick(); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PUSH-first/Hour.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PUSH-first/Hour.java new file mode 100644 index 0000000..a305b95 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PUSH-first/Hour.java @@ -0,0 +1,22 @@ +import java.util.*; + +public class Hour { + private int value = 0; + private Hour_ang hour_ang; + public int getValue() { + return value; + } + public void updateFromMin(int min) { + int temp_if0; + if ((min==0)) { + temp_if0 = ((this.value+1)%24); + } else { + temp_if0 = this.value; + } + this.value = temp_if0; + this.hour_ang.updateFromHour(value); + } + public Hour(Hour_ang hour_ang) { + this.hour_ang = hour_ang; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PUSH-first/Hour_ang.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PUSH-first/Hour_ang.java new file mode 100644 index 0000000..8463ad7 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PUSH-first/Hour_ang.java @@ -0,0 +1,11 @@ +import java.util.*; + +public class Hour_ang { + private double value = 0.0; + public double getValue() { + return value; + } + public void updateFromHour(int hour) { + this.value = ((hour/6)*Math.PI); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PUSH-first/Min.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PUSH-first/Min.java new file mode 100644 index 0000000..c0e4de0 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PUSH-first/Min.java @@ -0,0 +1,19 @@ +import java.util.*; + +public class Min { + private int value = 0; + private Min_ang min_ang; + private Hour hour; + public int getValue() { + return value; + } + public void tick() { + this.value = ((this.value+1)%60); + this.min_ang.updateFromMin(value); + this.hour.updateFromMin(value); + } + public Min(Min_ang min_ang, Hour hour) { + this.min_ang = min_ang; + this.hour = hour; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PUSH-first/Min_ang.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PUSH-first/Min_ang.java new file mode 100644 index 0000000..756097f --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PUSH-first/Min_ang.java @@ -0,0 +1,11 @@ +import java.util.*; + +public class Min_ang { + private double value = 0.0; + public double getValue() { + return value; + } + public void updateFromMin(int min) { + this.value = ((min/30)*Math.PI); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/CustomerManagement/Companies.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/CustomerManagement/Companies.java new file mode 100644 index 0000000..148543f --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/CustomerManagement/Companies.java @@ -0,0 +1,14 @@ +import java.util.*; + +public class Companies { + private Map value = new HashMap<>(); + public Map getValue() { + return new HashMap<>(value); + } + public Company getCompany(String cid) { + return this.value.get(cid); + } + public void addCampany(String address, String cid) { + this.value.put(cid,new Company(address)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/CustomerManagement/Company.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/CustomerManagement/Company.java new file mode 100644 index 0000000..fd0eaba --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/CustomerManagement/Company.java @@ -0,0 +1,19 @@ +import java.util.*; + +public class Company { + private String address; + public Map getValue() { + Map temp_nil6 = new HashMap<>(); + temp_nil6.put("address",this.getAddress()); + return temp_nil6; + } + public String getAddress() { + return this.address; + } + public void setAddress(String cid, String add) { + this.address = add; + } + public Company(String address) { + this.address = address; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/CustomerManagement/Customer.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/CustomerManagement/Customer.java new file mode 100644 index 0000000..4c19192 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/CustomerManagement/Customer.java @@ -0,0 +1,28 @@ +import java.util.*; + +public class Customer { + private String organization; + private Company company; + private Companies companies; + public Map getValue() { + Map temp_nil7 = new HashMap<>(); + temp_nil7.put("address",this.getAddress()); + temp_nil7.put("organization",this.getOrganization()); + return temp_nil7; + } + public String getOrganization() { + return this.organization; + } + public String getAddress() { + return this.company.getAddress(); + } + public void setOrganization(String uid, String cid) { + this.organization = cid; + this.company = this.companies.getCompany(this.organization); + } + public Customer(String organization, Companies companies) { + this.organization = organization; + this.companies = companies; + this.company = this.companies.getCompany(this.organization); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/CustomerManagement/CustomerManagement.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/CustomerManagement/CustomerManagement.java new file mode 100644 index 0000000..a919176 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/CustomerManagement/CustomerManagement.java @@ -0,0 +1,43 @@ +import java.util.*; + +public class CustomerManagement { + private Companies companies; + private Customers customers; + public CustomerManagement() { + companies = new Companies(); + customers = new Customers(companies); + } + public Map getCompanies() { + return this.companies.getValue(); + } + public void addCampany(String address, String cid) { + this.companies.addCampany(address, cid); + } + public String getAddress(String cid) { + return this.companies.getCompany(cid).getAddress(); + } + public void setAddress(String cid, String add) { + this.companies.getCompany(cid).setAddress(cid, add); + } + public String getOrganization(String uid) { + return this.customers.getCustomer(uid).getOrganization(); + } + public void setOrganization(String uid, String cid) { + this.customers.getCustomer(uid).setOrganization(uid, cid); + } + public String getAddress(String uid) { + return this.customers.getCustomer(uid).getAddress(); + } + public Map getCompany(String cid) { + return this.companies.getCompany(cid).getValue(); + } + public Map getCustomers() { + return this.customers.getValue(); + } + public void addCustomer(String org, String uid) { + this.customers.addCustomer(org, uid); + } + public Map getCustomer(String uid) { + return this.customers.getCustomer(uid).getValue(); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/CustomerManagement/Customers.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/CustomerManagement/Customers.java new file mode 100644 index 0000000..04725d7 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/CustomerManagement/Customers.java @@ -0,0 +1,18 @@ +import java.util.*; + +public class Customers { + private Map value = new HashMap<>(); + private Companies companies; + public Map getValue() { + return new HashMap<>(value); + } + public Customer getCustomer(String uid) { + return this.value.get(uid); + } + public void addCustomer(String org, String uid) { + this.value.put(uid,new Customer(org, companies)); + } + public Customers(Companies companies) { + this.companies = companies; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/Account.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/Account.java new file mode 100644 index 0000000..10f53f1 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/Account.java @@ -0,0 +1,22 @@ +import java.util.*; + +public class Account { + private Map notifications; + public Map getValue() { + Map temp_nil17 = new HashMap<>(); + temp_nil17.put("notifications",this.getNotifications()); + return temp_nil17; + } + public Map getNotifications() { + return new HashMap<>(notifications); + } + public void updateNotificationsFromMessages(String self, String gid, int mno, List messages, String member) { + this.notifications.put(gid,true); + } + public void hasRead(String aid, String gid) { + this.notifications.remove(gid); + } + public Account(Map notifications) { + this.notifications = notifications; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/Accounts.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/Accounts.java new file mode 100644 index 0000000..2e7c4e3 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/Accounts.java @@ -0,0 +1,14 @@ +import java.util.*; + +public class Accounts { + private Map value = new HashMap<>(); + public Map getValue() { + return new HashMap<>(value); + } + public Account getAccount(String v1) { + return this.value.get(v1); + } + public void signUp(String aid) { + this.value.put(aid,new Account(new HashMap<>())); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/Group.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/Group.java new file mode 100644 index 0000000..faa7d19 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/Group.java @@ -0,0 +1,39 @@ +import java.util.*; + +public class Group { + private List messages; + private Account account; + private Accounts accounts; + private List members; + public Map getValue() { + Map temp_nil16 = new HashMap<>(); + temp_nil16.put("members",this.getMembers()); + temp_nil16.put("messages",this.getMessages()); + return temp_nil16; + } + public List getMessages() { + return this.messages; + } + public List getMembers() { + return this.members; + } + public String getMember(int mno) { + return this.members.get(mno); + } + public void postMessage(String gid, String message) { + this.messages.add(message); + for (int mno = 0; mno < this.members.size(); mno++) { + String member = getMember(mno); + this.account = accounts.getAccount(member); + this.account.updateNotificationsFromMessages(member, gid, mno, messages, member); + } + } + public void addGroupMember(String gid, String aid) { + this.members.add(aid); + } + public Group(List messages, Accounts accounts, List members) { + this.messages = messages; + this.accounts = accounts; + this.members = members; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/GroupChat.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/GroupChat.java new file mode 100644 index 0000000..6ee5abf --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/GroupChat.java @@ -0,0 +1,49 @@ +import java.util.*; + +public class GroupChat { + private Accounts accounts; + private Groups groups; + public GroupChat() { + accounts = new Accounts(); + groups = new Groups(accounts); + } + public Map getGroup(String gid) { + return this.groups.getGroup(gid).getValue(); + } + public Map getAccount(String v1) { + return this.accounts.getAccount(v1).getValue(); + } + public List getMessages(String gid) { + return this.groups.getGroup(gid).getMessages(); + } + public void postMessage(String gid, String message) { + this.groups.getGroup(gid).postMessage(gid, message); + } + public List getMembers(String gid) { + return this.groups.getGroup(gid).getMembers(); + } + public void addGroupMember(String gid, String aid) { + this.groups.getGroup(gid).addGroupMember(gid, aid); + } + public Map getNotifications(String v1) { + return this.accounts.getAccount(v1).getNotifications(); + } + public void hasRead(String aid, String gid) { + this.accounts.getAccount(aid).hasRead(aid, gid); + } + public String getMember(String gid, int mno) { + return this.groups.getGroup(gid).getMember(mno); + } + public Map getAccounts() { + return this.accounts.getValue(); + } + public void signUp(String aid) { + this.accounts.signUp(aid); + } + public Map getGroups() { + return this.groups.getValue(); + } + public void createGroup(String gid) { + this.groups.createGroup(gid); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/Groups.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/Groups.java new file mode 100644 index 0000000..ada656a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/Groups.java @@ -0,0 +1,18 @@ +import java.util.*; + +public class Groups { + private Map value = new HashMap<>(); + private Accounts accounts; + public Map getValue() { + return new HashMap<>(value); + } + public Group getGroup(String gid) { + return this.value.get(gid); + } + public void createGroup(String gid) { + this.value.put(gid,new Group(new ArrayList<>(), accounts, new ArrayList<>())); + } + public Groups(Accounts accounts) { + this.accounts = accounts; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/InventoryManagement/Inventory.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/InventoryManagement/Inventory.java new file mode 100644 index 0000000..da9dc38 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/InventoryManagement/Inventory.java @@ -0,0 +1,14 @@ +import java.util.*; + +public class Inventory { + private Map value = new HashMap<>(); + public Map getValue() { + return new HashMap<>(value); + } + public InventoryElement getInventoryElement(String itemId) { + return this.value.get(itemId); + } + public void registerItem(String itemName, int quantity, String itemId) { + this.value.put(itemId,new InventoryElement(quantity)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/InventoryManagement/InventoryElement.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/InventoryManagement/InventoryElement.java new file mode 100644 index 0000000..366444a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/InventoryManagement/InventoryElement.java @@ -0,0 +1,19 @@ +import java.util.*; + +public class InventoryElement { + private int count; + public Map getValue() { + Map temp_nil17 = new HashMap<>(); + temp_nil17.put("count",this.getCount()); + return temp_nil17; + } + public int getCount() { + return this.count; + } + public void receiveOrShip(String itemId, int quantity) { + this.count = (this.value+quantity); + } + public InventoryElement(int count) { + this.count = count; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/InventoryManagement/InventoryManagement.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/InventoryManagement/InventoryManagement.java new file mode 100644 index 0000000..3f3d2fc --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/InventoryManagement/InventoryManagement.java @@ -0,0 +1,23 @@ +import java.util.*; + +public class InventoryManagement { + private Inventory inventory; + public InventoryManagement() { + inventory = new Inventory(); + } + public Map getInventory() { + return this.inventory.getValue(); + } + public void registerItem(String itemName, int quantity, String itemId) { + this.inventory.registerItem(itemName, quantity, itemId); + } + public int getCount(String itemId) { + return this.inventory.getInventoryElement(itemId).getCount(); + } + public void receiveOrShip(String itemId, int quantity) { + this.inventory.getInventoryElement(itemId).receiveOrShip(itemId, quantity); + } + public Map getInventoryElement(String itemId) { + return this.inventory.getInventoryElement(itemId).getValue(); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PULL-first/Account.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PULL-first/Account.java new file mode 100644 index 0000000..5838e95 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PULL-first/Account.java @@ -0,0 +1,19 @@ +import java.util.*; + +public class Account { + private String name; + public Map getValue() { + Map temp_nil23 = new HashMap<>(); + temp_nil23.put("name",this.getName()); + return temp_nil23; + } + public String getName() { + return this.name; + } + public void changeName(String aid, String name) { + this.name = name; + } + public Account(String name) { + this.name = name; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PULL-first/Accounts.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PULL-first/Accounts.java new file mode 100644 index 0000000..feb415f --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PULL-first/Accounts.java @@ -0,0 +1,14 @@ +import java.util.*; + +public class Accounts { + private Map value = new HashMap<>(); + public Map getValue() { + return new HashMap<>(value); + } + public Account getAccount(String aid) { + return this.value.get(aid); + } + public void signUp(String name, String aid) { + this.value.put(aid,new Account(name)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PULL-first/OnlineBattleGame.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PULL-first/OnlineBattleGame.java new file mode 100644 index 0000000..5339f4c --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PULL-first/OnlineBattleGame.java @@ -0,0 +1,52 @@ +import java.util.*; + +public class OnlineBattleGame { + private Accounts accounts; + private Rooms rooms; + public OnlineBattleGame() { + accounts = new Accounts(); + rooms = new Rooms(accounts); + } + public String getBlue_id(String rid) { + return this.rooms.getRoom(rid).getBlue_id(); + } + public void changeBlueId(String rid, String blueId) { + this.rooms.getRoom(rid).changeBlueId(rid, blueId); + } + public Map getAccount(String aid) { + return this.accounts.getAccount(aid).getValue(); + } + public String getName(String aid) { + return this.accounts.getAccount(aid).getName(); + } + public void changeName(String aid, String name) { + this.accounts.getAccount(aid).changeName(aid, name); + } + public String getBlue_name(String rid) { + return this.rooms.getRoom(rid).getBlue_name(); + } + public Map getRoom(String rid) { + return this.rooms.getRoom(rid).getValue(); + } + public String getRed_id(String rid) { + return this.rooms.getRoom(rid).getRed_id(); + } + public void changeRedId(String rid, String redId) { + this.rooms.getRoom(rid).changeRedId(rid, redId); + } + public Map getAccounts() { + return this.accounts.getValue(); + } + public void signUp(String name, String aid) { + this.accounts.signUp(name, aid); + } + public Map getRooms() { + return this.rooms.getValue(); + } + public void createRoom(String blueId, String redId, String rid) { + this.rooms.createRoom(blueId, redId, rid); + } + public String getRed_name(String rid) { + return this.rooms.getRoom(rid).getRed_name(); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PULL-first/Room.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PULL-first/Room.java new file mode 100644 index 0000000..399506e --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PULL-first/Room.java @@ -0,0 +1,43 @@ +import java.util.*; + +public class Room { + private String blue_id; + private Account account; + private Accounts accounts; + private String red_id; + public Map getValue() { + Map temp_nil22 = new HashMap<>(); + temp_nil22.put("blue_id",this.getBlue_id()); + temp_nil22.put("red_name",this.getRed_name()); + temp_nil22.put("blue_name",this.getBlue_name()); + temp_nil22.put("red_id",this.getRed_id()); + return temp_nil22; + } + public String getBlue_id() { + return this.blue_id; + } + public String getBlue_name() { + return this.account.getName(); + } + public String getRed_id() { + return this.red_id; + } + public String getRed_name() { + return this.account.getName(); + } + public void changeRedId(String rid, String redId) { + this.red_id = redId; + this.account = this.accounts.getAccount(this.red_id); + } + public void changeBlueId(String rid, String blueId) { + this.blue_id = blueId; + this.account = this.accounts.getAccount(this.blue_id); + } + public Room(String blue_id, Accounts accounts, String red_id) { + this.blue_id = blue_id; + this.accounts = accounts; + this.red_id = red_id; + this.account = this.accounts.getAccount(this.blue_id); + this.account = this.accounts.getAccount(this.red_id); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PULL-first/Rooms.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PULL-first/Rooms.java new file mode 100644 index 0000000..2016c96 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PULL-first/Rooms.java @@ -0,0 +1,18 @@ +import java.util.*; + +public class Rooms { + private Map value = new HashMap<>(); + private Accounts accounts; + public Map getValue() { + return new HashMap<>(value); + } + public Room getRoom(String rid) { + return this.value.get(rid); + } + public void createRoom(String blueId, String redId, String rid) { + this.value.put(rid,new Room(blueId, accounts, redId)); + } + public Rooms(Accounts accounts) { + this.accounts = accounts; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PUSH-first/Account.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PUSH-first/Account.java new file mode 100644 index 0000000..63f2dd3 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PUSH-first/Account.java @@ -0,0 +1,19 @@ +import java.util.*; + +public class Account { + private String name; + public Map getValue() { + Map temp_nil21 = new HashMap<>(); + temp_nil21.put("name",this.getName()); + return temp_nil21; + } + public String getName() { + return this.name; + } + public void changeName(String aid, String name) { + this.name = name; + } + public Account(String name) { + this.name = name; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PUSH-first/Accounts.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PUSH-first/Accounts.java new file mode 100644 index 0000000..feb415f --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PUSH-first/Accounts.java @@ -0,0 +1,14 @@ +import java.util.*; + +public class Accounts { + private Map value = new HashMap<>(); + public Map getValue() { + return new HashMap<>(value); + } + public Account getAccount(String aid) { + return this.value.get(aid); + } + public void signUp(String name, String aid) { + this.value.put(aid,new Account(name)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PUSH-first/OnlineBattleGame.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PUSH-first/OnlineBattleGame.java new file mode 100644 index 0000000..02e2ca7 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PUSH-first/OnlineBattleGame.java @@ -0,0 +1,52 @@ +import java.util.*; + +public class OnlineBattleGame { + private Accounts accounts; + private Rooms rooms; + public OnlineBattleGame() { + accounts = new Accounts(); + rooms = new Rooms(accounts); + } + public Map getAccount(String aid) { + return this.accounts.getAccount(aid).getValue(); + } + public String getBlue_name(String rid) { + return this.rooms.getRoom(rid).getBlue_name(); + } + public String getBlue_id(String rid) { + return this.rooms.getRoom(rid).getBlue_id(); + } + public void changeBlueId(String rid, String blueId) { + this.rooms.getRoom(rid).changeBlueId(rid, blueId); + } + public String getRed_name(String rid) { + return this.rooms.getRoom(rid).getRed_name(); + } + public String getName(String aid) { + return this.accounts.getAccount(aid).getName(); + } + public void changeName(String aid, String name) { + this.accounts.getAccount(aid).changeName(aid, name); + } + public Map getRoom(String rid) { + return this.rooms.getRoom(rid).getValue(); + } + public String getRed_id(String rid) { + return this.rooms.getRoom(rid).getRed_id(); + } + public void changeRedId(String rid, String redId) { + this.rooms.getRoom(rid).changeRedId(rid, redId); + } + public Map getAccounts() { + return this.accounts.getValue(); + } + public void signUp(String name, String aid) { + this.accounts.signUp(name, aid); + } + public Map getRooms() { + return this.rooms.getValue(); + } + public void createRoom(String blueId, String redId, String rid) { + this.rooms.createRoom(blueId, redId, rid); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PUSH-first/Room.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PUSH-first/Room.java new file mode 100644 index 0000000..251aacf --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PUSH-first/Room.java @@ -0,0 +1,53 @@ +import java.util.*; + +public class Room { + private Account account; + private Accounts accounts; + private String blue_id; + private String red_id; + public Map getValue() { + Map temp_nil20 = new HashMap<>(); + temp_nil20.put("blue_id",this.getBlue_id()); + temp_nil20.put("red_name",this.getRed_name()); + temp_nil20.put("blue_name",this.getBlue_name()); + temp_nil20.put("red_id",this.getRed_id()); + return temp_nil20; + } + public String getBlue_name() { + return name; + } + public String getBlue_id() { + return this.blue_id; + } + public String getRed_name() { + return name; + } + public String getRed_id() { + return this.red_id; + } + public void updateBlue_nameFromBlue_id(String self, String rid, String blue_id) { + this.blue_name = name; + this.blue_id = blue_id; + } + public void updateRed_nameFromRed_id(String self, String rid, String red_id) { + this.red_name = name; + this.red_id = red_id; + } + public void changeRedId(String rid, String redId) { + this.red_id = redId; + this.account = this.accounts.getAccount(this.red_id); + this.updateRed_nameFromRed_id(rid, rid, red_id); + } + public void changeBlueId(String rid, String blueId) { + this.blue_id = blueId; + this.account = this.accounts.getAccount(this.blue_id); + this.updateBlue_nameFromBlue_id(rid, rid, blue_id); + } + public Room(Accounts accounts, String blue_id, String red_id) { + this.accounts = accounts; + this.blue_id = blue_id; + this.red_id = red_id; + this.account = this.accounts.getAccount(this.blue_id); + this.account = this.accounts.getAccount(this.red_id); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PUSH-first/Rooms.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PUSH-first/Rooms.java new file mode 100644 index 0000000..f674087 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PUSH-first/Rooms.java @@ -0,0 +1,18 @@ +import java.util.*; + +public class Rooms { + private Map value = new HashMap<>(); + private Accounts accounts; + public Map getValue() { + return new HashMap<>(value); + } + public Room getRoom(String rid) { + return this.value.get(rid); + } + public void createRoom(String blueId, String redId, String rid) { + this.value.put(rid,new Room(accounts, blueId, redId)); + } + public Rooms(Accounts accounts) { + this.accounts = accounts; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/Account.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/Account.java new file mode 100644 index 0000000..f95e5e6 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/Account.java @@ -0,0 +1,33 @@ +import java.util.*; + +public class Account { + private String name; + private int point; + public Map getValue() { + Map temp_nil1 = new HashMap<>(); + temp_nil1.put("name",this.getName()); + temp_nil1.put("point",this.getPoint()); + return temp_nil1; + } + public String getName() { + return this.name; + } + public int getPoint() { + return point; + } + public void updatePointFromBattle(String self, String rid, int mno, boolean battle, String id) { + int temp_if0; + if (battle) { + temp_if0 = (this.point+1); + } else { + temp_if0 = this.point; + }this.point = temp_if0; + } + public void changeName(String aid, String name) { + this.name = name; + } + public Account(String name, int point) { + this.name = name; + this.point = point; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/Accounts.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/Accounts.java new file mode 100644 index 0000000..6d92c89 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/Accounts.java @@ -0,0 +1,14 @@ +import java.util.*; + +public class Accounts { + private Map value = new HashMap<>(); + public Map getValue() { + return new HashMap<>(value); + } + public Account getAccount(String mid) { + return this.value.get(mid); + } + public void signUp(String name, String aid) { + this.value.put(aid,new Account(name, 0)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/Member.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/Member.java new file mode 100644 index 0000000..aac4c47 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/Member.java @@ -0,0 +1,23 @@ +import java.util.*; + +public class Member { + private String id; + private Account account; + private Accounts accounts; + public Map getValue() { + Map temp_nil2 = new HashMap<>(); + temp_nil2.put("name",this.getName()); + temp_nil2.put("id",this.getId()); + return temp_nil2; + } + public String getId() { + return this.id; + } + public String getName() { + return this.account.getName(); + } + public Member(String id, Accounts accounts) { + this.id = id; + this.accounts = accounts; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/Members.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/Members.java new file mode 100644 index 0000000..091b561 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/Members.java @@ -0,0 +1,14 @@ +import java.util.*; + +public class Members { + private List value = new ArrayList<>(); + public List getValue() { + return new ArrayList<>(value); + } + public Member getMember(int mno) { + return this.value.get(mno); + } + public void addRoomMember(String rid, String id) { + this.value.add(new Member(id, accounts)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/OnlineBattleGame2.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/OnlineBattleGame2.java new file mode 100644 index 0000000..cc758ed --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/OnlineBattleGame2.java @@ -0,0 +1,58 @@ +import java.util.*; + +public class OnlineBattleGame2 { + private Accounts accounts; + private Rooms rooms; + public OnlineBattleGame2() { + accounts = new Accounts(); + rooms = new Rooms(accounts); + } + public boolean getBattle(String rid) { + return this.rooms.getRoom(rid).getBattle(); + } + public void battle(String rid, boolean hasWon) { + this.rooms.getRoom(rid).battle(rid, hasWon); + } + public Map getRoom(String rid) { + return this.rooms.getRoom(rid).getValue(); + } + public String getName(String mid) { + return this.accounts.getAccount(mid).getName(); + } + public void changeName(String aid, String name) { + this.accounts.getAccount(aid).changeName(aid, name); + } + public Map getAccounts() { + return this.accounts.getValue(); + } + public void signUp(String name, String aid) { + this.accounts.signUp(name, aid); + } + public List getMembers(String rid) { + return this.rooms.getRoom(rid).getMembers().getValue(); + } + public void addRoomMember(String rid, String id) { + this.rooms.getRoom(rid).getMembers().addRoomMember(rid, id); + } + public int getPoint(String mid) { + return this.accounts.getAccount(mid).getPoint(); + } + public Map getRooms() { + return this.rooms.getValue(); + } + public void createRoom(String rid) { + this.rooms.createRoom(rid); + } + public Map getAccount(String mid) { + return this.accounts.getAccount(mid).getValue(); + } + public Map getMember(String rid, int mno) { + return this.rooms.getRoom(rid).getMembers().getMember(mno).getValue(); + } + public String getId(String rid, int mno) { + return this.rooms.getRoom(rid).getMembers().getMember(mno).getId(); + } + public String getName(String rid, int mno) { + return this.rooms.getRoom(rid).getMembers().getMember(mno).getName(); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/Room.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/Room.java new file mode 100644 index 0000000..43129a4 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/Room.java @@ -0,0 +1,32 @@ +import java.util.*; + +public class Room { + private Members members = new Members(); + private boolean battle; + private Account account; + private Accounts accounts; + public Map getValue() { + Map temp_nil0 = new HashMap<>(); + temp_nil0.put("members",this.members.getValue()); + temp_nil0.put("battle",this.getBattle()); + return temp_nil0; + } + public Members getMembers() { + return this.members; + } + public boolean getBattle() { + return this.battle; + } + public void battle(String rid, boolean hasWon) { + this.battle = hasWon; + for (int mno = 0; mno < this.members.getValue().size(); mno++) { + String id = this.members.getMember(mno).getId(); + this.account = accounts.getAccount(id); + this.account.updatePointFromBattle(id, rid, mno, battle, id); + } + } + public Room(boolean battle, Accounts accounts) { + this.battle = battle; + this.accounts = accounts; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/Rooms.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/Rooms.java new file mode 100644 index 0000000..d4230fa --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/Rooms.java @@ -0,0 +1,18 @@ +import java.util.*; + +public class Rooms { + private Map value = new HashMap<>(); + private Accounts accounts; + public Map getValue() { + return new HashMap<>(value); + } + public Room getRoom(String rid) { + return this.value.get(rid); + } + public void createRoom(String rid) { + this.value.put(rid,new Room(false, accounts)); + } + public Rooms(Accounts accounts) { + this.accounts = accounts; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PULL-first/History.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PULL-first/History.java new file mode 100644 index 0000000..75373bd --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PULL-first/History.java @@ -0,0 +1,11 @@ +import java.util.*; + +public class History { + private List value = new ArrayList<>(); + public List getValue() { + return new ArrayList<>(value); + } + public void updateFromPayment(int payment) { + this.value.add(0, payment); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PULL-first/POS.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PULL-first/POS.java new file mode 100644 index 0000000..55d7d08 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PULL-first/POS.java @@ -0,0 +1,29 @@ +import java.util.*; + +public class POS { + private History history; + private Total total; + private Payment payment; + private Points points; + public POS() { + history = new History(); + total = new Total(history); + payment = new Payment(history); + points = new Points(payment); + } + public List getHistory() { + return this.history.getValue(); + } + public int getTotal() { + return this.total.getValue(); + } + public int getPayment() { + return this.payment.getValue(); + } + public void purchase(int x) { + this.payment.purchase(x); + } + public int getPoints() { + return this.points.getValue(); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PULL-first/Payment.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PULL-first/Payment.java new file mode 100644 index 0000000..23c1088 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PULL-first/Payment.java @@ -0,0 +1,16 @@ +import java.util.*; + +public class Payment { + private int value = 0; + private History history; + public int getValue() { + return value; + } + public void purchase(int x) { + this.value = x; + this.history.updateFromPayment(value); + } + public Payment(History history) { + this.history = history; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PULL-first/Points.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PULL-first/Points.java new file mode 100644 index 0000000..2b11644 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PULL-first/Points.java @@ -0,0 +1,11 @@ +import java.util.*; + +public class Points { + private Payment payment; + public int getValue() { + return (int)Math.floor((this.payment.getValue()*0.05)); + } + public Points(Payment payment) { + this.payment = payment; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PULL-first/Total.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PULL-first/Total.java new file mode 100644 index 0000000..62564ff --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PULL-first/Total.java @@ -0,0 +1,15 @@ +import java.util.*; + +public class Total { + private History history; + public int getValue() { + Integer temp_sum1 = 0; + for (Integer x: this.history.getValue()) { + temp_sum1 += x; + } + return temp_sum1; + } + public Total(History history) { + this.history = history; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PUSH-first/History.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PUSH-first/History.java new file mode 100644 index 0000000..6aa27b8 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PUSH-first/History.java @@ -0,0 +1,17 @@ +import java.util.*; + +public class History { + private List value = new ArrayList<>(); + private Total total; + public List getValue() { + return new ArrayList<>(value); + } + public void updateFromPayment(double payment) { + this.value.add(0, payment); + + this.total.updateFromHistory(value); + } + public History(Total total) { + this.total = total; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PUSH-first/POS.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PUSH-first/POS.java new file mode 100644 index 0000000..2d3e544 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PUSH-first/POS.java @@ -0,0 +1,29 @@ +import java.util.*; + +public class POS { + private Points points; + private Total total; + private History history; + private Payment payment; + public POS() { + points = new Points(); + total = new Total(); + history = new History(total); + payment = new Payment(points,history); + } + public int getPoints() { + return this.points.getValue(); + } + public int getTotal() { + return this.total.getValue(); + } + public List getHistory() { + return this.history.getValue(); + } + public double getPayment() { + return this.payment.getValue(); + } + public void purchase(double x) { + this.payment.purchase(x); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PUSH-first/Payment.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PUSH-first/Payment.java new file mode 100644 index 0000000..ebf56ac --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PUSH-first/Payment.java @@ -0,0 +1,19 @@ +import java.util.*; + +public class Payment { + private double value = 0.0; + private Points points; + private History history; + public double getValue() { + return value; + } + public void purchase(double x) { + this.value = x; + this.points.updateFromPayment(value); + this.history.updateFromPayment(value); + } + public Payment(Points points, History history) { + this.points = points; + this.history = history; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PUSH-first/Points.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PUSH-first/Points.java new file mode 100644 index 0000000..55cb552 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PUSH-first/Points.java @@ -0,0 +1,11 @@ +import java.util.*; + +public class Points { + private int value = 0; + public int getValue() { + return value; + } + public void updateFromPayment(double payment) { + this.value = (int)Math.floor((payment*0.05)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PUSH-first/Total.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PUSH-first/Total.java new file mode 100644 index 0000000..2a3c00e --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PUSH-first/Total.java @@ -0,0 +1,15 @@ +import java.util.*; + +public class Total { + private int value = 0; + public int getValue() { + return value; + } + public void updateFromHistory(List history) { + Integer temp_sum1 = 0; + for (Integer x: history) { + temp_sum1 += x; + } + this.value = temp_sum1; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/SimpleTwitter/Account.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/SimpleTwitter/Account.java new file mode 100644 index 0000000..f007a58 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/SimpleTwitter/Account.java @@ -0,0 +1,19 @@ +import java.util.*; + +public class Account { + private List tweets; + public Map getValue() { + Map temp_nil27 = new HashMap<>(); + temp_nil27.put("tweets",this.getTweets()); + return temp_nil27; + } + public List getTweets() { + return this.tweets; + } + public void tweet(String accountId, String contents) { + this.tweets.add(contents); + } + public Account(List tweets) { + this.tweets = tweets; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/SimpleTwitter/Accounts.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/SimpleTwitter/Accounts.java new file mode 100644 index 0000000..7f2a17b --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/SimpleTwitter/Accounts.java @@ -0,0 +1,14 @@ +import java.util.*; + +public class Accounts { + private Map value = new HashMap<>(); + public Map getValue() { + return new HashMap<>(value); + } + public Account getAccount(String accountId) { + return this.value.get(accountId); + } + public void signUp(String name, String accountId) { + this.value.put(accountId,new Account(new ArrayList<>())); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/SimpleTwitter/SimpleTwitter.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/SimpleTwitter/SimpleTwitter.java new file mode 100644 index 0000000..36aaf77 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/SimpleTwitter/SimpleTwitter.java @@ -0,0 +1,23 @@ +import java.util.*; + +public class SimpleTwitter { + private Accounts accounts; + public SimpleTwitter() { + accounts = new Accounts(); + } + public List getTweets(String accountId) { + return this.accounts.getAccount(accountId).getTweets(); + } + public void tweet(String accountId, String contents) { + this.accounts.getAccount(accountId).tweet(accountId, contents); + } + public Map getAccount(String accountId) { + return this.accounts.getAccount(accountId).getValue(); + } + public Map getAccounts() { + return this.accounts.getValue(); + } + public void signUp(String name, String accountId) { + this.accounts.signUp(name, accountId); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/Account.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/Account.java new file mode 100644 index 0000000..605d6c1 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/Account.java @@ -0,0 +1,19 @@ +import java.util.*; + +public class Account { + private String vote; + public Map getValue() { + Map temp_nil4 = new HashMap<>(); + temp_nil4.put("vote",this.getVote()); + return temp_nil4; + } + public String getVote() { + return this.vote; + } + public void cast(String aid, String v) { + this.vote = v; + } + public Account(String vote) { + this.vote = vote; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/Accounts.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/Accounts.java new file mode 100644 index 0000000..d0b8bb3 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/Accounts.java @@ -0,0 +1,14 @@ +import java.util.*; + +public class Accounts { + private Map value = new HashMap<>(); + public Map getValue() { + return new HashMap<>(value); + } + public Account getAccount(String aid) { + return this.value.get(aid); + } + public void signUp(String name, String aid) { + this.value.put(aid,new Account(null)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/Counts.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/Counts.java new file mode 100644 index 0000000..a0d5b9f --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/Counts.java @@ -0,0 +1,18 @@ +import java.util.*; + +public class Counts { + private Map value = new HashMap<>(); + private Account account; + private Accounts accounts; + public Map getValue() { + Map v0 = new HashMap<>(); + for (String aid: this.accounts.getValue().keySet()) { + String vote = this.accounts.getAccount(aid).getVote(); + v0.put(aid,vote); + } + return v0; + } + public Counts(Accounts accounts) { + this.accounts = accounts; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/VotingSystem.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/VotingSystem.java new file mode 100644 index 0000000..27950ce --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/VotingSystem.java @@ -0,0 +1,28 @@ +import java.util.*; + +public class VotingSystem { + private Accounts accounts; + private Counts counts; + public VotingSystem() { + accounts = new Accounts(); + counts = new Counts(accounts); + } + public Map getAccount(String aid) { + return this.accounts.getAccount(aid).getValue(); + } + public Map getAccounts() { + return this.accounts.getValue(); + } + public void signUp(String name, String aid) { + this.accounts.signUp(name, aid); + } + public Map getCounts() { + return this.counts.getValue(); + } + public String getVote(String aid) { + return this.accounts.getAccount(aid).getVote(); + } + public void cast(String aid, String v) { + this.accounts.getAccount(aid).cast(aid, v); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PULL-first/Highest.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PULL-first/Highest.java new file mode 100644 index 0000000..407263c --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PULL-first/Highest.java @@ -0,0 +1,20 @@ +import java.util.*; + +public class Highest { + private double value = 0.0; + public double getValue() { + return value; + } + public void updateFromTemp_f(double temp_f) { + double temp_if5; + if ((temp_f>=this.value)) { + temp_if5 = temp_f; + } else { + temp_if5 = this.value; + } + this.value = temp_if5; + } + public void reset(double v) { + this.value = v; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PULL-first/Temp_c.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PULL-first/Temp_c.java new file mode 100644 index 0000000..7eb9df6 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PULL-first/Temp_c.java @@ -0,0 +1,11 @@ +import java.util.*; + +public class Temp_c { + private Temp_f temp_f; + public double getValue() { + return ((this.temp_f.getValue()-32)/1.8); + } + public Temp_c(Temp_f temp_f) { + this.temp_f = temp_f; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PULL-first/Temp_f.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PULL-first/Temp_f.java new file mode 100644 index 0000000..2b57682 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PULL-first/Temp_f.java @@ -0,0 +1,16 @@ +import java.util.*; + +public class Temp_f { + private double value = 0.0; + private Highest highest; + public double getValue() { + return value; + } + public void observe(double x) { + this.value = x; + this.highest.updateFromTemp_f(value); + } + public Temp_f(Highest highest) { + this.highest = highest; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PULL-first/WeatherObservationSystem.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PULL-first/WeatherObservationSystem.java new file mode 100644 index 0000000..940b5a7 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PULL-first/WeatherObservationSystem.java @@ -0,0 +1,27 @@ +import java.util.*; + +public class WeatherObservationSystem { + private Highest highest; + private Temp_f temp_f; + private Temp_c temp_c; + public WeatherObservationSystem() { + highest = new Highest(); + temp_f = new Temp_f(highest); + temp_c = new Temp_c(temp_f); + } + public double getHighest() { + return this.highest.getValue(); + } + public void reset(double v) { + this.highest.reset(v); + } + public double getTemp_f() { + return this.temp_f.getValue(); + } + public void observe(double x) { + this.temp_f.observe(x); + } + public double getTemp_c() { + return this.temp_c.getValue(); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PUSH-first/Highest.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PUSH-first/Highest.java new file mode 100644 index 0000000..be085e2 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PUSH-first/Highest.java @@ -0,0 +1,20 @@ +import java.util.*; + +public class Highest { + private double value = 0.0; + public double getValue() { + return value; + } + public void updateFromTemp_f(double temp_f) { + double temp_if6; + if ((temp_f>=this.value)) { + temp_if6 = temp_f; + } else { + temp_if6 = this.value; + } + this.value = temp_if6; + } + public void reset(double v) { + this.value = v; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PUSH-first/Temp_c.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PUSH-first/Temp_c.java new file mode 100644 index 0000000..8aea709 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PUSH-first/Temp_c.java @@ -0,0 +1,11 @@ +import java.util.*; + +public class Temp_c { + private double value = 0.0; + public double getValue() { + return value; + } + public void updateFromTemp_f(double temp_f) { + this.value = ((temp_f-32)/1.8); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PUSH-first/Temp_f.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PUSH-first/Temp_f.java new file mode 100644 index 0000000..3a63072 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PUSH-first/Temp_f.java @@ -0,0 +1,19 @@ +import java.util.*; + +public class Temp_f { + private double value = 0.0; + private Temp_c temp_c; + private Highest highest; + public double getValue() { + return value; + } + public void observe(double x) { + this.value = x; + this.temp_c.updateFromTemp_f(value); + this.highest.updateFromTemp_f(value); + } + public Temp_f(Temp_c temp_c, Highest highest) { + this.temp_c = temp_c; + this.highest = highest; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PUSH-first/WeatherObservationSystem.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PUSH-first/WeatherObservationSystem.java new file mode 100644 index 0000000..9d43e68 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PUSH-first/WeatherObservationSystem.java @@ -0,0 +1,27 @@ +import java.util.*; + +public class WeatherObservationSystem { + private Temp_c temp_c; + private Highest highest; + private Temp_f temp_f; + public WeatherObservationSystem() { + temp_c = new Temp_c(); + highest = new Highest(); + temp_f = new Temp_f(temp_c,highest); + } + public double getTemp_c() { + return this.temp_c.getValue(); + } + public double getHighest() { + return this.highest.getValue(); + } + public void reset(double v) { + this.highest.reset(v); + } + public double getTemp_f() { + return this.temp_f.getValue(); + } + public void observe(double x) { + this.temp_f.observe(x); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/src/Main.java b/AlgebraicDataflowArchitectureModel/src/Main.java index 54fdaf8..d815006 100644 --- a/AlgebraicDataflowArchitectureModel/src/Main.java +++ b/AlgebraicDataflowArchitectureModel/src/Main.java @@ -1,12 +1,10 @@ - - import application.ApplicationWindow; public class Main { - + public static void main(String[] args) { ApplicationWindow frame = new ApplicationWindow(); frame.setVisible(true); } - + } diff --git a/AlgebraicDataflowArchitectureModel/src/algorithms/DataTransferModelAnalyzer.java b/AlgebraicDataflowArchitectureModel/src/algorithms/DataTransferModelAnalyzer.java index 836227a..651f5cc 100644 --- a/AlgebraicDataflowArchitectureModel/src/algorithms/DataTransferModelAnalyzer.java +++ b/AlgebraicDataflowArchitectureModel/src/algorithms/DataTransferModelAnalyzer.java @@ -1,85 +1,137 @@ package algorithms; -import java.util.ArrayList; +import models.Edge; +import models.Node; +import models.NodeAttribute; +import models.dataConstraintModel.Channel; +import models.dataConstraintModel.ChannelMember; +import models.dataFlowModel.*; + import java.util.Collection; import java.util.HashSet; -import java.util.List; - -import models.*; -import models.algebra.*; -import models.dataConstraintModel.*; -import models.dataFlowModel.*; +import java.util.Set; /** * Algorithms to analyze data transfer model. - * + * * @author Nitta * */ public class DataTransferModelAnalyzer { /** * Create data flow graph annotated with node attributes that indicate whether each resource state needs to be stored. + * * @param model a data transfer model * @return annotated data flow graph */ 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); + for (Channel channel : channels) { + for (ChannelMember member : ((DataTransferChannel) channel).getOutputChannelMembers()) { + 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); } } - + /** * Annotate data flow graph with edge attributes that indicate selectable data transfer methods. + * * @param graph a data flow graph * @return annotated data flow graph */ static public DataFlowGraph annotateWithSelectableDataTransferAttiribute(DataFlowGraph graph) { - List nodes = new ArrayList<>(graph.getNodes()); - // set push only attributes - for (Node n: graph.getNodes()) { - if (nodes.contains(n) && ((StoreAttribute) ((ResourceNode) n).getAttribute()).isNeeded()) { - nodes.remove(n); - trackEdges(n, nodes); + HashSet unvisitedNodes = new HashSet<>(graph.getResourceNodes()); + // Turn push only + for (Node resNode : graph.getResourceNodes()) { + if (unvisitedNodes.contains(resNode) && ((StoreAttribute) ((ResourceNode) resNode).getAttribute()).isNeeded()) { + unvisitedNodes.remove(resNode); + trackEdgesBackwardForPush(resNode, unvisitedNodes); + } + } + // Turn push/pull only with respect to channel hierarchies. + while (!unvisitedNodes.isEmpty()) { + Node resNode = unvisitedNodes.iterator().next(); + unvisitedNodes.remove(resNode); + for (Edge chToRes : ((ResourceNode) resNode).getInEdges()) { + ChannelNode chNode = (ChannelNode) chToRes.getSource(); + // Should take into account the channel hierarchy. + boolean pullContained = false; + Set ancestorChannels = chNode.getAncestors(); + for (ChannelNode ancestorCh : ancestorChannels) { + for (Edge resToCh : ancestorCh.getInEdges()) { + ResourceNode srcResNode = (ResourceNode) resToCh.getSource(); + DataTransferChannel ch = (DataTransferChannel) ancestorCh.getChannel(); + for (ChannelMember cm : ch.getInputChannelMembers()) { + if (cm.isOutside()) { + PushPullAttribute ppat = new PushPullAttribute(); + ppat.addOption(PushPullValue.PULL); // To refer to outside resource. + ((DataFlowEdge) resToCh).setAttribute(ppat); + pullContained = true; + } else { + PushPullAttribute ppat = new PushPullAttribute(); + ppat.addOption(PushPullValue.PUSH); // For broadcasting transfer. + ((DataFlowEdge) resToCh).setAttribute(ppat); + unvisitedNodes.remove(srcResNode); + trackEdgesBackwardForPush(srcResNode, unvisitedNodes); + } + } + } + } + Set descendantChannels = chNode.getDescendants(); + for (ChannelNode descendantCh : descendantChannels) { + for (Edge resToCh : descendantCh.getInEdges()) { + PushPullAttribute ppat = new PushPullAttribute(); + ppat.addOption(PushPullValue.PULL); // For collecting transfer. + ((DataFlowEdge) resToCh).setAttribute(ppat); + pullContained = true; + } + } + if (pullContained) { + trackEdgesForwardForPull(resNode, unvisitedNodes); + } } } // 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); @@ -89,17 +141,59 @@ } return graph; } - - static private void trackEdges(Node n, List nodes) { - // recursively set push only attributes to input side edges - for (Edge e : ((ResourceNode) n).getInEdges()) { + + static private void trackEdgesBackwardForPush(Node resNode, HashSet unvisitedNodes) { + // recursively turn push only backward in data-flow. + for (Edge chToRes : ((ResourceNode) resNode).getInEdges()) { + ChannelNode chNode = (ChannelNode) chToRes.getSource(); + // Should take into account the channel hierarchy. + Set ancestorChannels = chNode.getAncestors(); + Set descendantChannels = chNode.getDescendants(); + Set inEdges = new HashSet<>(); + inEdges.addAll(chNode.getInEdges()); + for (ChannelNode ancestorCh : ancestorChannels) { + inEdges.addAll(ancestorCh.getInEdges()); + } + for (ChannelNode descendantCh : descendantChannels) { + inEdges.addAll(descendantCh.getInEdges()); + } + for (Edge resToCh : inEdges) { + PushPullAttribute ppat = new PushPullAttribute(); + ppat.addOption(PushPullValue.PUSH); + ((DataFlowEdge) resToCh).setAttribute(ppat); + Node resNode2 = resToCh.getSource(); + if (unvisitedNodes.contains(resNode2)) { + unvisitedNodes.remove(resNode2); + trackEdgesBackwardForPush(resNode2, unvisitedNodes); + } + } + } + } + + private static void trackEdgesForwardForPull(Node resNode, HashSet unvisitedNodes) { + // recursively turn pull only forward in data-flow. + for (Edge resToCh : ((ResourceNode) resNode).getOutEdges()) { 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); + ppat.addOption(PushPullValue.PULL); + ((DataFlowEdge) resToCh).setAttribute(ppat); + ChannelNode chNode = (ChannelNode) resToCh.getDestination(); + // Should take into account the channel hierarchy. + Set ancestorChannels = chNode.getAncestors(); + Set descendantChannels = chNode.getDescendants(); + Set outEdges = new HashSet<>(); + outEdges.addAll(chNode.getOutEdges()); + for (ChannelNode ancestorCh : ancestorChannels) { + outEdges.addAll(ancestorCh.getOutEdges()); + } + for (ChannelNode descendantCh : descendantChannels) { + outEdges.addAll(descendantCh.getOutEdges()); + } + for (Edge chToRes : outEdges) { + Node resNode2 = chToRes.getDestination(); + if (unvisitedNodes.contains(resNode2)) { + unvisitedNodes.remove(resNode2); + trackEdgesForwardForPull(resNode2, unvisitedNodes); + } } } } diff --git a/AlgebraicDataflowArchitectureModel/src/algorithms/TypeInference.java b/AlgebraicDataflowArchitectureModel/src/algorithms/TypeInference.java index d29070d..b8c68b9 100644 --- a/AlgebraicDataflowArchitectureModel/src/algorithms/TypeInference.java +++ b/AlgebraicDataflowArchitectureModel/src/algorithms/TypeInference.java @@ -1,35 +1,14 @@ package algorithms; -import java.util.AbstractMap; -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 javax.xml.crypto.Data; - -import models.Node; -import models.algebra.Expression; -import models.algebra.Position; -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.ResourcePath; -import models.dataConstraintModel.StateTransition; +import models.algebra.*; +import models.dataConstraintModel.*; import models.dataFlowModel.DataTransferModel; -import models.dataFlowModel.ResourceNode; + +import java.util.*; /** * Type inference for data transfer model - * + * * @author Nitta * */ @@ -42,71 +21,90 @@ 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); } - + public static Type getListComponentType(Type listType) { return listComponentTypes.get(listType); } - + public static Collection getListTypes() { return listTypes.values(); } - + public static Type getTupleType(List compTypes) { return tupleTypes.get(compTypes); } - + public static List getTupleComponentTypes(Type tupleType) { return tupleComponentTypes.get(tupleType); } - + public static Collection getTupleTypes() { return tupleTypes.values(); } - + public static Type getPairType(Type compType) { return pairTypes.get(compType); } - + public static Type getPairComponentType(Type pairType) { return pairComponentTypes.get(pairType); } - + public static Type getMapType(List compTypes) { return mapTypes.get(compTypes); } - + public static List getMapComponentTypes(Type mapType) { 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); listComponentTypes.put(DataConstraintModel.typeListStr, DataConstraintModel.typeString); @@ -119,817 +117,1125 @@ pairTypes.put(DataConstraintModel.typeInt, DataConstraintModel.typePairInt); pairTypes.put(DataConstraintModel.typeString, DataConstraintModel.typePairStr); pairTypes.put(DataConstraintModel.typeDouble, DataConstraintModel.typePairDouble); - tupleComponentTypes.put(DataConstraintModel.typeTuple, Arrays.asList(new Type[] { null, null })); - mapComponentTypes.put(DataConstraintModel.typeMap, Arrays.asList(new Type[] { null, null })); - + tupleComponentTypes.put(DataConstraintModel.typeTuple, Arrays.asList(new Type[]{null, null})); + 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(); - - // 1.1 Group expressions by resources. - List sameResource = resources.get(id); - if (sameResource == null) { - sameResource = new ArrayList<>(); - resources.put(id, sameResource); - } - 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(); - Expression exp = st.getCurStateExpression(); - Type expType = getExpTypeIfUpdatable(resType, exp); - if (expType != null) { - id.setResourceStateType(expType); - for (Expression resExp : sameResource) { - if (resExp != exp) { - if (resExp instanceof Variable && compareTypes(((Variable) resExp).getType(), expType)) { - ((Variable) resExp).setType(expType); - updatedExps.put(System.identityHashCode(resExp), resExp); - } else if (resExp instanceof Term && compareTypes(((Term) resExp).getType(), expType)) { - ((Term) resExp).setType(expType); - updatedExps.put(System.identityHashCode(resExp), resExp); - } + for (Channel ch : channels) { + // 1.1 Group expressions by resources. + IGroupExpressionsByResource groupExpressionsByResource = new IGroupExpressionsByResource() { + public void groupForChannel(Channel ch) { + for (ChannelMember cm : ch.getChannelMembers()) { + StateTransition st = cm.getStateTransition(); + ResourceHierarchy res = cm.getResource().getResourceHierarchy(); + List identicalResources = resources.get(res); + if (identicalResources == null) { + identicalResources = new ArrayList<>(); + resources.put(res, identicalResources); } - } - } else if (exp instanceof Variable) { - if (compareTypes(((Variable) exp).getType(), resType)) { - ((Variable) exp).setType(resType); - updatedExps.put(System.identityHashCode(exp), exp); - } - } else if (exp instanceof Term) { - if (compareTypes(((Term) exp).getType(), resType)) { - ((Term) exp).setType(resType); - updatedExps.put(System.identityHashCode(exp), exp); - } - } - resType = id.getResourceStateType(); - exp = st.getNextStateExpression(); - if (exp != null) { - expType = getExpTypeIfUpdatable(resType, exp); - if (expType != null) { - id.setResourceStateType(expType); - for (Expression resExp : sameResource) { - if (resExp != exp) { - if (resExp instanceof Variable && compareTypes(((Variable) resExp).getType(), expType)) { - ((Variable) resExp).setType(expType); - updatedExps.put(System.identityHashCode(resExp), resExp); - } else if (resExp instanceof Term && compareTypes(((Term) resExp).getType(), expType)) { - ((Term) resExp).setType(expType); - updatedExps.put(System.identityHashCode(resExp), resExp); - } - } + 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); } - } else if (exp instanceof Variable) { - if (compareTypes(((Variable) exp).getType(), resType)) { - ((Variable) exp).setType(resType); - updatedExps.put(System.identityHashCode(exp), exp); - } - } else if (exp instanceof Term) { - if (compareTypes(((Term) exp).getType(), resType)) { - ((Term) exp).setType(resType); - updatedExps.put(System.identityHashCode(exp), exp); - } - } - } - - // 1.2 Group expressions by variable. - Map> locals = new HashMap<>(); - Map localTypes = new HashMap<>(); - List allVariables = new ArrayList<>(); - allVariables.addAll(st.getCurStateExpression().getVariables().values()); - allVariables.addAll(st.getMessageExpression().getVariables().values()); - if (st.getNextStateExpression() != null) - allVariables.addAll(st.getNextStateExpression().getVariables().values()); - for (Variable var : allVariables) { - List sameVariable = locals.get(var.getName()); - if (sameVariable == null) { - sameVariable = new ArrayList<>(); - sameVariable.add(var); - expToVariable.put(System.identityHashCode(var), sameVariable); - locals.put(var.getName(), sameVariable); - localTypes.put(var.getName(), var.getType()); - } else { - sameVariable.add(var); - expToVariable.put(System.identityHashCode(var), sameVariable); - Type varType = localTypes.get(var.getName()); - Map updatedVars = getUpdateSet(updateFromVariable, sameVariable); - if (compareTypes(varType, var.getType())) { - localTypes.put(var.getName(), var.getType()); - for (Expression v : sameVariable) { - if (v != var) { - if (compareTypes(((Variable) v).getType(), var.getType())) { - ((Variable) v).setType(var.getType()); - updatedVars.put(System.identityHashCode(v), v); + Map updatedExps = getUpdateSet(updateFromResource, identicalResources); + Type resType = res.getResourceStateType(); + Expression exp = st.getCurStateExpression(); + Type expType = getExpTypeIfUpdatable(resType, exp); + if (expType != null) { + res.setResourceStateType(expType); + for (Expression resExp : identicalResources) { + if (resExp != exp) { + if (resExp instanceof Variable && compareTypes(((Variable) resExp).getType(), expType)) { + ((Variable) resExp).setType(expType); + updatedExps.put(System.identityHashCode(resExp), resExp); + } else if (resExp instanceof Term && compareTypes(((Term) resExp).getType(), expType)) { + ((Term) resExp).setType(expType); + updatedExps.put(System.identityHashCode(resExp), resExp); } } } - } else if (compareTypes(var.getType(), varType)) { - var.setType(varType); - updatedVars.put(System.identityHashCode(var), var); - } - } - } - for (String varName : locals.keySet()) { - variables.put(System.identityHashCode(locals.get(varName)), localTypes.get(varName)); - } - - // 1.3 Group expressions by message. - Expression message = st.getMessageExpression(); - if (message instanceof Variable) { - Type msgType = ((Variable) message).getType(); - Map, Type>> msgTypeMap = messages.get(c); - if (msgTypeMap == null) { - msgTypeMap = new HashMap<>(); - messages.put(c, msgTypeMap); - } - Map.Entry, Type> typeAndExps = msgTypeMap.get(0); - if (typeAndExps == null) { - List exps = new ArrayList<>(); - exps.add(message); - typeAndExps = new AbstractMap.SimpleEntry<>(exps, msgType); - msgTypeMap.put(0, typeAndExps); - expToMessage.put(System.identityHashCode(message), exps); - } else { - typeAndExps.getKey().add(message); - expToMessage.put(System.identityHashCode(message), typeAndExps.getKey()); - Map updateExps = getUpdateSet(updateFromMessage, typeAndExps.getKey()); - if (compareTypes(typeAndExps.getValue(), msgType)) { - typeAndExps.setValue(msgType); - for (Expression e : typeAndExps.getKey()) { - if (e != message) { - if (e instanceof Variable) { - ((Variable) e).setType(msgType); - updateExps.put(System.identityHashCode(e), e); - } - } + } else if (exp instanceof Variable) { + if (compareTypes(((Variable) exp).getType(), resType)) { + ((Variable) exp).setType(resType); + updatedExps.put(System.identityHashCode(exp), exp); } - } else if (compareTypes(msgType, typeAndExps.getValue())) { - ((Variable) message).setType(typeAndExps.getValue()); - updateExps.put(System.identityHashCode(message), message); + } else if (exp instanceof Term) { + if (compareTypes(((Term) exp).getType(), resType)) { + ((Term) exp).setType(resType); + updatedExps.put(System.identityHashCode(exp), exp); + } } - } - } else if (message instanceof Term) { - Map, Type>> msgTypeMap = messages.get(c); - if (msgTypeMap == null) { - msgTypeMap = new HashMap<>(); - messages.put(c, msgTypeMap); - } - for (int i = 0; i < ((Term) message).getArity(); i++) { - Expression arg = ((Term) message).getChild(i); - Type argType = null; - if (arg instanceof Variable) { - argType = ((Variable) arg).getType(); - } else if (arg instanceof Term) { - argType = ((Term) arg).getType(); - } else { - continue; - } - Map.Entry, Type> typeAndExps = msgTypeMap.get(i); - if (typeAndExps == null) { - List exps = new ArrayList<>(); - exps.add(arg); - typeAndExps = new AbstractMap.SimpleEntry<>(exps, argType); - msgTypeMap.put(i, typeAndExps); - expToMessage.put(System.identityHashCode(arg), exps); - } else { - typeAndExps.getKey().add(arg); - expToMessage.put(System.identityHashCode(arg), typeAndExps.getKey()); - Map updateExps = getUpdateSet(updateFromMessage, typeAndExps.getKey()); - if (compareTypes(typeAndExps.getValue(), argType)) { - typeAndExps.setValue(argType); - for (Expression e : typeAndExps.getKey()) { - if (e != arg) { - if (e instanceof Variable) { - ((Variable) e).setType(argType); - updateExps.put(System.identityHashCode(e), e); + resType = res.getResourceStateType(); + exp = st.getNextStateExpression(); + if (exp != null) { + expType = getExpTypeIfUpdatable(resType, exp); + if (expType != null) { + res.setResourceStateType(expType); + for (Expression resExp : identicalResources) { + if (resExp != exp) { + if (resExp instanceof Variable && compareTypes(((Variable) resExp).getType(), expType)) { + ((Variable) resExp).setType(expType); + updatedExps.put(System.identityHashCode(resExp), resExp); + } else if (resExp instanceof Term && compareTypes(((Term) resExp).getType(), expType)) { + ((Term) resExp).setType(expType); + updatedExps.put(System.identityHashCode(resExp), resExp); } } } - } else if (compareTypes(argType, typeAndExps.getValue())) { - if (arg instanceof Variable) { - ((Variable) arg).setType(typeAndExps.getValue()); - updateExps.put(System.identityHashCode(arg), arg); - } else if (arg instanceof Term) { - ((Term) arg).setType(typeAndExps.getValue()); - updateExps.put(System.identityHashCode(arg), arg); + } else if (exp instanceof Variable) { + if (compareTypes(((Variable) exp).getType(), resType)) { + ((Variable) exp).setType(resType); + updatedExps.put(System.identityHashCode(exp), exp); + } + } else if (exp instanceof Term) { + if (compareTypes(((Term) exp).getType(), resType)) { + ((Term) exp).setType(resType); + updatedExps.put(System.identityHashCode(exp), exp); } } } } + for (Channel childCh : ch.getChildren()) { + groupForChannel(childCh); + } } - - // 1.4 Extract constraints on expressions in each term. - List terms = new ArrayList<>(); - if (st.getCurStateExpression() instanceof Term) { - Map subTerms = ((Term) st.getCurStateExpression()).getSubTerms(Term.class); - terms.addAll(subTerms.values()); - } - if (st.getMessageExpression() instanceof Term) { - Map subTerms = ((Term) st.getMessageExpression()).getSubTerms(Term.class); - terms.addAll(subTerms.values()); - } - if (st.getNextStateExpression() != null && st.getNextStateExpression() instanceof Term) { - Map subTerms = ((Term) st.getNextStateExpression()).getSubTerms(Term.class); - terms.addAll(subTerms.values()); - } - for (Term t : terms) { - Symbol symbol = t.getSymbol(); - 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); - updateExpressionBelonging(expToConsOrSet, t, consExps); - if (symbol.equals(DataConstraintModel.cons)) { - // If the root symbol of the term is cons. - for (Expression e : t.getChildren()) { - consExps.add(e); - updateExpressionBelonging(expToConsOrSet, e, consExps); - } - } else { - // If the root symbol of the term is set. - Expression e = t.getChildren().get(2); - consExps.add(e); - updateExpressionBelonging(expToConsOrSet, e, consExps); - e = t.getChildren().get(0); - consExps.add(e); - updateExpressionBelonging(expToConsOrSet, e, consExps); + }; + groupExpressionsByResource.groupForChannel(ch); + + // 1.2 Group expressions by variable. + IGroupExpressionsByVariable groupExpressionsByVariable = new IGroupExpressionsByVariable() { + public void groupForChannel(Channel ch) { + for (ChannelMember cm : ch.getChannelMembers()) { + StateTransition st = cm.getStateTransition(); + Map> locals = new HashMap<>(); + Map localTypes = new HashMap<>(); + List allVariables = new ArrayList<>(); + allVariables.addAll(st.getCurStateExpression().getVariables().values()); + allVariables.addAll(st.getMessageExpression().getVariables().values()); + if (st.getNextStateExpression() != null) { + allVariables.addAll(st.getNextStateExpression().getVariables().values()); } - Type newType = getExpTypeIfUpdatable(t.getType(), consExps.get(2)); - if (newType != null) { - // If the type of the 2nd argument of cons (1st argument of set) is more concrete than the type of the term. - t.setType(newType); - Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); - updateCons.put(System.identityHashCode(t), t); - } else { - Type arg2Type = null; - if (consExps.get(2) != null && consExps.get(2) instanceof Variable) { - arg2Type = ((Variable) consExps.get(2)).getType(); - if (compareTypes(arg2Type, t.getType())) { - // If the type of the term is more concrete than the type of the 2nd argument of cons (1st argument of set). - ((Variable) consExps.get(2)).setType(t.getType()); - Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); - updateCons.put(System.identityHashCode(consExps.get(2)), consExps.get(2)); - } - } else if (consExps.get(2) != null && consExps.get(2) instanceof Term) { - arg2Type = ((Term) consExps.get(2)).getType(); - if (compareTypes(arg2Type, t.getType())) { - // If the type of the term is more concrete than the type of the 2nd argument of cons (1st argument of set). - ((Term) consExps.get(2)).setType(t.getType()); - Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); - updateCons.put(System.identityHashCode(consExps.get(2)), consExps.get(2)); - } + for (Selector s : ch.getAllSelectors()) { // add channel selectors + if (s.getExpression() instanceof Variable) { + allVariables.add((Variable) s.getExpression()); } } - Type newCompType = getExpTypeIfUpdatable(listComponentTypes.get(t.getType()), consExps.get(1)); - if (newCompType != null) { - // If the type of the 1st argument of cons (3rd argument of set) is more concrete than the type of list component. - Type newListType = listTypes.get(newCompType); - if (newListType == null) { - // Create new list type. - newListType = createNewListType(newCompType, DataConstraintModel.typeList); - } - t.setType(newListType); - Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); - updateCons.put(System.identityHashCode(t), t); - if (consExps.get(2) != null && consExps.get(2) instanceof Variable) { - ((Variable) consExps.get(2)).setType(newListType); - updateCons.put(System.identityHashCode(consExps.get(2)), consExps.get(2)); - } else if (consExps.get(2) != null && consExps.get(2) instanceof Term) { - ((Term) consExps.get(2)).setType(newListType); - updateCons.put(System.identityHashCode(consExps.get(2)), consExps.get(2)); + 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()); } } - consOrSet.put(System.identityHashCode(consExps), t.getType()); - } else if (symbol.equals(DataConstraintModel.head) || symbol.equals(DataConstraintModel.get)) { - // If the root symbol of the term is head or get. - List consExps = new ArrayList<>(); - Expression e = t.getChildren().get(0); - consExps.add(e); - updateExpressionBelonging(expToConsOrSet, e, consExps); - consExps.add(t); - updateExpressionBelonging(expToConsOrSet, t, consExps); - consExps.add(null); - Type listType = listTypes.get(t.getType()); - if (listType == null && t.getType() != null) { - // Create a new list type. - listType = createNewListType(t.getType(), DataConstraintModel.typeList); - } - Type newListType = getExpTypeIfUpdatable(listType, consExps.get(0)); - if (newListType != null) { - // If the type of the component of the 1st argument is more concrete than the type of the term. - Type newCompType = listComponentTypes.get(newListType); - if (newCompType != null) { - t.setType(newCompType); - Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); - updateCons.put(System.identityHashCode(t), t); - } - consOrSet.put(System.identityHashCode(consExps), newListType); - } else { - // If the type of the term is more concrete than the type of the component of the 1st argument. - if (consExps.get(0) != null && consExps.get(0) instanceof Variable) { - ((Variable) consExps.get(0)).setType(listType); - Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); - updateCons.put(System.identityHashCode(consExps.get(0)), consExps.get(0)); - } else if (consExps.get(0) != null && consExps.get(0) instanceof Term) { - ((Term) consExps.get(0)).setType(listType); - Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); - updateCons.put(System.identityHashCode(consExps.get(0)), consExps.get(0)); - } - consOrSet.put(System.identityHashCode(consExps), listType); - } - } else if (symbol.equals(DataConstraintModel.tail)) { - // If the root symbol of the term is tail. - List consExps = new ArrayList<>(); - consExps.add(t); - updateExpressionBelonging(expToConsOrSet, t, consExps); - consExps.add(null); - Expression e = t.getChildren().get(0); - consExps.add(e); - updateExpressionBelonging(expToConsOrSet, e, consExps); - Type newType = getExpTypeIfUpdatable(t.getType(), consExps.get(2)); - if (newType != null) { - // If the type of the argument is more concrete than the type of the term. - t.setType(newType); - Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); - updateCons.put(System.identityHashCode(t), t); - } else { - Type argType = null; - if (consExps.get(2) != null && consExps.get(2) instanceof Variable) { - argType = ((Variable) consExps.get(2)).getType(); - if (compareTypes(argType, t.getType())) { - // If the type of the term is more concrete than the type of the argument. - ((Variable) consExps.get(2)).setType(t.getType()); - Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); - updateCons.put(System.identityHashCode(consExps.get(2)), consExps.get(2)); - } - } else if (consExps.get(2) != null && consExps.get(2) instanceof Term) { - argType = ((Term) consExps.get(2)).getType(); - if (compareTypes(argType, t.getType())) { - // If the type of the term is more concrete than the type of the argument. - ((Term) consExps.get(2)).setType(t.getType()); - Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); - updateCons.put(System.identityHashCode(consExps.get(2)), consExps.get(2)); - } - } - } - consOrSet.put(System.identityHashCode(consExps), t.getType()); - } 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); - updateExpressionBelonging(expToTuple, t, tupleExps); - for (Expression e : t.getChildren()) { - tupleExps.add(e); - updateExpressionBelonging(expToTuple, e, tupleExps); - if (e instanceof Variable) { - argsTypeList.add(((Variable) e).getType()); - } else if (e instanceof Term) { - argsTypeList.add(((Term) e).getType()); + for (Variable var : allVariables) { + List sameVariable = locals.get(var.getName()); + if (sameVariable == null) { + sameVariable = new ArrayList<>(); + sameVariable.add(var); + expToVariable.put(System.identityHashCode(var), sameVariable); + locals.put(var.getName(), sameVariable); + localTypes.put(var.getName(), var.getType()); } else { - argsTypeList.add(null); + sameVariable.add(var); + expToVariable.put(System.identityHashCode(var), sameVariable); + Type varType = localTypes.get(var.getName()); + Map updatedVars = getUpdateSet(updateFromVariable, sameVariable); + if (compareTypes(varType, var.getType())) { + localTypes.put(var.getName(), var.getType()); + for (Expression v : sameVariable) { + if (v != var) { + if (compareTypes(((Variable) v).getType(), var.getType())) { + ((Variable) v).setType(var.getType()); + updatedVars.put(System.identityHashCode(v), v); + } + } + } + } else if (compareTypes(var.getType(), varType)) { + var.setType(varType); + updatedVars.put(System.identityHashCode(var), var); + } } } - if (t.getType() == DataConstraintModel.typeTuple) { - Type newTupleType = tupleTypes.get(argsTypeList); - if (newTupleType == null) { - // Create new tuple type; - newTupleType = createNewTupleType(argsTypeList, DataConstraintModel.typeTuple); - } - t.setType(newTupleType); - Map updateExps = getUpdateSet(updateFromTuple, tupleExps); - updateExps.put(System.identityHashCode(t), t); + for (String varName : locals.keySet()) { + variables.put(System.identityHashCode(locals.get(varName)), localTypes.get(varName)); } - tuple.put(System.identityHashCode(tupleExps), t.getType()); - } else if (symbol.equals(DataConstraintModel.pair)) { - // If the root symbol of the term is pair. - List pairExps = new ArrayList<>(); - pairExps.add(t); - updateExpressionBelonging(expToPair, t, pairExps); - if (t.getType() == DataConstraintModel.typePair) { - for (Expression e : t.getChildren()) { - pairExps.add(e); - updateExpressionBelonging(expToPair, e, pairExps); + } + for (Channel childCh : ch.getChildren()) { + groupForChannel(childCh); + } + } + }; + groupExpressionsByVariable.groupForChannel(ch); + + // 1.3 Group expressions by message. + IGroupExpressionsByMessage groupExpressionsByMessage = new IGroupExpressionsByMessage() { + public void groupForChannel(Channel rootCh, Channel ch) { + for (ChannelMember cm : ch.getChannelMembers()) { + Expression message = cm.getStateTransition().getMessageExpression(); + if (message instanceof Variable) { + Type msgType = ((Variable) message).getType(); + Map, Type>> msgTypeMap = messages.get(rootCh); + if (msgTypeMap == null) { + msgTypeMap = new HashMap<>(); + messages.put(rootCh, msgTypeMap); + } + Map.Entry, Type> typeAndExps = msgTypeMap.get(0); + if (typeAndExps == null) { + List exps = new ArrayList<>(); + exps.add(message); + typeAndExps = new AbstractMap.SimpleEntry<>(exps, msgType); + msgTypeMap.put(0, typeAndExps); + expToMessage.put(System.identityHashCode(message), exps); + } else { + typeAndExps.getKey().add(message); + expToMessage.put(System.identityHashCode(message), typeAndExps.getKey()); + Map updateExps = getUpdateSet(updateFromMessage, typeAndExps.getKey()); + if (compareTypes(typeAndExps.getValue(), msgType)) { + typeAndExps.setValue(msgType); + for (Expression e : typeAndExps.getKey()) { + if (e != message) { + if (e instanceof Variable) { + ((Variable) e).setType(msgType); + updateExps.put(System.identityHashCode(e), e); + } + } + } + } else if (compareTypes(msgType, typeAndExps.getValue())) { + ((Variable) message).setType(typeAndExps.getValue()); + updateExps.put(System.identityHashCode(message), message); + } + } + } else if (message instanceof Term) { + Map, Type>> msgTypeMap = messages.get(rootCh); + if (msgTypeMap == null) { + msgTypeMap = new HashMap<>(); + messages.put(rootCh, msgTypeMap); + } + for (int i = 0; i < ((Term) message).getArity(); i++) { + Expression arg = ((Term) message).getChild(i); Type argType = null; - if (e instanceof Variable) { - argType = (((Variable) e).getType()); - - } else if (e instanceof Term) { - argType = (((Term) e).getType()); - } - - if (argType != null) { - Type newPairType = pairTypes.get(argType); - if (newPairType != null) { - t.setType(newPairType); - Map updateExps = getUpdateSet(updateFromPair, pairExps); - updateExps.put(System.identityHashCode(t), t); - } - } - } - pair.put(System.identityHashCode(pairExps), t.getType()); - - } - } else if (symbol.equals(DataConstraintModel.fst)) { - // If the root symbol of the term is fst. - List tupleExps = new ArrayList<>(); - Expression arg = t.getChildren().get(0); - tupleExps.add(arg); - updateExpressionBelonging(expToTuple, arg, tupleExps); - tupleExps.add(t); - updateExpressionBelonging(expToTuple, t, tupleExps); - tupleExps.add(null); - Type argType = null; - if (arg instanceof Variable) { - argType = ((Variable) arg).getType(); - } else if (arg instanceof Term) { - argType = ((Term) arg).getType(); - } - 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); - if (newTupleType == null) { - // Create new tuple type; - newTupleType = createNewTupleType(compTypeList, DataConstraintModel.typeTuple); - } - } - if (argType != newTupleType && newTupleType != null) { - if (arg instanceof Variable) { - ((Variable) arg).setType(newTupleType); - argType = newTupleType; - } else if (arg instanceof Term) { - ((Term) arg).setType(newTupleType); - argType = newTupleType; - } - Map updateExps = getUpdateSet(updateFromTuple, tupleExps); - updateExps.put(System.identityHashCode(arg), arg); - } - tuple.put(System.identityHashCode(tupleExps), argType); - } else if (symbol.equals(DataConstraintModel.snd)) { - // If the root symbol of the term is snd. - List tupleExps = new ArrayList<>(); - Expression arg = t.getChildren().get(0); - tupleExps.add(arg); - updateExpressionBelonging(expToTuple, arg, tupleExps); - tupleExps.add(null); - tupleExps.add(t); - updateExpressionBelonging(expToTuple, t, tupleExps); - Type argType = null; - if (arg instanceof Variable) { - argType = ((Variable) arg).getType(); - } else if (arg instanceof Term) { - argType = ((Term) arg).getType(); - } - Type newTupleType = DataConstraintModel.typeTuple; - if (argType == DataConstraintModel.typeTuple && t.getType() != null) { - List compTypeList = new ArrayList<>(); - compTypeList.add(null); - if (DataConstraintModel.typeTuple.isAncestorOf(t.getType())) { - List sndTypes = tupleComponentTypes.get(t.getType()); - if (sndTypes != null) { - for (Type t2: sndTypes) { - compTypeList.add(t2); - } + if (arg instanceof Variable) { + argType = ((Variable) arg).getType(); + } else if (arg instanceof Term) { + argType = ((Term) arg).getType(); } else { - compTypeList.add(t.getType()); + continue; } - } else { - compTypeList.add(t.getType()); - } - newTupleType = tupleTypes.get(compTypeList); - if (newTupleType == null) { - // Create new tuple type; - newTupleType = createNewTupleType(compTypeList, DataConstraintModel.typeTuple); - } - } - if (argType != newTupleType && newTupleType != null) { - if (arg instanceof Variable) { - ((Variable) arg).setType(newTupleType); - argType = newTupleType; - } else if (arg instanceof Term) { - ((Term) arg).setType(newTupleType); - argType = newTupleType; - } - Map updateExps = getUpdateSet(updateFromTuple, tupleExps); - updateExps.put(System.identityHashCode(arg), arg); - } - tuple.put(System.identityHashCode(tupleExps), argType); - } else if (symbol.equals(DataConstraintModel.left)) { - // If the root symbol of the term is left. - List pairExps = new ArrayList<>(); - Expression arg = t.getChildren().get(0); - pairExps.add(arg); - updateExpressionBelonging(expToPair, arg, pairExps); - pairExps.add(t); - updateExpressionBelonging(expToPair, t, pairExps); - pairExps.add(null); - Type argType = null; - if (arg instanceof Variable) { - argType = ((Variable) arg).getType(); - } else if (arg instanceof Term) { - argType = ((Term) arg).getType(); - } - 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); - if (newPairType == null) { - // Create new tuple type; - newPairType = createNewTupleType(compTypeList, DataConstraintModel.typePair); - } - } - if (argType != newPairType && newPairType != null) { - if (arg instanceof Variable) { - ((Variable) arg).setType(newPairType); - argType = newPairType; - } else if (arg instanceof Term) { - ((Term) arg).setType(newPairType); - argType = newPairType; - } - Map updateExps = getUpdateSet(updateFromPair, pairExps); - updateExps.put(System.identityHashCode(arg), arg); - } - pair.put(System.identityHashCode(pairExps), argType); - } else if (symbol.equals(DataConstraintModel.right)) { - // If the root symbol of the term is right. - List pairExps = new ArrayList<>(); - Expression arg = t.getChildren().get(0); - pairExps.add(arg); - updateExpressionBelonging(expToPair, arg, pairExps); - pairExps.add(null); - pairExps.add(t); - updateExpressionBelonging(expToPair, t, pairExps); - Type argType = null; - if (arg instanceof Variable) { - argType = ((Variable) arg).getType(); - } else if (arg instanceof Term) { - argType = ((Term) arg).getType(); - } - 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); - if (newPairType == null) { - // Create new tuple type; - newPairType = createNewTupleType(compTypeList, DataConstraintModel.typePair); - } - } - if (argType != newPairType && newPairType != null) { - if (arg instanceof Variable) { - ((Variable) arg).setType(newPairType); - argType = newPairType; - } else if (arg instanceof Term) { - ((Term) arg).setType(newPairType); - argType = newPairType; - } - Map updateExps = getUpdateSet(updateFromPair, pairExps); - updateExps.put(System.identityHashCode(arg), arg); - } - pair.put(System.identityHashCode(pairExps), argType); - } else if (symbol.equals(DataConstraintModel.lookup)) { - // If the root symbol of the term is lookup. - List mapExps = new ArrayList<>(); - Expression arg1 = t.getChildren().get(0); // map - mapExps.add(arg1); - updateExpressionBelonging(expToMap, arg1, mapExps); - Expression arg2 = t.getChildren().get(1); // key - mapExps.add(arg2); - updateExpressionBelonging(expToMap, arg2, mapExps); - mapExps.add(t); // value - updateExpressionBelonging(expToMap, t, mapExps); - Type arg1Type = null; - if (arg1 instanceof Variable) { - arg1Type = ((Variable) arg1).getType(); - } else if (arg1 instanceof Term) { - arg1Type = ((Term) arg1).getType(); - } - List compTypeList = new ArrayList<>(); - if (arg2 instanceof Variable) { - compTypeList.add(((Variable) arg2).getType()); - } else if (arg2 instanceof Term) { - compTypeList.add(((Term) arg2).getType()); - } else { - compTypeList.add(null); - } - compTypeList.add(t.getType()); - if (arg1Type == DataConstraintModel.typeMap || arg1Type == null) { - Type newMapType = mapTypes.get(compTypeList); - if (newMapType == null) { - // Create new tuple type; - newMapType = createNewMapType(compTypeList, DataConstraintModel.typeMap); - } - if (arg1 instanceof Variable) { - ((Variable) arg1).setType(newMapType); - arg1Type = newMapType; - } else if (arg1 instanceof Term) { - ((Term) arg1).setType(newMapType); - arg1Type = newMapType; - } - Map updateExps = getUpdateSet(updateFromMap, mapExps); - updateExps.put(System.identityHashCode(arg1), arg1); - } - map.put(System.identityHashCode(mapExps), arg1Type); - } else if (symbol.equals(DataConstraintModel.insert)) { - // If the root symbol of the term is insert. - List mapExps = new ArrayList<>(); - mapExps.add(t); // map - updateExpressionBelonging(expToMap, t, mapExps); - Expression arg1 = t.getChildren().get(1); // key - mapExps.add(arg1); - updateExpressionBelonging(expToMap, arg1, mapExps); - Expression arg2 = t.getChildren().get(2); // value - mapExps.add(arg2); - updateExpressionBelonging(expToMap, arg2, mapExps); - Expression arg0 = t.getChildren().get(0); // map - mapExps.add(arg0); - updateExpressionBelonging(expToMap, arg0, mapExps); - Type termType = t.getType(); - List compTypeList = new ArrayList<>(); - if (arg1 instanceof Variable) { - compTypeList.add(((Variable) arg1).getType()); - } else if (arg1 instanceof Term) { - compTypeList.add(((Term) arg1).getType()); - } else { - compTypeList.add(null); - } - if (arg2 instanceof Variable) { - compTypeList.add(((Variable) arg2).getType()); - } else if (arg2 instanceof Term) { - compTypeList.add(((Term) arg2).getType()); - } else { - compTypeList.add(null); - } - if (termType == DataConstraintModel.typeMap || termType == null) { - Type newMapType = mapTypes.get(compTypeList); - if (newMapType == null) { - // Create new tuple type; - newMapType = createNewMapType(compTypeList, DataConstraintModel.typeMap); - } - 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.cond)) { - // If the root symbol of the term is if function. - Expression c1 = t.getChild(1); - Expression c2 = t.getChild(2); - List condTerms = new ArrayList<>(); - condTerms.add(t); - condTerms.add(c1); - condTerms.add(c2); - expToVariable.put(System.identityHashCode(t), condTerms); - expToVariable.put(System.identityHashCode(c1), condTerms); - expToVariable.put(System.identityHashCode(c2), condTerms); - Type condType = t.getType(); - Map updatedVars = getUpdateSet(updateFromVariable, condTerms); - Type child1Type = getExpTypeIfUpdatable(condType, c1); - if (child1Type != null) { - condType = child1Type; - t.setType(child1Type); - updatedVars.put(System.identityHashCode(t), t); - } else { - if (c1 instanceof Variable && compareTypes(((Variable) c1).getType(), condType)) { - ((Variable) c1).setType(condType); - updatedVars.put(System.identityHashCode(c1), c1); - } else if (c1 instanceof Term && compareTypes(((Term) c1).getType(), condType)) { - ((Term) c1).setType(condType); - updatedVars.put(System.identityHashCode(c1), c1); - } - } - Type child2Type = getExpTypeIfUpdatable(condType, c2); - if (child2Type != null) { - condType = child2Type; - t.setType(child2Type); - updatedVars.put(System.identityHashCode(t), t); - if (c1 instanceof Variable) { - ((Variable) c1).setType(child2Type); - updatedVars.put(System.identityHashCode(c1), c1); - } else if (c1 instanceof Term) { - ((Term) c1).setType(child2Type); - updatedVars.put(System.identityHashCode(c1), c1); - } - } else { - if (c2 instanceof Variable && compareTypes(((Variable) c2).getType(), condType)) { - ((Variable) c2).setType(condType); - updatedVars.put(System.identityHashCode(c2), c2); - } else if (c2 instanceof Term && compareTypes(((Term) c2).getType(), condType)) { - ((Term) c2).setType(condType); - updatedVars.put(System.identityHashCode(c2), c2); - } - } - variables.put(System.identityHashCode(condTerms), condType); - } else if (symbol.equals(DataConstraintModel.add) || symbol.equals(DataConstraintModel.sub) - || symbol.equals(DataConstraintModel.mul) || symbol.equals(DataConstraintModel.div)) { - // If the root symbol of the term is arithmetic operators. - Expression c1 = t.getChild(0); - Expression c2 = t.getChild(1); - List operands = new ArrayList<>(); - operands.add(t); - operands.add(c1); - operands.add(c2); - expToVariable.put(System.identityHashCode(t), operands); - expToVariable.put(System.identityHashCode(c1), operands); - expToVariable.put(System.identityHashCode(c2), operands); - Type opType = t.getType(); - Map updatedVars = getUpdateSet(updateFromVariable, operands); - Type child1Type = getExpTypeIfUpdatable(opType, c1); - if (child1Type != null) { - opType = child1Type; - t.setType(child1Type); - updatedVars.put(System.identityHashCode(t), t); - } else { - if (c1 instanceof Variable && compareTypes(((Variable) c1).getType(), opType)) { - ((Variable) c1).setType(opType); - updatedVars.put(System.identityHashCode(c1), c1); - } else if (c1 instanceof Term && compareTypes(((Term) c1).getType(), opType)) { - ((Term) c1).setType(opType); - updatedVars.put(System.identityHashCode(c1), c1); - } - } - Type child2Type = getExpTypeIfUpdatable(opType, c2); - if (child2Type != null) { - opType = child2Type; - t.setType(child2Type); - updatedVars.put(System.identityHashCode(t), t); - if (c1 instanceof Variable) { - ((Variable) c1).setType(child2Type); - updatedVars.put(System.identityHashCode(c1), c1); - } else if (c1 instanceof Term) { - ((Term) c1).setType(child2Type); - updatedVars.put(System.identityHashCode(c1), c1); - } - } else { - if (c2 instanceof Variable && compareTypes(((Variable) c2).getType(), opType)) { - ((Variable) c2).setType(opType); - updatedVars.put(System.identityHashCode(c2), c2); - } else if (c2 instanceof Term && compareTypes(((Term) c2).getType(), opType)) { - ((Term) c2).setType(opType); - updatedVars.put(System.identityHashCode(c2), c2); - } - } - variables.put(System.identityHashCode(operands), opType); - } else if (symbol.getSignature() != null - && symbol.getSignature()[0] == DataConstraintModel.typeList) { - // If the root symbol of the term is the list type (except for the cons - // function). - List consExps = new ArrayList<>(); - consExps.add(t); - expToVariable.put(System.identityHashCode(t), consExps); - Type condType = t.getType(); - Map updatedVars = getUpdateSet(updateFromVariable, consExps); - for (int i = 1; i < symbol.getSignature().length; i++) { - Type tc = symbol.getSignature()[i]; - if (tc == DataConstraintModel.typeList) { - Expression e = t.getChildren().get(i - 1); - Type newType = getExpTypeIfUpdatable(condType, e); - if (newType != null) { - condType = newType; - for (Expression e2 : consExps) { - if (e2 instanceof Variable) { - ((Variable) e2).setType(newType); - updatedVars.put(System.identityHashCode(e2), e2); - } else if (e2 instanceof Term) { - ((Term) e2).setType(newType); - updatedVars.put(System.identityHashCode(e2), e2); + Map.Entry, Type> typeAndExps = msgTypeMap.get(i); + if (typeAndExps == null) { + List exps = new ArrayList<>(); + exps.add(arg); + typeAndExps = new AbstractMap.SimpleEntry<>(exps, argType); + msgTypeMap.put(i, typeAndExps); + expToMessage.put(System.identityHashCode(arg), exps); + } else { + typeAndExps.getKey().add(arg); + expToMessage.put(System.identityHashCode(arg), typeAndExps.getKey()); + Map updateExps = getUpdateSet(updateFromMessage, typeAndExps.getKey()); + if (compareTypes(typeAndExps.getValue(), argType)) { + typeAndExps.setValue(argType); + for (Expression e : typeAndExps.getKey()) { + if (e != arg) { + if (e instanceof Variable) { + ((Variable) e).setType(argType); + updateExps.put(System.identityHashCode(e), e); + } + } + } + } else if (compareTypes(argType, typeAndExps.getValue())) { + if (arg instanceof Variable) { + ((Variable) arg).setType(typeAndExps.getValue()); + updateExps.put(System.identityHashCode(arg), arg); + } else if (arg instanceof Term) { + ((Term) arg).setType(typeAndExps.getValue()); + updateExps.put(System.identityHashCode(arg), arg); } } - } else { - if (e instanceof Variable && compareTypes(((Variable) e).getType(), condType)) { - ((Variable) e).setType(condType); - updatedVars.put(System.identityHashCode(e), e); - } else if (e instanceof Term && compareTypes(((Term) e).getType(), condType)) { - ((Term) e).setType(condType); - updatedVars.put(System.identityHashCode(e), e); - } } - consExps.add(e); - expToVariable.put(System.identityHashCode(e), consExps); } } - variables.put(System.identityHashCode(consExps), condType); + } + for (Channel childCh : ch.getChildren()) { + groupForChannel(rootCh, childCh); } } + }; + groupExpressionsByMessage.groupForChannel(ch, ch); + + // 1.4 Extract constraints on expressions in each term. + IExtractConstraintsOnExpressionsInTerm extractConstraintsOnExpressionsInTerm = new IExtractConstraintsOnExpressionsInTerm() { + public void extractForChannel(Channel ch) { + for (ChannelMember cm : ch.getChannelMembers()) { + StateTransition st = cm.getStateTransition(); + List terms = new ArrayList<>(); + if (st.getCurStateExpression() instanceof Term) { + Map subTerms = ((Term) st.getCurStateExpression()).getSubTerms(Term.class); + terms.addAll(subTerms.values()); + } + if (st.getMessageExpression() instanceof Term) { + Map subTerms = ((Term) st.getMessageExpression()).getSubTerms(Term.class); + terms.addAll(subTerms.values()); + } + if (st.getNextStateExpression() != null && st.getNextStateExpression() instanceof Term) { + Map subTerms = ((Term) st.getNextStateExpression()).getSubTerms(Term.class); + terms.addAll(subTerms.values()); + } + for (Term t : terms) { + Symbol symbol = t.getSymbol(); + if (symbol.equals(DataConstraintModel.cons) || symbol.equals(DataConstraintModel.set) || symbol.equals(DataConstraintModel.append)) { + // If the root symbol of the term is cons or set. + List consExps = new ArrayList<>(); + consExps.add(t); // list term + updateExpressionBelonging(expToConsOrSet, t, consExps); + if (symbol.equals(DataConstraintModel.cons)) { + // If the root symbol of the term is cons. + for (Expression e : t.getChildren()) { + consExps.add(e); + updateExpressionBelonging(expToConsOrSet, e, consExps); + } + } else if (symbol.equals(DataConstraintModel.append)) { + // If the root symbol of the term is append. + Expression e = t.getChildren().get(1); + consExps.add(e); // list element + updateExpressionBelonging(expToConsOrSet, e, consExps); + e = t.getChildren().get(0); + consExps.add(e); // list argument + updateExpressionBelonging(expToConsOrSet, e, consExps); + } else { + // If the root symbol of the term is set. + Expression e = t.getChildren().get(2); + consExps.add(e); // list element + updateExpressionBelonging(expToConsOrSet, e, consExps); + e = t.getChildren().get(0); + consExps.add(e); // list argument + updateExpressionBelonging(expToConsOrSet, e, consExps); + } + Type newType = getExpTypeIfUpdatable(t.getType(), consExps.get(2)); + if (newType != null) { + // If the type of the 2nd argument of cons (1st argument of set/append) is more concrete than the type of the term. + t.setType(newType); + Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); + updateCons.put(System.identityHashCode(t), t); + } else { + Type arg2Type = null; + if (consExps.get(2) != null && consExps.get(2) instanceof Variable) { + arg2Type = ((Variable) consExps.get(2)).getType(); + if (compareTypes(arg2Type, t.getType())) { + // If the type of the term is more concrete than the type of the 2nd argument of cons (1st argument of set/append). + ((Variable) consExps.get(2)).setType(t.getType()); + Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); + updateCons.put(System.identityHashCode(consExps.get(2)), consExps.get(2)); + } + } else if (consExps.get(2) != null && consExps.get(2) instanceof Term) { + arg2Type = ((Term) consExps.get(2)).getType(); + if (compareTypes(arg2Type, t.getType())) { + // If the type of the term is more concrete than the type of the 2nd argument of cons (1st argument of set/append). + ((Term) consExps.get(2)).setType(t.getType()); + Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); + updateCons.put(System.identityHashCode(consExps.get(2)), consExps.get(2)); + } + } + } + Type newCompType = getExpTypeIfUpdatable(listComponentTypes.get(t.getType()), consExps.get(1)); + if (newCompType != null) { + // If the type of the 1st argument of cons (3rd argument of set) is more concrete than the type of list component. + Type newListType = listTypes.get(newCompType); + if (newListType == null) { + // Create new list type. + newListType = createNewListType(newCompType, DataConstraintModel.typeList); + } + t.setType(newListType); + Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); + updateCons.put(System.identityHashCode(t), t); + if (consExps.get(2) != null && consExps.get(2) instanceof Variable) { + ((Variable) consExps.get(2)).setType(newListType); + updateCons.put(System.identityHashCode(consExps.get(2)), consExps.get(2)); + } else if (consExps.get(2) != null && consExps.get(2) instanceof Term) { + ((Term) consExps.get(2)).setType(newListType); + updateCons.put(System.identityHashCode(consExps.get(2)), consExps.get(2)); + } + } + consOrSet.put(System.identityHashCode(consExps), t.getType()); + } else if (symbol.equals(DataConstraintModel.head) || symbol.equals(DataConstraintModel.get)) { + // If the root symbol of the term is head or get. + List consExps = new ArrayList<>(); + Expression e = t.getChildren().get(0); + consExps.add(e); // list argument + updateExpressionBelonging(expToConsOrSet, e, consExps); + consExps.add(t); // list's component + updateExpressionBelonging(expToConsOrSet, t, consExps); + consExps.add(null); + Type listType = listTypes.get(t.getType()); + if (listType == null && t.getType() != null) { + // Create a new list type. + listType = createNewListType(t.getType(), DataConstraintModel.typeList); + } + Type newListType = getExpTypeIfUpdatable(listType, consExps.get(0)); + if (newListType != null) { + // If the type of the component of the 1st argument is more concrete than the type of the term. + Type newCompType = listComponentTypes.get(newListType); + if (newCompType != null) { + t.setType(newCompType); + Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); + updateCons.put(System.identityHashCode(t), t); + } + consOrSet.put(System.identityHashCode(consExps), newListType); + } else { + // If the type of the term is more concrete than the type of the component of the 1st argument. + if (consExps.get(0) != null && consExps.get(0) instanceof Variable) { + ((Variable) consExps.get(0)).setType(listType); + Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); + updateCons.put(System.identityHashCode(consExps.get(0)), consExps.get(0)); + } else if (consExps.get(0) != null && consExps.get(0) instanceof Term) { + ((Term) consExps.get(0)).setType(listType); + Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); + updateCons.put(System.identityHashCode(consExps.get(0)), consExps.get(0)); + } + consOrSet.put(System.identityHashCode(consExps), listType); + } + } else if (symbol.equals(DataConstraintModel.tail)) { + // If the root symbol of the term is tail. + List consExps = new ArrayList<>(); + consExps.add(t); // list term + updateExpressionBelonging(expToConsOrSet, t, consExps); + consExps.add(null); // list's component + Expression e = t.getChildren().get(0); + consExps.add(e); // list argument + updateExpressionBelonging(expToConsOrSet, e, consExps); + Type newType = getExpTypeIfUpdatable(t.getType(), consExps.get(2)); + if (newType != null) { + // If the type of the argument is more concrete than the type of the term. + t.setType(newType); + Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); + updateCons.put(System.identityHashCode(t), t); + } else { + Type argType = null; + if (consExps.get(2) != null && consExps.get(2) instanceof Variable) { + argType = ((Variable) consExps.get(2)).getType(); + if (compareTypes(argType, t.getType())) { + // If the type of the term is more concrete than the type of the argument. + ((Variable) consExps.get(2)).setType(t.getType()); + Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); + updateCons.put(System.identityHashCode(consExps.get(2)), consExps.get(2)); + } + } else if (consExps.get(2) != null && consExps.get(2) instanceof Term) { + argType = ((Term) consExps.get(2)).getType(); + if (compareTypes(argType, t.getType())) { + // If the type of the term is more concrete than the type of the argument. + ((Term) consExps.get(2)).setType(t.getType()); + Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); + updateCons.put(System.identityHashCode(consExps.get(2)), consExps.get(2)); + } + } + } + consOrSet.put(System.identityHashCode(consExps), t.getType()); + } else if (symbol.equals(DataConstraintModel.tuple)) { + // If the root symbol of the term is tuple. + List tupleExps = new ArrayList<>(); + List newArgTypesList = new ArrayList<>(); + tupleExps.add(t); // tuple term + updateExpressionBelonging(expToTuple, t, tupleExps); + for (Expression e : t.getChildren()) { + tupleExps.add(e); // tuple's component + updateExpressionBelonging(expToTuple, e, tupleExps); + if (e instanceof Variable) { + newArgTypesList.add(((Variable) e).getType()); + } else if (e instanceof Term) { + newArgTypesList.add(((Term) e).getType()); + } else { + newArgTypesList.add(null); + } + } + if (t.getType() == DataConstraintModel.typeTuple) { + Type newTupleType = tupleTypes.get(newArgTypesList); + if (newTupleType == null) { + // Create new tuple type; + 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); + } + tuple.put(System.identityHashCode(tupleExps), t.getType()); + } else if (symbol.equals(DataConstraintModel.pair)) { + // If the root symbol of the term is pair. + List pairExps = new ArrayList<>(); + pairExps.add(t); // pair + updateExpressionBelonging(expToPair, t, pairExps); + if (t.getType() == DataConstraintModel.typePair) { + for (Expression e : t.getChildren()) { + pairExps.add(e); // left/right + updateExpressionBelonging(expToPair, e, pairExps); + Type newArgType = null; + if (e instanceof Variable) { + newArgType = (((Variable) e).getType()); + + } else if (e instanceof Term) { + newArgType = (((Term) e).getType()); + } + + if (newArgType != null) { + Type newPairType = pairTypes.get(newArgType); + if (newPairType != null) { + t.setType(newPairType); + Map updateExps = getUpdateSet(updateFromPair, pairExps); + updateExps.put(System.identityHashCode(t), t); + } + } + } + pair.put(System.identityHashCode(pairExps), t.getType()); + + } + } else if (symbol.equals(DataConstraintModel.fst)) { + // If the root symbol of the term is fst. + List tupleExps = new ArrayList<>(); + Expression arg = t.getChildren().get(0); + tupleExps.add(arg); // tuple argument + updateExpressionBelonging(expToTuple, arg, tupleExps); + tupleExps.add(t); // first component + updateExpressionBelonging(expToTuple, t, tupleExps); + tupleExps.add(null); // second component + Type argType = null; + if (arg instanceof Variable) { + argType = ((Variable) arg).getType(); + } else if (arg instanceof Term) { + argType = ((Term) arg).getType(); + } + Type newTupleType = DataConstraintModel.typeTuple; + if (argType == DataConstraintModel.typeTuple && t.getType() != null) { + List newCompTypeList = new ArrayList<>(); + newCompTypeList.add(t.getType()); + newCompTypeList.add(null); + newTupleType = tupleTypes.get(newCompTypeList); + if (newTupleType == null) { + // Create new tuple type; + 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; + } else if (arg instanceof Term) { + ((Term) arg).setType(newTupleType); + argType = newTupleType; + } + Map updateExps = getUpdateSet(updateFromTuple, tupleExps); + updateExps.put(System.identityHashCode(arg), arg); + } + tuple.put(System.identityHashCode(tupleExps), argType); + } else if (symbol.equals(DataConstraintModel.snd)) { + // If the root symbol of the term is snd. + List tupleExps = new ArrayList<>(); + Expression arg = t.getChildren().get(0); + tupleExps.add(arg); // tuple argument + updateExpressionBelonging(expToTuple, arg, tupleExps); + tupleExps.add(null); // first component + tupleExps.add(t); // second component + updateExpressionBelonging(expToTuple, t, tupleExps); + Type argType = null; + if (arg instanceof Variable) { + argType = ((Variable) arg).getType(); + } else if (arg instanceof Term) { + argType = ((Term) arg).getType(); + } + Type newTupleType = DataConstraintModel.typeTuple; + if (argType == DataConstraintModel.typeTuple && t.getType() != 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) { + newCompTypeList.add(t2); + } + } else { + newCompTypeList.add(t.getType()); + } + } else { + newCompTypeList.add(t.getType()); + } + newTupleType = tupleTypes.get(newCompTypeList); + if (newTupleType == null) { + // Create new tuple type; + 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; + } else if (arg instanceof Term) { + ((Term) arg).setType(newTupleType); + argType = newTupleType; + } + Map updateExps = getUpdateSet(updateFromTuple, tupleExps); + updateExps.put(System.identityHashCode(arg), arg); + } + tuple.put(System.identityHashCode(tupleExps), argType); + } else if (symbol.equals(DataConstraintModel.left)) { + // If the root symbol of the term is left. + List pairExps = new ArrayList<>(); + Expression arg = t.getChildren().get(0); + pairExps.add(arg); // pair + updateExpressionBelonging(expToPair, arg, pairExps); + pairExps.add(t); // left + updateExpressionBelonging(expToPair, t, pairExps); + pairExps.add(null); // right + Type argType = null; + if (arg instanceof Variable) { + argType = ((Variable) arg).getType(); + } else if (arg instanceof Term) { + argType = ((Term) arg).getType(); + } + Type newPairType = DataConstraintModel.typePair; + if (argType == DataConstraintModel.typePair && t.getType() != null) { + List newCompTypeList = new ArrayList<>(); + newCompTypeList.add(t.getType()); + newCompTypeList.add(null); + newPairType = pairTypes.get(newCompTypeList); + if (newPairType == null) { + // Create new tuple type; + newPairType = createNewTupleType(newCompTypeList, DataConstraintModel.typePair); + } + } + if (argType != newPairType && newPairType != null) { + if (arg instanceof Variable) { + ((Variable) arg).setType(newPairType); + argType = newPairType; + } else if (arg instanceof Term) { + ((Term) arg).setType(newPairType); + argType = newPairType; + } + Map updateExps = getUpdateSet(updateFromPair, pairExps); + updateExps.put(System.identityHashCode(arg), arg); + } + pair.put(System.identityHashCode(pairExps), argType); + } else if (symbol.equals(DataConstraintModel.right)) { + // If the root symbol of the term is right. + List pairExps = new ArrayList<>(); + Expression arg = t.getChildren().get(0); + pairExps.add(arg); // pair + updateExpressionBelonging(expToPair, arg, pairExps); + pairExps.add(null); // left + pairExps.add(t); // right + updateExpressionBelonging(expToPair, t, pairExps); + Type argType = null; + if (arg instanceof Variable) { + argType = ((Variable) arg).getType(); + } else if (arg instanceof Term) { + argType = ((Term) arg).getType(); + } + Type newPairType = DataConstraintModel.typePair; + if (argType == DataConstraintModel.typePair && t.getType() != null) { + List newCompTypeList = new ArrayList<>(); + newCompTypeList.add(null); + newCompTypeList.add(t.getType()); + newPairType = pairTypes.get(newCompTypeList); + if (newPairType == null) { + // Create new tuple type; + newPairType = createNewTupleType(newCompTypeList, DataConstraintModel.typePair); + } + } + if (argType != newPairType && newPairType != null) { + if (arg instanceof Variable) { + ((Variable) arg).setType(newPairType); + argType = newPairType; + } else if (arg instanceof Term) { + ((Term) arg).setType(newPairType); + argType = newPairType; + } + Map updateExps = getUpdateSet(updateFromPair, pairExps); + updateExps.put(System.identityHashCode(arg), arg); + } + pair.put(System.identityHashCode(pairExps), argType); + } else if (symbol.equals(DataConstraintModel.lookup)) { + // If the root symbol of the term is lookup. + List mapExps = new ArrayList<>(); + Expression arg1 = t.getChildren().get(0); // map + mapExps.add(arg1); + updateExpressionBelonging(expToMap, arg1, mapExps); + Expression arg2 = t.getChildren().get(1); // key + mapExps.add(arg2); + updateExpressionBelonging(expToMap, arg2, mapExps); + mapExps.add(t); // value + updateExpressionBelonging(expToMap, t, mapExps); + Type arg1Type = null; + if (arg1 instanceof Variable) { + arg1Type = ((Variable) arg1).getType(); + } else if (arg1 instanceof Term) { + arg1Type = ((Term) arg1).getType(); + } + List newCompTypeList = new ArrayList<>(); + if (arg2 instanceof Variable) { + newCompTypeList.add(((Variable) arg2).getType()); + } else if (arg2 instanceof Term) { + newCompTypeList.add(((Term) arg2).getType()); + } else { + newCompTypeList.add(null); + } + newCompTypeList.add(t.getType()); + if (arg1Type == DataConstraintModel.typeMap || arg1Type == null) { + Type newMapType = mapTypes.get(newCompTypeList); + if (newMapType == null) { + // Create new tuple type; + 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; + } else if (arg1 instanceof Term) { + ((Term) arg1).setType(newMapType); + arg1Type = newMapType; + } + Map updateExps = getUpdateSet(updateFromMap, mapExps); + updateExps.put(System.identityHashCode(arg1), arg1); + } + map.put(System.identityHashCode(mapExps), arg1Type); + } else if (symbol.equals(DataConstraintModel.insert)) { + // If the root symbol of the term is insert. + List mapExps = new ArrayList<>(); + mapExps.add(t); // map + updateExpressionBelonging(expToMap, t, mapExps); + Expression arg1 = t.getChildren().get(1); // key + mapExps.add(arg1); + updateExpressionBelonging(expToMap, arg1, mapExps); + Expression arg2 = t.getChildren().get(2); // value + mapExps.add(arg2); + updateExpressionBelonging(expToMap, arg2, mapExps); + Expression arg0 = t.getChildren().get(0); // map + mapExps.add(arg0); + updateExpressionBelonging(expToMap, arg0, mapExps); + Type termType = t.getType(); + List newCompTypeList = new ArrayList<>(); + if (arg1 instanceof Variable) { + newCompTypeList.add(((Variable) arg1).getType()); + } else if (arg1 instanceof Term) { + newCompTypeList.add(((Term) arg1).getType()); + } else { + newCompTypeList.add(null); + } + if (arg2 instanceof Variable) { + newCompTypeList.add(((Variable) arg2).getType()); + } else if (arg2 instanceof Term) { + newCompTypeList.add(((Term) arg2).getType()); + } else { + newCompTypeList.add(null); + } + Type newTermType = getExpTypeIfUpdatable(termType, mapExps.get(3)); + if (newTermType != null) { + // If the type of the 1st argument of insert is more concrete than the type of the term. + t.setType(newTermType); + termType = newTermType; + Map updateExps = getUpdateSet(updateFromMap, mapExps); + updateExps.put(System.identityHashCode(t), t); + } else { + Type arg3Type = null; + if (mapExps.get(3) != null && mapExps.get(3) instanceof Variable) { + arg3Type = ((Variable) mapExps.get(3)).getType(); + if (compareTypes(arg3Type, t.getType())) { + // If the type of the term is more concrete than the type of the 1st argument of insert. + ((Variable) mapExps.get(3)).setType(t.getType()); + Map updateExps = getUpdateSet(updateFromMap, mapExps); + updateExps.put(System.identityHashCode(mapExps.get(3)), mapExps.get(3)); + } + } else if (mapExps.get(3) != null && mapExps.get(3) instanceof Term) { + arg3Type = ((Term) mapExps.get(3)).getType(); + if (compareTypes(arg3Type, t.getType())) { + // If the type of the term is more concrete than the type of the 1st argument of insert. + ((Term) mapExps.get(3)).setType(t.getType()); + Map updateExps = getUpdateSet(updateFromMap, mapExps); + updateExps.put(System.identityHashCode(mapExps.get(3)), mapExps.get(3)); + } + } + } + if (termType == DataConstraintModel.typeMap || termType == null) { + Type newMapType = mapTypes.get(newCompTypeList); + if (newMapType == null) { + // Create new tuple type; + newMapType = createNewMapType(newCompTypeList, DataConstraintModel.typeMap); + } + // Update the type of the map term and record the updated expression. + t.setType(newMapType); + Map updateExps = getUpdateSet(updateFromMap, mapExps); + updateExps.put(System.identityHashCode(t), t); + if (mapExps.get(3) != null && mapExps.get(3) instanceof Variable) { + ((Variable) mapExps.get(3)).setType(newMapType); + updateExps.put(System.identityHashCode(mapExps.get(3)), mapExps.get(3)); + } else if (mapExps.get(3) != null && mapExps.get(3) instanceof Term) { + ((Term) mapExps.get(3)).setType(newMapType); + updateExps.put(System.identityHashCode(mapExps.get(3)), mapExps.get(3)); + } + termType = newMapType; + } + 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); + Expression c2 = t.getChild(2); + List condTerms = new ArrayList<>(); + condTerms.add(t); + condTerms.add(c1); + condTerms.add(c2); + expToVariable.put(System.identityHashCode(t), condTerms); + expToVariable.put(System.identityHashCode(c1), condTerms); + expToVariable.put(System.identityHashCode(c2), condTerms); + Type condType = t.getType(); + Map updatedVars = getUpdateSet(updateFromVariable, condTerms); + Type child1Type = getExpTypeIfUpdatable(condType, c1); + if (child1Type != null) { + condType = child1Type; + t.setType(child1Type); + updatedVars.put(System.identityHashCode(t), t); + } else { + if (c1 instanceof Variable && compareTypes(((Variable) c1).getType(), condType)) { + ((Variable) c1).setType(condType); + updatedVars.put(System.identityHashCode(c1), c1); + } else if (c1 instanceof Term && compareTypes(((Term) c1).getType(), condType)) { + ((Term) c1).setType(condType); + updatedVars.put(System.identityHashCode(c1), c1); + } + } + Type child2Type = getExpTypeIfUpdatable(condType, c2); + if (child2Type != null) { + condType = child2Type; + t.setType(child2Type); + updatedVars.put(System.identityHashCode(t), t); + if (c1 instanceof Variable) { + ((Variable) c1).setType(child2Type); + updatedVars.put(System.identityHashCode(c1), c1); + } else if (c1 instanceof Term) { + ((Term) c1).setType(child2Type); + updatedVars.put(System.identityHashCode(c1), c1); + } + } else { + if (c2 instanceof Variable && compareTypes(((Variable) c2).getType(), condType)) { + ((Variable) c2).setType(condType); + updatedVars.put(System.identityHashCode(c2), c2); + } else if (c2 instanceof Term && compareTypes(((Term) c2).getType(), condType)) { + ((Term) c2).setType(condType); + updatedVars.put(System.identityHashCode(c2), c2); + } + } + variables.put(System.identityHashCode(condTerms), condType); + } else if (symbol.equals(DataConstraintModel.add) || symbol.equals(DataConstraintModel.sub) + || symbol.equals(DataConstraintModel.mul) || symbol.equals(DataConstraintModel.div)) { + // If the root symbol of the term is arithmetic operators. + Expression c1 = t.getChild(0); + Expression c2 = t.getChild(1); + List operands = new ArrayList<>(); + operands.add(t); + operands.add(c1); + operands.add(c2); + expToVariable.put(System.identityHashCode(t), operands); + expToVariable.put(System.identityHashCode(c1), operands); + expToVariable.put(System.identityHashCode(c2), operands); + Type opType = t.getType(); + Map updatedVars = getUpdateSet(updateFromVariable, operands); + Type child1Type = getExpTypeIfUpdatable(opType, c1); + if (child1Type != null) { + opType = child1Type; + t.setType(child1Type); + updatedVars.put(System.identityHashCode(t), t); + } else { + if (c1 instanceof Variable && compareTypes(((Variable) c1).getType(), opType)) { + ((Variable) c1).setType(opType); + updatedVars.put(System.identityHashCode(c1), c1); + } else if (c1 instanceof Term && compareTypes(((Term) c1).getType(), opType)) { + ((Term) c1).setType(opType); + updatedVars.put(System.identityHashCode(c1), c1); + } + } + Type child2Type = getExpTypeIfUpdatable(opType, c2); + if (child2Type != null) { + opType = child2Type; + t.setType(child2Type); + updatedVars.put(System.identityHashCode(t), t); + if (c1 instanceof Variable) { + ((Variable) c1).setType(child2Type); + updatedVars.put(System.identityHashCode(c1), c1); + } else if (c1 instanceof Term) { + ((Term) c1).setType(child2Type); + updatedVars.put(System.identityHashCode(c1), c1); + } + } else { + if (c2 instanceof Variable && compareTypes(((Variable) c2).getType(), opType)) { + ((Variable) c2).setType(opType); + updatedVars.put(System.identityHashCode(c2), c2); + } else if (c2 instanceof Term && compareTypes(((Term) c2).getType(), opType)) { + ((Term) c2).setType(opType); + updatedVars.put(System.identityHashCode(c2), c2); + } + } + variables.put(System.identityHashCode(operands), opType); + } else if (symbol.getSignature() != null + && symbol.getSignature()[0] == DataConstraintModel.typeList) { + // If the root symbol of the term is the list type (except for the cons function). + List consExps = new ArrayList<>(); + consExps.add(t); + expToVariable.put(System.identityHashCode(t), consExps); + Type condType = t.getType(); + Map updatedVars = getUpdateSet(updateFromVariable, consExps); + for (int i = 1; i < symbol.getSignature().length; i++) { + Type tc = symbol.getSignature()[i]; + if (tc == DataConstraintModel.typeList) { + Expression e = t.getChildren().get(i - 1); + Type newType = getExpTypeIfUpdatable(condType, e); + if (newType != null) { + condType = newType; + for (Expression e2 : consExps) { + if (e2 instanceof Variable) { + ((Variable) e2).setType(newType); + updatedVars.put(System.identityHashCode(e2), e2); + } else if (e2 instanceof Term) { + ((Term) e2).setType(newType); + updatedVars.put(System.identityHashCode(e2), e2); + } + } + } else { + if (e instanceof Variable && compareTypes(((Variable) e).getType(), condType)) { + ((Variable) e).setType(condType); + updatedVars.put(System.identityHashCode(e), e); + } else if (e instanceof Term && compareTypes(((Term) e).getType(), condType)) { + ((Term) e).setType(condType); + updatedVars.put(System.identityHashCode(e), e); + } + } + consExps.add(e); + expToVariable.put(System.identityHashCode(e), consExps); + } + } + variables.put(System.identityHashCode(consExps), condType); + } + } + } + for (Channel childCh : ch.getChildren()) { + extractForChannel(childCh); + } + } + }; + extractConstraintsOnExpressionsInTerm.extractForChannel(ch); + + // 1.5 Extract constraints on path parameters and resources. + IExtractConstraintsOnPathParametersAndResources extractConstraintsOnPathParametersAndResources = new IExtractConstraintsOnPathParametersAndResources() { + public void extractForChannel(Channel ch) { + for (ChannelMember cm : ch.getChannelMembers()) { + 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(); + } + } + for (Channel childCh : ch.getChildren()) { + extractForChannel(childCh); + } + } + }; + extractConstraintsOnPathParametersAndResources.extractForChannel(ch); + } + + // 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 +1249,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 +1260,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 +1278,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 +1295,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 +1312,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 +1329,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,18 +1346,41 @@ 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); + } } } - + private static void updateExpressionBelonging(Map>> belonging, Expression exp, List group) { Set> groups = belonging.get(System.identityHashCode(exp)); if (groups == null) { @@ -1055,19 +1393,21 @@ groups.add(group); } } - - 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,9 +1422,148 @@ } } } - + + 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) { + Map> expToVariable, Map> updateFromVariable) { List sameVariable = expToVariable.get(System.identityHashCode(exp)); if (sameVariable == null) return; Type varType = variables.get(System.identityHashCode(sameVariable)); @@ -1122,10 +1601,10 @@ } } } - + private static void updateMessageTypes(Expression exp, - Map, Type>>> messages, - Map> expToMessage, Map> updateFromMessage) { + Map, Type>>> messages, + Map> expToMessage, Map> updateFromMessage) { List messageExps = expToMessage.get(System.identityHashCode(exp)); if (messageExps == null) return; Type msgType = null; @@ -1158,93 +1637,97 @@ } } } - + private static void updateConsOrSetTypes(Expression exp, Map consOrSet, - Map>> expToConsOrSet, Map> updateFromConsOrSet) { + Map>> expToConsOrSet, Map> updateFromConsOrSet) { Set> consComponentGroups = expToConsOrSet.get(System.identityHashCode(exp)); if (consComponentGroups == null) return; - for (List consOrSetComponentGroup: consComponentGroups) { + for (List consOrSetComponentGroup : consComponentGroups) { int idx = consOrSetComponentGroup.indexOf(exp); switch (idx) { - case 0: - if (!(exp instanceof Term)) break; - Type listType = consOrSet.get(System.identityHashCode(consOrSetComponentGroup)); - Type expType = getExpTypeIfUpdatable(listType, exp); - if (expType != null) { - consOrSet.put(System.identityHashCode(consOrSetComponentGroup), expType); - Map updateExps = getUpdateSet(updateFromConsOrSet, consOrSetComponentGroup); - if (consOrSetComponentGroup.get(2) instanceof Variable) { - ((Variable) consOrSetComponentGroup.get(2)).setType(expType); - updateExps.put(System.identityHashCode(consOrSetComponentGroup.get(2)), consOrSetComponentGroup.get(2)); - } else if (consOrSetComponentGroup.get(2) instanceof Term) { - ((Term) consOrSetComponentGroup.get(2)).setType(expType); - updateExps.put(System.identityHashCode(consOrSetComponentGroup.get(2)), consOrSetComponentGroup.get(2)); + case 0: + // exp is a list itself. + if (!(exp instanceof Term)) break; + Type listType = consOrSet.get(System.identityHashCode(consOrSetComponentGroup)); + Type expType = getExpTypeIfUpdatable(listType, exp); + if (expType != null) { + consOrSet.put(System.identityHashCode(consOrSetComponentGroup), expType); + Map updateExps = getUpdateSet(updateFromConsOrSet, consOrSetComponentGroup); + if (consOrSetComponentGroup.get(2) instanceof Variable) { + ((Variable) consOrSetComponentGroup.get(2)).setType(expType); + updateExps.put(System.identityHashCode(consOrSetComponentGroup.get(2)), consOrSetComponentGroup.get(2)); + } else if (consOrSetComponentGroup.get(2) instanceof Term) { + ((Term) consOrSetComponentGroup.get(2)).setType(expType); + updateExps.put(System.identityHashCode(consOrSetComponentGroup.get(2)), consOrSetComponentGroup.get(2)); + } + Type compType = listComponentTypes.get(expType); + if (consOrSetComponentGroup.get(1) != null && consOrSetComponentGroup.get(1) instanceof Variable) { + ((Variable) consOrSetComponentGroup.get(1)).setType(compType); + updateExps.put(System.identityHashCode(consOrSetComponentGroup.get(1)), consOrSetComponentGroup.get(1)); + } else if (consOrSetComponentGroup.get(1) != null && consOrSetComponentGroup.get(1) instanceof Term) { + ((Term) consOrSetComponentGroup.get(1)).setType(compType); + updateExps.put(System.identityHashCode(consOrSetComponentGroup.get(1)), consOrSetComponentGroup.get(1)); + } } - Type compType = listComponentTypes.get(expType); - if (consOrSetComponentGroup.get(1) != null && consOrSetComponentGroup.get(1) instanceof Variable) { - ((Variable) consOrSetComponentGroup.get(1)).setType(compType); - updateExps.put(System.identityHashCode(consOrSetComponentGroup.get(1)), consOrSetComponentGroup.get(1)); - } else if (consOrSetComponentGroup.get(1) != null && consOrSetComponentGroup.get(1) instanceof Term) { - ((Term) consOrSetComponentGroup.get(1)).setType(compType); - updateExps.put(System.identityHashCode(consOrSetComponentGroup.get(1)), consOrSetComponentGroup.get(1)); + 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); + if (newCompType != null) { + Type newListType = listTypes.get(newCompType); + if (newListType == null) { + // Create new list type. + newListType = createNewListType(newCompType, listType); + } + consOrSet.put(System.identityHashCode(consOrSetComponentGroup), newListType); + Map updateExps = getUpdateSet(updateFromConsOrSet, consOrSetComponentGroup); + if (consOrSetComponentGroup.get(0) instanceof Term) { + ((Term) consOrSetComponentGroup.get(0)).setType(newListType); + updateExps.put(System.identityHashCode(consOrSetComponentGroup.get(0)), consOrSetComponentGroup.get(0)); + } + if (consOrSetComponentGroup.get(2) instanceof Variable) { + ((Variable) consOrSetComponentGroup.get(2)).setType(newListType); + updateExps.put(System.identityHashCode(consOrSetComponentGroup.get(2)), consOrSetComponentGroup.get(2)); + } else if (consOrSetComponentGroup.get(2) instanceof Term) { + ((Term) consOrSetComponentGroup.get(2)).setType(newListType); + updateExps.put(System.identityHashCode(consOrSetComponentGroup.get(2)), consOrSetComponentGroup.get(2)); + } } - } - break; - case 1: - listType = consOrSet.get(System.identityHashCode(consOrSetComponentGroup)); - Type compType = listComponentTypes.get(listType); - Type newCompType = getExpTypeIfUpdatable(compType, exp); - if (newCompType != null) { - Type newListType = listTypes.get(newCompType); - if (newListType == null) { - // Create new list type. - newListType = createNewListType(newCompType, listType); + break; + case 2: + // exp is a list itself. + listType = consOrSet.get(System.identityHashCode(consOrSetComponentGroup)); + expType = getExpTypeIfUpdatable(listType, exp); + if (expType != null) { + consOrSet.put(System.identityHashCode(consOrSetComponentGroup), expType); + Map updateExps = getUpdateSet(updateFromConsOrSet, consOrSetComponentGroup); + if (consOrSetComponentGroup.get(0) instanceof Term) { + ((Term) consOrSetComponentGroup.get(0)).setType(expType); + updateExps.put(System.identityHashCode(consOrSetComponentGroup.get(0)), consOrSetComponentGroup.get(0)); + } + compType = listComponentTypes.get(expType); + if (consOrSetComponentGroup.get(1) != null && consOrSetComponentGroup.get(1) instanceof Variable) { + ((Variable) consOrSetComponentGroup.get(1)).setType(compType); + updateExps.put(System.identityHashCode(consOrSetComponentGroup.get(1)), consOrSetComponentGroup.get(1)); + } else if (consOrSetComponentGroup.get(1) != null && consOrSetComponentGroup.get(1) instanceof Term) { + ((Term) consOrSetComponentGroup.get(1)).setType(compType); + updateExps.put(System.identityHashCode(consOrSetComponentGroup.get(1)), consOrSetComponentGroup.get(1)); + } } - consOrSet.put(System.identityHashCode(consOrSetComponentGroup), newListType); - Map updateExps = getUpdateSet(updateFromConsOrSet, consOrSetComponentGroup); - if (consOrSetComponentGroup.get(0) instanceof Term) { - ((Term) consOrSetComponentGroup.get(0)).setType(newListType); - updateExps.put(System.identityHashCode(consOrSetComponentGroup.get(0)), consOrSetComponentGroup.get(0)); - } - if (consOrSetComponentGroup.get(2) instanceof Variable) { - ((Variable) consOrSetComponentGroup.get(2)).setType(newListType); - updateExps.put(System.identityHashCode(consOrSetComponentGroup.get(2)), consOrSetComponentGroup.get(2)); - } else if (consOrSetComponentGroup.get(2) instanceof Term) { - ((Term) consOrSetComponentGroup.get(2)).setType(newListType); - updateExps.put(System.identityHashCode(consOrSetComponentGroup.get(2)), consOrSetComponentGroup.get(2)); - } - } - break; - case 2: - listType = consOrSet.get(System.identityHashCode(consOrSetComponentGroup)); - expType = getExpTypeIfUpdatable(listType, exp); - if (expType != null) { - consOrSet.put(System.identityHashCode(consOrSetComponentGroup), expType); - Map updateExps = getUpdateSet(updateFromConsOrSet, consOrSetComponentGroup); - if (consOrSetComponentGroup.get(0) instanceof Term) { - ((Term) consOrSetComponentGroup.get(0)).setType(expType); - updateExps.put(System.identityHashCode(consOrSetComponentGroup.get(0)), consOrSetComponentGroup.get(0)); - } - compType = listComponentTypes.get(expType); - if (consOrSetComponentGroup.get(1) != null && consOrSetComponentGroup.get(1) instanceof Variable) { - ((Variable) consOrSetComponentGroup.get(1)).setType(compType); - updateExps.put(System.identityHashCode(consOrSetComponentGroup.get(1)), consOrSetComponentGroup.get(1)); - } else if (consOrSetComponentGroup.get(1) != null && consOrSetComponentGroup.get(1) instanceof Term) { - ((Term) consOrSetComponentGroup.get(1)).setType(compType); - updateExps.put(System.identityHashCode(consOrSetComponentGroup.get(1)), consOrSetComponentGroup.get(1)); - } - } } } } - + private static void updateTupleTypes(Expression exp, Map tuple, - Map>> expToTuple, Map> updateFromTuple) { + Map>> expToTuple, Map> updateFromTuple) { Set> tupleComponentGroups = expToTuple.get(System.identityHashCode(exp)); if (tupleComponentGroups == null) return; - for (List tupleComponentGroup: tupleComponentGroups) { + 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) { @@ -1272,13 +1755,13 @@ if (compareTypes(compType, componentTypes.get(i - 1))) { ((Variable) compExp).setType(componentTypes.get(i - 1)); updateExps.put(System.identityHashCode(compExp), compExp); - } + } } else { // for insert if (compareTypes(compType, newTupleType)) { ((Variable) compExp).setType(newTupleType); updateExps.put(System.identityHashCode(compExp), compExp); - } + } } } } else if (compExp instanceof Term) { @@ -1312,6 +1795,7 @@ } } } else { + // exp is a tuple's component. Type tupleType = tuple.get(System.identityHashCode(tupleComponentGroup)); List componentTypes = tupleComponentTypes.get(tupleType); boolean updated = false; @@ -1374,14 +1858,15 @@ } } } - + private static void updatePairTypes(Expression exp, Map pair, - Map>> expToPair, Map> updateFromPair) { + Map>> expToPair, Map> updateFromPair) { Set> pairComponentGroups = expToPair.get(System.identityHashCode(exp)); if (pairComponentGroups == null) return; - for (List pairComponentGroup: pairComponentGroups) { + 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 +1890,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); @@ -1429,12 +1915,13 @@ } private static void updateMapTypes(Expression exp, Map map, - Map>> expToMap, Map> updateFromMap) { + Map>> expToMap, Map> updateFromMap) { Set> mapComponentGroups = expToMap.get(System.identityHashCode(exp)); if (mapComponentGroups == null) return; - for (List mapComponentGroup: mapComponentGroups) { + 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) { @@ -1458,10 +1945,10 @@ } // Propagate an update of a map's type to another map's type. Expression compExp = null; - if (idx == 0 && mapComponentGroup.size() == 4) { // for insert + if (idx == 0 && mapComponentGroup.size() == 4) { // for insert compExp = mapComponentGroup.get(3); } else if (idx == 3) { - compExp = mapComponentGroup.get(0); + compExp = mapComponentGroup.get(0); } if (compExp != null) { if (compExp instanceof Variable) { @@ -1478,6 +1965,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); @@ -1500,7 +1988,7 @@ ((Term) mapExp).setType(newMapType); updateExps.put(System.identityHashCode(mapExp), mapExp); } - if (mapComponentGroup.size() == 4) { // for insert + if (mapComponentGroup.size() == 4) { // for insert mapExp = mapComponentGroup.get(3); if (mapExp instanceof Variable) { ((Variable) mapExp).setType(newMapType); @@ -1508,7 +1996,7 @@ } else if (mapExp instanceof Term) { ((Term) mapExp).setType(newMapType); updateExps.put(System.identityHashCode(mapExp), mapExp); - } + } } map.put(System.identityHashCode(mapComponentGroup), newMapType); } @@ -1516,6 +2004,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()); @@ -1535,7 +2135,7 @@ } return newListType; } - + private static Type createNewTupleType(List componentTypes, Type parentTupleType) { String implTypeName = "AbstractMap.SimpleEntry<>"; String interfaceTypeName = "Map.Entry<$x>"; @@ -1570,7 +2170,7 @@ } return newTupleType; } - + private static Type createNewMapType(List componentTypes, Type parentMapType) { String implTypeName = "HashMap<>"; String interfaceTypeName = "Map<$x, $y>"; @@ -1597,17 +2197,48 @@ } 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); } } return childrenTypes; } - + private static String getImplementationTypeName(Type type) { if (type == null) return "Object"; @@ -1616,7 +2247,7 @@ return wrapperType; return type.getImplementationTypeName(); } - + private static String getInterfaceTypeName(Type type) { if (type == null) return "Object"; @@ -1625,9 +2256,8 @@ return wrapperType; 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<>(); @@ -1635,7 +2265,7 @@ } return updatedExps; } - + private static Type getExpTypeIfUpdatable(Type originalType, Expression newExp) { Type expType = null; if (newExp instanceof Term) { @@ -1648,14 +2278,14 @@ } return null; } - + /** * Is an given original type an ancestor of a given new type? - * + * * @param originalType original type * @param newType new type (may not have been registered) * @return true: if the original type equals to the new type or is an ancestor - * of the new type, false: otherwise + * of the new type, false: otherwise */ private static boolean compareTypes(Type originalType, Type newType) { if (originalType == null) return true; @@ -1668,8 +2298,9 @@ List newCompTypes = mapComponentTypes.get(newType); if (originalCompTypes == null) return true; for (int i = 0; i < originalCompTypes.size(); i++) { - if (originalCompTypes.get(i) != null && - (newCompTypes.get(i) == null || !originalCompTypes.get(i).isAncestorOf(newCompTypes.get(i)))) return false; + if (originalCompTypes.get(i) != null && + (newCompTypes.get(i) == null || !originalCompTypes.get(i).isAncestorOf(newCompTypes.get(i)))) + return false; } return true; } @@ -1683,19 +2314,22 @@ for (int i = 0; i < originalCompTypes.size(); i++) { if (originalCompTypes.get(i) != null) { if (DataConstraintModel.typeTuple.isAncestorOf(originalCompTypes.get(i))) { - Type tupleType = originalCompTypes.remove(i); - for (Type t: tupleComponentTypes.get(tupleType)) { + // Unfold the nested tuple's types. + Type tupleType = originalCompTypes.remove(i); + for (Type t : tupleComponentTypes.get(tupleType)) { originalCompTypes.add(t); } } if (newCompTypes.size() - 1 < i) return false; if (newCompTypes.get(i) != null && DataConstraintModel.typeTuple.isAncestorOf(newCompTypes.get(i))) { - Type tupleType = newCompTypes.remove(i); - for (Type t: tupleComponentTypes.get(tupleType)) { + // Unfold the nested tuple's types. + Type tupleType = newCompTypes.remove(i); + for (Type t : tupleComponentTypes.get(tupleType)) { newCompTypes.add(t); } } - if (newCompTypes.get(i) == null || !originalCompTypes.get(i).isAncestorOf(newCompTypes.get(i))) return false; + if (newCompTypes.get(i) == null || !originalCompTypes.get(i).isAncestorOf(newCompTypes.get(i))) + return false; } } return true; @@ -1704,10 +2338,44 @@ && DataConstraintModel.typeList.isAncestorOf(newType)) { Type originalCompType = listComponentTypes.get(originalType); Type newCompType = listComponentTypes.get(newType); - if (originalCompType != null && (newCompType == null || !originalCompType.isAncestorOf(newCompType))) return false; + 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; } + + private static interface IGroupExpressionsByResource { + public void groupForChannel(Channel ch); + } + + private static interface IGroupExpressionsByVariable { + public void groupForChannel(Channel ch); + } + + private static interface IGroupExpressionsByMessage { + public void groupForChannel(Channel rootCh, Channel ch); + } + + private static interface IExtractConstraintsOnExpressionsInTerm { + public void extractForChannel(Channel ch); + } + + private static interface IExtractConstraintsOnPathParametersAndResources { + public void extractForChannel(Channel ch); + } } diff --git a/AlgebraicDataflowArchitectureModel/src/algorithms/Validation.java b/AlgebraicDataflowArchitectureModel/src/algorithms/Validation.java index 1744af5..8ee7bc4 100644 --- a/AlgebraicDataflowArchitectureModel/src/algorithms/Validation.java +++ b/AlgebraicDataflowArchitectureModel/src/algorithms/Validation.java @@ -1,13 +1,13 @@ package algorithms; -import java.util.*; +import models.Node; +import models.dataFlowModel.DataTransferModel; -import models.*; -import models.dataFlowModel.*; +import java.util.*; /** * Validation checker for data transfer model - * + * * @author Nitta * */ @@ -18,7 +18,7 @@ private static Map ids = new HashMap<>(); private static Map lowlink = new HashMap<>(); private static Map onStack = new HashMap<>(); - + static public boolean checkUpdateConflict(DataTransferModel model) { init(); boolean check = true; @@ -29,7 +29,7 @@ } return strong.size() == 0 && check; } - + static private void init() { index = 0; stack = new ArrayDeque<>(); @@ -38,14 +38,14 @@ lowlink = new HashMap<>(); onStack = new HashMap<>(); } - + static private void traverseStronglyConnectedComponents(Node node) { ids.put(node, index); lowlink.put(node, index); index++; stack.push(node); onStack.put(node, true); - + for (Node n : node.getSuccessors()) { if (lowlink.containsKey(n)) { traverseStronglyConnectedComponents(n); diff --git a/AlgebraicDataflowArchitectureModel/src/application/ApplicationMenuBar.java b/AlgebraicDataflowArchitectureModel/src/application/ApplicationMenuBar.java index 38e2370..70ae0dc 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/ApplicationMenuBar.java +++ b/AlgebraicDataflowArchitectureModel/src/application/ApplicationMenuBar.java @@ -1,35 +1,18 @@ package application; -import javax.swing.JMenu; -import javax.swing.JMenuBar; - -import application.actions.CircleLayoutAction; -import application.actions.DAGLayoutAction; -import application.actions.DeleteAction; -import application.actions.ExitAction; -import application.actions.JavaPrototypeGenerateAction; -import application.actions.JerseyPrototypeGenerateAction; -import application.actions.NewChannelAction; -import application.actions.NewFormulaChannelAction; -import application.actions.NewIOChannelAction; -import application.actions.NewModelAction; -import application.actions.NewResourceAction; -import application.actions.OpenAction; -import application.actions.SaveAction; -import application.actions.SaveAsAction; -import application.actions.TreeLayoutAction; -import application.actions.ZoomInAction; -import application.actions.ZoomOutAction; +import application.actions.*; import application.editor.Editor; +import javax.swing.*; + public class ApplicationMenuBar extends JMenuBar { private static final long serialVersionUID = 4811536194182272888L; - + private ApplicationWindow applicationWindow = null; private NewResourceAction newResourceAction = null; private NewChannelAction newChannelAction = null; - private NewIOChannelAction newIOChannelAction = null; + private NewEventChannelAction newIOChannelAction = null; private NewFormulaChannelAction newFormulaChannelAction = null; private DeleteAction deleteAction = null; private JavaPrototypeGenerateAction javaPrototypeGenerateAction = null; @@ -37,18 +20,22 @@ private DAGLayoutAction dagLayoutAction = null; private TreeLayoutAction treeLayoutAction = null; private CircleLayoutAction circleLayoutAction = null; - + private ShowNavigationAction showNavigationAction; + private ShowFlowLayerWindowAction showFlowLayerWindowAction; + //private SimulateAction simulateAction = null; + + public ApplicationMenuBar(ApplicationWindow applicationWindow) { this.applicationWindow = applicationWindow; JMenu newMenu = new JMenu("New"); newMenu.add(new NewModelAction(applicationWindow)); - + newMenu.add(newResourceAction = new NewResourceAction(applicationWindow.getEditor())); newMenu.add(newChannelAction = new NewChannelAction(applicationWindow.getEditor())); - newMenu.add(newIOChannelAction = new NewIOChannelAction(applicationWindow.getEditor())); + newMenu.add(newIOChannelAction = new NewEventChannelAction(applicationWindow.getEditor())); newMenu.add(newFormulaChannelAction = new NewFormulaChannelAction(applicationWindow.getEditor())); - + JMenu menu = null; menu = add(new JMenu("File")); menu.add(newMenu); @@ -58,29 +45,36 @@ menu.add(new SaveAsAction(applicationWindow)); menu.addSeparator(); menu.add(new ExitAction()); - + menu = add(new JMenu("Edit")); menu.add(deleteAction = new DeleteAction(applicationWindow.getEditor())); - - + + menu = add(new JMenu("Layout")); - menu.add(dagLayoutAction = new DAGLayoutAction(applicationWindow.getEditor())); - menu.add(treeLayoutAction = new TreeLayoutAction(applicationWindow.getEditor())); - menu.add(circleLayoutAction = new CircleLayoutAction(applicationWindow.getEditor())); - + menu.add(dagLayoutAction = new DAGLayoutAction(applicationWindow.getEditor())); + menu.add(treeLayoutAction = new TreeLayoutAction(applicationWindow.getEditor())); + menu.add(circleLayoutAction = new CircleLayoutAction(applicationWindow.getEditor())); + menu = add(new JMenu("View")); 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())); + + menu = add(new JMenu("Window")); + menu.add(showNavigationAction = new ShowNavigationAction(applicationWindow)); + menu.add(showFlowLayerWindowAction = new ShowFlowLayerWindowAction(applicationWindow)); } - + public Editor getEditor() { return applicationWindow.getEditor(); } - + public void setEditor(Editor editor) { newResourceAction.setEditor(editor); newChannelAction.setEditor(editor); diff --git a/AlgebraicDataflowArchitectureModel/src/application/ApplicationWindow.java b/AlgebraicDataflowArchitectureModel/src/application/ApplicationWindow.java index 63324d0..8983a22 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/ApplicationWindow.java +++ b/AlgebraicDataflowArchitectureModel/src/application/ApplicationWindow.java @@ -1,34 +1,27 @@ package application; -import java.util.ArrayList; -import java.util.List; - -import javax.swing.JFrame; - -import com.mxgraph.model.mxCell; +import application.editor.Editor; +import application.views.FlowLayerWindow; +import application.views.NavigationWindow; import com.mxgraph.model.mxGeometry; -import com.mxgraph.model.mxGraphModel; -import com.mxgraph.swing.mxGraphComponent; import com.mxgraph.swing.handler.mxRubberband; -import com.mxgraph.swing.view.mxICellEditor; -import com.mxgraph.util.mxEvent; -import com.mxgraph.util.mxEventObject; -import com.mxgraph.util.mxEventSource.mxIEventListener; +import com.mxgraph.swing.mxGraphComponent; import com.mxgraph.view.mxGraph; -import application.editor.Editor; -import application.editor.DataTransferModelingCellEditor; +import javax.swing.*; public class ApplicationWindow extends JFrame { private static final long serialVersionUID = -8690140317781055614L; public static final String title = "Visual Modeling Tool"; - private Editor editor = null; - private mxGraph graph = null; - private mxGraphComponent graphComponent = null; + private Editor editor; + private final mxGraph graph; + private final mxGraphComponent graphComponent; - private ApplicationMenuBar menuBar = null; - + private final ApplicationMenuBar menuBar; + private final NavigationWindow navigationWindow; + private final FlowLayerWindow showFlowLayerWindow; + public ApplicationWindow() { setTitle(title); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); @@ -36,7 +29,6 @@ this.graph = new mxGraph() { public boolean isPort(Object cell) { mxGeometry geo = getCellGeometry(cell); - return (geo != null) ? geo.isRelative() : false; } @@ -49,49 +41,47 @@ this.editor = new Editor(graphComponent); - 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); - + menuBar = new ApplicationMenuBar(this); setJMenuBar(menuBar); setSize(870, 640); + setLocationRelativeTo(null); // Center on screen + + navigationWindow = new NavigationWindow(this, editor); + navigationWindow.setVisible(true); + + showFlowLayerWindow = new FlowLayerWindow(this); + showFlowLayerWindow.setVisible(false); + + editor.addStageChangeListener(navigationWindow); + editor.addStageChangeListener(showFlowLayerWindow); } - + public mxGraph getGraph() { return graph; } - + public mxGraphComponent getGraphComponent() { return graphComponent; } - + public Editor getEditor() { return editor; } - + public void setEditor(Editor editor) { this.editor = editor; } - -} + + public void showNavigationWindow() { + navigationWindow.setVisible(true); + } + + public void showSwitchLayerWindow() { + showFlowLayerWindow.setVisible(true); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/AbstractEditorAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/AbstractEditorAction.java index 8b09b17..1a07e72 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/actions/AbstractEditorAction.java +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/AbstractEditorAction.java @@ -1,23 +1,19 @@ package application.actions; -import javax.swing.AbstractAction; -import javax.swing.Icon; - -import com.mxgraph.view.mxGraph; - import application.editor.Editor; +import javax.swing.*; + public abstract class AbstractEditorAction extends AbstractAction { - + protected Editor editor; - + public AbstractEditorAction(String name, Editor editor) { super(name); this.editor = editor; } - + public void setEditor(Editor editor) { this.editor = editor; } - } \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/AbstractPopupAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/AbstractPopupAction.java new file mode 100644 index 0000000..e595801 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/AbstractPopupAction.java @@ -0,0 +1,27 @@ +package application.actions; + +import com.mxgraph.model.mxCell; +import com.mxgraph.swing.mxGraphComponent; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public abstract class AbstractPopupAction extends AbstractAction { + protected mxGraphComponent graphComponent; + protected mxCell targetCell; + + public AbstractPopupAction(final String propName, final mxCell targetCell, final mxGraphComponent graphComponent) { + super(propName); + this.graphComponent = graphComponent; + this.targetCell = targetCell; + } + + public void updateTargetCell(final mxCell targetCell) { + this.targetCell = targetCell; + } + + @Override + public void actionPerformed(ActionEvent e) { + // TODO: Logging + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/AbstractSystemAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/AbstractSystemAction.java index f734ae0..3d1c26b 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/actions/AbstractSystemAction.java +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/AbstractSystemAction.java @@ -1,23 +1,20 @@ package application.actions; -import java.awt.event.ActionEvent; - -import javax.swing.AbstractAction; - import application.ApplicationWindow; -import application.editor.Editor; + +import javax.swing.*; public abstract class AbstractSystemAction extends AbstractAction { - + protected ApplicationWindow frame; - + public AbstractSystemAction(String name, ApplicationWindow frame) { super(name); this.frame = frame; } - + public void setFrame(ApplicationWindow frame) { this.frame = frame; } - + } diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/AbstractViewerAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/AbstractViewerAction.java index 422c537..8787840 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/actions/AbstractViewerAction.java +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/AbstractViewerAction.java @@ -1,15 +1,13 @@ package application.actions; -import java.awt.event.ActionEvent; - -import javax.swing.AbstractAction; - import com.mxgraph.swing.mxGraphComponent; +import javax.swing.*; + public abstract class AbstractViewerAction extends AbstractAction { - + protected mxGraphComponent graphComponent = null; - + public AbstractViewerAction(String name, mxGraphComponent graphComponent) { super(name); this.graphComponent = graphComponent; diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/CircleLayoutAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/CircleLayoutAction.java index ba643bc..13a5a44 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/actions/CircleLayoutAction.java +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/CircleLayoutAction.java @@ -1,18 +1,18 @@ package application.actions; -import java.awt.event.ActionEvent; - import application.editor.Editor; -public class CircleLayoutAction extends AbstractEditorAction { +import java.awt.event.ActionEvent; +public class CircleLayoutAction extends AbstractEditorAction { + public CircleLayoutAction(Editor editor) { super("Circle Layout", editor); } - + @Override public void actionPerformed(ActionEvent e) { editor.setCircleLayout(); } - + } diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/DAGLayoutAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/DAGLayoutAction.java index 8dcc54f..8b5113e 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/actions/DAGLayoutAction.java +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/DAGLayoutAction.java @@ -1,11 +1,11 @@ package application.actions; -import java.awt.event.ActionEvent; - import application.editor.Editor; -public class DAGLayoutAction extends AbstractEditorAction { +import java.awt.event.ActionEvent; +public class DAGLayoutAction extends AbstractEditorAction { + public DAGLayoutAction(Editor editor) { super("DAG Layout", editor); } @@ -14,5 +14,5 @@ public void actionPerformed(ActionEvent e) { editor.setDAGLayout(); } - + } diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/DeleteAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/DeleteAction.java index 23225f1..8ac4dfc 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/actions/DeleteAction.java +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/DeleteAction.java @@ -1,23 +1,23 @@ package application.actions; -import java.awt.event.ActionEvent; - import application.editor.Editor; -public class DeleteAction extends AbstractEditorAction { +import java.awt.event.ActionEvent; +public class DeleteAction extends AbstractEditorAction { + /** - * + * */ private static final long serialVersionUID = -4410145389391154784L; - + public DeleteAction(Editor editor) { super("Delete", editor); } - + @Override public void actionPerformed(ActionEvent e) { editor.delete(); } - + } diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/ExitAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/ExitAction.java index 4069533..97de52e 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/actions/ExitAction.java +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/ExitAction.java @@ -1,22 +1,21 @@ package application.actions; +import javax.swing.*; import java.awt.event.ActionEvent; -import javax.swing.AbstractAction; - public class ExitAction extends AbstractAction { /** - * + * */ private static final long serialVersionUID = -8733766417378036850L; - + public ExitAction() { super("Exit"); } - + @Override public void actionPerformed(ActionEvent e) { System.exit(0); } - + } diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/JavaPrototypeGenerateAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/JavaPrototypeGenerateAction.java index 83acc69..46bf35c 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/actions/JavaPrototypeGenerateAction.java +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/JavaPrototypeGenerateAction.java @@ -1,38 +1,33 @@ package application.actions; +import algorithms.TypeInference; +import application.editor.Editor; +import code.ast.CompilationUnit; +import generators.*; +import models.dataConstraintModel.ResourceHierarchy; +import models.dataFlowModel.DataFlowGraph; +import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.ModelExtension; + +import javax.swing.*; import java.awt.event.ActionEvent; -import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; -import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; -import javax.swing.JFileChooser; - -import algorithms.*; -import application.editor.Editor; -import code.ast.*; -import generators.DataTransferMethodAnalyzer; -import generators.JavaCodeGenerator; -import generators.JavaMethodBodyGenerator; -import models.dataConstraintModel.ResourcePath; -import models.dataFlowModel.DataTransferModel; -import models.dataFlowModel.ModelExtension; -import models.dataFlowModel.DataFlowGraph; - public class JavaPrototypeGenerateAction extends AbstractEditorAction { /** - * + * */ private static final long serialVersionUID = -3694103632055735068L; - + private String lastDir = null; - + public JavaPrototypeGenerateAction(Editor editor) { super("Generate Plain Java Prototype", editor); } - + @Override public void actionPerformed(ActionEvent e) { DataFlowGraph graph = editor.getDataFlowGraph(); @@ -45,24 +40,25 @@ 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; } } if (!exist) { - JavaCodeGenerator.setMainTypeName(mainTypeName); // use model's file name as the main type's name. + JavaCodeGenerator.setMainTypeName(mainTypeName); // use model's file name as the main type's name. } else { - JavaCodeGenerator.resetMainTypeName(); // use the default main type's name. + JavaCodeGenerator.resetMainTypeName(); // use the default main type's name. } - editor.setCodes(JavaMethodBodyGenerator.doGenerate(graph, model, JavaCodeGenerator.doGenerate(graph, model))); +// editor.setCodes(JavaMethodBodyGenerator.doGenerate(graph, model, JavaCodeGenerator.doGenerate(graph, model))); + editor.setCodes(new CodeGeneratorFromDataFlowGraph().generateCode(model, graph, new StandaloneSpecific(), new JavaSpecific())); ModelExtension.recoverModel(model); for (CompilationUnit file : editor.getCodes()) { System.out.println(file); } - String wd = (lastDir != null) ? lastDir : System.getProperty("user.dir"); + String wd = (lastDir != null) ? lastDir : System.getProperty("user.dir"); JFileChooser fc = new JFileChooser(wd); fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); int rc = fc.showSaveDialog(null); @@ -74,7 +70,7 @@ } } } - + private void save(File dir, CompilationUnit cu) { File javaFile = new File(dir.getPath(), cu.getFileName()); try { @@ -85,5 +81,5 @@ e.printStackTrace(); } } - + } diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/JerseyPrototypeGenerateAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/JerseyPrototypeGenerateAction.java index 766c384..2a7cc25 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/actions/JerseyPrototypeGenerateAction.java +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/JerseyPrototypeGenerateAction.java @@ -1,39 +1,33 @@ package application.actions; +import algorithms.TypeInference; +import application.editor.Editor; +import code.ast.CompilationUnit; +import generators.*; +import models.dataConstraintModel.ResourceHierarchy; +import models.dataFlowModel.DataFlowGraph; +import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.ModelExtension; + +import javax.swing.*; import java.awt.event.ActionEvent; -import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; -import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; -import javax.swing.JFileChooser; - -import algorithms.*; -import application.editor.Editor; -import code.ast.*; -import generators.DataTransferMethodAnalyzer; -import generators.JerseyCodeGenerator; -import generators.JerseyMethodBodyGenerator; -import models.algebra.Type; -import models.dataConstraintModel.ResourcePath; -import models.dataFlowModel.DataTransferModel; -import models.dataFlowModel.ModelExtension; -import models.dataFlowModel.DataFlowGraph; - public class JerseyPrototypeGenerateAction extends AbstractEditorAction { /** - * + * */ private static final long serialVersionUID = 1117065654665887436L; private String lastDir = null; - + public JerseyPrototypeGenerateAction(Editor editor) { super("Generate JAX-RS Prototype", editor); } - + @Override public void actionPerformed(ActionEvent e) { DataFlowGraph graph = editor.getDataFlowGraph(); @@ -46,24 +40,25 @@ 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; } } if (!exist) { - JerseyCodeGenerator.setMainTypeName(mainTypeName); // use model's file name as the main type's name. + JerseyCodeGenerator.setMainTypeName(mainTypeName); // use model's file name as the main type's name. } else { - JerseyCodeGenerator.resetMainTypeName(); // use the default main type's name. + JerseyCodeGenerator.resetMainTypeName(); // use the default main type's name. } - editor.setCodes(JerseyMethodBodyGenerator.doGenerate(graph, model, JerseyCodeGenerator.doGenerate(graph, model))); +// editor.setCodes(JerseyMethodBodyGenerator.doGenerate(graph, model, JerseyCodeGenerator.doGenerate(graph, model))); + editor.setCodes(new CodeGeneratorFromDataFlowGraph().generateCode(model, graph, new JerseySpecific(), new JavaSpecific())); ModelExtension.recoverModel(model); for (CompilationUnit file : editor.getCodes()) { System.out.println(file); } - String wd = (lastDir != null) ? lastDir : System.getProperty("user.dir"); + String wd = (lastDir != null) ? lastDir : System.getProperty("user.dir"); JFileChooser fc = new JFileChooser(wd); fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); int rc = fc.showSaveDialog(null); @@ -75,7 +70,7 @@ } } } - + private void save(File dir, CompilationUnit cu) { File javaFile = new File(dir.getPath(), cu.getFileName()); try { @@ -86,5 +81,5 @@ e.printStackTrace(); } } - + } diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/NewChannelAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/NewChannelAction.java index 6ee664c..6535f18 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/actions/NewChannelAction.java +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/NewChannelAction.java @@ -1,28 +1,26 @@ package application.actions; +import application.editor.Editor; + +import javax.swing.*; import java.awt.event.ActionEvent; -import javax.swing.JOptionPane; - -import application.editor.Editor; -import models.dataFlowModel.DataTransferChannel; - public class NewChannelAction extends AbstractEditorAction { - + /** - * + * */ private static final long serialVersionUID = 5979007029473101802L; public NewChannelAction(Editor editor) { super("Channel...", editor); } - + @Override public void actionPerformed(ActionEvent e) { String channelName = JOptionPane.showInputDialog("Channel Name:"); if (channelName == null) return; - editor.addChannel(new DataTransferChannel(channelName)); + editor.addChannel(channelName); } - + } diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/NewEventChannelAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/NewEventChannelAction.java new file mode 100644 index 0000000..315b07a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/NewEventChannelAction.java @@ -0,0 +1,26 @@ +package application.actions; + +import application.editor.Editor; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class NewEventChannelAction extends AbstractEditorAction { + + /** + * + */ + private static final long serialVersionUID = -1657072017390171313L; + + public NewEventChannelAction(Editor editor) { + super("Event Channel...", editor); + } + + @Override + public void actionPerformed(ActionEvent e) { + String channelName = JOptionPane.showInputDialog("Event Channel Name:"); + if (channelName == null) return; + editor.addEventChannel(channelName); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/NewFormulaChannelAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/NewFormulaChannelAction.java index 068c758..6487bf5 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/actions/NewFormulaChannelAction.java +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/NewFormulaChannelAction.java @@ -1,41 +1,27 @@ package application.actions; -import java.awt.BorderLayout; -import java.awt.Container; -import java.awt.FlowLayout; -import java.awt.Frame; -import java.awt.GridLayout; +import application.editor.Editor; + +import javax.swing.*; +import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import javax.swing.BoxLayout; -import javax.swing.JButton; -import javax.swing.JDialog; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JTextField; - -import application.ApplicationWindow; -import application.editor.Editor; -import models.visualModel.FormulaChannel; - public class NewFormulaChannelAction extends AbstractEditorAction implements ActionListener { /** - * + * */ private static final long serialVersionUID = 5345875219049178252L; public NewFormulaChannelAction(Editor editor) { super("FormulaChannel...", editor); } - + @Override public void actionPerformed(ActionEvent e) { JPanel panel = new JPanel(); - GridLayout layout = new GridLayout(2,2); + GridLayout layout = new GridLayout(2, 2); panel.setLayout(layout); layout.setVgap(5); layout.setHgap(20); @@ -45,18 +31,18 @@ panel.add(new JLabel("Symbol:")); JTextField symbolText = new JTextField(); panel.add(symbolText); - + int r = JOptionPane.showConfirmDialog( - null, - panel, - "New Formula Channel", - JOptionPane.OK_CANCEL_OPTION, - JOptionPane.QUESTION_MESSAGE); + null, + panel, + "New Formula Channel", + JOptionPane.OK_CANCEL_OPTION, + JOptionPane.QUESTION_MESSAGE); String channelName = channelText.getText(); String symbol = symbolText.getText(); - if(r == JOptionPane.OK_OPTION) { - editor.addFormulaChannel(new FormulaChannel(channelName, editor.getModel().getSymbol(symbol))); + if (r == JOptionPane.OK_OPTION) { + editor.addFormulaChannel(channelName, editor.getModel().getSymbol(symbol)); } } } diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/NewIOChannelAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/NewIOChannelAction.java deleted file mode 100644 index 85e48de..0000000 --- a/AlgebraicDataflowArchitectureModel/src/application/actions/NewIOChannelAction.java +++ /dev/null @@ -1,28 +0,0 @@ -package application.actions; - -import java.awt.event.ActionEvent; - -import javax.swing.JOptionPane; - -import application.editor.Editor; -import models.dataFlowModel.DataTransferChannel; - -public class NewIOChannelAction extends AbstractEditorAction { - - /** - * - */ - private static final long serialVersionUID = -1657072017390171313L; - - public NewIOChannelAction(Editor editor) { - super("I/O Channel", editor); - } - - @Override - public void actionPerformed(ActionEvent e) { - String channelName = JOptionPane.showInputDialog("I/O Channel Name:"); - if (channelName == null) return; - editor.addIOChannel(new DataTransferChannel(channelName)); - } - -} diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/NewModelAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/NewModelAction.java index 517f525..e81e5d7 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/actions/NewModelAction.java +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/NewModelAction.java @@ -1,24 +1,23 @@ package application.actions; -import java.awt.event.ActionEvent; - import application.ApplicationWindow; -import application.editor.Editor; + +import java.awt.event.ActionEvent; public class NewModelAction extends AbstractSystemAction { /** - * + * */ private static final long serialVersionUID = 8484493203589724589L; - + public NewModelAction(ApplicationWindow frame) { super("Model", frame); } - + @Override public void actionPerformed(ActionEvent e) { frame.getEditor().clear(); - frame.setTitle(frame.title); + frame.setTitle(frame.title); } - + } diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/NewResourceAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/NewResourceAction.java index 50b1512..c322ad9 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/actions/NewResourceAction.java +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/NewResourceAction.java @@ -1,28 +1,41 @@ package application.actions; -import java.awt.event.ActionEvent; - -import javax.swing.JOptionPane; - import application.editor.Editor; -import models.dataConstraintModel.ResourcePath; +import models.dataFlowModel.ResourceNode; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.util.List; public class NewResourceAction extends AbstractEditorAction { - - /** - * - */ + private static final long serialVersionUID = -4439207504700741286L; - + public NewResourceAction(Editor editor) { super("Resource...", editor); } - + @Override public void actionPerformed(ActionEvent e) { - String resName = JOptionPane.showInputDialog("Resourece Name:"); - if (resName == null) return; - editor.addResourcePath(new ResourcePath(resName, 0)); + List selectedResNodes = editor.getSelectedResourceNodes(); + String initialName = ""; + if (selectedResNodes != null && selectedResNodes.size() == 1) { + initialName = selectedResNodes.get(0).getPrimaryResourcePath().toString(); + } + String resName = JOptionPane.showInputDialog("Resource Name:", initialName); + if (resName == null) { + return; + } + if (selectedResNodes == null || selectedResNodes.size() == 0) { + editor.addResourceNode(null, resName); + } else if (selectedResNodes.size() == 1) { + if (initialName.length() > 0 && resName.startsWith(initialName)) { + resName = resName.substring(initialName.length()); + if (resName.startsWith(".")) { + resName = resName.substring(1); + } + } + editor.addResourceNode(selectedResNodes.get(0), resName); + } } - } diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/OpenAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/OpenAction.java index d01c518..7e7ba3e 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/actions/OpenAction.java +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/OpenAction.java @@ -1,19 +1,16 @@ package application.actions; -import java.awt.event.ActionEvent; -import java.io.File; - -import javax.swing.JFileChooser; -import javax.swing.filechooser.FileFilter; -import javax.swing.filechooser.FileNameExtensionFilter; - import application.ApplicationWindow; import application.editor.Editor; +import javax.swing.*; +import javax.swing.filechooser.FileFilter; +import javax.swing.filechooser.FileNameExtensionFilter; +import java.awt.event.ActionEvent; +import java.io.File; + public class OpenAction extends AbstractSystemAction { - /** - * - */ + private static final long serialVersionUID = -8290761032629599683L; private String lastDir = null; @@ -21,42 +18,46 @@ public OpenAction(ApplicationWindow frame) { super("Open...", frame); } - + @Override public void actionPerformed(ActionEvent e) { Editor editor = frame.getEditor(); - if (editor != null) { - String wd = (lastDir != null) ? lastDir : System.getProperty("user.dir"); - - JFileChooser fc = new JFileChooser(wd); - - FileFilter model = new FileNameExtensionFilter("model","model"); - FileFilter dtram = new FileNameExtensionFilter("dtram", "dtram"); - - // Adds file filter for supported file format - FileFilter defaultFilter = new FileFilter() { - - public boolean accept(File file) { - String lcase = file.getName().toLowerCase(); - return lcase.endsWith(".model"); - } - - @Override - public String getDescription() { - return null; - } - }; - - fc.addChoosableFileFilter(defaultFilter); - fc.addChoosableFileFilter(model); - fc.addChoosableFileFilter(dtram); - int rc = fc.showDialog(null, "Open Model File"); - if (rc == JFileChooser.APPROVE_OPTION) { - lastDir = fc.getSelectedFile().getParent(); - editor.open(fc.getSelectedFile()); - frame.setTitle(frame.title + " - " + fc.getSelectedFile().getAbsolutePath()); - } + if (editor == null) { + return; + } + String wd = (lastDir != null) ? lastDir : System.getProperty("user.dir"); + + JFileChooser fc = buildFileChooser(wd); + int option = fc.showDialog(null, "Open Model File"); + if (option == JFileChooser.APPROVE_OPTION) { + lastDir = fc.getSelectedFile().getParent(); + editor.open(fc.getSelectedFile()); + frame.setTitle(ApplicationWindow.title + " - " + fc.getSelectedFile().getAbsolutePath()); } } - + + private static JFileChooser buildFileChooser(String wd) { + JFileChooser fc = new JFileChooser(wd); + + FileFilter model = new FileNameExtensionFilter("model", "model"); + FileFilter dtram = new FileNameExtensionFilter("dtram", "dtram"); + + // Adds file filter for supported file format + FileFilter defaultFilter = new FileFilter() { + public boolean accept(File file) { + String lowerCase = file.getName().toLowerCase(); + return lowerCase.endsWith(".model"); + } + + @Override + public String getDescription() { + return null; + } + }; + + fc.addChoosableFileFilter(defaultFilter); + fc.addChoosableFileFilter(model); + fc.addChoosableFileFilter(dtram); + return fc; + } } diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/SaveAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/SaveAction.java index 0b41331..2b12295 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/actions/SaveAction.java +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/SaveAction.java @@ -1,20 +1,20 @@ package application.actions; -import java.awt.event.ActionEvent; - import application.ApplicationWindow; import application.editor.Editor; +import java.awt.event.ActionEvent; + public class SaveAction extends AbstractSystemAction { /** - * + * */ private static final long serialVersionUID = 5660460585305281982L; - + public SaveAction(ApplicationWindow frame) { super("Save", frame); } - + @Override public void actionPerformed(ActionEvent e) { Editor editor = frame.getEditor(); @@ -22,5 +22,5 @@ editor.save(); } } - + } diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/SaveAsAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/SaveAsAction.java index 1a38b08..049d6a8 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/actions/SaveAsAction.java +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/SaveAsAction.java @@ -1,45 +1,43 @@ package application.actions; -import java.awt.event.ActionEvent; -import java.io.File; - -import javax.swing.AbstractAction; -import javax.swing.JFileChooser; -import javax.swing.filechooser.FileFilter; -import javax.swing.filechooser.FileNameExtensionFilter; - import application.ApplicationWindow; import application.editor.Editor; +import javax.swing.*; +import javax.swing.filechooser.FileFilter; +import javax.swing.filechooser.FileNameExtensionFilter; +import java.awt.event.ActionEvent; +import java.io.File; + public class SaveAsAction extends AbstractSystemAction { /** - * + * */ private static final long serialVersionUID = -2599502783032684084L; - + private String lastDir = null; - + public SaveAsAction(ApplicationWindow frame) { super("Save As...", frame); } - + @Override public void actionPerformed(ActionEvent e) { Editor editor = frame.getEditor(); if (editor != null) { - String wd = (lastDir != null) ? lastDir : System.getProperty("user.dir"); - + String wd = (lastDir != null) ? lastDir : System.getProperty("user.dir"); + JFileChooser fc = new JFileChooser(wd); - FileFilter model = new FileNameExtensionFilter("model","model"); + FileFilter model = new FileNameExtensionFilter("model", "model"); FileFilter dtram = new FileNameExtensionFilter("dtram", "dtram"); // Adds file filter for supported file format FileFilter defaultFilter = new FileFilter() { public boolean accept(File file) { String lcase = file.getName().toLowerCase(); - return lcase.endsWith(".model"); + return lcase.endsWith(".model"); } - + @Override public String getDescription() { return null; @@ -53,21 +51,19 @@ // choose a file extension from a dialog. if (rc == JFileChooser.APPROVE_OPTION) { - + // if extension filter is filled, then attaching extension by choosing filter. // but if it's not filled, then using default extension name. - String extension = ""; - if(fc.getFileFilter() instanceof FileNameExtensionFilter) { - FileNameExtensionFilter selectedFilter = (FileNameExtensionFilter)fc.getFileFilter(); + String extension = ""; + if (fc.getFileFilter() instanceof FileNameExtensionFilter) { + FileNameExtensionFilter selectedFilter = (FileNameExtensionFilter) fc.getFileFilter(); extension = "." + selectedFilter.getExtensions()[0].toString(); } lastDir = fc.getSelectedFile().getParent(); String fileName = fc.getSelectedFile().getAbsolutePath() + extension; - - // checking file duplicates - if(! (fc.getSelectedFile().exists())) editor.setCurFilePath(fileName); + editor.setCurFilePath(fileName); // overwriting file editor.save(); @@ -75,5 +71,5 @@ } } } - + } diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/ShowFlowLayerWindowAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/ShowFlowLayerWindowAction.java new file mode 100644 index 0000000..6a68bc0 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/ShowFlowLayerWindowAction.java @@ -0,0 +1,17 @@ +package application.actions; + +import application.ApplicationWindow; + +import java.awt.event.ActionEvent; + +public class ShowFlowLayerWindowAction extends AbstractSystemAction { + + public ShowFlowLayerWindowAction(ApplicationWindow frame) { + super(/*propName*/"Show FlowLayer", frame); + } + + @Override + public void actionPerformed(ActionEvent e) { + frame.showSwitchLayerWindow(); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/ShowNavigationAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/ShowNavigationAction.java new file mode 100644 index 0000000..396ff73 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/ShowNavigationAction.java @@ -0,0 +1,17 @@ +package application.actions; + +import application.ApplicationWindow; + +import java.awt.event.ActionEvent; + +public class ShowNavigationAction extends AbstractSystemAction { + + public ShowNavigationAction(ApplicationWindow frame) { + super("Show Navigation", frame); + } + + @Override + public void actionPerformed(ActionEvent e) { + frame.showNavigationWindow(); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/SimulateAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/SimulateAction.java new file mode 100644 index 0000000..d1c35bd --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/SimulateAction.java @@ -0,0 +1,29 @@ +package application.actions; + +import application.ApplicationWindow; +import application.simulator.SimulatorWindow; + +import java.awt.event.ActionEvent; + +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/actions/TreeLayoutAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/TreeLayoutAction.java index c2324c8..507b4bd 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/actions/TreeLayoutAction.java +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/TreeLayoutAction.java @@ -1,18 +1,18 @@ package application.actions; -import java.awt.event.ActionEvent; - import application.editor.Editor; -public class TreeLayoutAction extends AbstractEditorAction { +import java.awt.event.ActionEvent; +public class TreeLayoutAction extends AbstractEditorAction { + public TreeLayoutAction(Editor editor) { super("Tree Layout", editor); } - + @Override public void actionPerformed(ActionEvent e) { editor.setTreeLayout(); } - + } diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/ZoomInAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/ZoomInAction.java index d3b2c85..60f65d7 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/actions/ZoomInAction.java +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/ZoomInAction.java @@ -1,28 +1,22 @@ package application.actions; -import java.awt.event.ActionEvent; - -import javax.swing.AbstractAction; -import javax.swing.JOptionPane; - import com.mxgraph.swing.mxGraphComponent; -import com.mxgraph.util.mxResources; -import application.editor.Editor; +import java.awt.event.ActionEvent; public class ZoomInAction extends AbstractViewerAction { /** - * + * */ private static final long serialVersionUID = 6758532166946195926L; - + public ZoomInAction(mxGraphComponent graphComponent) { super("Zoom In", graphComponent); } - + @Override public void actionPerformed(ActionEvent e) { graphComponent.zoomIn(); } - + } diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/ZoomOutAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/ZoomOutAction.java index 032e8b2..a447c43 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/actions/ZoomOutAction.java +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/ZoomOutAction.java @@ -1,26 +1,22 @@ package application.actions; -import java.awt.event.ActionEvent; - -import javax.swing.AbstractAction; - import com.mxgraph.swing.mxGraphComponent; -import application.editor.Editor; +import java.awt.event.ActionEvent; public class ZoomOutAction extends AbstractViewerAction { /** - * + * */ private static final long serialVersionUID = 8657530769383486605L; - + public ZoomOutAction(mxGraphComponent graphComponent) { super("Zoom Out", graphComponent); } - + @Override public void actionPerformed(ActionEvent e) { graphComponent.zoomOut(); } - + } diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/DataTransferModelingCellEditor.java b/AlgebraicDataflowArchitectureModel/src/application/editor/DataTransferModelingCellEditor.java deleted file mode 100644 index b4d81a6..0000000 --- a/AlgebraicDataflowArchitectureModel/src/application/editor/DataTransferModelingCellEditor.java +++ /dev/null @@ -1,248 +0,0 @@ -package application.editor; - -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Rectangle; -import java.util.EventObject; -import java.util.List; - -import javax.swing.BorderFactory; -import javax.swing.JComboBox; -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.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 models.algebra.Expression; -import models.dataFlowModel.DataTransferModel; -import models.dataFlowModel.DataTransferChannel; -import models.dataFlowModel.PushPullAttribute; -import models.dataFlowModel.PushPullValue; -import models.visualModel.FormulaChannel; -import parser.Parser; -import parser.Parser.TokenStream; -import parser.exceptions.ExpectedRightBracket; - -public class DataTransferModelingCellEditor implements mxICellEditor { - public int DEFAULT_MIN_WIDTH = 70; - public int DEFAULT_MIN_HEIGHT = 30; - public double DEFAULT_MINIMUM_EDITOR_SCALE = 1; - - 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 Editor editor; - - public DataTransferModelingCellEditor(mxGraphComponent graphComponent, Editor editor) { - this.graphComponent = graphComponent; - this.editor = editor; - } - - @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)) { - DataTransferModel model = editor.getModel(); - DataTransferChannel ch = (DataTransferChannel) model.getChannel((String) ((mxCell) cell).getValue()); - if (ch == null) { - ch = (DataTransferChannel) model.getIOChannel((String) ((mxCell) cell).getValue()); - if(ch == null) { - //resource - return; - } - } - - if(ch instanceof FormulaChannel) { - - JPanel panel = new JPanel(); - JLabel label1 = new JLabel("Formula: "); - JLabel label2 = new JLabel("Source: "); - GridBagLayout layout = new GridBagLayout(); - panel.setLayout(layout); - GridBagConstraints gbc = new GridBagConstraints(); - - gbc.gridx = 0; - gbc.gridy = 0; - layout.setConstraints(label1, gbc); - panel.add(label1); - - gbc.gridx = 1; - gbc.gridy = 0; - JTextField formulaText = new JTextField(((FormulaChannel) ch).getFormula(),15); - layout.setConstraints(formulaText, gbc); - panel.add(formulaText); - - gbc.gridx = 0; - gbc.gridy = 1; - layout.setConstraints(label2, gbc); - panel.add(label2); - - gbc.gridx = 1; - gbc.gridy = 1; - JTextArea textArea = new JTextArea(ch.getSourceText(),7,15); - textArea.setEditable(false); - layout.setConstraints(textArea, gbc); - panel.add(textArea); - - int r = JOptionPane.showConfirmDialog( - null, // owner window - panel, // message - "Edit Formula Channel", // window's title - JOptionPane.OK_CANCEL_OPTION, // option (button types) - JOptionPane.QUESTION_MESSAGE); // message type (icon types) - if(r == JOptionPane.OK_OPTION) { - TokenStream stream = new Parser.TokenStream(); - Parser parser = new Parser(stream); - - String formula = formulaText.getText(); - stream.addLine(formula.split(Parser.EQUALS)[1]); - - try { - Expression exp = parser.parseTerm(stream, editor.getModel()); - ((FormulaChannel) ch).setFormula(formula); - ((FormulaChannel) ch).setFormulaTerm(exp); - } catch (ExpectedRightBracket e) { - e.printStackTrace(); - } - } - }else { - JPanel panel = new JPanel(); - JTextArea textArea = new JTextArea(ch.getSourceText(), 10, 20); - panel.add(textArea); - // JEditorPane panel = new JEditorPane("text/plain", ch.toString()); - // panel.setEditable(true); - int ret = JOptionPane.showConfirmDialog(null, panel, "Channel Code", JOptionPane.OK_CANCEL_OPTION); - if (ret == JOptionPane.OK_OPTION) { - editor.setChannelCode(ch, textArea.getText()); - } - } - 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(); - } - } - } - - @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/editor/Editor.java b/AlgebraicDataflowArchitectureModel/src/application/editor/Editor.java index b285ce0..878ef60 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/editor/Editor.java +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/Editor.java @@ -1,291 +1,298 @@ package application.editor; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -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 application.editor.stages.ControlFlowModelingStage; +import application.editor.stages.DataFlowModelingStage; +import application.editor.stages.PushPullSelectionStage; +import application.layouts.DAGLayout; +import code.ast.CompilationUnit; import com.mxgraph.layout.mxCircleLayout; import com.mxgraph.layout.mxCompactTreeLayout; import com.mxgraph.model.mxCell; -import com.mxgraph.model.mxGeometry; -import com.mxgraph.model.mxGraphModel; -import com.mxgraph.model.mxIGraphModel; import com.mxgraph.swing.mxGraphComponent; -import com.mxgraph.util.mxConstants; -import com.mxgraph.util.mxPoint; +import com.mxgraph.util.mxEvent; +import com.mxgraph.util.mxEventSource.mxIEventListener; import com.mxgraph.view.mxCellState; -import com.mxgraph.util.mxRectangle; import com.mxgraph.view.mxGraph; import com.mxgraph.view.mxGraphView; - -import algorithms.DataTransferModelAnalyzer; -import algorithms.Validation; -import application.layouts.*; -import code.ast.CompilationUnit; -import models.Edge; import models.EdgeAttribute; -import models.Node; +import models.algebra.Symbol; +import models.algebra.Variable; +import models.controlFlowModel.ControlFlowGraph; import models.dataConstraintModel.Channel; -import models.dataConstraintModel.ChannelMember; import models.dataConstraintModel.ResourcePath; -import models.dataFlowModel.DataTransferModel; -import models.dataFlowModel.DataTransferChannel; -import models.dataFlowModel.PushPullAttribute; -import models.dataFlowModel.DataFlowEdge; import models.dataFlowModel.DataFlowGraph; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.DataTransferModel; import models.dataFlowModel.ResourceNode; import models.visualModel.FormulaChannel; import parser.Parser; -import parser.Parser.TokenStream; -import parser.exceptions.ExpectedAssignment; -import parser.exceptions.ExpectedChannel; -import parser.exceptions.ExpectedChannelName; -import parser.exceptions.ExpectedEquals; -import parser.exceptions.ExpectedFormulaChannel; -import parser.exceptions.ExpectedGeometry; -import parser.exceptions.ExpectedInOrOutOrRefKeyword; -import parser.exceptions.ExpectedIoChannel; -import parser.exceptions.ExpectedLeftCurlyBracket; -import parser.exceptions.ExpectedModel; -import parser.exceptions.ExpectedNode; -import parser.exceptions.ExpectedRHSExpression; -import parser.exceptions.ExpectedResource; -import parser.exceptions.ExpectedRightBracket; -import parser.exceptions.ExpectedStateTransition; -import parser.exceptions.WrongLHSExpression; -import parser.exceptions.WrongRHSExpression; import parser.ParserDTRAM; +import parser.exceptions.*; + +import java.awt.event.MouseListener; +import java.io.*; +import java.util.ArrayList; +import java.util.List; public class Editor { - final int PORT_DIAMETER = 8; - final int PORT_RADIUS = PORT_DIAMETER / 2; - + protected DataTransferModel model = null; - protected mxGraph graph = null; - private mxGraphComponent graphComponent = null; - - protected DataFlowGraph dataFlowGraph = null; - + + protected mxGraph graph; + private final mxGraphComponent graphComponent; + + protected Stage curStage; + private final List stageChangeListeners; + + private mxIEventListener curChangeEventListener = null; + private MouseListener curMouseEventListener = null; + protected String curFileName = null; protected String curFilePath = null; protected ArrayList codes = null; - + + public static DataFlowModelingStage STAGE_DATA_FLOW_MODELING = null; + public static PushPullSelectionStage STAGE_PUSH_PULL_SELECTION = null; + public static ControlFlowModelingStage STAGE_CONTROL_FLOW_DELEGATION = null; + public Editor(mxGraphComponent graphComponent) { this.graphComponent = graphComponent; this.graph = graphComponent.getGraph(); - graphComponent.setCellEditor(new DataTransferModelingCellEditor(graphComponent, this)); + STAGE_DATA_FLOW_MODELING = new DataFlowModelingStage(graphComponent); + STAGE_PUSH_PULL_SELECTION = new PushPullSelectionStage(graphComponent); + STAGE_CONTROL_FLOW_DELEGATION = new ControlFlowModelingStage(graphComponent); + + graphComponent.setCellEditor(STAGE_DATA_FLOW_MODELING.createCellEditor(graphComponent)); + + curStage = STAGE_DATA_FLOW_MODELING; + + stageChangeListeners = new ArrayList<>(); } - - public mxGraph getGraph() { - return graph; + + public boolean canChange(Stage nextStage) { + return nextStage.canChangeFrom(curStage); } - - public mxGraphComponent getGraphComponent() { - return this.graphComponent; - } - - public DataTransferModel getModel() { - if (model == null) { - model = new DataTransferModel(); + + public boolean changeStage(Stage nextStage) { + if (!nextStage.canChangeFrom(curStage)) { + return false; } - return model; + nextStage.init(curStage); + graphComponent.setCellEditor(nextStage.createCellEditor(graphComponent)); + + // add listeners + // "curChangeEventListener" will be called when updating the mxGraph. + if (curChangeEventListener != null) { + graph.getModel().removeListener(curChangeEventListener); + } + curChangeEventListener = nextStage.createChangeEventListener(this); + if (curChangeEventListener != null) { + graph.getModel().addListener(mxEvent.CHANGE, curChangeEventListener); + } + + // A handler of a mouse event. + if (curMouseEventListener != null) { + graphComponent.getGraphControl().removeMouseListener(curMouseEventListener); + } + curMouseEventListener = nextStage.createMouseEventListener(this); + if (curMouseEventListener != null) { + graphComponent.getGraphControl().addMouseListener(curMouseEventListener); + } + curStage = nextStage; + notifyStageChangeListeners(); + return true; } - + + public void addStageChangeListener(IStageChangeListener stageChangeListener) { + stageChangeListeners.add(stageChangeListener); + } + + private void notifyStageChangeListeners() { + for (IStageChangeListener l : stageChangeListeners) { + l.stageChanged(curStage); + } + } + public DataFlowGraph getDataFlowGraph() { - if (dataFlowGraph == null) { - analyzeDataTransferModel(getModel()); + if (curStage instanceof PushPullSelectionStage) { + return ((PushPullSelectionStage) curStage).getDataFlowGraph(); + } else if (curStage instanceof ControlFlowModelingStage) { + return ((ControlFlowModelingStage) curStage).getControlFlowGraph().getDataFlowGraph(); } - return dataFlowGraph; + return null; } - - public DataFlowGraph analyzeDataTransferModel(DataTransferModel model) { - DataFlowGraph flowGraph = DataTransferModelAnalyzer.createDataFlowGraphWithStateStoringAttribute(model); - dataFlowGraph = DataTransferModelAnalyzer.annotateWithSelectableDataTransferAttiribute(flowGraph); - updateEdgeAttiributes(dataFlowGraph); - return dataFlowGraph; + + public ControlFlowGraph getControlFlowGraph() { + if (curStage instanceof ControlFlowModelingStage) { + return ((ControlFlowModelingStage) curStage).getControlFlowGraph(); + } + return null; } - - public void resetDataFlowGraph() { - dataFlowGraph = null; - } - - public void setDataFlowGraph(DataFlowGraph dataFlowGraph) { - this.dataFlowGraph = dataFlowGraph; - } - - public ArrayList getCodes() { - return codes; - } - - public void setCodes(ArrayList codes) { - this.codes = codes; - } - - public String getCurFileName() { - return curFileName; - } - - public String getCurFilePath() { - return curFilePath; - } - + public void setCurFilePath(String curFilePath) { this.curFilePath = curFilePath; this.curFileName = new File(curFilePath).getName(); } - + public void clear() { + // Force to change to the data-flow modeling stage + boolean stageChanged = changeStage(STAGE_DATA_FLOW_MODELING); + if (!stageChanged) { + return; + } + ((DataFlowModelingStage) curStage).clear(); + model = null; - ((mxGraphModel) graph.getModel()).clear(); - dataFlowGraph = null; curFilePath = null; curFileName = null; codes = null; } - + /** * Open a given file, parse the file, construct a DataFlowModel and a mxGraph + * * @param file given file - * @return a constructed DataFlowModel */ - public DataTransferModel open(File file) { - try { - - String extension =""; - if(file != null && file.exists()) { - // get a file's name - String name = file.getName(); - - // get a file's extension - extension = name.substring(name.lastIndexOf(".")); - } - if(extension.contains(".model")) { - openModel(file); - } else { - ParserDTRAM parserDTRAM = new ParserDTRAM(new BufferedReader(new FileReader(file))); - try { - model = parserDTRAM.doParseModel(); - graph = constructGraph(model); - parserDTRAM.doParseGeometry(graph); - curFilePath = file.getAbsolutePath(); - curFileName = file.getName(); - if (!Validation.checkUpdateConflict(model)) return null; - analyzeDataTransferModel(model); - return model; - } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefKeyword - | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression - | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedModel | ExpectedGeometry | ExpectedNode | ExpectedResource | ExpectedFormulaChannel | ExpectedIoChannel e) { - e.printStackTrace(); - } - } - } catch (FileNotFoundException e) { - e.printStackTrace(); + public void open(File file) { + // Force to change to data-modeling stage + boolean stageChanged = changeStage(STAGE_DATA_FLOW_MODELING); + if (!stageChanged) { + return; } - - return null; + if (file == null || !file.exists()) { + return; + } + // get a file's extension + String extension = file.getName().substring(file.getName().lastIndexOf(".")); + if (extension.contains(".model")) { + openModel(file); + } + if (extension.contains(".dtram")) { + openDTRAM(file); + } } - - public DataTransferModel openModel(File file) { + + private void openModel(File file) { try { - Parser parser = new Parser(new BufferedReader(new FileReader(file))); - - try { + try { // Parse the .model file. model = parser.doParse(); + // Update stage's model to new parsed one + if (curStage instanceof DataFlowModelingStage) { + ((DataFlowModelingStage) curStage).setModel(model); + } + + // Force to change PushPullSelectionStage to construct mxGraph + boolean stageChanged = changeStage(STAGE_PUSH_PULL_SELECTION); + + // Set layout + setDAGLayout(); + + // Update current file info curFilePath = file.getAbsolutePath(); curFileName = file.getName(); - - // Analyze the model. - if (!Validation.checkUpdateConflict(model)) return null; - graph = constructGraph(model); - analyzeDataTransferModel(model); - - // Set DAG layout. - setDAGLayout(); - return model; - } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefKeyword - | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression - | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment e) { + } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | + ExpectedInOrOutOrRefOrSubKeyword | ExpectedStateTransition | ExpectedEquals | + ExpectedRHSExpression | WrongLHSExpression | WrongRHSExpression | ExpectedRightBracket | + ExpectedAssignment | ExpectedRightCurlyBracket | WrongPathExpression | WrongJsonExpression | + ExpectedColon | ExpectedDoubleQuotation e) { e.printStackTrace(); } } catch (FileNotFoundException e) { e.printStackTrace(); } - return null; } - - /**-------------------------------------------------------------------------------- - * save - /**-------------------------------------------------------------------------------- - * - */ + + private void openDTRAM(File file) { + try { + ParserDTRAM parser = new ParserDTRAM(new BufferedReader(new FileReader(file))); + try { + // Parse the .dtram file. + model = parser.doParseModel(); + + // Update stage's model to new parsed one + if (curStage instanceof DataFlowModelingStage) { + ((DataFlowModelingStage) curStage).setModel(model); + } + + // Force to change PushPullSelectionStage to construct mxGraph + boolean stageChanged = changeStage(STAGE_PUSH_PULL_SELECTION); + + // Restore the geometry + parser.doParseGeometry(graph); + + // Update current file info + curFilePath = file.getAbsolutePath(); + curFileName = file.getName(); + } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | + ExpectedInOrOutOrRefOrSubKeyword | ExpectedStateTransition | ExpectedEquals | + ExpectedRHSExpression | WrongLHSExpression | WrongRHSExpression | ExpectedRightBracket | + ExpectedAssignment | ExpectedModel | ExpectedGeometry | ExpectedNode | ExpectedResource | + ExpectedFormulaChannel | ExpectedIoChannel | ExpectedRightCurlyBracket | WrongPathExpression | + WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + } + public void save() { - if (curFilePath != null) { - try { - File file = new File(curFilePath); - String extension = ""; - if(file != null && file.exists()) { - // get a file's name - String name = file.getName(); - - // get a file's extension - extension = name.substring(name.lastIndexOf(".")); - } - if(extension.contains(".model")) { - saveModel(file); - } else { - FileWriter filewriter = new FileWriter(file); - filewriter.write(toOutputString()); - filewriter.close(); - } - } catch (IOException e) { - e.printStackTrace(); - } + if (curFilePath == null) { + return; + } + File file = new File(curFilePath); + // get a file's extension + String extension = file.getName().substring(file.getName().lastIndexOf(".")); + if (extension.contains(".model")) { + saveModel(file); + } else if (extension.contains(".dtram")) { + saveDTRAM(file); } } - - public void saveModel(File file) { - if (curFilePath != null) { - try { - FileWriter filewriter = new FileWriter(file); - filewriter.write(model.getSourceText()); - filewriter.close(); - } catch (IOException e) { - e.printStackTrace(); - } + + private void saveModel(File file) { + try { + FileWriter filewriter = new FileWriter(file); + filewriter.write(model.getSourceText()); + filewriter.close(); + } catch (IOException e) { + e.printStackTrace(); } } - - /**-------------------------------------------------------------------------------- + + private void saveDTRAM(File file) { + try { + FileWriter filewriter = new FileWriter(file); + filewriter.write(toOutputString()); + filewriter.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** * get writing texts "dtram" file information is written. - * + * * @return formatted "dtram" info texts. */ - protected String toOutputString() { - String fileString = ""; + private String toOutputString() { + StringBuilder fileString = new StringBuilder(); - fileString += "model {\n"; - fileString += this.model.getSourceText(); - fileString += "}\n"; - - fileString += "geometry {\n"; - + fileString.append("model {\n"); + fileString.append(model.getSourceText()); + fileString.append("}\n"); + + fileString.append("geometry {\n"); + Object root = graph.getDefaultParent(); - for (int i = 0; i < graph.getModel().getChildCount(root); i++) { - Object cell = graph.getModel().getChildAt(root, i); + mxCell dataFlowLayer = (mxCell) ((mxCell) root).getChildAt(Stage.DATA_FLOW_LAYER); + + for (int i = 0; i < graph.getModel().getChildCount(dataFlowLayer); i++) { + Object cell = graph.getModel().getChildAt(dataFlowLayer, i); if (graph.getModel().isVertex(cell)) { mxGraphView view = graph.getView(); mxCellState state = view.getState(cell); @@ -293,384 +300,270 @@ int y = (int) state.getY(); int w = (int) state.getWidth(); int h = (int) state.getHeight(); - - for(Channel ch: model.getChannels()) { - if(ch instanceof FormulaChannel && state.getLabel().equals(ch.getChannelName())) { - fileString += "\tnode fc " + state.getLabel() + ":" + x + "," + y + "," + w + "," + h+"\n"; - } else if(ch instanceof Channel && state.getLabel().equals(ch.getChannelName())) { - fileString +="\tnode c " + state.getLabel() + ":" + x + "," + y + "," + w + "," + h+"\n"; + + for (ResourcePath res : model.getResourcePaths()) { + if (res != null && state.getLabel().equals(res.getLeafResourceName())) + fileString.append("\tnode r ").append(state.getLabel()).append(":").append(x).append(",").append(y).append(",").append(w).append(",").append(h).append("\n"); + } + + for (Channel ioC : model.getInputChannels()) { + if (ioC != null && state.getLabel().equals(ioC.getChannelName())) { + fileString.append("\tnode ioc ").append(state.getLabel()).append(":").append(x).append(",").append(y).append(",").append(w).append(",").append(h).append("\n"); } } - - for (ResourcePath res: model.getResourcePaths()){ - if(res instanceof ResourcePath && state.getLabel().equals(res.getResourceName())) - fileString += "\tnode r " + state.getLabel() + ":" + x + "," + y + "," + w + "," + h + "\n"; - } - - for (Channel ioC: model.getIOChannels()) { - if(ioC instanceof Channel && state.getLabel().equals(ioC.getChannelName())) { - fileString += "\tnode ioc " + state.getLabel() + ":" + x + "," + y + "," + w + "," + h + "\n"; + for (Channel ch : model.getChannels()) { + if (ch instanceof FormulaChannel && state.getLabel().equals(ch.getChannelName())) { + fileString.append("\tnode fc ").append(state.getLabel()).append(":").append(x).append(",").append(y).append(",").append(w).append(",").append(h).append("\n"); + } else if (ch != null && state.getLabel().equals(ch.getChannelName())) { + fileString.append("\tnode c ").append(state.getLabel()).append(":").append(x).append(",").append(y).append(",").append(w).append(",").append(h).append("\n"); } } } - } - fileString += "}\n"; - - return fileString; - } - - /** - * Construct a mxGraph from DataFlowModel and DataFlowModel - * @param model - * @param dataFlowGraph - * @return constructed mxGraph - */ - public mxGraph constructGraph(DataTransferModel model) { - ((mxGraphModel) graph.getModel()).clear(); - Object parent = graph.getDefaultParent(); - graph.getModel().beginUpdate(); - try { - mxGeometry geo1 = new mxGeometry(0, 0.5, PORT_DIAMETER, PORT_DIAMETER); - geo1.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS)); - geo1.setRelative(true); - - mxGeometry geo2 = new mxGeometry(1.0, 0.5, PORT_DIAMETER, PORT_DIAMETER); - geo2.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS)); - geo2.setRelative(true); - - 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); - } - } - - // create resource vertices - for (ResourcePath res: model.getResourcePaths()) { - Object resource = graph.insertVertex(parent, null, - res.getResourceName(), 20, 20, 80, 30, - "shape=ellipse;perimeter=ellipsePerimeter"); // insert a resource as a vertex - resources.put(res, resource); - } - - // 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"); - } - } - } - } finally { - graph.getModel().endUpdate(); } - setTreeLayout(); - - return graph; + fileString.append("}\n"); + return fileString.toString(); } - + public void setDAGLayout() { - Object parent = graph.getDefaultParent(); + mxCell root = (mxCell) graph.getDefaultParent(); + mxCell dataFlowLayer = (mxCell) root.getChildAt(Stage.DATA_FLOW_LAYER); graph.getModel().beginUpdate(); try { DAGLayout ctl = new DAGLayout(graph); - ctl.execute(parent); + ctl.execute(dataFlowLayer); +// for(int i = 0; i < dataFlowLayer.getChildCount(); i++){ +// ctl.execute(dataFlowLayer.getChildAt(i)); +// } } finally { graph.getModel().endUpdate(); } } - - public void updateEdgeAttiributes(DataFlowGraph dataFlowGraph) { - Object parent = graph.getDefaultParent(); - graph.getModel().beginUpdate(); - try { - // add input, output and reference edges - 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; - } - } - } - } - } - } finally { - graph.getModel().endUpdate(); - } - graph.refresh(); - } - + public void setTreeLayout() { - Object parent = graph.getDefaultParent(); + mxCell root = (mxCell) graph.getDefaultParent(); graph.getModel().beginUpdate(); try { mxCompactTreeLayout ctl = new mxCompactTreeLayout(graph); ctl.setLevelDistance(100); // ctl.setHorizontal(false); ctl.setEdgeRouting(false); - ctl.execute(parent); + for (int i = 0; i < root.getChildCount(); i++) { + ctl.execute(root.getChildAt(i)); + } } finally { graph.getModel().endUpdate(); } } - + public void setCircleLayout() { - Object parent = graph.getDefaultParent(); + mxCell root = (mxCell) graph.getDefaultParent(); graph.getModel().beginUpdate(); try { mxCircleLayout ctl = new mxCircleLayout(graph); - ctl.execute(parent); + for (int i = 0; i < root.getChildCount(); i++) { + ctl.execute(root.getChildAt(i)); + } } finally { graph.getModel().endUpdate(); } } - - public void addResourcePath(ResourcePath res) { - getModel().addResourcePath(res); - resetDataFlowGraph(); - graph.getModel().beginUpdate(); - Object parent = graph.getDefaultParent(); - try { - graph.insertVertex(parent, null, res.getResourceName(), 20, 20, 80, 30, - "shape=ellipse;perimeter=ellipsePerimeter"); // insert a resource as a vertex - } finally { - graph.getModel().endUpdate(); - } - } - - public void addChannel(DataTransferChannel channelGen) { - getModel().addChannel(channelGen); - resetDataFlowGraph(); - graph.getModel().beginUpdate(); - Object parent = graph.getDefaultParent(); - try { - mxGeometry geo1 = new mxGeometry(0, 0.5, PORT_DIAMETER, PORT_DIAMETER); - geo1.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS)); - geo1.setRelative(true); - - mxGeometry geo2 = new mxGeometry(1.0, 0.5, PORT_DIAMETER, PORT_DIAMETER); - geo2.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS)); - geo2.setRelative(true); - - 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 - } finally { - graph.getModel().endUpdate(); - } - } - - public void addIOChannel(DataTransferChannel ioChannelGen) { - getModel().addIOChannel(ioChannelGen); - resetDataFlowGraph(); - graph.getModel().beginUpdate(); - Object parent = graph.getDefaultParent(); - try { - mxGeometry geo2 = new mxGeometry(1.0, 0.5, PORT_DIAMETER, PORT_DIAMETER); - geo2.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS)); - geo2.setRelative(true); - - 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 - } finally { - graph.getModel().endUpdate(); - } - } - - public void addFormulaChannel(FormulaChannel formulaChannelGen) { - getModel().addChannel(formulaChannelGen); - resetDataFlowGraph(); - graph.getModel().beginUpdate(); - Object parent = graph.getDefaultParent(); - try { - mxGeometry geo1 = new mxGeometry(0, 0.5, PORT_DIAMETER, PORT_DIAMETER); - geo1.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS)); - geo1.setRelative(true); - - mxGeometry geo2 = new mxGeometry(1.0, 0.5, PORT_DIAMETER, PORT_DIAMETER); - geo2.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS)); - geo2.setRelative(true); - - Object channel = graph.insertVertex(parent, null, formulaChannelGen.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 - } finally { - graph.getModel().endUpdate(); - } - } - - public boolean connectEdge(mxCell edge, mxCell src, mxCell dst) { - DataTransferModel model = getModel(); - Channel srcCh = model.getChannel((String) src.getValue()); - if (srcCh == null) { - srcCh = model.getIOChannel((String) src.getValue()); - if (srcCh == null) { - ResourcePath srcRes = model.getResourcePath((String) src.getValue()); - Channel dstCh = model.getChannel((String) dst.getValue()); - if (srcRes == null || dstCh == null) return false; - // resource to channel edge - ChannelMember srcCm = new ChannelMember(srcRes); - ((DataTransferChannel ) dstCh).addChannelMemberAsInput(srcCm); - edge.setValue(new SrcDstAttribute(srcRes, dstCh)); - resetDataFlowGraph(); - return true; + + public List getSelectedResourceNodes() { + Object[] sels = graph.getSelectionCells(); + List resNodes = new ArrayList<>(); + if (curStage instanceof DataFlowModelingStage) { + for (Object sel : sels) { + if (sel instanceof mxCell && ((mxCell) sel).isVertex()) { + mxCell cell = ((mxCell) sel); + ResourceNode resNode = ((DataFlowModelingStage) curStage).getResourceNode(cell); + if (resNode != null) { + resNodes.add(resNode); + } + } } } - ResourcePath dstRes = model.getResourcePath((String) dst.getValue()); - if (dstRes == null) return false; - // channel to resource edge - ChannelMember dstCm = new ChannelMember(dstRes); - ((DataTransferChannel) srcCh).addChannelMemberAsOutput(dstCm); - edge.setValue(new SrcDstAttribute(srcCh, dstRes)); - resetDataFlowGraph(); - return true; + return resNodes; } - + + public List getSelectedChannels() { + Object[] sels = graph.getSelectionCells(); + List channels = new ArrayList<>(); + for (Object sel : sels) { + if (sel instanceof mxCell && ((mxCell) sel).isVertex()) { + mxCell cell = ((mxCell) sel); + Channel channel = model.getChannel((String) cell.getValue()); + if (channel != null) { + channels.add(channel); + } else { + channel = model.getInputChannel((String) cell.getValue()); + if (channel != null) { + channels.add(channel); + } + } + } + } + return channels; + } + public void delete() { - for (Object obj: graph.getSelectionCells()) { - mxCell cell = (mxCell) obj; - if (cell.isEdge()) { - String srcName = (String) cell.getSource().getValue(); - String dstName = (String) cell.getTarget().getValue(); - if (model.getResourcePath(srcName) != null) { - // resource to channel edge - Channel ch = model.getChannel(dstName); - ch.removeChannelMember(model.getResourcePath(srcName)); - } else if (model.getResourcePath(dstName) != null) { - // channel to resource edge - Channel ch = model.getChannel(srcName); - if (ch == null) { - ch = model.getIOChannel(srcName); - } - ch.removeChannelMember(model.getResourcePath(dstName)); - } - } else if (cell.isVertex()) { - 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.getResourcePath(name) != null) { - model.removeResourcePath(name); - } - } + boolean stageChanged = changeStage(STAGE_DATA_FLOW_MODELING); + if (!stageChanged) { + return; } - graph.removeCells(graph.getSelectionCells()); - resetDataFlowGraph(); + ((DataFlowModelingStage) curStage).delete(); } - - public void setChannelCode(DataTransferChannel ch, String code) { - ch.setSourceText(code); - TokenStream stream = new Parser.TokenStream(); - Parser parser = new Parser(stream); + + public void addResourceNode(ResourceNode parentNode, String resName) { + // Force to change to data-flow modeling stage + boolean stageChanged = changeStage(STAGE_DATA_FLOW_MODELING); + if (!stageChanged) { + return; + } + ((DataFlowModelingStage) curStage).addResourceNode(parentNode, resName); + model = curStage.getModel(); + } + + public void addChannel(String channelName) { + // Force to change to data-flow modeling stage + boolean stageChanged = changeStage(STAGE_DATA_FLOW_MODELING); + if (!stageChanged) { + return; + } + DataTransferChannel channel = null; + if (channelName.contains(Parser.LEFT_BRACKET) && channelName.contains(Parser.RIGHT_BRACKET)) { + channel = new DataTransferChannel(channelName.substring(0, channelName.indexOf(Parser.LEFT_BRACKET))); + Parser.TokenStream stream = new Parser.TokenStream(); + Parser parser = new Parser(stream); + stream.addLine(channelName.substring(channelName.indexOf(Parser.LEFT_BRACKET), channelName.length())); + try { + String leftBracket = stream.next(); + if (leftBracket.equals(Parser.LEFT_BRACKET)) { + // has selectors + String rightBracket = null; + do { + String selector = stream.next(); + Variable var = parser.parseVariable(stream, model, selector); + channel.addSelector(var); + rightBracket = stream.next(); + } while (rightBracket.equals(Parser.COMMA)); + if (!rightBracket.equals(Parser.RIGHT_BRACKET)) throw new ExpectedRightBracket(stream.getLine()); + leftBracket = stream.next(); + } + } catch (ExpectedRightBracket e) { + e.printStackTrace(); + } + } else { + channel = new DataTransferChannel(channelName); + } + ((DataFlowModelingStage) curStage).addChannel(channel); + model = curStage.getModel(); + } + + public void addEventChannel(String channelName) { + // Force to change to data-flow modeling stage + boolean stageChanged = changeStage(STAGE_DATA_FLOW_MODELING); + if (!stageChanged) { + return; + } + DataTransferChannel eventChannel = null; + if (channelName.contains(Parser.LEFT_BRACKET) && channelName.contains(Parser.RIGHT_BRACKET)) { + eventChannel = new DataTransferChannel(channelName.substring(0, channelName.indexOf(Parser.LEFT_BRACKET))); + Parser.TokenStream stream = new Parser.TokenStream(); + Parser parser = new Parser(stream); + stream.addLine(channelName.substring(channelName.indexOf(Parser.LEFT_BRACKET), channelName.length())); + try { + String leftBracket = stream.next(); + if (leftBracket.equals(Parser.LEFT_BRACKET)) { + // has selectors + String rightBracket = null; + do { + String selector = stream.next(); + Variable var = parser.parseVariable(stream, model, selector); + eventChannel.addSelector(var); + rightBracket = stream.next(); + } while (rightBracket.equals(Parser.COMMA)); + if (!rightBracket.equals(Parser.RIGHT_BRACKET)) throw new ExpectedRightBracket(stream.getLine()); + leftBracket = stream.next(); + } + } catch (ExpectedRightBracket e) { + e.printStackTrace(); + } + } else { + eventChannel = new DataTransferChannel(channelName); + } + ((DataFlowModelingStage) curStage).addEventChannel(eventChannel); + model = curStage.getModel(); + } + + public void addFormulaChannel(String channelName, Symbol op) { + // Force to change to data-flow modeling stage + boolean stageChanged = changeStage(STAGE_DATA_FLOW_MODELING); + if (!stageChanged) { + return; + } + FormulaChannel formulaChannel = new FormulaChannel(channelName, op); + ((DataFlowModelingStage) curStage).addFormulaChannel(formulaChannel); + model = curStage.getModel(); + } + + public boolean connectEdge(mxCell edge, mxCell src, mxCell dst) { + boolean stageChanged = changeStage(STAGE_DATA_FLOW_MODELING); + if (!stageChanged) { + return false; + } + return ((DataFlowModelingStage) curStage).connectEdge(edge, src, dst); + } + + public mxGraph getGraph() { + return graph; + } + + public mxGraphComponent getGraphComponent() { + return graphComponent; + } + + public DataTransferModel getModel() { + model = curStage.getModel(); + return model; + } + + public Stage getCurStage() { + return curStage; + } + + public ArrayList getCodes() { + return codes; + } + + public void setCodes(ArrayList codes) { + this.codes = codes; + } + + public String getCurFileName() { + return curFileName; + } + + public String getCurFilePath() { + return curFilePath; + } + + public static class SrcDstAttribute extends EdgeAttribute { + private final Object src; + private final Object dst; - for (String line: code.split("\n")) { - stream.addLine(line); - } - try { - DataTransferChannel ch2 = parser.parseChannel(getModel()); - for (ChannelMember chm2: ch2.getInputChannelMembers()) { - for (ChannelMember chm: ch.getInputChannelMembers()) { - if (chm2.getResource() == chm.getResource()) { - chm.setStateTransition(chm2.getStateTransition()); - break; - } - } - } - for (ChannelMember chm2: ch2.getOutputChannelMembers()) { - for (ChannelMember chm: ch.getOutputChannelMembers()) { - if (chm2.getResource() == chm.getResource()) { - chm.setStateTransition(chm2.getStateTransition()); - break; - } - } - } - for (ChannelMember chm2: ch2.getReferenceChannelMembers()) { - for (ChannelMember chm: ch.getReferenceChannelMembers()) { - if (chm2.getResource() == chm.getResource()) { - chm.setStateTransition(chm2.getStateTransition()); - break; - } - } - } - resetDataFlowGraph(); - } catch (ExpectedRightBracket | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket - | ExpectedInOrOutOrRefKeyword | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression - | WrongLHSExpression | WrongRHSExpression | ExpectedAssignment e) { - e.printStackTrace(); - } - } - - private class SrcDstAttribute extends EdgeAttribute { - private Object src; - private Object dst; - public SrcDstAttribute(Object src, Object dst) { this.src = src; this.dst = dst; } - - public Object getSrouce() { + + public Object getSource() { return src; } - + public Object getDestination() { return dst; } - + public String toString() { return ""; } diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/FlowCellEditor.java b/AlgebraicDataflowArchitectureModel/src/application/editor/FlowCellEditor.java new file mode 100644 index 0000000..7bd498c --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/FlowCellEditor.java @@ -0,0 +1,26 @@ +package application.editor; + +import com.mxgraph.swing.mxGraphComponent; +import com.mxgraph.swing.view.mxICellEditor; + +import java.util.EventObject; + +public abstract class FlowCellEditor implements mxICellEditor { + protected Stage stage; + protected mxGraphComponent graphComponent; + + protected Object editingCell = null; + + protected FlowCellEditor(Stage stage, mxGraphComponent graphComponent) { + this.stage = stage; + this.graphComponent = graphComponent; + } + + public abstract void startEditing(Object cellObj, EventObject eventObj); + + public abstract void stopEditing(boolean cancel); + + public Object getEditingCell() { + return this.editingCell; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/IStageChangeListener.java b/AlgebraicDataflowArchitectureModel/src/application/editor/IStageChangeListener.java new file mode 100644 index 0000000..4d149f8 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/IStageChangeListener.java @@ -0,0 +1,5 @@ +package application.editor; + +public interface IStageChangeListener { + void stageChanged(Stage newStage); +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/Stage.java b/AlgebraicDataflowArchitectureModel/src/application/editor/Stage.java new file mode 100644 index 0000000..c08e08a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/Stage.java @@ -0,0 +1,94 @@ +package application.editor; + +import com.mxgraph.model.mxCell; +import com.mxgraph.swing.mxGraphComponent; +import com.mxgraph.swing.view.mxICellEditor; +import com.mxgraph.util.mxEventSource.mxIEventListener; +import com.mxgraph.view.mxGraph; +import models.dataFlowModel.DataTransferModel; + +import java.awt.event.MouseListener; + +public abstract class Stage { + public static final int PORT_DIAMETER = 8; + public static final int PORT_RADIUS = PORT_DIAMETER / 2; + + protected DataTransferModel model = null; + + protected mxGraphComponent graphComponent; + protected mxGraph graph; + + public static final int NODE_LAYER = 0; + public static final int DATA_FLOW_LAYER = 0; + public static final int PUSH_FLOW_LAYER = 1; + public static final int PULL_FLOW_LAYER = 2; + + public Stage(mxGraphComponent graphComponent) { + this.graphComponent = graphComponent; + this.graph = graphComponent.getGraph(); + } + + public abstract void init(Stage prevStage); + + public abstract mxICellEditor createCellEditor(mxGraphComponent graphComponent); + + public abstract mxIEventListener createChangeEventListener(Editor editor); + + public abstract MouseListener createMouseEventListener(Editor editor); + + public abstract boolean canChangeFrom(Stage prevStage); + + public DataTransferModel getModel() { + return model; + } + + /** + * Constructs the hierarchical layer structure under the root cell. + */ + public void constructLayer(mxGraph graph) { + mxCell root = (mxCell) graph.getDefaultParent(); + + mxCell nodeAndDataFlowLayer = new mxCell(); // 0 + mxCell pushFlowLayer = new mxCell(); // 1 + mxCell pullFlowLayer = new mxCell(); // 2 + + graph.getModel().beginUpdate(); + try { + graph.addCell(nodeAndDataFlowLayer, root); + graph.addCell(pushFlowLayer, root); + graph.addCell(pullFlowLayer, root); + } finally { + graph.getModel().endUpdate(); + } + } + + /** + * Enable or disable the specified layer (layerNo) + */ + public void setLayerEnabled(final int layerNo, final boolean isEnable) { + mxCell rootCell = (mxCell) graph.getDefaultParent(); + if (rootCell == null) return; + if (rootCell.getChildCount() <= 0) return; + + graph.getModel().setVisible(rootCell.getChildAt(layerNo), isEnable); + graph.refresh(); + } + + /** + * Show the specified layers only (argsOfLayers) + */ + public void showLayers(final int... argsOfLayers) { + mxCell rootCell = (mxCell) graph.getDefaultParent(); + if (rootCell == null) return; + if (rootCell.getChildCount() <= 0) return; + + + for (int i = 0; i < rootCell.getChildCount(); i++) { + graph.getModel().setVisible(rootCell.getChildAt(i), false); + } + + for (int layerNo : argsOfLayers) { + graph.getModel().setVisible(rootCell.getChildAt(layerNo), true); + } + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowModelingCellEditor.java b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowModelingCellEditor.java new file mode 100644 index 0000000..0d47462 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowModelingCellEditor.java @@ -0,0 +1,24 @@ +package application.editor.stages; + +import application.editor.FlowCellEditor; +import com.mxgraph.swing.mxGraphComponent; + +import java.util.EventObject; + +public class ControlFlowModelingCellEditor extends FlowCellEditor { + public ControlFlowModelingCellEditor(ControlFlowModelingStage stage, mxGraphComponent graphComponent) { + super(stage, graphComponent); + } + + @Override + public void startEditing(Object cellObj, EventObject eventObj) { + if (editingCell != null) { + stopEditing(true); + } + } + + @Override + public void stopEditing(boolean cancel) { + // Do nothing + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowModelingStage.java b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowModelingStage.java new file mode 100644 index 0000000..feea075 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowModelingStage.java @@ -0,0 +1,611 @@ +package application.editor.stages; + +import application.editor.Editor; +import application.editor.FlowCellEditor; +import application.editor.Stage; +import com.mxgraph.model.mxCell; +import com.mxgraph.model.mxGeometry; +import com.mxgraph.model.mxGraphModel; +import com.mxgraph.swing.mxGraphComponent; +import com.mxgraph.util.mxEventObject; +import com.mxgraph.util.mxEventSource.mxIEventListener; +import com.mxgraph.util.mxPoint; +import com.mxgraph.view.mxGraph; +import models.Edge; +import models.Node; +import models.controlFlowModel.*; +import models.dataFlowModel.DataFlowGraph; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.PushPullValue; +import models.dataFlowModel.ResourceNode; + +import javax.swing.*; +import java.awt.event.*; +import java.util.*; + +public class ControlFlowModelingStage extends Stage { + public final int PORT_DIAMETER = 8; + public final int PORT_RADIUS = PORT_DIAMETER / 2; + + private ControlFlowGraph controlFlowGraph = null; + + private Map resNodeToCell = null; + private Map channelToCell = null; + + public ControlFlowModelingStage(mxGraphComponent graphComponent) { + super(graphComponent); + } + + public ControlFlowGraph getControlFlowGraph() { + return controlFlowGraph; + } + + @Override + public boolean canChangeFrom(Stage prevStage) { + if (prevStage instanceof PushPullSelectionStage) return true; + return false; + } + + @Override + public void init(Stage prevStage) { + if (prevStage instanceof PushPullSelectionStage) { + PushPullSelectionStage stage = (PushPullSelectionStage) prevStage; + model = stage.getModel(); + resNodeToCell = stage.getResNodeToCell(); + channelToCell = stage.getChannelToCell(); + + DataFlowGraph dataFlowGraph = stage.getDataFlowGraph(); + controlFlowGraph = new ControlFlowGraph(dataFlowGraph, model); + + clearControlFlowGraphCells(graph); + graph = constructGraph(graph, resNodeToCell, channelToCell); + } + } + + @Override + public FlowCellEditor createCellEditor(mxGraphComponent graphComponent) { + return new ControlFlowModelingCellEditor(this, graphComponent); + } + + /** + * Create an event listener for changing the terminal of edges + * + * @param editor The editor instance + * @return An event listener for changing the terminal of edges + */ + @Override + public mxIEventListener createChangeEventListener(Editor editor) { + return 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 (curState != ControlFlowModelingStageStatus.SELECTING_AN_EDGE) return; + + if (terminals.size() == 2) { + graph.removeCells(new mxCell[]{cell}); + graph.clearSelection(); + } + } + }; + } + + /** + * Creates and returns a mouse event listener for the given editor. + * The listener monitors mouse events such as `mousePressed` and `mouseReleased` + * to display context menus and handle shortcut creation when certain + * conditions are met on the selected graph elements. + * + * @param editor The editor instance for which the mouse event listener is created. + * It is used to access the editor's context and operations. + * @return A `MouseListener` that adds custom behavior for mouse events, including + * showing popup actions and handling shortcut creation on selected graph edges. + */ + @Override + public MouseListener createMouseEventListener(final Editor editor) { + return new MouseAdapter() { + @Override + public void mouseReleased(MouseEvent e) { + if (e.isPopupTrigger()) { + showPopupMenu(e); + } + } + + @Override + public void mousePressed(MouseEvent e) { + if (e.isPopupTrigger()) { + showPopupMenu(e); + } + } + + private void showPopupMenu(MouseEvent e) { + Object[] selectedCells = graph.getSelectionCells(); + if (selectedCells == null || selectedCells.length < 2) { + return; + } + + List selectedEdges = new ArrayList<>(); + for (Object cellObj : selectedCells) { + if (graph.getModel().isEdge(cellObj)) { + selectedEdges.add((mxCell) cellObj); + } else { + return; + } + } + + if (isValidPath(selectedEdges)) { + JPopupMenu popup = new JPopupMenu(); + JMenuItem menuItem = new JMenuItem("ショートカットを作成する"); + menuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + createShortcut(selectedEdges); + } + }); + popup.add(menuItem); + popup.show(e.getComponent(), e.getX(), e.getY()); + } + } + + private void createShortcut(List selectedEdges) { + List callEdges = new ArrayList<>(); + for (mxCell edgeCell : selectedEdges) { + CallEdgeAttribute attr = (CallEdgeAttribute) edgeCell.getValue(); + callEdges.add(attr.getCallEdge()); + } + + Map inDegrees = new HashMap<>(); + for (CallEdge edge : callEdges) { + Node dest = edge.getDestination(); + inDegrees.put(dest, inDegrees.getOrDefault(dest, 0) + 1); + } + + Node startNode = null; + Node endNode = null; + for (CallEdge edge : callEdges) { + if (!inDegrees.containsKey(edge.getSource())) { + startNode = edge.getSource(); + break; + } + } + + Set sources = new HashSet<>(); + for (CallEdge edge : callEdges) { + sources.add(edge.getSource()); + } + for (CallEdge edge : callEdges) { + if (!sources.contains(edge.getDestination())) { + endNode = edge.getDestination(); + break; + } + } + + if (startNode == null || endNode == null) return; + + PushPullValue option = callEdges.get(0).getSelectedOption(); + CallGraph callGraph = getCallGraph(option); + if (callGraph == null) return; + + for (CallEdge edge : callEdges) { + callGraph.removeEdge(edge); + } + + if (startNode instanceof ObjectNode && endNode instanceof ObjectNode) { + callGraph.insertEdge((ObjectNode) startNode, (ObjectNode) endNode, option, 0); + } + + reconstructGraph(); + } + + private boolean isValidPath(List selectedEdges) { + Map inDegrees = new HashMap<>(); + Map outDegrees = new HashMap<>(); + Set nodes = new HashSet<>(); + + for (mxCell edge : selectedEdges) { + mxCell src = (mxCell) edge.getSource(); + mxCell dest = (mxCell) edge.getTarget(); + if (src == null || dest == null) return false; + + outDegrees.put(src, outDegrees.getOrDefault(src, 0) + 1); + inDegrees.put(dest, inDegrees.getOrDefault(dest, 0) + 1); + nodes.add(src); + nodes.add(dest); + } + + mxCell startNode = null; + int startCount = 0; + int endCount = 0; + + for (mxCell node : nodes) { + int in = inDegrees.getOrDefault(node, 0); + int out = outDegrees.getOrDefault(node, 0); + + if (in > 1 || out > 1) { + return false; + } + + if (in == 0 && out == 1) { + startNode = node; + startCount++; + } else if (in == 1 && out == 0) { + endCount++; + } else if (in == 1 && out == 1) { + // Intermediate node + } else { + return false; + } + } + + if (startCount != 1 || endCount != 1) { + return false; + } + + int visitedEdgesCount = 0; + mxCell currentNode = startNode; + while (currentNode != null) { + mxCell nextEdge = null; + for (mxCell edge : selectedEdges) { + if (edge.getSource() == currentNode) { + nextEdge = edge; + break; + } + } + if (nextEdge != null) { + visitedEdgesCount++; + currentNode = (mxCell) nextEdge.getTarget(); + } else { + currentNode = null; + } + } + + return visitedEdgesCount == selectedEdges.size(); + } + }; + } + + /** + * Reconstructs the control flow graph by clearing the current graph structure + * and rebuilding it using the provided mapping of nodes and channels to cells. + * This method resets the existing graph and generates a new one based on + * the current state of the mappings. + */ + public void reconstructGraph() { + clearControlFlowGraphCells(graph); + graph = constructGraph(graph, resNodeToCell, channelToCell); + } + + /** + * Constructs and initializes a control flow graph for the PUSH and PULL layers. This method creates graph cells + * for resource nodes and event-channel nodes and connects them with edges corresponding to the control flow. + * Updates to the graph are performed within a single transaction for consistency. + * + * @param graph The {@link mxGraph} instance to be constructed and updated. + * @param originalResourceNodeCells A map associating {@link ResourceNode} instances to their corresponding {@link mxCell} + * instances in the graph. + * @param originalChannelCells A map associating {@link DataTransferChannel} instances to their corresponding + * {@link mxCell} instances in the graph. + * @return The updated {@link mxGraph} instance after constructing the control flow graph. + */ + private mxGraph constructGraph(mxGraph graph, final Map originalResourceNodeCells, final Map originalChannelCells) { + showLayers(PUSH_FLOW_LAYER, PULL_FLOW_LAYER); + + graph.getModel().beginUpdate(); + try { + // Create cells correspond to resource nodes for the PUSH/PULL layer. + Map pushResourceNodeCells = createResourceNodeCells(graph, originalResourceNodeCells, PUSH_FLOW_LAYER); + Map pullResourceNodeCells = createResourceNodeCells(graph, originalResourceNodeCells, PULL_FLOW_LAYER); + + // Create cells correspond to event-channel nodes for the PUSH layer. + Map pushFlowEventNodeCells = createEventChannelNodeCells(graph, originalChannelCells, PUSH_FLOW_LAYER); + + // Insert edges between the connected vertices + graph = insertControlFlowEdges(PUSH_FLOW_LAYER, pushResourceNodeCells, pushFlowEventNodeCells); + graph = insertControlFlowEdges(PULL_FLOW_LAYER, pullResourceNodeCells, null); + } finally { + graph.getModel().endUpdate(); + } + return graph; + } + + /** + * Clear all cells on the PUSH/PULL layer. + * + * @param graph mxGraph to be modified + */ + private void clearControlFlowGraphCells(mxGraph graph) { + mxCell root = (mxCell) graph.getDefaultParent(); + + graph.getModel().beginUpdate(); + try { + root.remove(root.getChildAt(PULL_FLOW_LAYER)); + root.remove(root.getChildAt(PUSH_FLOW_LAYER)); + + root.insert(new mxCell()); + root.insert(new mxCell()); + } finally { + graph.getModel().endUpdate(); + graph.refresh(); + } + } + + /** + * Creates and registers resource node cells on the specified layer of the given graph. This method processes + * the resource nodes in the call graph corresponding to the specified layer and maps them to newly created + * graph cells. It uses the original resource node cells as templates for dimensions, coordinates, and styles. + * + * @param graph The {@link mxGraph} instance where the resource node cells will be created and added. + * @param originalResourceNodeCells A map of {@link ResourceNode} instances to their original {@link mxCell} instances. + * Used for determining dimensions, coordinates, and styles of the new cells. + * @param layer The layer number. Must be either {@code PUSH_FLOW_LAYER} or {@code PULL_FLOW_LAYER}. + * @return A map associating {@link StatefulObjectNode} instances to the created {@link mxCell} instances + * representing the resource nodes in the control flow graph. + * @throws IllegalArgumentException if the provided layer is neither {@code PUSH_FLOW_LAYER} nor {@code PULL_FLOW_LAYER}. + */ + private Map createResourceNodeCells(final mxGraph graph, final Map originalResourceNodeCells, final int layer) { + if (layer != PUSH_FLOW_LAYER && layer != PULL_FLOW_LAYER) { + throw new IllegalArgumentException("The layer number must be either PUSH_FLOW_LAYER or PULL_FLOW_LAYER."); + } + + final mxCell root = (mxCell) graph.getDefaultParent(); + final mxCell layerCell = (mxCell) root.getChildAt(layer); + final Map resourceNodeCells = new HashMap<>(); + + final CallGraph callGraph = getCallGraph(layer); + if (callGraph == null) { + System.out.println("CallGraph is invalid."); + return Collections.emptyMap(); + } + + for (Node rootNode : callGraph.getRootNodes()) { + if (!(rootNode instanceof StatefulObjectNode)) { + continue; + } + StatefulObjectNode objectNode = (StatefulObjectNode) rootNode; + ResourceNode resourceNode = objectNode.getResource(); + if (!originalResourceNodeCells.containsKey(resourceNode)) { + continue; + } + ObjectNodeAttribute attribute = new ObjectNodeAttribute(objectNode); + + mxCell originalCell = originalResourceNodeCells.get(resourceNode); + double width = originalCell.getGeometry().getWidth(); + double height = originalCell.getGeometry().getHeight(); + double x = originalCell.getGeometry().getX(); + double y = originalCell.getGeometry().getY(); + String style = originalCell.getStyle(); + + mxCell insertedCell = (mxCell) graph.insertVertex(layerCell, null, resourceNode.getPrimaryResourcePath().getName(), x, y, width, height, style); // insert a resource as a vertex + resourceNodeCells.put(objectNode, insertedCell); + + createChildResourceNodeCell(graph, insertedCell, objectNode, resourceNodeCells, originalResourceNodeCells); + } + return resourceNodeCells; + } + + /** + * Creates and registers a child resource node cell for the specified parent node in the control flow graph. + * This method recursively processes the children of the parent node to generate corresponding graph cells. + * It utilizes the original resource node cells for dimensions, positioning, and styling information. + * + * @param graph The {@link mxGraph} instance where the cells will be created and added. + * @param parentCell The parent {@link mxCell} to which the newly created child cells will be linked. + * @param parentNode The {@link StatefulObjectNode} representing the parent resource node whose children are processed. + * @param resourceNodeCells A map associating {@link StatefulObjectNode} instances to the created {@link mxCell} instances. + * @param originalResourceNodeCells A map of {@link ResourceNode} instances to their original {@link mxCell} instances, + * used to determine dimensions, coordinates, and styles for new cells. + */ + private void createChildResourceNodeCell(final mxGraph graph, final mxCell parentCell, final StatefulObjectNode parentNode, final Map resourceNodeCells, final Map originalResourceNodeCells) { + for (ObjectNode childNode : parentNode.getChildren()) { + if (!(childNode instanceof StatefulObjectNode)) { + continue; + } + StatefulObjectNode childObjectNode = (StatefulObjectNode) childNode; + ResourceNode childResourceNode = childObjectNode.getResource(); + if (!originalResourceNodeCells.containsKey(childResourceNode)) { + continue; + } + ObjectNodeAttribute attribute = new ObjectNodeAttribute(childObjectNode); + + mxCell originalCell = originalResourceNodeCells.get(childResourceNode); + double width = originalCell.getGeometry().getWidth(); + double height = originalCell.getGeometry().getHeight(); + double x = originalCell.getGeometry().getX(); + double y = originalCell.getGeometry().getY(); + String style = originalCell.getStyle(); + + mxCell insertedCell = (mxCell) graph.insertVertex(parentCell, null, childResourceNode.getPrimaryResourcePath().getName(), x, y, width, height, style); // insert a resource as a vertex + resourceNodeCells.put(childObjectNode, insertedCell); + + createChildResourceNodeCell(graph, insertedCell, childObjectNode, resourceNodeCells, originalResourceNodeCells); + } + } + + /** + * Create and register a new graph cell for event channel nodes. + * + * @param graph {@link mxGraph} instance to be modified. + * @param layer The layer number. Only {@link Stage#PUSH_FLOW_LAYER} is supported. + * @return The map of {@link EventChannelObjectNode} and its corresponding cell. + */ + private Map createEventChannelNodeCells(final mxGraph graph, final Map channelCells, final int layer) { + if (layer != PUSH_FLOW_LAYER && layer != PULL_FLOW_LAYER) { + throw new IllegalArgumentException("The layer number must be either PUSH_FLOW_LAYER or PULL_FLOW_LAYER."); + } + final mxCell rootCell = (mxCell) graph.getDefaultParent(); + final mxCell layerCell = (mxCell) rootCell.getChildAt(layer); + final Map eventChannelNodeCells = new HashMap<>(); + final Map outputEventChannelNodeCells = new HashMap<>(); + + final CallGraph callGraph = getCallGraph(layer); + if (callGraph == null) { + System.out.println("CallGraph is invalid."); + return Collections.emptyMap(); + } + + for (Node rootNode : callGraph.getRootNodes()) { + if (!(rootNode instanceof EventChannelObjectNode)) { + continue; + } + final EventChannelObjectNode rootEventChannelNode = (EventChannelObjectNode) rootNode; + + createEventChannelNodeCell(graph, layerCell, rootEventChannelNode, eventChannelNodeCells, outputEventChannelNodeCells, channelCells); + } + return eventChannelNodeCells; + } + + /** + * Creates and initializes a new event channel node cell in the graph and associates it with the given event channel object node. + * This method also adds an output port to the created channel cell and updates the corresponding mappings. + * + * @param graph The mxGraph instance where the event channel node cell is to be created. + * @param parentCell The parent mxCell that serves as the container for the newly created channel cell. + * @param eventChannelNode The EventChannelObjectNode representing the data and logic for the event channel node. + * @param eventChannelNodeCells A mapping of EventChannelObjectNode instances to their corresponding mxCell representations in the graph. + * @param outputEventChannelNodeCells A mapping of EventChannelObjectNode instances to their corresponding output port mxCells. + * @param originalChannelCells A mapping of DataTransferChannel instances to their original mxCell representations in the graph. + */ + private void createEventChannelNodeCell(final mxGraph graph, final mxCell parentCell, final EventChannelObjectNode eventChannelNode, Map eventChannelNodeCells, Map outputEventChannelNodeCells, Map originalChannelCells) { + DataTransferChannel channel = eventChannelNode.getIOChannel(); + + // Corresponding cell is already created + if (outputEventChannelNodeCells.get(eventChannelNode) != null) { + return; + } + + if (!originalChannelCells.containsKey(channel)) { + System.out.println("Channel " + channel.getChannelName() + " is not found in the original graph."); + return; + } + + // Get original cell information from DataFlowGraph + mxCell originalCell = originalChannelCells.get(channel); + double cellWidth = originalCell.getGeometry().getWidth(); + double cellHeight = originalCell.getGeometry().getHeight(); + double x = originalCell.getGeometry().getX(); + double y = originalCell.getGeometry().getY(); + String style = originalCell.getStyle(); + + // Get channel information + String channelName = channel.getChannelName(); + if (originalCell.getValue() instanceof String) { + channelName = (String) originalCell.getValue(); + } + + // Create and register a new cell for a channel node + mxCell insertedChannelCell = (mxCell) graph.insertVertex(parentCell, null, channelName, x, y, cellWidth, cellHeight, style); // insert a channel as a vertex + eventChannelNodeCells.put(eventChannelNode, insertedChannelCell); + + // Attach an output port to the channel + mxGeometry outputPortGeometry = new mxGeometry(1.0, 0.5, PORT_DIAMETER, PORT_DIAMETER); + mxCell outputPort = new mxCell(null, outputPortGeometry, "shape=ellipse;perimeter=ellipsePerimeter"); + outputPortGeometry.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS)); + outputPortGeometry.setRelative(true); + outputPort.setVertex(true); + graph.addCell(outputPort, insertedChannelCell); // insert the output port of a channel + outputEventChannelNodeCells.put(eventChannelNode, outputPort); + } + + /** + * Inserts control flow edges into the specified layer of the given graph. This method + * processes edges in the call graph and creates corresponding edges between nodes + * in the mxGraph representation. The source and destination nodes of the call edges + * must be valid instances of StatefulObjectNode or EventChannelObjectNode. + * + * @param layer The index of the layer within the graph where the edges will be inserted. + * @param resourceNodeCells A map associating StatefulObjectNode instances with mxCell objects. + * @param eventChannelNodeCells A map associating EventChannelObjectNode instances with mxCell objects. + * @return The updated mxGraph with the inserted control flow edges. + */ + private mxGraph insertControlFlowEdges(final int layer, final Map resourceNodeCells, final Map eventChannelNodeCells) { + mxCell root = (mxCell) graph.getDefaultParent(); + mxCell layerCell = (mxCell) root.getChildAt(layer); + + CallGraph callGraph = getCallGraph(layer); + if (callGraph == null) { + System.out.println("CallGraph is invalid."); + return graph; + } + + for (Edge edge : callGraph.getEdges()) { + if (!(edge instanceof CallEdge)) { + continue; + } + + CallEdge callEdge = (CallEdge) edge; + if (callEdge.getSource() == null || callEdge.getDestination() == null) { // Skip if the edge is not placed between 2 cells + continue; + } + if (!(callEdge.getSource() instanceof StatefulObjectNode) && !(callEdge.getSource() instanceof EventChannelObjectNode)) { // The source object of the edge isn't valid. + continue; + } + + ObjectNode sourceNode = null; // Source node must be either StatefulObjectNode or EventChannelObejctNode + mxCell sourceNodeCell = null; + mxCell srcOutputPortCell = null; + if (callEdge.getSource() instanceof StatefulObjectNode) { + sourceNode = (StatefulObjectNode) callEdge.getSource(); + sourceNodeCell = resourceNodeCells.get(sourceNode); + } else if (callEdge.getSource() instanceof EventChannelObjectNode) { + sourceNode = (EventChannelObjectNode) (callEdge.getSource()); + sourceNodeCell = eventChannelNodeCells.get(sourceNode); + if (sourceNodeCell == null) { + continue; + } + srcOutputPortCell = (mxCell) sourceNodeCell.getChildAt(0); + } + StatefulObjectNode destinationNode = ((StatefulObjectNode) callEdge.getDestination()); // Destination node might be the StatefulObjectNode + mxCell destinationNodeCell = resourceNodeCells.get(destinationNode); + + if (sourceNode == null || sourceNodeCell == null || destinationNodeCell == null) { + continue; + } + + CallEdgeAttribute callEdgeAttr = new CallEdgeAttribute(callEdge, sourceNode, sourceNodeCell, destinationNodeCell); + if (sourceNode instanceof StatefulObjectNode) { + graph.insertEdge(layerCell, null, callEdgeAttr, sourceNodeCell, destinationNodeCell, "movable=false;"); + } else { + graph.insertEdge(layerCell, null, callEdgeAttr, srcOutputPortCell, destinationNodeCell, "movable=false;"); + } + } + return graph; + } + + private CallGraph getCallGraph(PushPullValue option) { + if (option == PushPullValue.PUSH) { + return controlFlowGraph.getPushCallGraph(); + } else if (option == PushPullValue.PULL) { + return controlFlowGraph.getPullCallGraph(); + } + return null; + } + + /** + * Retrieves the call graph associated with the specified control flow layer. + * + * @param layer The layer identifier. It determines whether to retrieve the PUSH or PULL call graph. + * Valid values are {@code PUSH_FLOW_LAYER} and {@code PULL_FLOW_LAYER}. + * @return The {@link CallGraph} instance for the specified layer, or {@code null} if the layer is invalid. + */ + private CallGraph getCallGraph(final int layer) { + switch (layer) { + case PUSH_FLOW_LAYER: { + return controlFlowGraph.getPushCallGraph(); + } + case PULL_FLOW_LAYER: { + return controlFlowGraph.getPullCallGraph(); + } + default: { + return null; + } + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowModelingStageStatus.java b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowModelingStageStatus.java new file mode 100644 index 0000000..7a82d68 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowModelingStageStatus.java @@ -0,0 +1,7 @@ +package application.editor.stages; + +public enum ControlFlowModelingStageStatus { + SELECTING_AN_EDGE, // いずれかのエッジを選ぼうとしている状態(Idle状態) + SHOWING_DELEGATABLE_NODES, // CFD適用可能範囲を表示している状態 + SHOWING_DEPENDABLE_NODES // DoM適用可能範囲を表示している状態 +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DataFlowCellEditor.java b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DataFlowCellEditor.java new file mode 100644 index 0000000..87539ab --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DataFlowCellEditor.java @@ -0,0 +1,100 @@ +package application.editor.stages; + +import application.editor.FlowCellEditor; +import com.mxgraph.model.mxCell; +import com.mxgraph.swing.mxGraphComponent; +import models.algebra.Expression; +import models.dataFlowModel.DataTransferChannel; +import models.visualModel.FormulaChannel; +import parser.Parser; +import parser.exceptions.ExpectedColon; +import parser.exceptions.ExpectedDoubleQuotation; +import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.WrongJsonExpression; + +import javax.swing.*; +import java.awt.*; +import java.util.EventObject; + +public class DataFlowCellEditor extends FlowCellEditor { + public DataFlowCellEditor(DataFlowModelingStage stage, mxGraphComponent graphComponent) { + super(stage, graphComponent); + } + + @Override + public void startEditing(Object cellObj, EventObject eventObj) { + if (editingCell != null) { + stopEditing(true); + } + if (graphComponent.getGraph().getModel().isEdge(cellObj)) { + return; + } + DataTransferChannel ch = (DataTransferChannel) ((DataFlowModelingStage) stage).getChannel((mxCell) cellObj); + if (ch == null) { + // selected cell is a resource + return; + } + JPanel panel = new JPanel(); + if (ch instanceof FormulaChannel) { + JLabel label1 = new JLabel("Formula: "); + JLabel label2 = new JLabel("Source: "); + GridBagLayout layout = new GridBagLayout(); + panel.setLayout(layout); + GridBagConstraints gbc = new GridBagConstraints(); + + gbc.gridx = 0; + gbc.gridy = 0; + layout.setConstraints(label1, gbc); + panel.add(label1); + + gbc.gridx = 1; + gbc.gridy = 0; + JTextField formulaText = new JTextField(((FormulaChannel) ch).getFormula(), 15); + layout.setConstraints(formulaText, gbc); + panel.add(formulaText); + + gbc.gridx = 0; + gbc.gridy = 1; + layout.setConstraints(label2, gbc); + panel.add(label2); + + gbc.gridx = 1; + gbc.gridy = 1; + JTextArea textArea = new JTextArea(ch.getSourceText(), 7, 15); + textArea.setEditable(false); + layout.setConstraints(textArea, gbc); + panel.add(textArea); + + int option = JOptionPane.showConfirmDialog(null, panel, "Edit Formula Channel", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE); + if (option == JOptionPane.OK_OPTION) { + Parser.TokenStream stream = new Parser.TokenStream(); + Parser parser = new Parser(stream); + + String formula = formulaText.getText(); + stream.addLine(formula.split(Parser.EQUALS)[1]); + + try { + Expression exp = parser.parseTerm(stream, stage.getModel()); + ((FormulaChannel) ch).setFormula(formula); + ((FormulaChannel) ch).setFormulaTerm(exp); + } catch (ExpectedRightBracket | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + } else { + JTextArea textArea = new JTextArea(ch.getSourceText(), 15, 50); + textArea.setLineWrap(true); + textArea.setTabSize(4); + panel.add(textArea); + + int option = JOptionPane.showConfirmDialog(null, panel, "Channel Code", JOptionPane.OK_CANCEL_OPTION); + if (option == JOptionPane.OK_OPTION) { + ((DataFlowModelingStage) stage).setChannelCode(ch, textArea.getText()); + } + } + } + + @Override + public void stopEditing(boolean cancel) { + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DataFlowModelingStage.java b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DataFlowModelingStage.java new file mode 100644 index 0000000..610f8a2 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DataFlowModelingStage.java @@ -0,0 +1,420 @@ +package application.editor.stages; + +import algorithms.Validation; +import application.editor.Editor; +import application.editor.FlowCellEditor; +import application.editor.Stage; +import com.mxgraph.model.mxCell; +import com.mxgraph.model.mxGeometry; +import com.mxgraph.model.mxGraphModel; +import com.mxgraph.model.mxGraphModel.mxTerminalChange; +import com.mxgraph.swing.mxGraphComponent; +import com.mxgraph.util.mxEventSource.mxIEventListener; +import com.mxgraph.util.mxPoint; +import models.algebra.Expression; +import models.dataConstraintModel.Channel; +import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.ResourcePath; +import models.dataConstraintModel.Selector; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.ResourceNode; +import models.visualModel.FormulaChannel; +import parser.Parser; +import parser.exceptions.*; + +import java.awt.event.MouseListener; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class DataFlowModelingStage extends Stage { + private HashMap resNodeToCell = new HashMap<>(); + private HashMap channelToCell = new HashMap<>(); + private final HashMap cellToResNode = new HashMap<>(); + private final HashMap cellToChannel = new HashMap<>(); + + public DataFlowModelingStage(mxGraphComponent graphComponent) { + super(graphComponent); + } + + @Override + public void init(Stage prevStage) { + if (prevStage instanceof PushPullSelectionStage) { + if (((PushPullSelectionStage) prevStage).getResNodeToCell() != null) { + resNodeToCell = ((PushPullSelectionStage) prevStage).getResNodeToCell(); + } + if (((PushPullSelectionStage) prevStage).getChannelToCell() != null) { + channelToCell = ((PushPullSelectionStage) prevStage).getChannelToCell(); + } + for (ResourceNode resNode : resNodeToCell.keySet()) { + cellToResNode.put(resNodeToCell.get(resNode), resNode); + } + for (DataTransferChannel ch : channelToCell.keySet()) { + cellToChannel.put(channelToCell.get(ch), ch); + } + } else if (prevStage instanceof ControlFlowModelingStage) { + showLayers(DATA_FLOW_LAYER); + } + } + + @Override + public FlowCellEditor createCellEditor(mxGraphComponent graphComponent) { + return new DataFlowCellEditor(this, graphComponent); + } + + @Override + public mxIEventListener createChangeEventListener(Editor editor) { + return (sender, event) -> { + List terminals = new ArrayList<>(); + mxCell cell = null; + for (Object change : ((List) event.getProperties().get("changes"))) { + if (change instanceof mxTerminalChange) { + mxTerminalChange terminalChange = (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}); + graph.clearSelection(); + } + } + }; + } + + @Override + public MouseListener createMouseEventListener(Editor editor) { + return null; + } + + @Override + public boolean canChangeFrom(Stage prevStage) { + return true; + } + + public void clear() { + model = null; + + ((mxGraphModel) graph.getModel()).clear(); + constructLayer(this.graph); + } + + public DataTransferModel getModel() { + if (model == null) { + model = new DataTransferModel(); + } + return model; + } + + public void setModel(DataTransferModel model) { + // Clear the mxGraph + clear(); + + this.model = model; + } + + public boolean isValid() { + if (model == null) { + return false; + } + return Validation.checkUpdateConflict(model); + } + + public ResourceNode getResourceNode(mxCell cell) { + return cellToResNode.get(cell); + } + + public Channel getChannel(mxCell cell) { + return cellToChannel.get(cell); + } + + public void addResourceNode(ResourceNode parentNode, String resName) { + ResourceNode resourceNode = null; + ResourcePath resourcePath = null; + if (parentNode == null) { + resourcePath = new ResourcePath(resName); + resourceNode = new ResourceNode(null, resourcePath); + getModel().addResourcePath(resourcePath); + } else { + ResourcePath parentPath = parentNode.getPrimaryResourcePath(); + if (resName.startsWith(Parser.LEFT_CURLY_BRACKET) && resName.endsWith(Parser.RIGHT_CURLY_BRACKET)) { + Parser.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); + resourceNode = new ResourceNode(parentNode, resourcePath); + getModel().addResourcePath(resourcePath); + } catch (ExpectedRightBracket | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } else { + resourcePath = new ResourcePath(parentPath, resName); + resourceNode = new ResourceNode(parentNode, resourcePath); + getModel().addResourcePath(resourcePath); + } + } + + graph.getModel().beginUpdate(); + mxCell root = (mxCell) graph.getDefaultParent(); + mxCell layer = (mxCell) root.getChildAt(DATA_FLOW_LAYER); + try { + if (parentNode == null) { + mxCell resCell = (mxCell) graph.insertVertex(layer, null, resourceNode.getPrimaryResourcePath().getName(), 20, 20, 80, 30, "shape=ellipse;perimeter=ellipsePerimeter;verticalAlign=top"); // insert a resource as a vertex + resNodeToCell.put(resourceNode, resCell); + cellToResNode.put(resCell, resourceNode); + } else { + mxCell parentCell = resNodeToCell.get(parentNode); + double pw = parentCell.getGeometry().getWidth(); + double ph = parentCell.getGeometry().getHeight(); + mxCell resCell = (mxCell) graph.insertVertex(parentCell, null, resourceNode.getPrimaryResourcePath().getName(), 20, 20, pw * 0.8, ph * 0.8, "shape=ellipse;perimeter=ellipsePerimeter;verticalAlign=top"); // insert a resource as a vertex + resNodeToCell.put(resourceNode, resCell); + cellToResNode.put(resCell, resourceNode); + } + } finally { + graph.getModel().endUpdate(); + } + } + + public void addChannel(DataTransferChannel channel) { + getModel().addChannel(channel); + + graph.getModel().beginUpdate(); + mxCell root = (mxCell) graph.getDefaultParent(); + mxCell layer = (mxCell) root.getChildAt(DATA_FLOW_LAYER); + try { + mxGeometry geo1 = new mxGeometry(0, 0.5, PORT_DIAMETER, PORT_DIAMETER); + geo1.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS)); + geo1.setRelative(true); + + mxGeometry geo2 = new mxGeometry(1.0, 0.5, PORT_DIAMETER, PORT_DIAMETER); + geo2.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS)); + geo2.setRelative(true); + + String channelName = channel.getChannelName(); + if (channel.getSelectors().size() > 0) { + channelName += "("; + String delimiter = ""; + for (Selector s : channel.getSelectors()) { + Expression exp = s.getExpression(); + String selectorName = exp.toString(); + channelName += delimiter + selectorName; + delimiter = ", "; + } + channelName += ")"; + } + Object chCell = graph.insertVertex(layer, null, channelName, 150, 20, 30, 30, "verticalAlign=top"); // 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 + channelToCell.put(channel, (mxCell) chCell); + cellToChannel.put((mxCell) chCell, channel); + cellToChannel.put(port_in, channel); + cellToChannel.put(port_out, channel); + } finally { + graph.getModel().endUpdate(); + } + } + + public void addEventChannel(DataTransferChannel eventChannel) { + getModel().addInputChannel(eventChannel); + + graph.getModel().beginUpdate(); + mxCell root = (mxCell) graph.getDefaultParent(); + mxCell layer = (mxCell) root.getChildAt(NODE_LAYER); + try { + mxGeometry geo2 = new mxGeometry(1.0, 0.5, PORT_DIAMETER, PORT_DIAMETER); + geo2.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS)); + geo2.setRelative(true); + + String channelName = eventChannel.getChannelName(); + if (eventChannel.getSelectors().size() > 0) { + channelName += "("; + String delimiter = ""; + for (Selector s : eventChannel.getSelectors()) { + Expression exp = s.getExpression(); + String selectorName = exp.toString(); + channelName += delimiter + selectorName; + delimiter = ", "; + } + channelName += ")"; + } + Object chCell = graph.insertVertex(layer, null, channelName, 150, 20, 30, 30, "verticalAlign=top"); // 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, chCell); // insert the output port of a channel + channelToCell.put(eventChannel, (mxCell) chCell); + cellToChannel.put((mxCell) chCell, eventChannel); + cellToChannel.put(port_out, eventChannel); + } finally { + graph.getModel().endUpdate(); + } + } + + public void addFormulaChannel(FormulaChannel formulaChannel) { + getModel().addChannel(formulaChannel); + + graph.getModel().beginUpdate(); + mxCell root = (mxCell) graph.getDefaultParent(); + mxCell layer = (mxCell) root.getChildAt(DATA_FLOW_LAYER); + try { + mxGeometry geo1 = new mxGeometry(0, 0.5, PORT_DIAMETER, PORT_DIAMETER); + geo1.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS)); + geo1.setRelative(true); + + mxGeometry geo2 = new mxGeometry(1.0, 0.5, PORT_DIAMETER, PORT_DIAMETER); + geo2.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS)); + geo2.setRelative(true); + + String channelName = formulaChannel.getChannelName(); + if (formulaChannel.getSelectors().size() > 0) { + channelName += "("; + String delimiter = ""; + for (Selector s : formulaChannel.getSelectors()) { + Expression exp = s.getExpression(); + String selectorName = exp.toString(); + channelName += delimiter + selectorName; + delimiter = ", "; + } + channelName += ")"; + } + Object chCell = graph.insertVertex(layer, null, channelName, 150, 20, 30, 30, "verticalAlign=top"); // 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 + channelToCell.put(formulaChannel, (mxCell) chCell); + cellToChannel.put((mxCell) chCell, formulaChannel); + cellToChannel.put(port_in, formulaChannel); + cellToChannel.put(port_out, formulaChannel); + } finally { + graph.getModel().endUpdate(); + } + } + + public boolean connectEdge(mxCell edge, mxCell src, mxCell dst) { + DataTransferChannel srcCh = cellToChannel.get(src); + if (srcCh == null) { + ResourceNode srcResNode = cellToResNode.get(src); + DataTransferChannel dstCh = cellToChannel.get(dst); + if (srcResNode == null || dstCh == null) { + return false; + } + // resource to channel edge + ResourcePath srcRes = srcResNode.getPrimaryResourcePath(); + if (srcResNode.getIndegree() + srcResNode.getOutdegree() == 0) { + srcResNode.addOutSideResource(dstCh, srcRes); + } else { + srcRes = new ResourcePath(srcRes.getName(), srcRes.getResourceHierarchy()); + model.addResourcePath(srcRes); + } + ChannelMember srcCm = new ChannelMember(srcRes); + dstCh.addChannelMemberAsInput(srcCm); + edge.setValue(new Editor.SrcDstAttribute(srcRes, dstCh)); + return true; + } + ResourceNode dstResNode = cellToResNode.get(dst); + if (dstResNode == null) { + return false; + } + // channel to resource edge + ResourcePath dstRes = dstResNode.getPrimaryResourcePath(); + if (dstResNode.getIndegree() + dstResNode.getOutdegree() == 0) { + dstResNode.addInSideResource(srcCh, dstRes); + } else { + dstRes = new ResourcePath(dstRes.getName(), dstRes.getResourceHierarchy()); + model.addResourcePath(dstRes); + } + ChannelMember dstCm = new ChannelMember(dstRes); + srcCh.addChannelMemberAsOutput(dstCm); + edge.setValue(new Editor.SrcDstAttribute(srcCh, dstRes)); + return true; + } + + public void delete() { + for (Object obj : graph.getSelectionCells()) { + mxCell cell = (mxCell) obj; + if (cell.isEdge()) { + mxCell srcCell = (mxCell) cell.getSource(); + mxCell dstCell = (mxCell) cell.getTarget(); + if (cellToResNode.get(srcCell) != null) { + // resource to channel edge + DataTransferChannel ch = cellToChannel.get(dstCell); + ch.removeChannelMember(cellToResNode.get(srcCell).getOutSideResource(ch)); + } else if (cellToResNode.get(dstCell) != null) { + // channel to resource edge + DataTransferChannel ch = cellToChannel.get(srcCell); + ch.removeChannelMember(cellToResNode.get(dstCell).getInSideResource(ch)); + } + } else if (cell.isVertex()) { + if (cellToChannel.get(cell) != null) { + DataTransferChannel ch = cellToChannel.get(cell); + if (ch.getInputChannelMembers().size() == 0) { + model.removeInputChannel(cellToChannel.get(cell).getChannelName()); + } else { + model.removeChannel(cellToChannel.get(cell).getChannelName()); + } + } else if (cellToResNode.get(cell) != null) { + for (ResourcePath resPath : cellToResNode.get(cell).getInSideResources()) { + model.removeResourcePath(resPath); + } + for (ResourcePath resPath : cellToResNode.get(cell).getOutSideResources()) { + model.removeResourcePath(resPath); + } + } + } + } + graph.removeCells(graph.getSelectionCells()); + } + + public void setChannelCode(DataTransferChannel ch, String code) { + ch.setSourceText(code); + Parser.TokenStream stream = new Parser.TokenStream(); + Parser parser = new Parser(stream); + + for (String line : code.split("\n")) { + stream.addLine(line); + } + try { + DataTransferChannel ch2 = parser.parseChannel(getModel()); + for (ChannelMember chm2 : ch2.getInputChannelMembers()) { + for (ChannelMember chm : ch.getInputChannelMembers()) { + if (chm2.getResource().getResourceHierarchy() == chm.getResource().getResourceHierarchy()) { + chm.setStateTransition(chm2.getStateTransition()); + break; + } + } + } + for (ChannelMember chm2 : ch2.getOutputChannelMembers()) { + for (ChannelMember chm : ch.getOutputChannelMembers()) { + if (chm2.getResource().getResourceHierarchy() == chm.getResource().getResourceHierarchy()) { + chm.setStateTransition(chm2.getStateTransition()); + break; + } + } + } + for (ChannelMember chm2 : ch2.getReferenceChannelMembers()) { + for (ChannelMember chm : ch.getReferenceChannelMembers()) { + if (chm2.getResource().getResourceHierarchy() == chm.getResource().getResourceHierarchy()) { + chm.setStateTransition(chm2.getStateTransition()); + break; + } + } + } + } catch (ExpectedRightBracket | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | + ExpectedInOrOutOrRefOrSubKeyword | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | + WrongLHSExpression | WrongRHSExpression | ExpectedAssignment | ExpectedRightCurlyBracket | + WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/PushPullSelectionCellEditor.java b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/PushPullSelectionCellEditor.java new file mode 100644 index 0000000..3936693 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/PushPullSelectionCellEditor.java @@ -0,0 +1,131 @@ +package application.editor.stages; + +import application.editor.FlowCellEditor; +import com.mxgraph.model.mxIGraphModel; +import com.mxgraph.swing.mxGraphComponent; +import com.mxgraph.util.mxConstants; +import com.mxgraph.util.mxUtils; +import com.mxgraph.view.mxCellState; +import models.dataFlowModel.PushPullAttribute; +import models.dataFlowModel.PushPullValue; + +import javax.swing.*; +import java.awt.*; +import java.util.EventObject; +import java.util.List; + +public class PushPullSelectionCellEditor extends FlowCellEditor { + public int DEFAULT_MIN_WIDTH = 70; + public int DEFAULT_MIN_HEIGHT = 30; + public double DEFAULT_MINIMUM_EDITOR_SCALE = 1; + + protected double minimumEditorScale = DEFAULT_MINIMUM_EDITOR_SCALE; + protected int minimumWidth = DEFAULT_MIN_WIDTH; + protected int minimumHeight = DEFAULT_MIN_HEIGHT; + + private EventObject trigger; + private JComboBox comboBox; + + public PushPullSelectionCellEditor(PushPullSelectionStage stage, mxGraphComponent graphComponent) { + super(stage, graphComponent); + } + + @Override + public void startEditing(Object cellObj, EventObject eventObj) { + if (editingCell != null) { + stopEditing(true); + } + if (!graphComponent.getGraph().getModel().isEdge(cellObj)) { + return; + } + mxCellState state = graphComponent.getGraph().getView().getState(cellObj); + if (state != null && state.getLabel() != null && !state.getLabel().isEmpty()) { + editingCell = cellObj; + trigger = eventObj; + + double scale = Math.max(minimumEditorScale, graphComponent.getGraph().getView().getScale()); + Object value = graphComponent.getGraph().getModel().getValue(cellObj); + if (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(); + } + } + } + + @Override + public void stopEditing(boolean cancel) { + if (editingCell == null) { + return; + } + comboBox.transferFocusUpCycle(); + Object cell = editingCell; + editingCell = null; + if (!cancel) { + EventObject trig = trigger; + trigger = null; + Object value = graphComponent.getGraph().getModel().getValue(cell); + if (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) { + attr.selectOption(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 = 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 -= (int) state.getWidth(); + } else if (horizontal.equals(mxConstants.ALIGN_RIGHT)) { + bounds.x += (int) state.getWidth(); + } + String vertical = mxUtils.getString(state.getStyle(), mxConstants.STYLE_VERTICAL_LABEL_POSITION, mxConstants.ALIGN_MIDDLE); + if (vertical.equals(mxConstants.ALIGN_TOP)) { + bounds.y -= (int) state.getHeight(); + } else if (vertical.equals(mxConstants.ALIGN_BOTTOM)) { + bounds.y += (int) 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/editor/stages/PushPullSelectionStage.java b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/PushPullSelectionStage.java new file mode 100644 index 0000000..d857953 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/PushPullSelectionStage.java @@ -0,0 +1,377 @@ +package application.editor.stages; + +import algorithms.DataTransferModelAnalyzer; +import application.editor.Editor; +import application.editor.FlowCellEditor; +import application.editor.Stage; +import com.mxgraph.model.mxCell; +import com.mxgraph.model.mxGeometry; +import com.mxgraph.model.mxGraphModel; +import com.mxgraph.swing.mxGraphComponent; +import com.mxgraph.util.mxEventSource.mxIEventListener; +import com.mxgraph.util.mxPoint; +import generators.JavaCodeGenerator; +import models.Edge; +import models.algebra.*; +import models.dataConstraintModel.Channel; +import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.ResourcePath; +import models.dataConstraintModel.Selector; +import models.dataFlowModel.*; + +import java.awt.event.MouseListener; +import java.util.*; + +public class PushPullSelectionStage extends Stage { + private DataFlowGraph dataFlowGraph = null; + private HashMap resNodeToCell; + private HashMap channelToCell; + + public PushPullSelectionStage(mxGraphComponent graphComponent) { + super(graphComponent); + } + + @Override + public void init(Stage prevStage) { + if (prevStage instanceof DataFlowModelingStage) { + model = prevStage.getModel(); + dataFlowGraph = analyzeDataTransferModel(model); + showLayers(DATA_FLOW_LAYER); + } + if (prevStage instanceof ControlFlowModelingStage) { + showLayers(DATA_FLOW_LAYER); + } + } + + @Override + public FlowCellEditor createCellEditor(mxGraphComponent graphComponent) { + return new PushPullSelectionCellEditor(this, graphComponent); + } + + @Override + public mxIEventListener createChangeEventListener(Editor editor) { + return (sender, eventObject) -> { + List terminals = new ArrayList<>(); + mxCell cell = null; + for (Object change : ((List) eventObject.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) { + // cancel connect + graph.removeCells(new mxCell[]{cell}); + graph.clearSelection(); + } + }; + } + + @Override + public MouseListener createMouseEventListener(Editor editor) { + return null; + } + + @Override + public boolean canChangeFrom(Stage prevStage) { + if (prevStage instanceof DataFlowModelingStage) { + return ((DataFlowModelingStage) prevStage).isValid(); + } + if (prevStage instanceof ControlFlowModelingStage) return true; + return false; + } + + private DataFlowGraph analyzeDataTransferModel(DataTransferModel model) { + DataFlowGraph flowGraph = DataTransferModelAnalyzer.createDataFlowGraphWithStateStoringAttribute(model); + dataFlowGraph = DataTransferModelAnalyzer.annotateWithSelectableDataTransferAttiribute(flowGraph); + mxCell parent = (mxCell) graph.getDefaultParent(); + mxCell layer = (mxCell) parent.getChildAt(DATA_FLOW_LAYER); + if (layer.getChildCount() == 0) { + constructGraph(); // Construct data-flow graph (on file open action) + } // Construct data-flow graph (on file open action) + updateEdgeAttributes(dataFlowGraph); // Update push/pull selection pull-downs + return dataFlowGraph; + } + + private void updateEdgeAttributes(DataFlowGraph dataFlowGraph) { + mxCell root = (mxCell) graph.getDefaultParent(); + mxCell layer = (mxCell) root.getChildAt(DATA_FLOW_LAYER); + + graph.getModel().beginUpdate(); + try { + // add input, output and reference edges + for (Edge e : dataFlowGraph.getEdges()) { + if (e instanceof DataFlowEdge) { + DataFlowEdge dataFlow = (DataFlowEdge) e; + if (!dataFlow.isChannelToResource()) { + ResourceNode srcRes = (ResourceNode) dataFlow.getSource(); + DataTransferChannel channel = ((ChannelNode) dataFlow.getDestination()).getChannel(); + // input edge + for (Object edge : graph.getChildEdges(layer)) { + mxCell edgeCell = (mxCell) edge; + if (edgeCell.getValue() instanceof Editor.SrcDstAttribute) { + Editor.SrcDstAttribute edgeAttr = (Editor.SrcDstAttribute) edgeCell.getValue(); + if (srcRes.getPrimaryResourcePath().equals(edgeAttr.getSource()) && channel.equals(edgeAttr.getDestination())) { + edgeCell.setValue(dataFlow.getAttribute()); + break; + } + } + } + } + } + } + } finally { + graph.getModel().endUpdate(); + } + graph.refresh(); + } + + /** + * Construct a mxGraph from DataFlowGraph + */ + public void constructGraph() { + ((mxGraphModel) graph.getModel()).clear(); + constructLayer(this.graph); + + mxCell parent = (mxCell) graph.getDefaultParent(); + mxCell layer = (mxCell) parent.getChildAt(DATA_FLOW_LAYER); + + graph.getModel().beginUpdate(); + try { + Map channelInToCell = new HashMap<>(); + Map channelOutToCell = new HashMap<>(); + channelToCell = new HashMap<>(); + resNodeToCell = new HashMap<>(); + + mxGeometry geoPortIn = new mxGeometry(0, 0.5, PORT_DIAMETER, PORT_DIAMETER); + geoPortIn.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS)); + geoPortIn.setRelative(true); + + mxGeometry geoPortOut = new mxGeometry(1.0, 0.5, PORT_DIAMETER, PORT_DIAMETER); + geoPortOut.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS)); + geoPortOut.setRelative(true); + + // create resource vertices + for (ResourceNode resourceNode : dataFlowGraph.getRootResourceNodes()) { + int w = 80; + int h = 30; + ResourcePath resourcePath = resourceNode.getPrimaryResourcePath(); + mxCell resourceCell = (mxCell) graph.insertVertex(layer, null, resourcePath.getLeafResourceName(), 20, 20, w, h, "shape=ellipse;perimeter=ellipsePerimeter;verticalAlign=top"); // insert a resource as a vertex + resNodeToCell.put(resourceNode, resourceCell); + + createChildResourceVertices(resourceCell, resourceNode, w, h); + } + + // create channel vertices + for (ChannelNode channelNode : dataFlowGraph.getRootChannelNodes()) { + DataTransferChannel channel = channelNode.getChannel(); + if (!channel.getInputResources().isEmpty()) { + // Normal channel + if (channelInToCell.get(channel) == null || channelOutToCell.get(channel) == null) { + String channelName = channel.getChannelName(); + if (channel.getSelectors().size() > 0) { + channelName += "("; + String delimiter = ""; + for (Selector s : channel.getSelectors()) { + Expression exp = s.getExpression(); + String selectorName = exp.toString(); + channelName += delimiter + selectorName; + delimiter = ", "; + } + channelName += ")"; + } + int w = 60; + int h = 30; + if (channelNode.getChildren().size() > 0) { + w *= 2; + h *= 2; + } + mxCell channelCell = (mxCell) graph.insertVertex(layer, null, channelName, 150, 20, w, h, "verticalAlign=top"); // insert a channel as a vertex + channelToCell.put(channel, channelCell); + + mxCell portIn = new mxCell(null, geoPortIn, "shape=ellipse;perimter=ellipsePerimeter"); + portIn.setVertex(true); + graph.addCell(portIn, channelCell); // insert the input port of a channel + channelInToCell.put(channel, portIn); + + mxCell portOut = new mxCell(null, geoPortOut, "shape=ellipse;perimter=ellipsePerimeter"); + portOut.setVertex(true); + graph.addCell(portOut, channelCell); // insert the output port of a channel + channelOutToCell.put(channel, portOut); + createChildChannelVertices(channelCell, channelNode, channelInToCell, channelOutToCell, geoPortIn, geoPortOut, w, h); + } + } else { + // Event channel + if (channelOutToCell.get(channel) == null) { + String channelName = channel.getChannelName(); + if (channel.getSelectors().size() > 0) { + channelName += "("; + String delimiter = ""; + for (Selector s : channel.getSelectors()) { + Expression exp = s.getExpression(); + String selectorName = exp.toString(); + channelName += delimiter + selectorName; + delimiter = ", "; + } + channelName += ")"; + } + int w = 40; + int h = 30; + if (channelNode.getChildren().size() > 0) { + w *= 2; + h *= 2; + } + mxCell channelCell = (mxCell) graph.insertVertex(layer, null, channelName, 150, 20, w, h, "verticalAlign=top"); // insert a channel as a vertex + channelToCell.put(channel, channelCell); + + mxCell portOut = new mxCell(null, geoPortOut, "shape=ellipse;perimter=ellipsePerimeter"); + portOut.setVertex(true); + graph.addCell(portOut, channelCell); // insert the output port of a channel + channelOutToCell.put(channel, portOut); + createChildChannelVertices(channelCell, channelNode, channelInToCell, channelOutToCell, geoPortIn, geoPortOut, w, h); + } + } + } + + // add input, output and reference edges + for (Edge edge : dataFlowGraph.getEdges()) { + DataFlowEdge dataFlowEdge = (DataFlowEdge) edge; + if (dataFlowEdge.isChannelToResource()) { + // output edge + DataTransferChannel channel = ((ChannelNode) dataFlowEdge.getSource()).getChannel(); + ResourcePath dstRes = ((ResourceNode) dataFlowEdge.getDestination()).getInSideResource(channel); + graph.insertEdge(layer, null, new Editor.SrcDstAttribute(channel, dstRes), channelOutToCell.get(channel), resNodeToCell.get((ResourceNode) dataFlowEdge.getDestination()), "strokeColor=red;movable=false"); + } else { + // input edge + DataTransferChannel channel = ((ChannelNode) dataFlowEdge.getDestination()).getChannel(); + ResourcePath srcRes = ((ResourceNode) dataFlowEdge.getSource()).getOutSideResource(channel); + Set> toRes = getResourceDependencyForChannel(channel, dataFlowGraph); + for (Map.Entry RtoR : toRes) { + graph.insertEdge(layer, null, null, resNodeToCell.get(RtoR.getValue()), resNodeToCell.get(RtoR.getKey()), "strokeColor=red;dashed=true;movable=false"); + } + + graph.insertEdge(layer, null, new Editor.SrcDstAttribute(srcRes, channel), resNodeToCell.get((ResourceNode) dataFlowEdge.getSource()), channelInToCell.get(channel), "strokeColor=red;movable=false"); + } + } + + for (Channel ch : model.getChannels()) { + // reference edges + DataTransferChannel channel = (DataTransferChannel) ch; + for (ResourcePath refRes : channel.getReferenceResources()) { + graph.insertEdge(layer, null, null, resNodeToCell.get(dataFlowGraph.getResourceNode(refRes)), channelInToCell.get(channel), "strokeColor=red;dashed=true;movable=false"); + } + } + } finally { + graph.getModel().endUpdate(); + } + } + + private void createChildResourceVertices(mxCell parentCell, ResourceNode parentResNode, int w, int h) { + for (ResourceNode resNode : parentResNode.getChildren()) { + ResourcePath resPath = resNode.getPrimaryResourcePath(); + mxCell resourceCell = (mxCell) graph.insertVertex(parentCell, null, resPath.getName(), 0, 0, w, h, "shape=ellipse;perimeter=ellipsePerimeter;verticalAlign=top"); // insert a resource as a vertex + resNodeToCell.put(resNode, resourceCell); + createChildResourceVertices(resourceCell, resNode, w, h); + } + } + + private void createChildChannelVertices(mxCell parentCell, ChannelNode parentChannelNode, + Map channelInToCell, Map channelOutToCell, mxGeometry geoPortIn, mxGeometry geoPortOut, int w, int h) { + for (ChannelNode channelNode : parentChannelNode.getChildren()) { + DataTransferChannel channel = channelNode.getChannel(); + if (!channel.getInputResources().isEmpty()) { + // Normal channel + if (channelInToCell.get(channel) == null || channelOutToCell.get(channel) == null) { + String channelName = channel.getChannelName(); + if (channel.getSelectors().size() > 0) { + channelName += "("; + String delimiter = ""; + for (Selector s : channel.getSelectors()) { + Expression exp = s.getExpression(); + String selectorName = exp.toString(); + channelName += delimiter + selectorName; + delimiter = ", "; + } + channelName += ")"; + } + mxCell channelCell = (mxCell) graph.insertVertex(parentCell, null, channelName, w / 4, h / 4, w / 2, h / 2, "verticalAlign=top"); // insert a channel as a vertex + channelToCell.put(channel, channelCell); + + mxCell portIn = new mxCell(null, geoPortIn, "shape=ellipse;perimter=ellipsePerimeter"); + portIn.setVertex(true); + graph.addCell(portIn, channelCell); // insert the input port of a channel + channelInToCell.put(channel, portIn); + + mxCell portOut = new mxCell(null, geoPortOut, "shape=ellipse;perimter=ellipsePerimeter"); + portOut.setVertex(true); + graph.addCell(portOut, channelCell); // insert the output port of a channel + channelOutToCell.put(channel, portOut); + createChildChannelVertices(channelCell, channelNode, channelInToCell, channelOutToCell, geoPortIn, geoPortOut, w / 2, h / 2); + } + } else { + // Event channel + if (channelOutToCell.get(channel) == null) { + String channelName = channel.getChannelName(); + if (channel.getSelectors().size() > 0) { + channelName += "("; + String delimiter = ""; + for (Selector s : channel.getSelectors()) { + Expression exp = s.getExpression(); + String selectorName = exp.toString(); + channelName += delimiter + selectorName; + delimiter = ", "; + } + channelName += ")"; + } + mxCell channelCell = (mxCell) graph.insertVertex(parentCell, null, channelName, w / 4, h / 4, w / 2, h / 2, "verticalAlign=top"); // insert a channel as a vertex + channelToCell.put(channel, channelCell); + + mxCell portOut = new mxCell(null, geoPortOut, "shape=ellipse;perimter=ellipsePerimeter"); + portOut.setVertex(true); + graph.addCell(portOut, channelCell); // insert the output port of a channel + channelOutToCell.put(channel, portOut); + createChildChannelVertices(channelCell, channelNode, channelInToCell, channelOutToCell, geoPortIn, geoPortOut, w / 2, h / 2); + } + } + } + } + + private Set> getResourceDependencyForChannel(DataTransferChannel ch, DataFlowGraph dataFlowGraph) { + Set> resourceDependency = new HashSet<>(); + if (!ch.getOutputChannelMembers().isEmpty()) { + 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(); + } + resourceDependency.add(new AbstractMap.SimpleEntry<>(srcNode, dstNode)); + } + } + } + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage | + UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + } + return resourceDependency; + } + + public DataFlowGraph getDataFlowGraph() { + return dataFlowGraph; + } + + public HashMap getResNodeToCell() { + return resNodeToCell; + } + + public HashMap getChannelToCell() { + return channelToCell; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/layouts/DAGLayout.java b/AlgebraicDataflowArchitectureModel/src/application/layouts/DAGLayout.java index d493d10..785be27 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/layouts/DAGLayout.java +++ b/AlgebraicDataflowArchitectureModel/src/application/layouts/DAGLayout.java @@ -1,9 +1,5 @@ package application.layouts; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - import com.mxgraph.layout.mxGraphLayout; import com.mxgraph.model.mxCell; import com.mxgraph.model.mxGeometry; @@ -12,132 +8,827 @@ import com.mxgraph.view.mxGraph; import com.mxgraph.view.mxGraphView; -public class DAGLayout extends mxGraphLayout { +import java.util.*; +public class DAGLayout extends mxGraphLayout { + private mxIGraphModel graphModel; + private mxGraphView view; + private Map> dotEdges; + private List> paths; + private List order; + private Map> orderInResourceHierarchy; + private Set usedPathIndex; + private List cells; + private Set roots; + private Map resourceToRoot; + private Set resources; + private Set channels; + private Set eventChannels; + private Map> vertexToDepths; + private Map> cellToPathIndex; + 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); - } - - public void execute(Object parent) { - mxIGraphModel model = graph.getModel(); - model.beginUpdate(); + graphModel = graph.getModel(); + view = graph.getView(); + dotEdges = new HashMap<>(); + paths = new ArrayList<>(); + order = new ArrayList<>(); + orderInResourceHierarchy = new HashMap<>(); + usedPathIndex = new HashSet<>(); + cells = new ArrayList<>(); + roots = new HashSet<>(); + resourceToRoot = new HashMap<>(); + resources = new HashSet<>(); + channels = new HashSet<>(); + eventChannels = new HashSet<>(); + vertexToDepths = new HashMap<>(); + cellToPathIndex = new HashMap<>(); + cellGeo = new HashMap<>(); + } + + public void execute(Object rootCell) { + graphModel.beginUpdate(); try { - List> map = new ArrayList>(); - List moved = new ArrayList<>(); - - for (int i = 0; i < model.getChildCount(parent); i++) { + // Initialize cells, roots, channels and eventChannels fields. + for (int i = 0; i < graphModel.getChildCount(rootCell); i++) { + mxCell cell = (mxCell) graphModel.getChildAt(rootCell, 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); + if ("ellipse".equals(state.getStyle().get("shape"))) { + // If the cell represents a root resource. + roots.add(cell); + orderInResourceHierarchy.put(cell, new ArrayList<>()); + traverseResourceHierarchy(cell, cell, 0); + } + + if ("rectangle".equals(state.getStyle().get("shape"))) { + // If the cell represents a root channel. + if ("true".equals(state.getStyle().get("dashed"))) { + continue; + } + + boolean bEventChannel = true; + for (int j = 0; j < cell.getEdgeCount(); j++) { + mxCell edge = (mxCell) cell.getEdgeAt(j); + if (edge.getTarget() == cell) { + bEventChannel = false; + } + } + + if (bEventChannel) { + // If the cell represents a root event channel. + eventChannels.add(cell); + } else { + channels.add(cell); + } + orderInResourceHierarchy.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++; - } + // For all cells + for (mxCell c : cells) { + c.getGeometry().setX(0); + c.getGeometry().setY(0); + + if (resources.contains(c)) { + // For a resource cell. + view.getState(c).getStyle().put("verticalAlign", "top"); + c.getGeometry().setWidth(WIDTH); + c.getGeometry().setHeight(HEIGHT); } - if (count >= map.get(i).size())skip++; } + // Initialize dotEdges. + for (mxCell c : cells) { + dotEdges.put(c, new HashSet<>()); + } + + for (mxCell eventCh : eventChannels) { + List newPath = new ArrayList(); + paths.add(newPath); + constructPaths(eventCh, 0); + } + + recalcDepths(); + + for (int i = 0; i < paths.size(); i++) { + for (mxCell cell : paths.get(i)) { + if (cellToPathIndex.get(cell) == null) { + cellToPathIndex.put(cell, new ArrayList<>()); + } + cellToPathIndex.get(cell).add(i); + } + } + + sortPaths(); + + boolean isVersion1 = true; + + System.out.println(vertexToDepths); + if (isVersion1) { + layout1((mxCell) rootCell); + } else { + layout2((mxCell) rootCell); + } } finally { - model.endUpdate(); + graphModel.endUpdate(); } } + public void traverseResourceHierarchy(mxCell rootResourceCell, mxCell curResourceCell, int layer) { + resourceToRoot.put(curResourceCell, rootResourceCell); + resources.add(curResourceCell); + if (rootResourceCell != curResourceCell) { + orderInResourceHierarchy.get(rootResourceCell).add(curResourceCell); + } + + int childNum = graphModel.getChildCount(curResourceCell); + for (int i = 0; i < childNum; i++) { + mxCell childCell = (mxCell) graphModel.getChildAt(curResourceCell, i); + cells.add(childCell); + resources.add(childCell); + traverseResourceHierarchy(rootResourceCell, childCell, layer + 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); + public void constructPaths(mxCell curCell, int depth) { + if (vertexToDepths.get(curCell) == null) { + vertexToDepths.put(curCell, Arrays.asList(depth, depth)); + } else { + List depths = vertexToDepths.get(curCell); + depths.set(0, Math.max(depths.get(0), depth)); + depths.set(1, depths.get(0)); + } + + paths.get(paths.size() - 1).add(curCell); + + int branchCount = 0; + for (int i = 0; i < curCell.getEdgeCount(); i++) { + mxCell edge = (mxCell) curCell.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 dstCell = (mxCell) edge.getTarget(); + + if ((curCell != dstCell) && (dstCell != null)) { + if ("true".equals(state.getStyle().get("dashed"))) { + state.getStyle().put("strokeColor", "#800080"); + dotEdges.get(curCell).add(dstCell); } else { - lines(mapping, (mxCell) edge.getTarget()); + branchCount++; + if (branchCount > 1) { + // If a branch is found. + List newPath = new ArrayList(paths.get(paths.size() - 1)); + while (newPath.get(newPath.size() - 1).getId() != curCell.getId()) { + newPath.remove(newPath.size() - 1); + } + paths.add(newPath); + constructPaths(dstCell, depth + 1); + } else { + constructPaths(dstCell, depth + 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; + public void recalcDepths() { + while (true) { + boolean isUpdated = false; + + for (List path : paths) { + for (int i = 1; i < path.size(); i++) { + mxCell curCell = path.get(i); + mxCell prevCell = path.get(i - 1); + if (!(resources.contains(curCell) && resources.contains(prevCell))) { + // An edge from a resource to a channel + if (vertexToDepths.get(curCell).get(0) < vertexToDepths.get(prevCell).get(1) + 1) { + List dists = vertexToDepths.get(curCell); + dists.set(1, Math.max(dists.get(0), dists.get(1))); + dists.set(0, vertexToDepths.get(prevCell).get(1) + 1); + isUpdated = true; + } + } + } + } + + for (mxCell rootCell : roots) { + + // For all resource cells included in (under) the root resource cell. + for (mxCell cell : orderInResourceHierarchy.get(rootCell)) { + mxCell parentCell = (mxCell) graphModel.getParent(cell); + if (!vertexToDepths.containsKey(cell)) { + if (vertexToDepths.get(parentCell) != null) + vertexToDepths.put(cell, Arrays.asList(vertexToDepths.get(parentCell).get(0), vertexToDepths.get(parentCell).get(0))); + } else { + if (vertexToDepths.get(parentCell) != null && vertexToDepths.get(parentCell).get(0) > vertexToDepths.get(cell).get(0)) { + List dists = vertexToDepths.get(cell); + dists.set(1, Math.max(dists.get(0), dists.get(1))); + dists.set(0, vertexToDepths.get(parentCell).get(0)); + isUpdated = true; + } + + if (vertexToDepths.get(parentCell) != null && vertexToDepths.get(parentCell).get(1) < vertexToDepths.get(cell).get(1)) { + List dists = vertexToDepths.get(parentCell); + dists.set(1, vertexToDepths.get(cell).get(1)); + isUpdated = true; + } + } + } + } + + if (!isUpdated) { + 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; + public void sortPaths() { + for (int i = 0; i < paths.size(); i++) { + List path = paths.get(i); + + if (path.size() > 1 && roots.contains(path.get(1))) { + for (mxCell c : path) { + addPath(c); } } + } + } + + public void addPath(mxCell c) { + if (cellToPathIndex.get(c) != null) { + for (int i : cellToPathIndex.get(c)) { + if (!usedPathIndex.contains(i)) { + order.add(i); + } + usedPathIndex.add(i); + } + } + + for (int i = 0; i < graphModel.getChildCount(c); i++) { + mxCell child = (mxCell) graphModel.getChildAt(c, i); + addPath(child); + } + } + + public void layout1(mxCell s) { + Set movedSet = new HashSet<>(); + Map> movedMap = new TreeMap<>(); + Map distToMaxX = new HashMap<>(); + double maxY = 0; + int maxD = 0; + + putCellGeo(s, 0, 0); + + for (mxCell c : cells) { + if (vertexToDepths.get(c) != null) maxD = Math.max(maxD, vertexToDepths.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; + for (mxCell cur : paths.get(i)) { + if (movedSet.contains(cur)) { + continue; + } + + int d = vertexToDepths.get(cur).get(0); + double x = distToMaxX.get(d - 1) + MOVE_X; + double endX = 0; + double endY = 0; + + 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; + double ny = y; + double nx = x; + + 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()); + + if (distToMaxX.get(d) < endX) { + updateDistToMaxX(distToMaxX, movedSet, movedMap, d, maxD, endX); + } + maxY = Math.max(maxY, endY); + + // For all resource cells included in (under) the root resource cell. + for (mxCell c : orderInResourceHierarchy.get(cur)) { + d = vertexToDepths.get(c).get(0); + nx = x; + mxCell parent = (mxCell) graphModel.getParent(c); + + if (d == vertexToDepths.get(parent).get(0)) { + nx = cellGeo.get(parent).get("x") + MARGIN_WIDTH; + } else { + nx = distToMaxX.get(d - 1) + MOVE_X; + } + + 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) && (vertexToDepths.get(child).get(0) <= d && d <= vertexToDepths.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); + + if (distToMaxX.get(d) < endX) { + updateDistToMaxX(distToMaxX, movedSet, movedMap, d, maxD, endX); + } + maxY = Math.max(maxY, endY); + } + } + } + } + + 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()); + } + } + } + } + } + + for (int i : order) { + mxCell cur = paths.get(i).get(1); + List ecs = new ArrayList<>(); + for (int j : cellToPathIndex.get(cur)) { + if (!ecs.contains(paths.get(j).get(0)) && cur == paths.get(j).get(1)) { + ecs.add(paths.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 = paths.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; + } + } + + 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); + } + + 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()); + } + } + } + + public void layout2(mxCell s) { + Set movedSet = new HashSet<>(); + Map> movedMap = new TreeMap<>(); + Map distToMaxX = new HashMap<>(); + double maxY = 0; + int maxD = 0; + + putCellGeo(s, 0, 0); + + for (mxCell c : cells) { + maxD = Math.max(maxD, vertexToDepths.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; + for (int j = 0; j < paths.get(i).size(); j++) { + mxCell cur = paths.get(i).get(j); + + if (movedSet.contains(cur)) { + continue; + } + + int d = vertexToDepths.get(cur).get(0); + double x = distToMaxX.get(d - 1) + MOVE_X; + double endX = 0; + double endY = 0; + + 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; + 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()); + + if (distToMaxX.get(d) < endX) { + updateDistToMaxX(distToMaxX, movedSet, movedMap, d, maxD, endX); + } + maxY = Math.max(maxY, endY); + + for (mxCell c : orderInResourceHierarchy.get(cur)) { + d = vertexToDepths.get(c).get(0); + nx = x; + mxCell parent = (mxCell) graphModel.getParent(c); + + if (d == vertexToDepths.get(parent).get(0)) { + nx = cellGeo.get(parent).get("x") + MARGIN_WIDTH; + } else { + nx = distToMaxX.get(d - 1) + MOVE_X; + } + + 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) && (vertexToDepths.get(child).get(0) <= d && d <= vertexToDepths.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); + + if (distToMaxX.get(d) < endX) { + updateDistToMaxX(distToMaxX, movedSet, movedMap, d, maxD, endX); + } + maxY = Math.max(maxY, endY); + } + } + + if (channels.contains(cur)) { + mxCell pre = paths.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()); + + if (distToMaxX.get(d) < endX) { + updateDistToMaxX(distToMaxX, movedSet, movedMap, d, maxD, endX); + } + maxY = Math.max(maxY, endY); + } + } + } + + 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()); + } + } + } + } + } + + for (int i : order) { + mxCell cur = paths.get(i).get(1); + List ecs = new ArrayList<>(); + for (int j : cellToPathIndex.get(cur)) { + if (!ecs.contains(paths.get(j).get(0)) && cur == paths.get(j).get(1)) { + ecs.add(paths.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 = paths.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; + } + } + + 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); + } + + 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()); + } + } + } + + 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); + } + + public void addMoved(Set ms, Map> mm, mxCell c, int d) { + ms.add(c); + mm.get(d).add(c); + } + + 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; } } + 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; + } + 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()); + } + } + } + } + } + } + + 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; + } + + 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/application/simulator/InputEventCellEditor.java b/AlgebraicDataflowArchitectureModel/src/application/simulator/InputEventCellEditor.java new file mode 100644 index 0000000..fb04068 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/simulator/InputEventCellEditor.java @@ -0,0 +1,339 @@ +package application.simulator; + +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 java.util.TreeMap; + +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.editor.Editor; +import application.layouts.DAGLayout; +import models.algebra.Expression; +import models.algebra.InvalidMessage; +import models.algebra.ParameterizedIdentifierIsFutureWork; +import models.algebra.Position; +import models.algebra.Term; +import models.algebra.Type; +import models.algebra.UnificationFailed; +import models.algebra.ValueUndefined; +import models.algebra.Variable; +import models.dataConstraintModel.Channel; +import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.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.ExpectedDoubleQuotation; +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 SimulatorWindow window; + 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; + + public InputEventCellEditor(SimulatorWindow window, mxGraphComponent graphComponent, Simulator simulator, Editor editor) { + this.window = window; + this.graphComponent = graphComponent; + this.simulator = simulator; + this.editor = editor; + } + + @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(); // clicked resource + ArrayList eventChs = new ArrayList<>(); // event channel list + ArrayList messageTexts = new ArrayList<>(); // message text list + ArrayList messageExps = new ArrayList<>(); // message expression list + ArrayList> refParams = new ArrayList<>(); // message parameters for ref ports + ResourcePath eventResPath = null; + + for (Channel ch: simulator.getModel().getInputChannels()) { // all channels + eventResPath = getSelectableMessages(ch, resId, eventChs, messageTexts, messageExps, refParams, eventResPath); + } + if (messageTexts.isEmpty()) { + return; + } + String[] messageList = messageTexts.toArray(new String[messageTexts.size()]); + JComboBox messageMenu = new JComboBox(messageList); + + JPanel eventSelectPanel = new JPanel(); + eventSelectPanel.add(messageMenu); + + int ret = JOptionPane.showConfirmDialog(window, eventSelectPanel, "Event Choice", JOptionPane.OK_CANCEL_OPTION); // Select an event. + if (ret == JOptionPane.OK_OPTION) { + int i, messageIdx; + i = messageIdx = 0; + for (String messageText : messageList) { + if(messageText.equals(messageMenu.getSelectedItem().toString())) { + messageIdx = i; + } + i++; + } + JPanel inputEventPanel = new JPanel(); + JTextArea textArea = new JTextArea(messageExps.get(messageIdx).toString(), 10, 30); + inputEventPanel.add(textArea); + + int approve = JOptionPane.showConfirmDialog(window, inputEventPanel, "Event Code", JOptionPane.OK_CANCEL_OPTION); // Input an message text for the event. + 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()); + if (eventMessage instanceof Term) { + TreeMap refMap = refParams.get(messageIdx); + for (Integer paramIdx: refMap.keySet()) { + ((Term) eventMessage).getSymbol().setArity(-1); + ((Term) eventMessage).addChild(paramIdx, refMap.get(paramIdx), true); + } + } + + Event newEvent = new Event(eventChs.get(messageIdx), eventMessage, eventResPath, simulator.getCurState().getResource(resId)); + simulator.transition(newEvent); + + graphComponent.setCellEditor(new InputEventCellEditor(window, graphComponent, simulator, this.editor)); + + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork + | InvalidMessage | UnificationFailed | ValueUndefined | ExpectedRightBracket | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation 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 ResourcePath getSelectableMessages(Channel ch, ResourceIdentifier resId, + ArrayList eventChs, ArrayList messageTexts, ArrayList messageExps, ArrayList> refParams, + ResourcePath eventResPath) { + if (((DataTransferChannel) ch).getInputResources().size() == 0) { // event ch. or normal ch. + for (ChannelMember out: ((DataTransferChannel) ch).getOutputChannelMembers()) { + ResourcePath resPath = out.getResource(); + if (!out.isOutside() && resId.isInstanceOf(resPath)) { + eventResPath = resPath; + eventChs.add(((DataTransferChannel) ch)); + String messageText = null; + Expression mesExp = out.getStateTransition().getMessageExpression(); + TreeMap refMap = new TreeMap<>(); + if (mesExp instanceof Term) { + // Reconstruct an input message template + List pathParams = resPath.getPathParams(); + List children = ((Term) mesExp).getChildren(); + mesExp = new Term(((Term) mesExp).getSymbol()); + for (int i = 0; i < children.size(); i++) { + Expression child = children.get(i); + boolean isRefVar = false; + for (ChannelMember refCm: ((DataTransferChannel) ch).getReferenceChannelMembers()) { + if (refCm.getStateTransition().getMessageExpression() instanceof Term) { + Expression varExp = ((Term) refCm.getStateTransition().getMessageExpression()).getChild(i); + if (varExp != null && varExp instanceof Variable) { + if (refCm.getStateTransition().getCurStateExpression().contains(varExp)) { + // child has come from a reference resource. + isRefVar = true; + break; + } + } + } + } + if (!isRefVar) { + // child has not come from a reference resource. + if (!pathParams.contains(child)) { + ((Term) mesExp).addChild(child); + } else { + int idx = pathParams.indexOf(child); + ((Term) mesExp).addChild(resId.getPathParams().get(idx)); + } + } else { + // child has come from a reference resource. + refMap.put(i, child); + } + } + messageText = ((Term) mesExp).getSymbol().toString(); + } else if(mesExp instanceof Variable) { + messageText = ((Variable) mesExp).getName(); + } + messageExps.add(mesExp); + messageTexts.add(messageText); // for the pull-down menu + refParams.add(refMap); + } + } + for (Channel childCh: ch.getChildren()) { + eventResPath = getSelectableMessages(childCh, resId, eventChs, messageTexts, messageExps, refParams, eventResPath); + } + } + return eventResPath; + } + + @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) { + attr.selectOption(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/simulator/SimulationLayout.java b/AlgebraicDataflowArchitectureModel/src/application/simulator/SimulationLayout.java new file mode 100644 index 0000000..141203c --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/simulator/SimulationLayout.java @@ -0,0 +1,162 @@ +package application.simulator; + +import com.mxgraph.model.mxGraphModel; +import com.mxgraph.view.mxGraph; +import models.dataConstraintModel.ResourcePath; +import simulator.Resource; +import simulator.ResourceIdentifier; +import simulator.Simulator; +import simulator.SystemState; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +public class SimulationLayout { + // Hold the division level for each resource + HashMap divisionLevelMap = new HashMap<>(); + // Hold the scaling factor for each resource + HashMap resourceScaleMap = new HashMap<>(); + int maxDivisionLevel = 1; + public final double BASE_WIDTH = 720; + public final double BASE_HEIGHT = BASE_WIDTH / 2; + public final double MARGIN_SCALE = 0.92; + SystemState systemState; + + public SimulationLayout(SystemState systemState) { + this.systemState = systemState; + // Calculate the division level for each resource + for (Resource res : systemState.getRootResources()) { + setDivisionLevel(divisionLevelMap, res.getResourceIdentifier(), 1); + } + // Calculate the scaling factor for each resource + for (ResourceIdentifier res : divisionLevelMap.keySet()) { + double scale = (double) maxDivisionLevel / divisionLevelMap.get(res); + resourceScaleMap.put(res, scale); + } + } + + public double getScale(ResourceIdentifier resource) { + return resourceScaleMap.get(resource); + } + + public double getDivision(ResourceIdentifier resource) { + return divisionLevelMap.get(resource); + } + + public int getMaxDivisionLevel() { + return maxDivisionLevel; + } + + public boolean isExistResource(ResourceIdentifier resourceIdentifier) { + return divisionLevelMap.containsKey(resourceIdentifier); + } + + private void setDivisionLevel(HashMap resourceSet, ResourceIdentifier resource, int childCount) { + int divisionLevel; + if (resourceSet.get(resource.getParent()) == null) { + divisionLevel = 1; + } else { + divisionLevel = resourceSet.get(resource.getParent()) * childCount; + } + if (divisionLevel > maxDivisionLevel) maxDivisionLevel = divisionLevel; + resourceSet.put(resource, divisionLevel); + + Collection identifiers = systemState.getResource(resource).getChildren(); + if (identifiers != null) { + for (Resource child : identifiers) { + setDivisionLevel(resourceSet, child.getResourceIdentifier(), identifiers.size()); + } + } + } + + /** + * + * Draw an object corresponding to the target resource on the mxGraph. + * + * @param graph The mxGraph for drawing. + * @param parent The parent object of the target resource. + * @param resourceIdentifier The ID of the target resource. + * @param index The index. + * @param length The number of sibling resources of the target resource. + * @return The drawing object of the target resource. + */ + + private Object setLayout(mxGraph graph, Object parent, ResourceIdentifier resourceIdentifier, double index, double length) { + double width, height, x, y; + double parentWidth = graph.getCellGeometry(parent).getWidth(); + double parentHeight = graph.getCellGeometry(parent).getHeight(); + + width = parentWidth / length * MARGIN_SCALE; + height = width / 2; + x = parentWidth * (index - 1) / length + (parentWidth / length) * (1 - MARGIN_SCALE) / 2; + + // Process to avoid hiding the parent's resource name + if ((int) length == 1) { + y = 20; + height -= 20; + } else { + y = parentHeight / 2 - height / 2; + } + + Object result = graph.insertVertex(parent, null, + resourceIdentifier.toString(), x, y, width, height, + "shape=ellipse;perimeter=ellipsePerimeter;verticalAlign=top;"); + System.out.println(result); + return result; + } + + public mxGraph constructSimulateGraph(mxGraph graph, Simulator simulator) { + ((mxGraphModel) graph.getModel()).clear(); + Object parent = graph.getDefaultParent(); + graph.getModel().beginUpdate(); + + try { + Map resources = new HashMap<>(); + int i = 0; + int childCount = simulator.getCurState().getRootResources().size(); + // create resource vertices + for (Resource resNode : simulator.getCurState().getRootResources()) { + double scale = this.getScale(resNode.getResourceIdentifier()); + double maxDiv = this.getMaxDivisionLevel(); + double w = this.BASE_WIDTH * scale / maxDiv / childCount; + double h = this.BASE_HEIGHT * scale / maxDiv / childCount; + double x = w * i + 50; + double y = 20; + 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); + i++; + createNextChildSimulateResourceVerticies(graph, resource, resNode, resources); + + } + + } finally { + graph.getModel().endUpdate(); + } + + return graph; + } + + private void createNextChildSimulateResourceVerticies(mxGraph graph, Object parent, Resource resNode, Map resources) { //sample + Collection children = resNode.getChildren(); + if (children != null) { + //List children = resNode.getChildren(); + double i = 1; + double childCount = children.size(); + for (Resource childNode : children) { + Object childResource = setLayout(graph, parent, childNode.getResourceIdentifier(), i, childCount); + resources.put(childNode, childResource); + i++; + createNextChildSimulateResourceVerticies(graph, childResource, childNode, resources); + + } + } + if (children == null || children.size() == 0) { + Object state = graph.insertVertex(parent, 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 + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/simulator/SimulatorMenuBar.java b/AlgebraicDataflowArchitectureModel/src/application/simulator/SimulatorMenuBar.java new file mode 100644 index 0000000..b998bd8 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/simulator/SimulatorMenuBar.java @@ -0,0 +1,26 @@ +package application.simulator; + +import application.actions.ZoomInAction; +import application.actions.ZoomOutAction; +import application.simulator.actions.ShowUISimulatorAction; + +import javax.swing.*; + +public class SimulatorMenuBar extends JMenuBar { + private static final long serialVersionUID = 7769410930379012970L; + + private SimulatorWindow simulatorWindow = null; + + public SimulatorMenuBar(SimulatorWindow simulatorWindow) { + this.simulatorWindow = simulatorWindow; + + JMenu menu = null; + menu = add(new JMenu("View")); + menu.add(new ZoomInAction(simulatorWindow.getGraphComponent())); + menu.add(new ZoomOutAction(simulatorWindow.getGraphComponent())); + + menu = add(new JMenu("Show")); + menu.add(new ShowUISimulatorAction(simulatorWindow.getSimulator())); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/simulator/SimulatorWindow.java b/AlgebraicDataflowArchitectureModel/src/application/simulator/SimulatorWindow.java new file mode 100644 index 0000000..11553a5 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/simulator/SimulatorWindow.java @@ -0,0 +1,175 @@ +package application.simulator; + +import algorithms.TypeInference; +import application.editor.Editor; +import com.mxgraph.model.mxCell; +import com.mxgraph.model.mxGeometry; +import com.mxgraph.model.mxGraphModel; +import com.mxgraph.swing.handler.mxRubberband; +import com.mxgraph.swing.mxGraphComponent; +import com.mxgraph.util.mxEvent; +import com.mxgraph.util.mxEventObject; +import com.mxgraph.util.mxEventSource.mxIEventListener; +import com.mxgraph.view.mxGraph; +import models.dataFlowModel.DataTransferModel; +import simulator.Event; +import simulator.Simulator; +import simulator.SystemState; +import simulator.interfaces.INativeReceiver; + +import javax.swing.*; +import java.util.ArrayList; +import java.util.List; + +public class SimulatorWindow extends JFrame implements INativeReceiver { + + 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; + private SimulatorMenuBar menuBar; + + public SimulatorWindow(Editor editor) { + setTitle(title); + setDefaultCloseOperation(JFrame.DISPOSE_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); + SimulationLayout layout = new SimulationLayout(simulator.getCurState()); + layout.constructSimulateGraph(graph, simulator); + graphComponent.setCellEditor(new InputEventCellEditor(this, graphComponent, simulator, this.editor)); + simulator.addSystemReceiver(this); + + menuBar = new SimulatorMenuBar(this); + setJMenuBar(menuBar); + } + + + @Override + public void onReceiveFromModel(Event event, SystemState nextSystemState) { + SimulationLayout layout = new SimulationLayout(nextSystemState); + layout.constructSimulateGraph(graph, simulator); + } + +// 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; + } + + public Simulator getSimulator() { + return simulator; + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/simulator/UISimulatorWindow.java b/AlgebraicDataflowArchitectureModel/src/application/simulator/UISimulatorWindow.java new file mode 100644 index 0000000..9c92c63 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/simulator/UISimulatorWindow.java @@ -0,0 +1,28 @@ +package application.simulator; + +import simulator.Simulator; +import simulator.interfaces.swing.SwingPresenter; +import simulator.interfaces.timers.TimerService; + +import javax.swing.*; + +public class UISimulatorWindow extends JFrame { + + private static final long serialVersionUID = 1770206525826167136L; + private SwingPresenter presenter; + private Simulator simulator; + private JPanel mainPanel; + private TimerService timerService; + + public UISimulatorWindow(Simulator simulator) { + setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + this.simulator = simulator; + mainPanel = new JPanel(); + presenter = new SwingPresenter(mainPanel, simulator); + this.add(mainPanel); + timerService = new TimerService(simulator); + + setSize(870, 640); + setVisible(true); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/simulator/actions/ShowUISimulatorAction.java b/AlgebraicDataflowArchitectureModel/src/application/simulator/actions/ShowUISimulatorAction.java new file mode 100644 index 0000000..c2c7aa0 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/simulator/actions/ShowUISimulatorAction.java @@ -0,0 +1,22 @@ +package application.simulator.actions; + +import application.simulator.UISimulatorWindow; +import simulator.Simulator; + +import javax.swing.*; +import java.awt.event.ActionEvent; + +public class ShowUISimulatorAction extends AbstractAction { + private Simulator simulator; + + public ShowUISimulatorAction(Simulator simulator) { + super("Show UI Simulator"); + this.simulator = simulator; + } + + @Override + public void actionPerformed(ActionEvent e) { + new UISimulatorWindow(simulator); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/views/FlowLayerWindow.java b/AlgebraicDataflowArchitectureModel/src/application/views/FlowLayerWindow.java new file mode 100644 index 0000000..d65d76e --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/views/FlowLayerWindow.java @@ -0,0 +1,99 @@ +package application.views; + +import application.ApplicationWindow; +import application.editor.Editor; +import application.editor.IStageChangeListener; +import application.editor.Stage; +import application.editor.stages.ControlFlowModelingStage; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/** + * {@link FlowLayerWindow} shows the window for enabling / disabling layers in the graph. + */ +public class FlowLayerWindow extends JDialog implements IStageChangeListener { + private final String title = "Flow Layer"; + private final JCheckBox dataFlowCheckBox; + private final JCheckBox pushFlowCheckBox; + private final JCheckBox pullFlowCheckBox; + + private final Stage stage; + + public FlowLayerWindow(final ApplicationWindow owner) { + super(owner); + + setTitle(title); + setDefaultCloseOperation(HIDE_ON_CLOSE); + + stage = Editor.STAGE_CONTROL_FLOW_DELEGATION; + + // Add checkboxes. + dataFlowCheckBox = new JCheckBox("Data-Flow", false); + pushFlowCheckBox = new JCheckBox("Push-Flow", true); + pullFlowCheckBox = new JCheckBox("Pull-Flow", true); + + // Add handlers to the checkboxes. + dataFlowCheckBox.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + stage.setLayerEnabled(Stage.DATA_FLOW_LAYER, dataFlowCheckBox.isSelected()); + } + }); + + pushFlowCheckBox.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + stage.setLayerEnabled(Stage.PUSH_FLOW_LAYER, pushFlowCheckBox.isSelected()); + } + }); + + pullFlowCheckBox.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + stage.setLayerEnabled(Stage.PULL_FLOW_LAYER, pullFlowCheckBox.isSelected()); + } + }); + + dataFlowCheckBox.setEnabled(false); + pushFlowCheckBox.setEnabled(false); + pullFlowCheckBox.setEnabled(false); + + // Initialize the layer panel layout. + Container panel = getContentPane(); + panel.setLayout(new GridLayout(4, 1)); + panel.add(dataFlowCheckBox); + panel.add(pushFlowCheckBox); + panel.add(pullFlowCheckBox); + Point location = new Point(owner.getX() + (owner.getWidth() / 2) - (this.getWidth() / 2), owner.getY() + (owner.getHeight() / 2) - (this.getHeight() / 2)); + setLocation(location); + + pack(); + setResizable(false); + } + + /** + * Enable all checkboxes when the stage is changed to {@link ControlFlowModelingStage}
+ * Disable all checkboxes whenever the active stage changed otherwise + */ + @Override + public void stageChanged(Stage newStage) { + if (newStage instanceof ControlFlowModelingStage) { + dataFlowCheckBox.setEnabled(true); + pushFlowCheckBox.setEnabled(true); + pullFlowCheckBox.setEnabled(true); + + newStage.setLayerEnabled(Stage.PUSH_FLOW_LAYER, pushFlowCheckBox.isSelected()); + newStage.setLayerEnabled(Stage.PULL_FLOW_LAYER, pullFlowCheckBox.isSelected()); + } else { + dataFlowCheckBox.setEnabled(false); + pushFlowCheckBox.setEnabled(false); + pullFlowCheckBox.setEnabled(false); + + newStage.setLayerEnabled(Stage.PUSH_FLOW_LAYER, false); + newStage.setLayerEnabled(Stage.PULL_FLOW_LAYER, false); + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/views/NavigationWindow.java b/AlgebraicDataflowArchitectureModel/src/application/views/NavigationWindow.java new file mode 100644 index 0000000..b31621c --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/views/NavigationWindow.java @@ -0,0 +1,166 @@ +package application.views; + +import application.ApplicationWindow; +import application.editor.Editor; +import application.editor.IStageChangeListener; +import application.editor.Stage; +import application.editor.stages.ControlFlowModelingStage; +import application.editor.stages.DataFlowModelingStage; +import application.editor.stages.PushPullSelectionStage; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class NavigationWindow extends JDialog implements IStageChangeListener { + private static final String TITLE = "Navigation"; + private final Editor editor; + + private final JToggleButton dataFlowModelingButton; + private final JToggleButton pushPullSelectionButton; + private final JToggleButton controlFlowDelegationButton; + + private boolean forbidReentry = false; + + public NavigationWindow(ApplicationWindow owner, Editor editor) { + super(owner); + this.editor = editor; + dataFlowModelingButton = new JToggleButton("Data Flow Modeling"); + pushPullSelectionButton = new JToggleButton("Push Pull Selection"); + controlFlowDelegationButton = new JToggleButton("Control Flow Modeling"); + dataFlowModelingButton.addActionListener(new DataFlowModelingButtonListener()); + pushPullSelectionButton.addActionListener(new PushPullSelectionButtonListener()); + controlFlowDelegationButton.addActionListener(new ControlFlowDelegationButtonListener()); + dataFlowModelingButton.setSelected(true); + pushPullSelectionButton.setEnabled(false); + controlFlowDelegationButton.setEnabled(false); + + setTitle(TITLE); + setDefaultCloseOperation(HIDE_ON_CLOSE); + Container panel = getContentPane(); + panel.setLayout(new GridLayout(3, 1)); + + ButtonGroup group = new ButtonGroup(); + group.add(dataFlowModelingButton); + group.add(pushPullSelectionButton); + group.add(controlFlowDelegationButton); + panel.add(dataFlowModelingButton); + panel.add(pushPullSelectionButton); + panel.add(controlFlowDelegationButton); + + pack(); + + Point location = new Point(owner.getX() + (owner.getWidth() / 2) - (this.getWidth() / 2), owner.getY() + (owner.getHeight() / 2) - (this.getHeight() / 2)); + setLocation(location); + + setResizable(false); + } + + @Override + public void stageChanged(Stage newStage) { + if (forbidReentry) { + return; + } + if (newStage instanceof DataFlowModelingStage) { + dataFlowModelingButton.setSelected(true); + + if (editor.canChange(Editor.STAGE_PUSH_PULL_SELECTION)) { + pushPullSelectionButton.setEnabled(true); + } else { + pushPullSelectionButton.setEnabled(false); + } + if (editor.canChange(Editor.STAGE_CONTROL_FLOW_DELEGATION)) { + controlFlowDelegationButton.setEnabled(true); + } else { + controlFlowDelegationButton.setEnabled(false); + } + } else if (newStage instanceof PushPullSelectionStage) { + pushPullSelectionButton.setSelected(true); + + if (editor.canChange(Editor.STAGE_DATA_FLOW_MODELING)) { + dataFlowModelingButton.setEnabled(true); + } else { + dataFlowModelingButton.setEnabled(false); + } + if (editor.canChange(Editor.STAGE_CONTROL_FLOW_DELEGATION)) { + controlFlowDelegationButton.setEnabled(true); + } else { + controlFlowDelegationButton.setEnabled(false); + } + } else if (newStage instanceof ControlFlowModelingStage) { + controlFlowDelegationButton.setSelected(true); + + if (editor.canChange(Editor.STAGE_DATA_FLOW_MODELING)) { + dataFlowModelingButton.setEnabled(true); + } else { + dataFlowModelingButton.setEnabled(false); + } + if (editor.canChange(Editor.STAGE_PUSH_PULL_SELECTION)) { + pushPullSelectionButton.setEnabled(true); + } else { + pushPullSelectionButton.setEnabled(false); + } + } + } + + private class DataFlowModelingButtonListener implements ActionListener { + @Override + public void actionPerformed(ActionEvent e) { + forbidReentry = true; + editor.changeStage(Editor.STAGE_DATA_FLOW_MODELING); + forbidReentry = false; + + if (editor.canChange(Editor.STAGE_PUSH_PULL_SELECTION)) { + pushPullSelectionButton.setEnabled(true); + } else { + pushPullSelectionButton.setEnabled(false); + } + if (editor.canChange(Editor.STAGE_CONTROL_FLOW_DELEGATION)) { + controlFlowDelegationButton.setEnabled(true); + } else { + controlFlowDelegationButton.setEnabled(false); + } + } + } + + private class PushPullSelectionButtonListener implements ActionListener { + @Override + public void actionPerformed(ActionEvent e) { + forbidReentry = true; + editor.changeStage(Editor.STAGE_PUSH_PULL_SELECTION); + forbidReentry = false; + + if (editor.canChange(Editor.STAGE_DATA_FLOW_MODELING)) { + dataFlowModelingButton.setEnabled(true); + } else { + dataFlowModelingButton.setEnabled(false); + } + if (editor.canChange(Editor.STAGE_CONTROL_FLOW_DELEGATION)) { + controlFlowDelegationButton.setEnabled(true); + } else { + controlFlowDelegationButton.setEnabled(false); + } + } + } + + private class ControlFlowDelegationButtonListener implements ActionListener { + @Override + public void actionPerformed(ActionEvent e) { + forbidReentry = true; + editor.changeStage(Editor.STAGE_CONTROL_FLOW_DELEGATION); + forbidReentry = false; + + if (editor.canChange(Editor.STAGE_DATA_FLOW_MODELING)) { + dataFlowModelingButton.setEnabled(true); + } else { + dataFlowModelingButton.setEnabled(false); + } + if (editor.canChange(Editor.STAGE_PUSH_PULL_SELECTION)) { + pushPullSelectionButton.setEnabled(true); + } else { + pushPullSelectionButton.setEnabled(false); + } + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/views/PopupMenuBase.java b/AlgebraicDataflowArchitectureModel/src/application/views/PopupMenuBase.java new file mode 100644 index 0000000..8bb0b1d --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/views/PopupMenuBase.java @@ -0,0 +1,28 @@ +package application.views; + +import com.mxgraph.swing.mxGraphComponent; + +import javax.swing.*; + +public abstract class PopupMenuBase { + protected JPopupMenu popupMenu = null; + protected mxGraphComponent graphComponent = null; + + public PopupMenuBase(final mxGraphComponent graphComponent) { + this.graphComponent = graphComponent; + this.popupMenu = new JPopupMenu(); + } + + /** + * ポップアップを開く. + * + * @param x, y ポップアップを開いた座標 + */ + public void show(int x, int y) { + popupMenu.show(graphComponent, x, y); + } + + protected void addMenuItem(JMenuItem menuItem) { + popupMenu.add(menuItem); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/code/ast/ASTNode.java b/AlgebraicDataflowArchitectureModel/src/code/ast/ASTNode.java index 0e250a1..9c4ddb9 100644 --- a/AlgebraicDataflowArchitectureModel/src/code/ast/ASTNode.java +++ b/AlgebraicDataflowArchitectureModel/src/code/ast/ASTNode.java @@ -2,11 +2,11 @@ public abstract class ASTNode { private ASTNode parent; - + public ASTNode getParent() { return parent; } - + public void setParent(ASTNode parent) { this.parent = parent; } diff --git a/AlgebraicDataflowArchitectureModel/src/code/ast/AbstractTypeDeclaration.java b/AlgebraicDataflowArchitectureModel/src/code/ast/AbstractTypeDeclaration.java index 383962c..fc11be8 100644 --- a/AlgebraicDataflowArchitectureModel/src/code/ast/AbstractTypeDeclaration.java +++ b/AlgebraicDataflowArchitectureModel/src/code/ast/AbstractTypeDeclaration.java @@ -2,11 +2,11 @@ public class AbstractTypeDeclaration extends BodyDeclaration { protected String typeName = null; - + public String getTypeName() { return typeName; } - + public void setTypeName(String typeName) { this.typeName = typeName; } diff --git a/AlgebraicDataflowArchitectureModel/src/code/ast/Annotation.java b/AlgebraicDataflowArchitectureModel/src/code/ast/Annotation.java index 15b1e6a..7fe95e7 100644 --- a/AlgebraicDataflowArchitectureModel/src/code/ast/Annotation.java +++ b/AlgebraicDataflowArchitectureModel/src/code/ast/Annotation.java @@ -18,11 +18,11 @@ keyValueMap = new HashMap<>(); keyValueMap.put("value", value); } - + public String getElementName() { return name; } - + public Map getParams() { return keyValueMap; } @@ -45,13 +45,13 @@ } else { code += "("; String delimitar = ""; - for (String key: keySet) { + for (String key : keySet) { Object value = keyValueMap.get(key); code += delimitar + key + " = \"" + value.toString() + "\""; delimitar = ", "; } code += ")"; } - return code; + return code; } } diff --git a/AlgebraicDataflowArchitectureModel/src/code/ast/Block.java b/AlgebraicDataflowArchitectureModel/src/code/ast/Block.java index 2b4f654..57a2a8c 100644 --- a/AlgebraicDataflowArchitectureModel/src/code/ast/Block.java +++ b/AlgebraicDataflowArchitectureModel/src/code/ast/Block.java @@ -1,15 +1,15 @@ package code.ast; -import java.util.List; import java.util.ArrayList; +import java.util.List; public class Block extends ASTNode { private List statements = new ArrayList(); - + public List getStatements() { return statements; } - + public void setStatements(List statements) { this.statements = statements; } @@ -24,7 +24,7 @@ public String toString() { String code = ""; - for (String statement: statements) { + for (String statement : statements) { code += (statement + "\n"); } return code; diff --git a/AlgebraicDataflowArchitectureModel/src/code/ast/BodyDeclaration.java b/AlgebraicDataflowArchitectureModel/src/code/ast/BodyDeclaration.java index 812f0d7..6d20c71 100644 --- a/AlgebraicDataflowArchitectureModel/src/code/ast/BodyDeclaration.java +++ b/AlgebraicDataflowArchitectureModel/src/code/ast/BodyDeclaration.java @@ -2,11 +2,11 @@ public abstract class BodyDeclaration extends ASTNode { private int modifiers = 0; - + public int getModifiers() { return modifiers; } - + public void setModifiers(int modifiers) { this.modifiers = modifiers; } diff --git a/AlgebraicDataflowArchitectureModel/src/code/ast/CodeUtil.java b/AlgebraicDataflowArchitectureModel/src/code/ast/CodeUtil.java index 99f1561..f2369dd 100644 --- a/AlgebraicDataflowArchitectureModel/src/code/ast/CodeUtil.java +++ b/AlgebraicDataflowArchitectureModel/src/code/ast/CodeUtil.java @@ -5,39 +5,9 @@ public static String insertTab(String code) { String newString = ""; String[] lines = code.split("\n"); - for (String line: lines) { + for (String line : lines) { newString = newString + "\t" + line + "\n"; } return newString; } - - public static String getToStringExp(String typeName, String rawExp) { - if (typeName.equals("int")) { - return "Integer.toString(" + rawExp + ")"; - } else if (typeName.equals("float")) { - return "Float.toString(" + rawExp + ")"; - } else if (typeName.equals("double")) { - return "Double.toString(" + rawExp + ")"; - } else if (typeName.equals("boolean")) { - return "Boolean.toString(" + rawExp + ")"; - } else { - return rawExp + ".toString()"; - } - } - - public static String getToValueExp(String typeName, String strExp) { - if (typeName.equals("int")) { - return "Integer.parseInt(" + strExp + ")"; - } else if (typeName.equals("float")) { - return "Float.parseFloat(" + strExp + ")"; - } else if (typeName.equals("double")) { - return "Double.parseDouble(" + strExp + ")"; - } else if (typeName.equals("boolean")) { - return "Boolean.parseBoolean(" + strExp + ")"; - } else if (typeName.startsWith("ArrayList") || typeName.startsWith("List")) { - return "Arrays.asList(" + strExp + ".replace(\"[\",\"\").replace(\"]\",\"\").split(\",\",0))"; - } else { - return strExp; - } - } } diff --git a/AlgebraicDataflowArchitectureModel/src/code/ast/CompilationUnit.java b/AlgebraicDataflowArchitectureModel/src/code/ast/CompilationUnit.java index 9a5a58a..87be092 100644 --- a/AlgebraicDataflowArchitectureModel/src/code/ast/CompilationUnit.java +++ b/AlgebraicDataflowArchitectureModel/src/code/ast/CompilationUnit.java @@ -10,7 +10,7 @@ public CompilationUnit(TypeDeclaration type) { types.add(type); - if(type.getTypeName().contains("<")) + if (type.getTypeName().contains("<")) fileName = type.getTypeName().split("<")[0] + ".java"; else fileName = type.getTypeName() + ".java"; @@ -31,18 +31,18 @@ public void addType(TypeDeclaration type) { types.add(type); } - + public String getFileName() { return fileName; } public String toString() { String result = ""; - for (ImportDeclaration imp: imports) { + for (ImportDeclaration imp : imports) { result += imp.toString(); } - result +="\n"; - for (TypeDeclaration type: types) { + result += "\n"; + for (TypeDeclaration type : types) { result += type.toString(); } return result; diff --git a/AlgebraicDataflowArchitectureModel/src/code/ast/FieldDeclaration.java b/AlgebraicDataflowArchitectureModel/src/code/ast/FieldDeclaration.java index 0993b92..cc7f2d0 100644 --- a/AlgebraicDataflowArchitectureModel/src/code/ast/FieldDeclaration.java +++ b/AlgebraicDataflowArchitectureModel/src/code/ast/FieldDeclaration.java @@ -1,11 +1,11 @@ package code.ast; +import models.algebra.Type; + import java.util.Collection; import java.util.HashMap; import java.util.Map; -import models.algebra.Type; - public class FieldDeclaration extends BodyDeclaration implements IAnnotatable { private Type type; private String fieldName; @@ -22,49 +22,49 @@ this.fieldName = fieldName; this.initializer = initializer; } - + public Type getType() { return type; } - + public void setType(Type type) { this.type = type; } - + public String getName() { return fieldName; } - + public void setName(String fieldName) { this.fieldName = fieldName; - } - + } + public String getInitializer() { return initializer; } - + public void setInitializer(String initializer) { this.initializer = initializer; } - + @Override public Annotation getAnnotation(String name) { return annotations.get(name); } - + @Override public Collection getAnnotations() { return annotations.values(); } - + @Override public void addAnnotation(Annotation annotation) { annotations.put(annotation.getElementName(), annotation); } - + public String toString() { String code = ""; - for (Annotation annotation: getAnnotations()) { + for (Annotation annotation : getAnnotations()) { code += annotation.toString() + "\n"; } if (initializer == null) { diff --git a/AlgebraicDataflowArchitectureModel/src/code/ast/IAnnotatable.java b/AlgebraicDataflowArchitectureModel/src/code/ast/IAnnotatable.java index 47001e5..2de8379 100644 --- a/AlgebraicDataflowArchitectureModel/src/code/ast/IAnnotatable.java +++ b/AlgebraicDataflowArchitectureModel/src/code/ast/IAnnotatable.java @@ -4,6 +4,8 @@ public interface IAnnotatable { Annotation getAnnotation(String name); + Collection getAnnotations(); + void addAnnotation(Annotation annotation); } diff --git a/AlgebraicDataflowArchitectureModel/src/code/ast/ImportDeclaration.java b/AlgebraicDataflowArchitectureModel/src/code/ast/ImportDeclaration.java index 9135d68..629528a 100644 --- a/AlgebraicDataflowArchitectureModel/src/code/ast/ImportDeclaration.java +++ b/AlgebraicDataflowArchitectureModel/src/code/ast/ImportDeclaration.java @@ -6,11 +6,11 @@ public ImportDeclaration(String name) { this.name = name; } - + public String getName() { return name; } - + public void setName(String name) { this.name = name; } diff --git a/AlgebraicDataflowArchitectureModel/src/code/ast/MethodDeclaration.java b/AlgebraicDataflowArchitectureModel/src/code/ast/MethodDeclaration.java index fad7629..6969e55 100644 --- a/AlgebraicDataflowArchitectureModel/src/code/ast/MethodDeclaration.java +++ b/AlgebraicDataflowArchitectureModel/src/code/ast/MethodDeclaration.java @@ -1,13 +1,9 @@ package code.ast; -import java.util.List; -import java.util.Map; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; - import models.algebra.Type; +import java.util.*; + public class MethodDeclaration extends BodyDeclaration implements IAnnotatable { private String name = null; private boolean isConstructor = false; @@ -41,35 +37,35 @@ this.parameters = parameters; this.body = body; } - + public String getName() { return name; } - + public void setName(String name) { this.name = name; } - + public boolean isConstructor() { return isConstructor; } - + public void setConstructor(boolean isConstructor) { this.isConstructor = isConstructor; } - + public Type getReturnType() { return returnType; } - + public void setReturnType(Type returnType) { this.returnType = returnType; } - + public List getParameters() { return parameters; } - + public void setParameters(List parameters) { this.parameters = parameters; } @@ -80,11 +76,11 @@ } parameters.add(parameter); } - + public Block getBody() { return body; } - + public void setBody(Block body) { this.body = body; } @@ -109,17 +105,21 @@ } thrws.addException(exception); } - + + public Throws getThrows() { + return thrws; + } + @Override public Annotation getAnnotation(String name) { return annotations.get(name); } - + @Override public Collection getAnnotations() { return annotations.values(); } - + @Override public void addAnnotation(Annotation annotation) { annotations.put(annotation.getElementName(), annotation); @@ -127,19 +127,19 @@ public String toString() { String code = ""; - for (Annotation annotation: getAnnotations()) { + for (Annotation annotation : getAnnotations()) { code += annotation.toString() + "\n"; } code += "public "; if (returnType == null) { - if(!isConstructor) code += "void "; - }else { + if (!isConstructor) code += "void "; + } else { code += returnType.getInterfaceTypeName() + " "; } code += (name + "("); if (parameters != null) { String delimitar = ""; - for (VariableDeclaration parameter: parameters) { + for (VariableDeclaration parameter : parameters) { code = code + delimitar + parameter.toString(); delimitar = ", "; } diff --git a/AlgebraicDataflowArchitectureModel/src/code/ast/Modifier.java b/AlgebraicDataflowArchitectureModel/src/code/ast/Modifier.java index 7d1645a..ae84b20 100644 --- a/AlgebraicDataflowArchitectureModel/src/code/ast/Modifier.java +++ b/AlgebraicDataflowArchitectureModel/src/code/ast/Modifier.java @@ -6,7 +6,7 @@ public static final int PROTECTED = 0x0004; public static final int PUBLIC = 0x0001; public static final int STATIC = 0x0008; - + public static boolean isAbstract(int flags) { return (flags & ABSTRACT) != 0; } @@ -18,7 +18,7 @@ public static boolean isProtected(int flags) { return (flags & PROTECTED) != 0; } - + public static boolean isPublic(int flags) { return (flags & PUBLIC) != 0; } diff --git a/AlgebraicDataflowArchitectureModel/src/code/ast/Throws.java b/AlgebraicDataflowArchitectureModel/src/code/ast/Throws.java index a8b41e5..1646c76 100644 --- a/AlgebraicDataflowArchitectureModel/src/code/ast/Throws.java +++ b/AlgebraicDataflowArchitectureModel/src/code/ast/Throws.java @@ -1,18 +1,18 @@ package code.ast; -import java.util.Set; import java.util.HashSet; +import java.util.Set; public class Throws extends ASTNode { private Set exceptions = new HashSet<>(); - + public Throws() { } public void addException(String exception) { exceptions.add(exception); } - + public Set getExceptions() { return exceptions; } @@ -20,7 +20,7 @@ public String toString() { String code = "throws "; String delimiter = ""; - for (String exception: exceptions) { + for (String exception : exceptions) { code += delimiter + exception; delimiter = ", "; } diff --git a/AlgebraicDataflowArchitectureModel/src/code/ast/TypeDeclaration.java b/AlgebraicDataflowArchitectureModel/src/code/ast/TypeDeclaration.java index 0406d59..19204e1 100644 --- a/AlgebraicDataflowArchitectureModel/src/code/ast/TypeDeclaration.java +++ b/AlgebraicDataflowArchitectureModel/src/code/ast/TypeDeclaration.java @@ -1,10 +1,6 @@ package code.ast; -import java.util.List; -import java.util.Map; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; +import java.util.*; public class TypeDeclaration extends AbstractTypeDeclaration implements IAnnotatable { private List fields = new ArrayList<>(); @@ -33,11 +29,11 @@ public void addMethod(MethodDeclaration method) { methods.add(method); } - + public void removeMethod(MethodDeclaration method) { methods.remove(method); } - + public List getFields() { return fields; } @@ -45,7 +41,7 @@ public List getMethods() { return methods; } - + public MethodDeclaration createConstructor() { MethodDeclaration constructor = new MethodDeclaration(typeName, true); addMethod(constructor); @@ -56,12 +52,12 @@ public Annotation getAnnotation(String name) { return annotations.get(name); } - + @Override public Collection getAnnotations() { return annotations.values(); } - + @Override public void addAnnotation(Annotation annotation) { annotations.put(annotation.getElementName(), annotation); @@ -69,14 +65,14 @@ public String toString() { String code = ""; - for (Annotation annotation: getAnnotations()) { + for (Annotation annotation : getAnnotations()) { code += annotation.toString() + "\n"; } code += "public class " + typeName + " {\n"; - for (FieldDeclaration f: fields) { + for (FieldDeclaration f : fields) { code += "\t" + f.toString(); } - for (MethodDeclaration m: methods) { + for (MethodDeclaration m : methods) { code += CodeUtil.insertTab(m.toString()); } code += "}"; diff --git a/AlgebraicDataflowArchitectureModel/src/code/ast/VariableDeclaration.java b/AlgebraicDataflowArchitectureModel/src/code/ast/VariableDeclaration.java index 6a7b25b..db4e9dc 100644 --- a/AlgebraicDataflowArchitectureModel/src/code/ast/VariableDeclaration.java +++ b/AlgebraicDataflowArchitectureModel/src/code/ast/VariableDeclaration.java @@ -1,11 +1,11 @@ package code.ast; +import models.algebra.Type; + import java.util.Collection; import java.util.HashMap; import java.util.Map; -import models.algebra.Type; - public class VariableDeclaration extends ASTNode implements IAnnotatable { private Type type; private String variableName; @@ -15,33 +15,33 @@ this.type = type; this.variableName = variableName; } - + public Type getType() { return type; } - + public void setType(Type type) { this.type = type; } - + public String getName() { return variableName; } - + public void setName(String variableName) { this.variableName = variableName; } - + @Override public Annotation getAnnotation(String name) { return annotations.get(name); } - + @Override public Collection getAnnotations() { return annotations.values(); } - + @Override public void addAnnotation(Annotation annotation) { annotations.put(annotation.getElementName(), annotation); @@ -49,7 +49,7 @@ public String toString() { String code = ""; - for (Annotation annotation: getAnnotations()) { + for (Annotation annotation : getAnnotations()) { code += annotation.toString() + " "; } code += type.getInterfaceTypeName() + " " + variableName; diff --git a/AlgebraicDataflowArchitectureModel/src/generators/CodeGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/CodeGenerator.java new file mode 100644 index 0000000..d16e014 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/generators/CodeGenerator.java @@ -0,0 +1,1262 @@ +package generators; + +import algorithms.TypeInference; +import code.ast.*; +import models.Edge; +import models.Node; +import models.algebra.*; +import models.controlFlowModel.StatefulObjectNode; +import models.dataConstraintModel.*; +import models.dataFlowModel.*; +import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor; + +import java.util.*; + +/** + * Common generator for prototypes + * + * @author Nitta + * + */ +public abstract class CodeGenerator { + public static final String fieldOfResourceState = "value"; + public static final String methoNameOfResourceState = "Value"; + public static final String getterPrefix = "get"; + public static final String getterOfResourceState = getterPrefix + methoNameOfResourceState; // "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; + private static IPlatformSpecific platformSpec = 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).getInterfaceTypeName()); + } + } 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).getInterfaceTypeName()); + } + } + return null; + } else { + // class + return res.getResourceStateType(); + } + } + } + + public static boolean generatesComponent(ResourceHierarchy res) { + if (res.getParent() == null) return true; + if (res.getChildren() == null || res.getChildren().size() == 0) return false; + if (res.getNumParameters() > 0 && res.getChildren().size() == 1 && res.getChildren().iterator().next().getNumParameters() > 0) + return false; + if (res.getChildren().size() == 1 && res.getChildren().iterator().next().getNumParameters() > 0 + && (res.getChildren().iterator().next().getChildren() == null || res.getChildren().iterator().next().getChildren().size() == 0)) + return false; + return true; +// return res.getParent() == null || !(res.getChildren() == null || res.getChildren().size() == 0); + } + + /** + * 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, IFlowGraph flowGraph, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { + CodeGenerator.langSpec = langSpec; + CodeGenerator.platformSpec = platformSpec; + ArrayList codes = new ArrayList<>(); + + Map> dependedRootComponentGraph = null; + Collection> components = null; + if (platformSpec.isMonolithic()) { + // To build monolithic application, the dependency of the components should be resolved in advance. + + // Get the dependency among root nodes. + dependedRootComponentGraph = getDependedRootComponentGraph(model); + + // Sort the all components. + components = determineComponentOrder(flowGraph, dependedRootComponentGraph); + } else { + // Get the all components. + components = flowGraph.getAllComponentNodes().values(); + } + + // Generate the other components. + generateCodeFromFlowGraph(model, flowGraph, components, codes, dependedRootComponentGraph, platformSpec, langSpec); + + return codes; + } + + public abstract void generateCodeFromFlowGraph(DataTransferModel model, IFlowGraph flowGraph, Collection> components, ArrayList codes, + Map> dependedRootComponentGraph, IPlatformSpecific platformSpec, ILanguageSpecific langSpec); + + private static Map> getDependedRootComponentGraph(DataTransferModel model) { + Map> dependedComponentGraph = new HashMap<>(); + for (Channel ch : model.getChannels()) { + Set insideRes = new HashSet<>(); + Set outsideRes = new HashSet<>(); + getDependedRootComponentGraphSub(ch, insideRes, outsideRes, true); + if (outsideRes.size() > 0 && insideRes.size() > 0) { + for (ResourceHierarchy outside : outsideRes) { + for (ResourceHierarchy inside : insideRes) { + Set dependings = dependedComponentGraph.get(outside.getRoot()); + if (dependings == null) { + dependings = new HashSet<>(); + dependedComponentGraph.put(outside.getRoot(), dependings); + } + if (!outside.getRoot().equals(inside.getRoot())) { + dependings.add(inside.getRoot()); + } + } + } + } + } + return dependedComponentGraph; + } + + private static void getDependedRootComponentGraphSub(Channel ch, Set insideRes, Set outsideRes, boolean isRoot) { + DataTransferChannel dtCh = (DataTransferChannel) ch; + for (ChannelMember cm : dtCh.getChannelMembers()) { + if (!isRoot && !cm.isOutside()) { + outsideRes.add(cm.getResource().getResourceHierarchy()); // dependency to a descendant channel resource. + } + if (cm.isOutside()) { + outsideRes.add(cm.getResource().getResourceHierarchy()); // dependency to an outside resource. + } else { + insideRes.add(cm.getResource().getResourceHierarchy()); // dependency from an inside resource. + } + } + for (Channel childCh : ch.getChildren()) { + getDependedRootComponentGraphSub(childCh, insideRes, outsideRes, false); + } + } + + private static ArrayList> determineComponentOrder(IFlowGraph graph, Map> dependedRootComponentGraph) { + ArrayList> objectList = new ArrayList<>(); + Set> visited = new HashSet<>(); + Map> allNodes = graph.getAllComponentNodes(); + for (Set nodeSet : allNodes.values()) { + topologicalSort(nodeSet, allNodes, dependedRootComponentGraph, visited, objectList); + } + return objectList; + } + + private static void topologicalSort(Set curNodeSet, Map> allNodeSets, Map> dependedRootComponentGraph, Set> visited, List> orderedList) { + if (visited.contains(curNodeSet)) return; + visited.add(curNodeSet); + // A caller is before the callee + + // For each incoming PUSH transfer. + for (Node curNode : curNodeSet) { + for (Edge chToRes : curNode.getInEdges()) { + for (Edge resToCh : chToRes.getSource().getInEdges()) { + if (!(resToCh instanceof DataFlowEdge) || ((PushPullAttribute) ((DataFlowEdge) resToCh).getAttribute()).getSelectedOption() == PushPullValue.PUSH) { + topologicalSort(allNodeSets.get(resToCh.getSource()), allNodeSets, dependedRootComponentGraph, visited, orderedList); + } + } + } + } + // For each outgoing PULL transfer. + if (curNodeSet.iterator().next() instanceof ResourceNode) { + for (Node curNode : curNodeSet) { + for (Edge resToCh : curNode.getOutEdges()) { + DataFlowEdge de = (DataFlowEdge) resToCh; + if (((PushPullAttribute) de.getAttribute()).getSelectedOption() != PushPullValue.PUSH) { + for (Edge chToRes : resToCh.getDestination().getOutEdges()) { + topologicalSort(allNodeSets.get(chToRes.getDestination()), allNodeSets, dependedRootComponentGraph, visited, orderedList); + } + } + } + } + } + // For each depending root node. + Node curNode = curNodeSet.iterator().next(); + if (curNode instanceof ResourceNode && dependedRootComponentGraph.get(((ResourceNode) curNode).getResourceHierarchy()) != null) { + for (ResourceHierarchy dependingRes : dependedRootComponentGraph.get(((ResourceNode) curNode).getResourceHierarchy())) { + for (Node node : allNodeSets.keySet()) { + if (node instanceof ResourceNode) { + ResourceNode rootNode = (ResourceNode) node; + ResourceHierarchy rootRes = rootNode.getResourceHierarchy(); + if (rootRes.getParent() == null && rootRes.equals(dependingRes)) { + topologicalSort(allNodeSets.get(rootNode), allNodeSets, dependedRootComponentGraph, visited, orderedList); + } + } + } + } + } + // For each reference resource. + ResourceNode cn = null; + if (curNode instanceof ResourceNode) { + cn = (ResourceNode) curNode; + } else if (curNode instanceof StatefulObjectNode) { + cn = ((StatefulObjectNode) curNode).getResource(); + } + if (cn != null) { + for (Node n : allNodeSets.keySet()) { + 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 (cn.getOutSideResources().contains(m.getResource())) { + topologicalSort(allNodeSets.get(resNode), allNodeSets, dependedRootComponentGraph, visited, orderedList); + } + } + } + } + } + } + orderedList.add(0, curNodeSet); + } + + 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()) { + if (!parameters.contains(var.getName())) { + 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, IPlatformSpecific platformSpec, 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 +// if (!platformSpec.isMonolithic()) { +// // For REST API +// stateGetter.addStatement(langSpec.getReturnStatement(langSpec.getFieldAccessor(fieldOfResourceState)) + langSpec.getStatementDelimiter()); // return value; +// } else { + String implTypeName = resStateType.getImplementationTypeName(); + // copy the current state to be returned as a 'value' + List parameters = new ArrayList<>(); + parameters.add(langSpec.getFieldAccessor(fieldOfResourceState)); + stateGetter.addStatement(langSpec.getReturnStatement(langSpec.getConstructorInvocation(implTypeName, parameters)) + langSpec.getStatementDelimiter()); // return new Resource(value); +// } + } else { + if (resourceHierarchy.getChildren() == null || resourceHierarchy.getChildren().size() == 0) { +// // a leaf resource +// if (!platformSpec.isMonolithic()) { +// // For REST API +// stateGetter.addStatement(langSpec.getReturnStatement(langSpec.getFieldAccessor(fieldOfResourceState)) + langSpec.getStatementDelimiter()); // return value; +// } else { + 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 (!generatesComponent(child)) { + // the child is not a class + childGetter = new Term(new Symbol(getterPrefix + childTypeName, 1, Symbol.Type.METHOD)); + childGetter.addChild(new Constant(langSpec.getSelfExp())); + } 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(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] + langSpec.getReturnStatement(returnValue) + langSpec.getStatementDelimiter()); + } else { + stateGetter.addStatement(langSpec.getReturnStatement(returnValue) + langSpec.getStatementDelimiter()); + } + } + } + } + } + + protected void fillDescendantGetterMethod(MethodDeclaration descendantGetter, ResourceHierarchy descendant, + ResourceHierarchy child, ResourceHierarchy ancestor, TypeDeclaration ancestorComponent, ILanguageSpecific langSpec) { + // (#4) descendant getter method (the implementation must be kept consistent with #3) + Expression selector; + if (DataConstraintModel.typeList.isAncestorOf(ancestor.getResourceStateType())) { + selector = new Variable(langSpec.getFieldAccessor(fieldOfResourceState)); + } else if (DataConstraintModel.typeMap.isAncestorOf(ancestor.getResourceStateType())) { + selector = new Variable(langSpec.getFieldAccessor(fieldOfResourceState)); + } else { + String fieldName = langSpec.toVariableName(getComponentName(child, langSpec)); + selector = new Variable(langSpec.getFieldAccessor(fieldName)); + } + if (descendantGetter.getParameters() != null) { + for (VariableDeclaration param : descendantGetter.getParameters()) { + if (DataConstraintModel.typeInt.isAncestorOf(param.getType())) { + Term newSelector = new Term(DataConstraintModel.get); + newSelector.addChild(selector); + newSelector.addChild(new Variable(param.getName())); + selector = newSelector; + } else if (DataConstraintModel.typeString.isAncestorOf(param.getType())) { + Term newSelector = new Term(DataConstraintModel.lookup); + newSelector.addChild(selector); + newSelector.addChild(new Variable(param.getName())); + selector = newSelector; + } + } + } + if (descendantGetter != null && (descendantGetter.getBody() == null || descendantGetter.getBody().getStatements().size() == 0)) { + String[] sideEffects = new String[]{null}; + String returnValue = selector.toImplementation(sideEffects); + if (sideEffects[0] != null) descendantGetter.addStatement(sideEffects[0]); + descendantGetter.addStatement(langSpec.getReturnStatement(returnValue) + langSpec.getStatementDelimiter()); + } + } + + protected void declareAccessorInMainComponent(TypeDeclaration mainComponent, ResourceNode accessRes, MethodDeclaration stateGetter, IPlatformSpecific platformSpec, 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++; + } + ResourcePath accessResPath = new ResourcePath(accessRes.getPrimaryResourcePath()); + for (int i = 0; i < mainGetterParams.size(); i++) { + Parameter pathParam = new Parameter(mainGetterParams.get(i).getName()); + accessResPath.replacePathParam(i, pathParam, null); + } + 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(platformSpec).getDirectStateAccessorFor(accessResPath, 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 static List getGetterMethods(TypeDeclaration component, String resourceName) { + List getters = new ArrayList<>(); + for (MethodDeclaration m : component.getMethods()) { + if (m.getName().equals(getterPrefix + resourceName)) { + getters.add(m); + } + } + return getters; + } + + 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, List params) { + for (MethodDeclaration m : component.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; + } + } + } + return null; + } + + protected MethodDeclaration getMethod(TypeDeclaration component, String methodName) { + for (MethodDeclaration m : component.getMethods()) { + if (m.getName().equals(methodName)) return m; + } + return null; + } + + protected boolean isPut(ChannelMember cm) { + return cm.getStateTransition().isRightUnary(); + } + + protected boolean isDelete(ChannelMember cm) { + boolean isDelete = false; + Expression nextExp = cm.getStateTransition().getNextStateExpression(); + if (nextExp instanceof Term) { + Symbol rootSymbol = ((Term) nextExp).getSymbol(); + if (rootSymbol.equals(DataConstraintModel.delete) || rootSymbol.equals(DataConstraintModel.remove)) { + isDelete = true; + } else if (rootSymbol.equals(DataConstraintModel.cond)) { + Expression childExp = ((Term) nextExp).getChild(1); + if (childExp instanceof Term) { + rootSymbol = ((Term) childExp).getSymbol(); + if (rootSymbol.equals(DataConstraintModel.delete) || rootSymbol.equals(DataConstraintModel.remove)) { + isDelete = true; + } + } + childExp = ((Term) nextExp).getChild(2); + if (childExp instanceof Term) { + rootSymbol = ((Term) childExp).getSymbol(); + if (rootSymbol.equals(DataConstraintModel.delete) || rootSymbol.equals(DataConstraintModel.remove)) { + isDelete = true; + } + } + } + } + return isDelete; + } + + protected String getGetterResourcePathAndPathParams(ResourcePath resPath, List pathParams, + IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { + 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 = langSpec.newVariableDeclaration(var.getType(), paramName); + if (!platformSpec.isMonolithic()) + ((RestApiSpecific) platformSpec).addPathParamAnnotation(param, paramName); + pathParams.add(param); + } else if (pathParam instanceof Term) { + Term var = (Term) pathParam; + String paramName = "v" + v; + params.add("{" + paramName + "}"); + VariableDeclaration param = langSpec.newVariableDeclaration(var.getType(), paramName); + if (!platformSpec.isMonolithic()) + ((RestApiSpecific) platformSpec).addPathParamAnnotation(param, paramName); + pathParams.add(param); + } + } + return resPath.getResourceHierarchy().toResourcePath(params); + } + + protected String getUpdateResourcePathAndPathParams(ResourcePath resPath, ArrayList pathParams, boolean isRestAPI, + IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { + 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 = langSpec.newVariableDeclaration(var.getType(), paramName); + if (isRestAPI) ((RestApiSpecific) platformSpec).addPathParamAnnotation(param, paramName); + pathParams.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 = langSpec.newVariableDeclaration(var.getType(), paramName); + if (isRestAPI) ((RestApiSpecific) platformSpec).addPathParamAnnotation(param, paramName); + pathParams.add(param); + } + v++; + } + return resPath.getResourceHierarchy().toResourcePath(params); + } + + protected String getInputMethodResourcePathAndPathParams(ResourcePath resPath, ArrayList rootInputParams, + IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { + 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 = langSpec.newVariableDeclaration(var.getType(), paramName); + if (!platformSpec.isMonolithic()) + ((RestApiSpecific) platformSpec).addPathParamAnnotation(param, paramName); + rootInputParams.add(param); + } else if (pathParam instanceof Term) { + Term var = (Term) pathParam; + String paramName = "v" + v; + params.add("{" + paramName + "}"); + VariableDeclaration param = langSpec.newVariableDeclaration(var.getType(), paramName); + if (!platformSpec.isMonolithic()) + ((RestApiSpecific) platformSpec).addPathParamAnnotation(param, 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 = langSpec.newVariableDeclaration(var.getType(), paramName); + if (!platformSpec.isMonolithic()) + ((RestApiSpecific) platformSpec).addPathParamAnnotation(param, paramName); + rootInputParams.add(param); + } else if (pathParam instanceof Term) { + Term var = (Term) pathParam; + String paramName = "v" + v; + params.add("{" + paramName + "}"); + VariableDeclaration param = langSpec.newVariableDeclaration(var.getType(), paramName); + if (!platformSpec.isMonolithic()) + ((RestApiSpecific) platformSpec).addPathParamAnnotation(param, paramName); + rootInputParams.add(param); + } + v++; + } + } + return resPath.getResourceHierarchy().toResourcePath(params); + } + + protected void generatePullDataTransfer(MethodDeclaration methodBody, String fromResourceName, String fromResourcePath, Type fromResourceType, boolean doesAddFirst, + IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { + RestApiSpecific restApiSpec = (RestApiSpecific) platformSpec; + String varName = new String(fromResourceName); + String respTypeName = fromResourceType.getInterfaceTypeName(); + String respImplTypeName = fromResourceType.getImplementationTypeName(); + String respConverter = ""; + if (DataConstraintModel.typeList.isAncestorOf(fromResourceType) && fromResourceType != DataConstraintModel.typeList) { + Type compType = TypeInference.getListComponentType(fromResourceType); + if (DataConstraintModel.typeTuple.isAncestorOf(compType)) { + varName += "_json"; + String mapTypeName = convertFromEntryToMapType(compType, langSpec); + respTypeName = langSpec.newListType(mapTypeName).getInterfaceTypeName(); + respConverter += langSpec.getVariableDeclaration(fromResourceType.getInterfaceTypeName(), fromResourceName) + langSpec.getAssignment() + langSpec.getConstructorInvocation(fromResourceType.getImplementationTypeName(), null) + langSpec.getStatementDelimiter() + "\n"; + respConverter += langSpec.getForStatementForCollection("i", mapTypeName, varName) + "\n"; + respConverter += "\t" + langSpec.getMethodInvocation(fromResourceName, DataConstraintModel.append.getImplName(), List.of(getCodeForConversionFromMapToTuple(compType, "i", langSpec))) + langSpec.getStatementDelimiter() + "\n"; + respConverter += langSpec.getEndForStatement("i"); + restApiSpec.addJsonException(methodBody); + } else if (DataConstraintModel.typeMap.isAncestorOf(compType)) { + // To do. + } + } else if (DataConstraintModel.typeTuple.isAncestorOf(fromResourceType)) { + varName += "_json"; + respTypeName = convertFromEntryToMapType(fromResourceType, langSpec); + respConverter += langSpec.getVariableDeclaration(fromResourceType.getInterfaceTypeName(), fromResourceName) + langSpec.getAssignment() + getCodeForConversionFromMapToTuple(fromResourceType, varName, langSpec) + langSpec.getStatementDelimiter(); + respImplTypeName = "HashMap"; + } else if (DataConstraintModel.typePair.isAncestorOf(fromResourceType)) { + varName += "_json"; + respTypeName = convertFromEntryToMapType(fromResourceType, langSpec); + respConverter += langSpec.getVariableDeclaration(fromResourceType.getInterfaceTypeName(), fromResourceName) + langSpec.getAssignment() + getCodeForConversionFromMapToPair(fromResourceType, varName, langSpec) + langSpec.getStatementDelimiter(); + respImplTypeName = "HashMap"; + } else if (DataConstraintModel.typeMap.isAncestorOf(fromResourceType)) { + varName += "_json"; + respTypeName = convertFromEntryToMapType(fromResourceType, langSpec); + respConverter += langSpec.getVariableDeclaration(fromResourceType.getInterfaceTypeName(), fromResourceName) + langSpec.getAssignment() + langSpec.getConstructorInvocation(fromResourceType.getImplementationTypeName(), null) + langSpec.getStatementDelimiter() + "\n"; + respConverter += getCodeForConversionFromMapToMap(fromResourceType, varName, fromResourceName, langSpec); + respImplTypeName = "HashMap"; + } + if (doesAddFirst) { + if (respConverter.length() > 0) { + methodBody.addFirstStatement(respConverter); + } + methodBody.addFirstStatement(langSpec.getVariableDeclaration(respTypeName, varName) + langSpec.getAssignment() + restApiSpec.getHttpMethodCallWithResponseStatement(restApiSpec.getBaseURL(), fromResourcePath, getterPrefix, respImplTypeName)); + } else { + methodBody.addStatement(langSpec.getVariableDeclaration(respTypeName, varName) + langSpec.getAssignment() + restApiSpec.getHttpMethodCallWithResponseStatement(restApiSpec.getBaseURL(), fromResourcePath, getterPrefix, respImplTypeName)); + if (respConverter.length() > 0) { + methodBody.addStatement(respConverter); + } + } + } + + protected String convertFromEntryToMapType(Type type, ILanguageSpecific langSpec) { + String mapTypeName = null; + if (DataConstraintModel.typePair.isAncestorOf(type)) { + Type compType = TypeInference.getPairComponentType(type); + String wrapperType = DataConstraintModel.getWrapperType(compType); + if (wrapperType != null) { + mapTypeName = langSpec.newMapType(DataConstraintModel.typeString, wrapperType).getInterfaceTypeName(); + } else { + mapTypeName = langSpec.newMapType(DataConstraintModel.typeString, compType.getInterfaceTypeName()).getInterfaceTypeName(); + } + } else if (DataConstraintModel.typeMap.isAncestorOf(type)) { + List compTypes = TypeInference.getMapComponentTypes(type); + String wrapperType = DataConstraintModel.getWrapperType(compTypes.get(1)); + if (wrapperType != null) { + mapTypeName = langSpec.newMapType(DataConstraintModel.typeString, wrapperType).getInterfaceTypeName(); + } else { + mapTypeName = langSpec.newMapType(DataConstraintModel.typeString, compTypes.get(1).getInterfaceTypeName()).getInterfaceTypeName(); + } + } else { + mapTypeName = type.getInterfaceTypeName(); + mapTypeName = mapTypeName.replace(DataConstraintModel.typeTuple.getInterfaceTypeName(), DataConstraintModel.typeMap.getInterfaceTypeName()); + for (int idx = mapTypeName.indexOf("<", 0); idx >= 0; idx = mapTypeName.indexOf("<", idx + 1)) { // Java specific + int to = mapTypeName.indexOf(",", idx); // Java specific + if (to > idx) { + mapTypeName = mapTypeName.substring(0, idx + 1) + DataConstraintModel.typeString.getInterfaceTypeName() + mapTypeName.substring(to); // All elements except for the last one have the string type. + } + } + } + return mapTypeName; + } + + protected String getCodeForConversionFromMapToTuple(Type tupleType, String mapVar, ILanguageSpecific langSpec) { + String decoded = "$x"; + List elementsTypes = TypeInference.getTupleComponentTypes(tupleType); + String elementBase = mapVar; + for (Type elmType : elementsTypes.subList(0, elementsTypes.size() - 1)) { + elementBase = langSpec.getFirstEntryFromMapExp(elementBase); // elementBase.entrySet().iterator().next() + if (elmType == DataConstraintModel.typeBoolean + || elmType == DataConstraintModel.typeInt + || elmType == DataConstraintModel.typeLong + || elmType == DataConstraintModel.typeFloat + || elmType == DataConstraintModel.typeDouble) { + String getKey = langSpec.getMethodInvocation(elementBase, DataConstraintModel.fst.getImplName()); // elementBase.getKey() + String elmVal = langSpec.getStringToValueExp(elmType.getImplementationTypeName(), getKey); // Integer.parseInt(elementBase.getKey()) + decoded = decoded.replace("$x", langSpec.getPairExp(elmVal, "$x")); // new AbstractMap.SimpleEntry<>(Integer.parseInt(elementBase.getKey()), $x) + } else if (elmType == DataConstraintModel.typeString) { + String getKey = langSpec.getMethodInvocation(elementBase, DataConstraintModel.fst.getImplName()); // elementBase.getKey() + decoded = decoded.replace("$x", langSpec.getPairExp(getKey, "$x")); // new AbstractMap.SimpleEntry<>(elementBase.getKey(), $x) + } else { + // To do. + } + elementBase = langSpec.getMethodInvocation(elementBase, DataConstraintModel.snd.getImplName()); // elementBase.getValue() + } + decoded = decoded.replace("$x", elementBase); + return decoded; + } + + protected String getCodeForConversionFromMapToPair(Type pairType, String mapVar, ILanguageSpecific langSpec) { + String decoded = "$x"; + decoded = decoded.replace("$x", "new Pair<>(" + mapVar + ".get(\"left\"), $x)"); + decoded = decoded.replace("$x", mapVar + ".get(\"right\")"); + return decoded; + } + + protected String getCodeForConversionFromMapToMap(Type mapType, String mapVal, String mapVar, ILanguageSpecific langSpec) { + List elementsTypes = TypeInference.getMapComponentTypes(mapType); + Type keyType = elementsTypes.get(0); +// Type valType = elementsTypes.get(1); + String decoded = ""; + if (keyType == DataConstraintModel.typeBoolean + || keyType == DataConstraintModel.typeInt + || keyType == DataConstraintModel.typeLong + || keyType == DataConstraintModel.typeFloat + || keyType == DataConstraintModel.typeDouble) { + String keyVal = langSpec.getStringToValueExp(keyType.getImplementationTypeName(), "k"); + String getInvocation = langSpec.getMethodInvocation(mapVal, DataConstraintModel.lookup.getImplName(), List.of(keyVal)); + decoded += langSpec.getForStatementForMap("k", DataConstraintModel.typeString.getInterfaceTypeName(), mapVal) + "\n"; + decoded += "\t" + langSpec.getMethodInvocation(mapVar, DataConstraintModel.insert.getImplName(), List.of(keyVal, getInvocation)) + langSpec.getStatementDelimiter() + "\n"; + decoded += langSpec.getEndForStatement("k"); + } else if (keyType == DataConstraintModel.typeString) { + decoded += mapVar + langSpec.getAssignment() + mapVal + langSpec.getStatementDelimiter(); + } + return decoded; + } + + protected IResourceStateAccessor getPushAccessor(IPlatformSpecific platformSpec) { + if (platformSpec.isMonolithic()) { + 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(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), + targetRes.getResourceStateType() != null ? targetRes.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(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + }; + } else { + 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(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), + targetRes.getResourceStateType() != null ? targetRes.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); + } + return null; + } + }; + } + } + + protected IResourceStateAccessor getPullAccessor(IPlatformSpecific platformSpec) { + if (platformSpec.isMonolithic()) { + 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 + if (fromRes.isAncestorOf(targetRes)) { + Stack pathStack = new Stack<>(); + ResourcePath curPath = targetRes; + do { + pathStack.push(curPath); + curPath = curPath.getParent(); + } while (!curPath.equals(fromRes)); + // iterate from the fromRes resource + return getRelativePath(targetRes, pathStack); + } + if (generatesComponent(targetRes.getResourceHierarchy())) { + Term getter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); + getter.addChild(new Field(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), targetRes.getResourceStateType())); + return getter; + } else { + return new Field(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), targetRes.getResourceStateType()); + } + } else { + // (#3) access from the outside of the hierarchy (must be kept consistent with #4) + Stack pathStack = new Stack<>(); + ResourcePath curPath = targetRes; + do { + pathStack.push(curPath); + curPath = curPath.getParent(); + } while (curPath != null); + // iterate from the root resource + return getRelativePath(targetRes, pathStack); + } + } + + private Expression getRelativePath(ResourcePath targetRes, Stack pathStack) { + ResourcePath curPath; + Term getter = null; + int arity = 2; + boolean doesChainInvocations = true; + while (!pathStack.empty()) { + curPath = pathStack.pop(); + String typeName = getComponentName(curPath.getResourceHierarchy(), langSpec); + if (getter == null && generatesComponent(curPath.getResourceHierarchy())) { + // root resource + String fieldName = langSpec.toVariableName(typeName); + getter = new Field(fieldName, new Type(typeName, typeName)); + } else { + if (generatesComponent(curPath.getResourceHierarchy())) { + if (arity == 2) { + Term newGetter = new Term(new Symbol(getterPrefix + typeName, -1, Symbol.Type.METHOD)); + newGetter.addChild(getter); + if (curPath.getResourceHierarchy().getNumParameters() > 0) { + Expression param = curPath.getLastParam(); + if (param != null) { + newGetter.addChild(param); + newGetter.getSymbol().setArity(2); + } + } + getter = newGetter; + } else { + // add the last path parameter. + if (curPath.getResourceHierarchy().getNumParameters() > 0) { + Expression param = curPath.getLastParam(); + if (param != null) { + getter.getSymbol().setArity(arity); + getter.addChild(param); + } + } + } + arity = 2; + doesChainInvocations = true; + } else { + // to get a descendant resource directly. (e.g, .todos.{year}.{month}.{day}.{id} ==> .getTodos().getTodo(year, month, day, id)) + if (doesChainInvocations) { + Term newGetter = new Term(new Symbol(getterPrefix + typeName, -1, Symbol.Type.METHOD)); + newGetter.addChild(getter); + getter = newGetter; + doesChainInvocations = false; + } + if (curPath.getResourceHierarchy().getNumParameters() > 0) { + // may change the symbol name + getter.getSymbol().changeName(getterPrefix + typeName); + // add a path parameter. + Expression param = curPath.getLastParam(); + if (param != null) { + getter.getSymbol().setArity(arity); + getter.addChild(param); + arity++; + } + } + } + } + } + + if (generatesComponent(targetRes.getResourceHierarchy())) { + Term newGetter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); + newGetter.addChild(getter); + getter = newGetter; + } + return getter; + } + }; + } else { + return new IResourceStateAccessor() { + @Override + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + if (from != null && !target.isOutside()) { + ResourcePath fromRes = from.getResource(); + if (targetRes.getCommonPrefix(fromRes) != null) { + return getDirectStateAccessorFor(targetRes, fromRes); + } + } + // for reference channel member + return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + if (from != null && !target.isOutside()) { + ResourcePath fromRes = from.getResource(); + if (targetRes.getCommonPrefix(fromRes) != null) { + return getDirectStateAccessorFor(targetRes, fromRes); + } + } + return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), + 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(fieldOfResourceState, + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } else { + // (#3) access from an ancestor or outside of the hierarchy (must be kept consistent with #4) + 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 arity = 2; + boolean doesChainInvocations = true; + while (!pathStack.empty()) { + curPath = pathStack.pop(); + String typeName = getComponentName(curPath.getResourceHierarchy(), langSpec); + if (getter == null && fromRes == null) { + // root resource + String fieldName = langSpec.toVariableName(typeName); + getter = new Field(fieldName, new Type(typeName, typeName)); + } else { + if (generatesComponent(curPath.getResourceHierarchy())) { + if (arity == 2) { + Term newGetter = new Term(new Symbol(getterPrefix + typeName, -1, Symbol.Type.METHOD)); + newGetter.addChild(getter); + if (curPath.getResourceHierarchy().getNumParameters() > 0) { + Expression param = curPath.getLastParam(); + if (param != null) { + newGetter.addChild(param); + newGetter.getSymbol().setArity(2); + } + } + getter = newGetter; + } else { + // add the last path parameter. + if (curPath.getResourceHierarchy().getNumParameters() > 0) { + Expression param = curPath.getLastParam(); + if (param != null) { + getter.getSymbol().setArity(arity); + getter.addChild(param); + } + } + } + arity = 2; + doesChainInvocations = true; + } else { + // to get a descendant resource directly. (e.g, .todos.{year}.{month}.{day}.{id} ==> .getTodos().getTodo(year, month, day, id)) + if (doesChainInvocations) { + Term newGetter = new Term(new Symbol(getterPrefix + typeName, -1, Symbol.Type.METHOD)); + newGetter.addChild(getter); + getter = newGetter; + doesChainInvocations = false; + } + if (curPath.getResourceHierarchy().getNumParameters() > 0) { + // may change the symbol name + getter.getSymbol().changeName(getterPrefix + typeName); + // add a path parameter. + Expression param = curPath.getLastParam(); + if (param != null) { + getter.getSymbol().setArity(arity); + getter.addChild(param); + arity++; + } + } + } + } + } + + 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(IPlatformSpecific platformSpec) { + if (platformSpec.isMonolithic()) { + 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(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), + targetRes.getResourceStateType() != null ? targetRes.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(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + }; + } else { + return new IResourceStateAccessor() { + @Override + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + ResourcePath fromRes = from.getResource(); + if (targetRes.equals(fromRes)) { + return new Field(fieldOfResourceState, + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), + targetRes.getResourceStateType() != null ? targetRes.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); + } + return null; + } + }; + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java b/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java new file mode 100644 index 0000000..9808d10 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java @@ -0,0 +1,3233 @@ +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.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import algorithms.TypeInference; + +import java.util.Set; +import java.util.Stack; + +import code.ast.Annotation; +import code.ast.Block; +import code.ast.CodeUtil; +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.Symbol; +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.JsonTerm; +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.IFlowGraph; +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, IFlowGraph flowGraph, Collection> components, ArrayList codes, + Map> dependedRootComponentGraph, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { + Map resourceComponents = new HashMap<>(); + Map resourceConstructors = new HashMap<>(); + Map> constructorParams = new HashMap<>(); + List> constructorStatements = new ArrayList<>(); + Map>> updateStatements = new HashMap<>(); + Map> descendantGetters = new HashMap<>(); + + TypeDeclaration mainComponent = null; + MethodDeclaration mainConstructor = null; + if (platformSpec.hasMain()) { + // Add the main component. + if (getMainTypeName() == null) { + setMainTypeName(langSpec.getMainComponentName()); + } + mainComponent = langSpec.newTypeDeclaration(getMainTypeName()); + mainConstructor = mainComponent.createConstructor(); + CompilationUnit mainCU = langSpec.newCompilationUnit(mainComponent); + codes.add(mainCU); + } + + // For each components (1st pass). + for (Set componentNodeSet: components) { + Node componentNode = componentNodeSet.iterator().next(); + 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); + if (!platformSpec.isMonolithic() && resourceNode.getResourceHierarchy().getParent() == null) { + // For each root node, add component annotations. + ((RestApiSpecific) platformSpec).addComponentAnnotations(component, resourceNode.getResourceName()); + } + resourceComponents.put(resourceNode.getResourceHierarchy(), component); + CompilationUnit cu = langSpec.newCompilationUnit(component); + if (!platformSpec.isMonolithic() && resourceNode.getResourceHierarchy().getParent() == null) { + // For each root node, add platform specific imports. + ((RestApiSpecific) platformSpec).addPlatformSpecificImports(cu); + } + codes.add(cu); + + if (platformSpec.isMonolithic()) { + // For monolithic applications (components are tightly coupled and must be built together). + + // Declare the constructor. + MethodDeclaration constructor = declareConstructor(resourceNode, component, dependedRootComponentGraph, depends, langSpec); + + if (platformSpec.hasMain() && 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, platformSpec, langSpec); + + // Declare the accessor method in the main component to call the getter method. + if (platformSpec.hasMain()) { + declareAccessorInMainComponent(mainComponent, resourceNode, stateGetter, platformSpec, langSpec); + } + } + if (component != null) { + // (#1) Declare the getter methods in this resource to obtain the descendant resources. (complementary to #2) + declareDescendantGetterMethods(resourceNode, component, descendantGetters, langSpec); + } + } + } + + // For each components (2nd pass). + Map priorMemberForInputChannel = new HashMap<>(); + Set generatedResources = new HashSet<>(); + for (Set componentNodeSet: components) { + Node componentNode = componentNodeSet.iterator().next(); + // Declare this resource. + ResourceNode resourceNode = (ResourceNode) componentNode; + Type resStateType = getImplStateType(resourceNode.getResourceHierarchy(), langSpec); + TypeDeclaration component = null; + TypeDeclaration parentComponent = null; + TypeDeclaration rootComponent = null; + if (generatesComponent(resourceNode.getResourceHierarchy())) { + component = resourceComponents.get(resourceNode.getResourceHierarchy()); + } + if (resourceNode.getResourceHierarchy().getParent() != null) { + parentComponent = resourceComponents.get(resourceNode.getResourceHierarchy().getParent()); + } + rootComponent = resourceComponents.get(resourceNode.getResourceHierarchy().getRoot()); + + // Declare cache fields and update methods in this resource, and an update accessor method in the type of root resource. + Map.Entry, Map>>> initStatementsAndUpdateUpdates + = declareCacheFieldsAndUpdateMethods(resourceNode, component, parentComponent, rootComponent, platformSpec, 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 for push/pull transfer in the parent/this component, and the state field in the parent component. + declareFieldsToReferToOtherResourcesAndStateFieldInParentComponent(resourceNode, component, parentComponent, constructorParams, platformSpec, langSpec); + + // (#2) Declare the getter method to obtain the resource state in an ancestor resource. (complementary to #1) + if (component == null) { + MethodDeclaration stateGetter = declareStateGetterMethodInAncestor(resourceNode, resourceComponents, resStateType, platformSpec, langSpec); + + if (stateGetter != null && platformSpec.hasMain()) { + // Declare the accessor method in the main component to call the getter method. + declareAccessorInMainComponent(mainComponent, resourceNode, stateGetter, platformSpec, langSpec); + } + } + + if (!platformSpec.isMonolithic() && !generatedResources.contains(resourceNode.getResourceHierarchy())) { + // Declare the getter accessor in the root resource. + declareGetterAccessorInTheRootResource(resourceNode, rootComponent, platformSpec, langSpec); + } + + // Declare input methods in this component and the main component. + if (!generatedResources.contains(resourceNode.getResourceHierarchy())) { + Map.Entry, Map>>> initStatementsAndInputUpdates + = declareInputMethodsInThisAndMainComponents(resourceNode, component, parentComponent, mainComponent, rootComponent, model, priorMemberForInputChannel, platformSpec, 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()); + } + } + + generatedResources.add(resourceNode.getResourceHierarchy()); + } + + // Add constructor parameters to the ancestor components. + for (ResourceNode root: ((DataFlowGraph) 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 resource = updateStatements.get(method).getValue().getKey(); + ResourceHierarchy descendantRes = updateStatements.get(method).getValue().getValue(); + TypeDeclaration descendantComponent = resourceComponents.get(descendantRes); + addUpdateStatementWithConstructorInvocationToMethod(method, updateExp, resource, descendantRes, descendantComponent, langSpec); + } + } + + private static List addConstructorParameters(ResourceHierarchy resource, Map resourceComponents, + Map resourceConstructors, Map> constructorParams, ILanguageSpecific langSpec) { + List params = new ArrayList<>(); + for (ResourceHierarchy child: resource.getChildren()) { + params.addAll(addConstructorParameters(child, resourceComponents, resourceConstructors, constructorParams, langSpec)); + } + 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, 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 resource, ResourceHierarchy descendantRes, TypeDeclaration descendantComponent, ILanguageSpecific langSpec) { + // Replace each json term in exp with the corresponding constructor invocation. + Type replacedJsonType = descendantRes.getResourceStateType(); + String replacingClassName = getComponentName(descendantRes, langSpec); + Type descendantType = new Type(replacingClassName, replacingClassName); + 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() != null) { + if (jsonTerm.getType().equals(replacedJsonType)) { + if (jsonTerm instanceof JsonTerm || jsonTerm.getSymbol().equals(DataConstraintModel.addMember)) { + MethodDeclaration childConstructor = getConstructor(descendantComponent); + List params = new ArrayList<>(); + if (childConstructor != null) { + for (VariableDeclaration var: childConstructor.getParameters()) { + // Extract the argument of each constructor parameter from jsonTerm. + JsonAccessor jsonMember = new JsonAccessor(DataConstraintModel.dot); + jsonMember.addChild(jsonTerm); + jsonMember.addChild(new Constant(var.getName(), DataConstraintModel.typeString)); + Expression param = jsonMember.reduce(); // Reduce {"name": "foo", age: 25}.name => "foo" + 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(); + } else { + jsonTerm.setType(descendantType); + } + } else { + Type oldType = jsonTerm.getType(); + Type newType = new Type(oldType.getTypeName(), + oldType.getImplementationTypeName().replace(replacedJsonType.getInterfaceTypeName(), replacingClassName), + oldType.getInterfaceTypeName().replace(replacedJsonType.getInterfaceTypeName(), replacingClassName)); + for (Type parent: oldType.getParentTypes()) { + newType.addParentType(parent); + } + jsonTerm.setType(newType); + } + } + } + // Replace the type of the state field. + Type fieldType = getImplStateType(resource, langSpec); + if (exp instanceof Term) { + ((Term) exp).setType(fieldType); + for (Map.Entry varEnt: ((Term) exp).getVariables().entrySet()) { + if (varEnt.getValue().getName().equals(fieldOfResourceState)) { + varEnt.getValue().setType(fieldType); + } + } + } else if (exp instanceof Variable) { + ((Variable) exp).setType(fieldType); + } + String[] sideEffects = new String[] {""}; + String newState = exp.toImplementation(sideEffects); + String updateStatement; + if (exp instanceof Term && ((Term) exp).getSymbol().isImplWithSideEffect()) { + updateStatement = sideEffects[0]; + if (updateStatement.endsWith("\n")) { + updateStatement = updateStatement.substring(0, updateStatement.length() - 1); + } + } 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()).getSelectedOption() == 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()).getSelectedOption() != 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, Map> 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); + // Add a parameter to initialize the state field to the constructor. +// Map nameToParam = constructorParams.get(resourceNode.getResourceHierarchy()); +// if (nameToParam == null) { +// nameToParam = new HashMap<>(); +// constructorParams.put(resourceNode.getResourceHierarchy(), nameToParam); +// } +// String varName = fieldOfResourceState; +// if (nameToParam.get(varName) == null) { +// nameToParam.put(varName, langSpec.newVariableDeclaration(resStateType, varName)); +// } + } 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.getConstructorInvocation(childTypeName, new ArrayList<>())); + component.addField(stateField); + } + } + } + } + } + + private void declareFieldsToReferToOtherResourcesAndStateFieldInParentComponent(ResourceNode resourceNode, TypeDeclaration component, TypeDeclaration parentComponent, + Map> constructorParams, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { + // Declare reference fields for push data transfer. + boolean noPullTransfer = true; + for (Edge resToCh : resourceNode.getOutEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + ChannelNode directDstChNode = (ChannelNode) re.getDestination(); + DataTransferChannel directDstCh = directDstChNode.getChannel(); + // Check if the source resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: directDstCh.getInputChannelMembers()) { + if (cm.getResource().equals(resourceNode.getOutSideResource(directDstCh)) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + // Should take into account the channel hierarchy. + Set ancestorDstChannels = directDstChNode.getAncestors(); + Set descendantDstChannels = directDstChNode.getDescendants(); + Set outEdges = new HashSet<>(); + outEdges.addAll(directDstChNode.getOutEdges()); + for (ChannelNode ancestorDst: ancestorDstChannels) { + outEdges.addAll(ancestorDst.getOutEdges()); + } + for (ChannelNode descendantDst: descendantDstChannels) { + outEdges.addAll(descendantDst.getOutEdges()); + } + for (Edge chToRes: outEdges) { + if (chToRes.getDestination() instanceof ResourceNode) { + ResourceHierarchy dstRes = ((ResourceNode) chToRes.getDestination()).getResourceHierarchy(); + ChannelNode chNode = (ChannelNode) chToRes.getSource(); + DataTransferChannel ch = chNode.getChannel(); + // Check if the destination resource is outside of the channel scope. + boolean outsideOutputResource = false; + ChannelMember out = null; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (((ResourceNode) chToRes.getDestination()).getInSideResources().contains(cm.getResource())) { + out = cm; + if (cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + } + ResourcePath dstResPath = out.getResource(); + // Also take into account the channel hierarchy to determine push/pull transfer. + if (descendantDstChannels.contains(chNode)) { + outsideOutputResource = true; // Regarded as (broadcasting) push transfer. + } + if (ancestorDstChannels.contains(chNode)) { + outsideInputResource = true; // Regarded as (collecting) pull transfer. + } + if ((((PushPullAttribute) re.getAttribute()).getSelectedOption() == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) { + // Declare a field in the parent or this 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 (!platformSpec.isMonolithic() + && (outsideOutputResource || (resourceNode.getOutSideResource(ch).getCommonPrefix(dstResPath) == null && platformSpec.isDifferentTreesAsDifferentServices()))) { + // Inter-service (for REST API) + if (parentComponent != null && + !((RestApiSpecific) platformSpec).hasHttpClientFieldDeclaration(parentComponent)) { + // Declare a client field to connect to the destination resource of push transfer. + ((RestApiSpecific) platformSpec).addHttpClientFieldDeclaration(parentComponent); + } + } else { + // Monolithic or Inner-service + if (component != null) { + // A component is created for this resource. + if (resourceNode.getResourceHierarchy() != dstRes) { + component.addField(refFieldForPush); + if (!outsideOutputResource) { + Map nameToParam = constructorParams.get(resourceNode.getResourceHierarchy()); + if (nameToParam == null) { + nameToParam = new HashMap<>(); + constructorParams.put(resourceNode.getResourceHierarchy(), nameToParam); + } + if (nameToParam.get(dstResName) == null) { + nameToParam.put(dstResName, refVarForPush); + } + } + } + } else { + // No component is created for this resource. + if (resourceNode.getParent().getResourceHierarchy() != dstRes && parentComponent != null) { + parentComponent.addField(refFieldForPush); + if (!outsideOutputResource) { + Map nameToParam = constructorParams.get(resourceNode.getParent().getResourceHierarchy()); + if (nameToParam == null) { + nameToParam = new HashMap<>(); + constructorParams.put(resourceNode.getParent().getResourceHierarchy(), nameToParam); + } + if (nameToParam.get(dstResName) == null) { + nameToParam.put(dstResName, 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); + Map nameToParam = constructorParams.get(resourceNode.getResourceHierarchy()); + if (nameToParam == null) { + nameToParam = new HashMap<>(); + constructorParams.put(resourceNode.getResourceHierarchy(), nameToParam); + } + if (nameToParam.get(dstRootResName) == null) { + nameToParam.put(dstRootResName, 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); + Map nameToParam = constructorParams.get(resourceNode.getParent().getResourceHierarchy()); + if (nameToParam == null) { + nameToParam = new HashMap<>(); + constructorParams.put(resourceNode.getParent().getResourceHierarchy(), nameToParam); + } + if (nameToParam.get(dstRootResName) == null) { + nameToParam.put(dstRootResName, refRootVarForPush); + } + } + } + } + } + } + } + } + } + } + // Declare reference fields for pull data transfer. + for (Edge chToRes : resourceNode.getInEdges()) { + ChannelNode directSrcChNode = (ChannelNode) chToRes.getSource(); + DataTransferChannel directSrcCh = directSrcChNode.getChannel(); + // Should take into account the channel hierarchy. + Set ancestorSrcChannels = directSrcChNode.getAncestors(); + Set descendantSrcChannels = directSrcChNode.getDescendants(); + Set inEdges = new HashSet<>(); + inEdges.addAll(directSrcChNode.getInEdges()); + for (ChannelNode ancestorSrc: ancestorSrcChannels) { + inEdges.addAll(ancestorSrc.getInEdges()); + } + for (ChannelNode descendantSrc: descendantSrcChannels) { + inEdges.addAll(descendantSrc.getInEdges()); + } + for (Edge resToCh: inEdges) { + DataFlowEdge re = (DataFlowEdge) resToCh; + ChannelNode chNode = (ChannelNode) re.getDestination(); + DataTransferChannel ch = chNode.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; + } + } + // Also take into account the channel hierarchy to determine push/pull transfer. + if (descendantSrcChannels.contains(chNode)) { + outsideInputResource = true; // Regarded as (collecting) pull transfer. + } + if (ancestorSrcChannels.contains(chNode)) { + outsideOutputResource = true; // Regarded as (broadcasting) push transfer. + } + if ((((PushPullAttribute) re.getAttribute()).getSelectedOption() != 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 (!platformSpec.isMonolithic() + && (outsideInputResource || (resourceNode.getInSideResource(ch).getCommonPrefix(srcRes) == null && platformSpec.isDifferentTreesAsDifferentServices()))) { + // Inter-service (for REST API) + if (parentComponent != null + && !((RestApiSpecific) platformSpec).hasHttpClientFieldDeclaration(parentComponent)) { + // Declare a client field to connect to the destination resource of push transfer. + ((RestApiSpecific) platformSpec).addHttpClientFieldDeclaration(parentComponent); + } + } else { + // Monolithic or Inner-service (for REST API) + // Declare a field to directly refer to the source resource of pull transfer. + if (component != null) { + // A component is created for this resource. + if (resourceNode.getResourceHierarchy() != srcRes.getResourceHierarchy()) { + component.addField(refFieldForPull); + if (!outsideInputResource) { + Map nameToParam = constructorParams.get(resourceNode.getResourceHierarchy()); + if (nameToParam == null) { + nameToParam = new HashMap<>(); + constructorParams.put(resourceNode.getResourceHierarchy(), nameToParam); + } + if (nameToParam.get(srcResName) == null) { + nameToParam.put(srcResName, refVarForPull); + } + } + } + } else { + // No component is created for this resource. + if (resourceNode.getParent().getResourceHierarchy() != srcRes.getResourceHierarchy() && parentComponent != null) { + parentComponent.addField(refFieldForPull); + if (!outsideInputResource) { + Map nameToParam = constructorParams.get(resourceNode.getParent().getResourceHierarchy()); + if (nameToParam == null) { + nameToParam = new HashMap<>(); + constructorParams.put(resourceNode.getParent().getResourceHierarchy(), nameToParam); + } + if (nameToParam.get(srcResName) == null) { + nameToParam.put(srcResName, 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); + Map nameToParam = constructorParams.get(resourceNode.getResourceHierarchy()); + if (nameToParam == null) { + nameToParam = new HashMap<>(); + constructorParams.put(resourceNode.getResourceHierarchy(), nameToParam); + } + if (nameToParam.get(srcRootResName) == null) { + nameToParam.put(srcRootResName, 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); + Map nameToParam = constructorParams.get(resourceNode.getParent().getResourceHierarchy()); + if (nameToParam == null) { + nameToParam = new HashMap<>(); + constructorParams.put(resourceNode.getParent().getResourceHierarchy(), nameToParam); + } + if (nameToParam.get(srcRootResName) == null) { + nameToParam.put(srcRootResName, 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); + Map nameToParam = constructorParams.get(resourceNode.getParent().getResourceHierarchy()); + if (nameToParam == null) { + nameToParam = new HashMap<>(); + constructorParams.put(resourceNode.getParent().getResourceHierarchy(), nameToParam); + } + if (nameToParam.get(resName) == null) { + nameToParam.put(resName, langSpec.newVariableDeclaration(res.getResourceStateType(), resName)); + } + } + } + } + } + + + private MethodDeclaration declareStateGetterMethod(ResourceNode resourceNode, TypeDeclaration component, Type resStateType, + IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { + // Declare the getter method of the resource state. + MethodDeclaration stateGetter = langSpec.newMethodDeclaration(getterOfResourceState, resStateType); + if (!platformSpec.isMonolithic() && resourceNode.getResourceHierarchy().getParent() == null) { + // Since this getter is also an accessor. + ((RestApiSpecific) platformSpec).addGetAnnotations(stateGetter); + } + component.addMethod(stateGetter); + + boolean hasDescendantIn = hasDescendantInput(resourceNode); + if (((StoreAttribute) resourceNode.getAttribute()).isStored() && !hasDescendantIn) { + fillStateGetterMethod(stateGetter, resourceNode.getResourceHierarchy(), resStateType, platformSpec, langSpec); + } else { + // invocations to other getter methods when at least one incoming data-flow edges is PULL-style. + if (addOtherGetterInvocationsToStateGatter(stateGetter, resourceNode, platformSpec, langSpec)) { + // Declare a client field to connect to the destination resource of push transfer. + if (!((RestApiSpecific) platformSpec).hasHttpClientFieldDeclaration(component)) { + ((RestApiSpecific) platformSpec).addHttpClientFieldDeclaration(component); + } + } + } + + return stateGetter; + } + + private MethodDeclaration declareStateGetterMethodInAncestor(ResourceNode resourceNode, Map resourceComponents, Type resStateType, + IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { + // Search an ancestor in which the getter method is declared. + ResourceNode ancestorNode = resourceNode; + ResourceNode childNode = null; + Stack ancestors = new Stack<>(); + do { + ancestors.push(ancestorNode); + childNode = ancestorNode; + ancestorNode = ancestorNode.getParent(); + } while (!generatesComponent(ancestorNode.getResourceHierarchy())); + TypeDeclaration ancestorComponent = resourceComponents.get(ancestorNode.getResourceHierarchy()); + List getterParams = new ArrayList<>(); + int v = 1; + while (ancestors.size() > 0) { + ResourceNode curAncestor = ancestors.pop(); + Expression param = curAncestor.getPrimaryResourcePath().getLastParam(); + if (param instanceof Variable) { + Variable var = (Variable) param; + getterParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + } else if (param instanceof Term) { + Term var = (Term) param; + getterParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + // Check duplication. + String getterName = getterPrefix + getComponentName(resourceNode.getResourceHierarchy(), langSpec); + for (MethodDeclaration method: ancestorComponent.getMethods()) { + if (method.getName().equals(getterName) + && (method.getParameters() == null ? 0 : method.getParameters().size()) == getterParams.size()) return null; + } + + // Declare the getter method of the resource state. + MethodDeclaration stateGetter = null; + if (getterParams.size() == 0) { + stateGetter = langSpec.newMethodDeclaration(getterName, resStateType); + } else { + stateGetter = langSpec.newMethodDeclaration(getterName, false, resStateType, getterParams); + } + if (ancestorComponent != null) { + ancestorComponent.addMethod(stateGetter); + } + + boolean hasDescendantIn = hasDescendantInput(resourceNode); + if (((StoreAttribute) resourceNode.getAttribute()).isStored() && !hasDescendantIn) { + fillDescendantGetterMethod(stateGetter, resourceNode.getResourceHierarchy(), childNode.getResourceHierarchy(), ancestorNode.getResourceHierarchy(), ancestorComponent, langSpec); + } else { + if (addOtherGetterInvocationsToStateGatter(stateGetter, resourceNode, platformSpec, langSpec)) { + // Declare a client field to connect to the destination resource of push transfer. + if (ancestorComponent != null && !((RestApiSpecific) platformSpec).hasHttpClientFieldDeclaration(ancestorComponent)) { + ((RestApiSpecific) platformSpec).addHttpClientFieldDeclaration(ancestorComponent); + } + } + } + + return stateGetter; + } + + private boolean hasDescendantInput(ResourceNode resourceNode) { + boolean hasDescendantIn = false; + outer: for (Edge chToRes: resourceNode.getInEdges()) { + ChannelNode chNode = (ChannelNode) chToRes.getSource(); + Set descendantChannels = chNode.getDescendants(); + for (ChannelNode descendantCh: descendantChannels) { + if (descendantCh.getIndegree() > 0) { + hasDescendantIn = true; + break outer; + } + } + } + return hasDescendantIn; + } + + private boolean addOtherGetterInvocationsToStateGatter(MethodDeclaration stateGetter, ResourceNode resourceNode, + IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { + boolean bDeclareClientField = false; + try { + // Data transfer on the same channel hierarchy. + boolean isContainedPush = false; + DataTransferChannel ch = null; + DataTransferChannel ch2 = null; + HashMap inputResourceToStateAccessor = new HashMap<>(); + for (Edge chToRes: resourceNode.getInEdges()) { + ch2 = ((ChannelNode) chToRes.getSource()).getChannel(); + for (Edge resToCh: chToRes.getSource().getInEdges()) { + DataFlowEdge dIn = (DataFlowEdge) resToCh; + ChannelMember in = null; + for (ChannelMember cm: ch2.getInputChannelMembers()) { + if (((ResourceNode) dIn.getSource()).getOutSideResources().contains(cm.getResource())) { + in = cm; + break; + } + } + if (((PushPullAttribute) dIn.getAttribute()).getSelectedOption() == PushPullValue.PUSH) { + // PUSH transfer + isContainedPush = true; + inputResourceToStateAccessor.put(in, getPushAccessor(platformSpec)); + } else { + // PULL transfer + inputResourceToStateAccessor.put(in, getPullAccessor(platformSpec)); + ch = ((ChannelNode) resToCh.getDestination()).getChannel(); // pull containing input side channel is at most one. + if (!platformSpec.isMonolithic() + && !in.isOutside() + && in.getResource().getCommonPrefix(resourceNode.getInSideResource(ch2)) == null + && platformSpec.isDifferentTreesAsDifferentServices()) { + // for REST API + ResourcePath srcResPath = in.getResource(); + String srcResourceName = langSpec.toVariableName(getComponentName(srcResPath.getResourceHierarchy(), langSpec)); + Type srcResourceType = srcResPath.getResourceStateType(); + List pathParams = new ArrayList<>(); + for (Expression pathExp: srcResPath.getPathParams()) { + String[] sideEffects = new String[] {""}; + pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); + } + generatePullDataTransfer(stateGetter, srcResourceName, + srcResPath.getResourceHierarchy().toResourcePath(pathParams), srcResourceType, + true, platformSpec, langSpec); + bDeclareClientField = true; + } + } + } + } + if (ch == null) { + ch = ch2; + } + ChannelMember out = null; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (resourceNode.getInSideResources().contains(cm.getResource())) { + out = cm; + break; + } + } + + // for reference channel members. + ResourcePath dstResPath = resourceNode.getInSideResource(ch); + for (ChannelMember rc: ch.getReferenceChannelMembers()) { + inputResourceToStateAccessor.put(rc, getPullAccessor(platformSpec)); // by pull data transfer + ResourcePath refResPath = rc.getResource(); + if (!platformSpec.isMonolithic() + && (rc.isOutside() + || (refResPath.getCommonPrefix(dstResPath) == null && platformSpec.isDifferentTreesAsDifferentServices()))) { + // for REST API + String refResourceName = langSpec.toVariableName(getComponentName(refResPath.getResourceHierarchy(), langSpec)); + Type refResourceType = refResPath.getResourceStateType(); + List pathParams = new ArrayList<>(); + for (Expression pathExp: refResPath.getPathParams()) { + String[] sideEffects = new String[] {""}; + pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); + } + generatePullDataTransfer(stateGetter, refResourceName, + refResPath.getResourceHierarchy().toResourcePath(pathParams), refResourceType, + true, platformSpec, langSpec); + bDeclareClientField = true; + } + } + + // Construct the base message. + Map.Entry>>, Term> resourcePathsAndMessage; + if (!isContainedPush) { + // All incoming edges are in PULL-style. + resourcePathsAndMessage = ch.fillOutsideResourcePaths(out, getPullAccessor(platformSpec), null); + } else { + // At least one incoming edge is in PUSH-style. + resourcePathsAndMessage = ch.fillOutsideResourcePaths(out, getPullAccessor(platformSpec), inputResourceToStateAccessor); + } + Map>> resourcePaths = resourcePathsAndMessage.getKey(); + Term messageTerm = resourcePathsAndMessage.getValue(); + + // Data transfer from path depending resource. + for (Entry>> pathEnt: resourcePaths.entrySet()) { + ChannelMember cm = pathEnt.getKey(); + ResourcePath srcResPath = pathEnt.getValue().getKey(); + // get outside srcResPath resource state by pull data transfer. + if (!platformSpec.isMonolithic() + && (cm.isOutside() + || (srcResPath.getCommonPrefix(dstResPath)) == null && platformSpec.isDifferentTreesAsDifferentServices())) { + // for REST API + // Data transfer from an outside input resource is regarded as PULL transfer. + List pathParams = new ArrayList<>(); + for (Expression pathExp: srcResPath.getPathParams()) { + String[] sideEffects = new String[] {""}; + pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); + } + // generate a pull data transfer from a depending in/ref resource. + Type srcResourceType = srcResPath.getResourceStateType(); + String srcResName2 = langSpec.toVariableName(getComponentName(srcResPath.getResourceHierarchy(), langSpec)); + String srcPath2 = srcResPath.toResourcePath().replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + generatePullDataTransfer(stateGetter, srcResName2, srcPath2, srcResourceType, false, platformSpec, langSpec); + bDeclareClientField = true; + } + } + + // Data transfer from the descendant channel hierarchies. + Stack> channelItrStack = new Stack<>(); + DataTransferChannel curChannel = ch; + if (curChannel.getChildren() != null && curChannel.getChildren().size() > 0) { + // retrieve descendant channels recursively. + Iterator chItr = curChannel.getChildren().iterator(); + do { + if (!chItr.hasNext()) { + chItr = channelItrStack.pop(); + } else { + curChannel = (DataTransferChannel) chItr.next(); + // generate pull data transfers. + Set chMems = new HashSet<>(curChannel.getInputChannelMembers()); + chMems.addAll(curChannel.getReferenceChannelMembers()); + for (ChannelMember cm2: chMems) { + if (resourcePaths == null || !resourcePaths.keySet().contains(cm2)) { + // not a depending channel member. + ResourcePath src2 = cm2.getResource(); + Type srcResType2 = src2.getResourceStateType(); + String srcResName2 = langSpec.toVariableName(getComponentName(src2.getResourceHierarchy(), langSpec)); + if (platformSpec.isMonolithic()) { + String srcGetter = getPullAccessor(platformSpec).getDirectStateAccessorFor(src2, resourceNode.getInSideResource(curChannel)).toImplementation(new String[] {}); + stateGetter.addStatement(langSpec.getVariableDeclaration(srcResType2.getInterfaceTypeName(), srcResName2) + + langSpec.getAssignment() + srcGetter + langSpec.getStatementDelimiter()); + } else { + String srcPath2 = src2.toResourcePath().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + generatePullDataTransfer(stateGetter, srcResName2, srcPath2, srcResType2, false, platformSpec, langSpec); + bDeclareClientField = true; + } + } else { + // a depending channel member. + ResourcePath src2 = resourcePaths.get(cm2).getKey(); + // get outside src2 resource state by pull data transfer. + if (cm2.isOutside() || src2.getCommonPrefix(resourceNode.getInSideResource(curChannel)) == null) { + // generate a pull data transfer from a depending in/ref resource. + Type srcResType2 = src2.getResourceStateType(); + String srcResName2 = langSpec.toVariableName(getComponentName(src2.getResourceHierarchy(), langSpec)); + if (platformSpec.isMonolithic()) { + String dependingGetter = getPullAccessor(platformSpec).getDirectStateAccessorFor(src2, resourceNode.getInSideResource(curChannel)).toImplementation(new String[] {}); + stateGetter.addStatement(langSpec.getVariableDeclaration(srcResType2.getInterfaceTypeName(), srcResName2) + + langSpec.getAssignment() + dependingGetter + langSpec.getStatementDelimiter()); + } else { + String srcPath2 = src2.toResourcePath().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + generatePullDataTransfer(stateGetter, srcResName2, srcPath2, srcResType2, false, platformSpec, langSpec); + bDeclareClientField = true; + } + } + } + } + // collect the message constraints by a descendant channel. + List varsForSideEffects = new ArrayList<>(); + int v = 0; + resourcePathsAndMessage = curChannel.fillOutsideResourcePaths(out, getPullAccessor(platformSpec), null); + if (resourcePathsAndMessage != null) { + resourcePaths = resourcePathsAndMessage.getKey(); + Term messageTermSub = resourcePathsAndMessage.getValue(); + for (Entry fieldEnt: ((Term) messageTermSub).getSubTerms(Field.class).entrySet()) { + Position pos = fieldEnt.getKey(); + Field field = fieldEnt.getValue(); + Variable var = new Variable(field.getSymbol().getName(), field.getType()); + ((Term) messageTermSub).replaceSubTerm(pos, var); + } + for (Map.Entry subTermEnt: messageTermSub.getSubTerms(Term.class).entrySet()) { + Term subTerm = subTermEnt.getValue(); + if (!(subTerm instanceof Constant) && subTerm.getSymbol().isImplWithSideEffect()) { + Variable var = new Variable("v" + v, subTerm.getType()); + varsForSideEffects.add(var); + v++; + // Add a side effect statement within the loop + Position pos = new Position(); + pos.addHeadOrder(0); + subTerm.replaceSubTerm(pos, var); + String[] sideEffects = new String[] {""}; + String curState = messageTermSub.toImplementation(sideEffects); + stateGetter.addStatement(sideEffects[0].replaceAll("\n", "")); + // Cancel the side effects in the return value. + pos = subTermEnt.getKey(); + messageTermSub.replaceSubTerm(pos, var); + } + } + if (messageTerm == null) { + messageTerm = messageTermSub; + } else { + messageTerm = (Term) messageTerm.unify(messageTermSub); + } + if (messageTerm == null) { + throw new UnificationFailed(); + } + } + // enclosed by a for loop (for data collecting pull transfer) + Expression selExp = curChannel.getSelectors().get(0).getExpression(); + Type selType = null; + String forVarName = null; + if (selExp instanceof Variable) { + selType = ((Variable) selExp).getType(); + forVarName = ((Variable) selExp).getName(); + ChannelMember insideChMem = null; + for (ChannelMember cm2 :curChannel.getInputChannelMembers()) { + if (!cm2.isOutside()) { + insideChMem = cm2; + break; + } + } + if (insideChMem == null) { + for (ChannelMember cm2 :curChannel.getReferenceChannelMembers()) { + if (!cm2.isOutside()) { + insideChMem = cm2; + break; + } + } + } + ResourcePath insideResPath = insideChMem.getResource(); + while (insideResPath.getParent() != null && (insideResPath.getLastParam() == null || !insideResPath.getLastParam().equals(selExp))) { + insideResPath = insideResPath.getParent(); + } + insideResPath = insideResPath.getParent(); + if (insideResPath != null) { + String parent = null; + if (platformSpec.isMonolithic() + || insideResPath.getCommonPrefix(dstResPath) != null + || !platformSpec.isDifferentTreesAsDifferentServices()) { + if (!platformSpec.isMonolithic() && generatesComponent(insideResPath.getResourceHierarchy())) { + Expression parentGetter = getPullAccessor(platformSpec).getDirectStateAccessorFor(insideResPath, dstResPath); + Term valueGetter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); + valueGetter.addChild(parentGetter); + parent = valueGetter.toImplementation(new String[] {}); + } else { + parent = getPullAccessor(platformSpec).getDirectStateAccessorFor(insideResPath, dstResPath).toImplementation(new String[] {}); + } + } else { + // for REST API + parent = langSpec.toVariableName(getComponentName(insideResPath.getResourceHierarchy(), langSpec)); + } + if (selType.equals(DataConstraintModel.typeInt)) { + // make a for loop (for a list) for data collecting. + stateGetter.addFirstStatement(langSpec.getForStatementForList(forVarName, parent)); + } else if (selType.equals(DataConstraintModel.typeString)) { + // make a for loop (for a map) for data collecting. + stateGetter.addFirstStatement(langSpec.getForStatementForMap(forVarName, DataConstraintModel.typeString.getInterfaceTypeName(), parent)); + } + if (!platformSpec.isMonolithic() + && insideResPath.getCommonPrefix(dstResPath) == null + && platformSpec.isDifferentTreesAsDifferentServices()) { + // for REST API + Type parentResType = insideResPath.getResourceStateType(); + String parentResName = langSpec.toVariableName(getComponentName(insideResPath.getResourceHierarchy(), langSpec)); + String parentResPath = insideResPath.toResourcePath().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + generatePullDataTransfer(stateGetter, parentResName, parentResPath, parentResType, true, platformSpec, langSpec); + bDeclareClientField = true; + } + } + } + // initialize the variables to hold side effects within the loop + for (Variable var: varsForSideEffects) { + stateGetter.addFirstStatement(langSpec.getVariableDeclaration(var.getType().getInterfaceTypeName(), var.getName()) + + langSpec.getAssignment() + langSpec.getConstructorInvocation(var.getType().getImplementationTypeName(), null) + langSpec.getStatementDelimiter()); + } + // end of the loop + stateGetter.addStatement("}"); + if (curChannel.getChildren() != null && curChannel.getChildren().size() > 0) { + channelItrStack.push(chItr); + chItr = curChannel.getChildren().iterator(); + } + } + } while (!channelItrStack.isEmpty()); + } + + // generate a return statement. + String[] sideEffects = new String[] {""}; + String curState = ch.deriveUpdateExpressionOf(out, messageTerm, getPullAccessor(platformSpec)).toImplementation(sideEffects); + stateGetter.addStatement(sideEffects[0] + langSpec.getReturnStatement(curState) + langSpec.getStatementDelimiter()); + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork + | InvalidMessage | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + return bDeclareClientField; + } + + private void declareDescendantGetterMethods(ResourceNode resourceNode, TypeDeclaration component, Map> descendantGetters, ILanguageSpecific langSpec) { + // Declare the getter methods in this resource to obtain descendant resources. + Set descendants = descendantGetters.get(resourceNode.getResourceHierarchy()); + if (descendants == null) { + descendants = new HashSet<>(); + descendantGetters.put(resourceNode.getResourceHierarchy(), descendants); + } + for (ResourceNode child: resourceNode.getChildren()) { + // A descendant of the child may generate a component. + List params = new ArrayList<>(); + int v = 1; + ResourceNode descendant = child; + Set childNodes; + do { + Expression param = descendant.getPrimaryResourcePath().getLastParam(); + if (param != null) { + if (param instanceof Variable) { + Variable var = (Variable) param; + params.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + } else if (param instanceof Term) { + Term var = (Term) param; + params.add(langSpec.newVariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + if (generatesComponent(descendant.getResourceHierarchy())) { + // If the descendant generates a component. + if (!descendants.contains(descendant.getResourceHierarchy())) { + descendants.add(descendant.getResourceHierarchy()); + String descendantCompName = getComponentName(descendant.getResourceHierarchy(), langSpec); + Type descendantType = new Type(descendantCompName, descendantCompName); + MethodDeclaration descendantGetter = null; + if (params.size() == 0) { + descendantGetter = langSpec.newMethodDeclaration(getterPrefix + descendantCompName, descendantType); + } else { + descendantGetter = langSpec.newMethodDeclaration(getterPrefix + descendantCompName, false, descendantType, params); + } + + fillDescendantGetterMethod(descendantGetter, descendant.getResourceHierarchy(), child.getResourceHierarchy(), resourceNode.getResourceHierarchy(), component, langSpec); + component.addMethod(descendantGetter); + } + break; + } + childNodes = descendant.getChildren(); + } while (childNodes != null && childNodes.size() == 1 && (descendant = childNodes.iterator().next()) != null); + } + } + + private Map.Entry, Map>>> declareCacheFieldsAndUpdateMethods(ResourceNode resourceNode, + TypeDeclaration component, TypeDeclaration parentComponent, TypeDeclaration rootComponent, IPlatformSpecific platformSpec, 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()) { + ChannelNode directSrcChannel = (ChannelNode) chToRes.getSource(); + DataTransferChannel ch = directSrcChannel.getChannel(); + // Should take into account the channel hierarchy. + Set ancestorSrcChannels = directSrcChannel.getAncestors(); + Set descendantSrcChannels = directSrcChannel.getDescendants(); + Set inEdges = new HashSet<>(); + inEdges.addAll(directSrcChannel.getInEdges()); + for (ChannelNode ancestorSrc: ancestorSrcChannels) { + inEdges.addAll(ancestorSrc.getInEdges()); + } + for (ChannelNode descendantSrc: descendantSrcChannels) { + inEdges.addAll(descendantSrc.getInEdges()); + } + for (Edge resToCh: inEdges) { + // For each data transfer from srcResPath:ResourcePath to resourceNode:ResourceNode. + DataFlowEdge re = (DataFlowEdge) resToCh; + ChannelNode indirectSrcChNode = (ChannelNode) re.getDestination(); + DataTransferChannel indirectSrcCh = indirectSrcChNode.getChannel(); + ResourcePath srcResPath = ((ResourceNode) re.getSource()).getOutSideResource(indirectSrcCh); + 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: indirectSrcCh.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; + ChannelMember out = null; + ResourcePath dstResPath = null; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (resourceNode.getInSideResources().contains(cm.getResource())) { + out = cm; + dstResPath = cm.getResource(); + if (cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + } + // Also take into account the channel hierarchy to determine push/pull transfer. + if (ancestorSrcChannels.contains(indirectSrcChNode)) { + outsideOutputResource = true; // Regarded as (broadcasting) push transfer. + } + if (descendantSrcChannels.contains(indirectSrcChNode)) { + outsideInputResource = true; // Regarded as (collecting) pull transfer. + } + if ((((PushPullAttribute) re.getAttribute()).getSelectedOption() == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) { + // For push data transfer + + boolean hasRestAPI = false; + boolean isRestAPI = false; + if (!platformSpec.isMonolithic() + && (outsideOutputResource || (resourceNode.getInSideResource(ch).getCommonPrefix(srcResPath) == null && platformSpec.isDifferentTreesAsDifferentServices()))) { + // Inter-service + hasRestAPI = true; + if (component != null && !((RestApiSpecific) platformSpec).hasHttpClientFieldDeclaration(component)) { + // Declare a client field to connect to the destination resource of push transfer. + ((RestApiSpecific) platformSpec).addHttpClientFieldDeclaration(component); + } + if (resourceNode.getParent() == null) { + // A root resource + isRestAPI = true; + } + } + // Declare an update method in the type of the destination resource. + ArrayList parameters = new ArrayList<>(); + getUpdateResourcePathAndPathParams(dstResPath, parameters, isRestAPI, platformSpec, langSpec); // Path parameters to identify the self resource. + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + VariableDeclaration chParam = langSpec.newVariableDeclaration(selVar.getType(), selVar.getName()); + if (isRestAPI) ((RestApiSpecific) platformSpec).addFormParamAnnotation(chParam, selVar.getName()); + parameters.add(chParam); // A channel parameter to specify the context of the collaboration. + } + } + VariableDeclaration param = langSpec.newVariableDeclaration(srcResPath.getResourceStateType(), srcResName); + if (isRestAPI) ((RestApiSpecific) platformSpec).addFormParamAnnotation(param, srcResName); + parameters.add(param); // The state of the source resource to carry the data-flow. + // For the refs. + for (ResourcePath ref: ch.getReferenceResources()) { + if (!resourceNode.getInSideResources().contains(ref)) { + String refName = langSpec.toVariableName(getComponentName(ref.getResourceHierarchy(), langSpec)); + param = langSpec.newVariableDeclaration(ref.getResourceStateType(), refName); + if (isRestAPI) ((RestApiSpecific) platformSpec).addFormParamAnnotation(param, refName); + parameters.add(param); + } + } + 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); + } + } 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); + } + } + // Calculate in-degree (PUSH transfer) of the destination resource. + int inDegree = 0; + for (Edge resToCh2: inEdges) { + DataFlowEdge df =(DataFlowEdge) resToCh2; + if (((PushPullAttribute) df.getAttribute()).getSelectedOption() == PushPullValue.PUSH) { + inDegree++; + } + } + if (isRestAPI) { + // Determine whether the update method is put or post or delete. + if (isPut(out)) { + ((RestApiSpecific) platformSpec).addPutAnnotations(update); + } else { + if (!isDelete(out)) { + ((RestApiSpecific) platformSpec).addPostAnnotations(update); + } else { + ((RestApiSpecific) platformSpec).addDeleteAnnotations(update); + } + } + if (inDegree > 1) { + // If incoming edges are multiple, then a child resource for each source resource is defined in the destination resource so that its state can be updated separately. + if (isRestAPI) ((RestApiSpecific) platformSpec).addPathAnnotation(update, "/" + srcResName); + } + } + if (update != null) { + if (component != null) { + // A component is created for this resource. + component.addMethod(update); + } else if (parentComponent != null) { + // No component is created for this resource. + parentComponent.addMethod(update); + } + } + // For a post/put REST API. + if (hasRestAPI) { + if (!isRestAPI) { + // If not a root resource. + // Declare an update accessor method in the type of root resource. + declareUpdateAccessorInTheRootResource(resourceNode, update.getName(), ch, out, srcResPath, dstResPath, + rootComponent, inDegree, platformSpec, langSpec); + } + // to convert a json param to a tuple, pair or map object. + for (VariableDeclaration jsonParam: update.getParameters()) { + Type paramType = jsonParam.getType(); + String paramName = jsonParam.getName(); + String paramTypeName = paramType.getInterfaceTypeName(); + String strTypeName = DataConstraintModel.typeString.getInterfaceTypeName(); + String paramConverter = ""; + if (DataConstraintModel.typeList.isAncestorOf(paramType) && paramType != DataConstraintModel.typeList) { + Type compType = TypeInference.getListComponentType(paramType); + if (DataConstraintModel.typeTuple.isAncestorOf(compType)) { + jsonParam.setType(DataConstraintModel.typeListStr); + jsonParam.setName(paramName + "_json"); + paramConverter += langSpec.getVariableDeclaration(paramTypeName, paramName) + langSpec.getAssignment() + langSpec.getConstructorInvocation(paramType.getImplementationTypeName(), null) + langSpec.getStatementDelimiter() + "\n"; + paramConverter += langSpec.getForStatementForCollection("str", strTypeName, jsonParam.getName()) + "\n"; + String mapTypeName = convertFromEntryToMapType(compType, langSpec); + paramConverter += "\t" + ((RestApiSpecific) platformSpec).getConversionFromJsonString("str", "i", mapTypeName) + langSpec.getStatementDelimiter() + "\n"; + paramConverter += "\t" + langSpec.getMethodInvocation(paramName, DataConstraintModel.append.getImplName(), List.of(getCodeForConversionFromMapToTuple(compType, "i", langSpec))) + langSpec.getStatementDelimiter() + "\n"; + paramConverter += langSpec.getEndForStatement("str"); + ((RestApiSpecific) platformSpec).addJsonException(update); + } else if (DataConstraintModel.typePair.isAncestorOf(compType)) { + jsonParam.setType(DataConstraintModel.typeListStr); + jsonParam.setName(paramName + "_json"); + paramConverter += langSpec.getVariableDeclaration(paramTypeName, paramName) + langSpec.getAssignment() + langSpec.getConstructorInvocation(paramType.getImplementationTypeName(), null) + langSpec.getStatementDelimiter() + "\n"; + paramConverter += langSpec.getForStatementForCollection("str", strTypeName, jsonParam.getName()) + "\n"; + String mapTypeName = convertFromEntryToMapType(compType, langSpec); + paramConverter += "\t" + ((RestApiSpecific) platformSpec).getConversionFromJsonString("str", "i", mapTypeName) + langSpec.getStatementDelimiter() + "\n"; + paramConverter += "\t" + langSpec.getMethodInvocation(paramName, DataConstraintModel.append.getImplName(), List.of(getCodeForConversionFromMapToPair(compType, "i", langSpec))) + langSpec.getStatementDelimiter() + "\n"; + paramConverter += langSpec.getEndForStatement("str"); + ((RestApiSpecific) platformSpec).addJsonException(update); + } else if (DataConstraintModel.typeMap.isAncestorOf(compType)) { + jsonParam.setType(DataConstraintModel.typeListStr); + // To do. + } + } else if (DataConstraintModel.typeTuple.isAncestorOf(paramType)) { + jsonParam.setType(DataConstraintModel.typeString); + jsonParam.setName(paramName + "_json"); + paramConverter += langSpec.getVariableDeclaration(paramTypeName, paramName) + langSpec.getStatementDelimiter() + "\n"; + paramConverter += langSpec.getOpeningScoreDelimiter() + "\n"; + String mapTypeName = convertFromEntryToMapType(paramType, langSpec); + paramConverter += "\t" + ((RestApiSpecific) platformSpec).getConversionFromJsonString(paramName + "_json", "i", mapTypeName) + langSpec.getStatementDelimiter() + "\n"; + paramConverter += "\t" + paramName + langSpec.getAssignment() + getCodeForConversionFromMapToTuple(paramType, "i", langSpec) + langSpec.getStatementDelimiter() + "\n"; + paramConverter += langSpec.getClosingScoreDelimiter(); + ((RestApiSpecific) platformSpec).addJsonException(update); + } else if (DataConstraintModel.typePair.isAncestorOf(paramType)) { + jsonParam.setType(DataConstraintModel.typeString); + jsonParam.setName(paramName + "_json"); + paramConverter += langSpec.getVariableDeclaration(paramTypeName, paramName) + langSpec.getStatementDelimiter() + "\n"; + paramConverter += langSpec.getOpeningScoreDelimiter() + "\n"; + String mapTypeName = convertFromEntryToMapType(paramType, langSpec); + paramConverter += "\t" + ((RestApiSpecific) platformSpec).getConversionFromJsonString(paramName + "_json", "i", mapTypeName) + langSpec.getStatementDelimiter() + "\n"; + paramConverter += "\t" + paramName + langSpec.getAssignment() + getCodeForConversionFromMapToPair(paramType, "i", langSpec) + langSpec.getStatementDelimiter() + "\n"; + paramConverter += langSpec.getClosingScoreDelimiter(); + ((RestApiSpecific) platformSpec).addJsonException(update); + } else if (DataConstraintModel.typeMap.isAncestorOf(paramType)) { + jsonParam.setType(DataConstraintModel.typeString); + jsonParam.setName(paramName + "_json"); + paramConverter += langSpec.getVariableDeclaration(paramTypeName, paramName) + langSpec.getAssignment() + langSpec.getConstructorInvocation(paramType.getImplementationTypeName(), null) + langSpec.getStatementDelimiter() + "\n"; + paramConverter += langSpec.getOpeningScoreDelimiter() + "\n"; + String mapTypeName = convertFromEntryToMapType(paramType, langSpec); + paramConverter += "\t" + ((RestApiSpecific) platformSpec).getConversionFromJsonString(paramName + "_json", "i", mapTypeName) + langSpec.getStatementDelimiter() + "\n"; + paramConverter += "\t" + getCodeForConversionFromMapToMap(paramType, "i", paramName, langSpec) + "\n"; + paramConverter += langSpec.getClosingScoreDelimiter(); + ((RestApiSpecific) platformSpec).addJsonException(update); + } + if (paramConverter.length() > 0 && !update.getBody().getStatements().contains(paramConverter)) { + update.addFirstStatement(paramConverter); + } + } + } + + // Add a statement to update the state field + if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { + try { + if (resourceNode.getInSideResources().contains(out.getResource())) { + Term unifiedMassage = null; + for (ChannelNode srcChNode: ancestorSrcChannels) { + DataTransferChannel abcestorSrcCh = (DataTransferChannel) srcChNode.getChannel(); + Term message = abcestorSrcCh.fillOutsideResourcePaths(out, getPushAccessor(platformSpec), null).getValue(); + if (unifiedMassage == null) { + unifiedMassage = message; + } else { + unifiedMassage = (Term) unifiedMassage.unify(message); + } + } + Expression updateExp = null; + if (ch.getReferenceChannelMembers().size() == 0) { + Term message = ch.fillOutsideResourcePaths(out, getPushAccessor(platformSpec), null).getValue(); + if (unifiedMassage == null) { + unifiedMassage = message; + } else { + unifiedMassage = (Term) unifiedMassage.unify(message); + } + updateExp = ch.deriveUpdateExpressionOf(out, unifiedMassage, getPushAccessor(platformSpec)); + } else { + // if there exists one or more reference channel member. + HashMap inputResourceToStateAccessor = new HashMap<>(); + for (ChannelMember in: ch.getInputChannelMembers()) { + inputResourceToStateAccessor.put(in, getPushAccessor(platformSpec)); + } + for (ChannelMember ref: ch.getReferenceChannelMembers()) { + inputResourceToStateAccessor.put(ref, getRefAccessor(platformSpec)); + } + Term message = ch.fillOutsideResourcePaths(out, getPushAccessor(platformSpec), inputResourceToStateAccessor).getValue(); + if (unifiedMassage == null) { + unifiedMassage = message; + } else { + unifiedMassage = (Term) unifiedMassage.unify(message); + } + updateExp = ch.deriveUpdateExpressionOf(out, unifiedMassage, getPushAccessor(platformSpec)); + } + // 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 descendantRes = outRes; + Set children = descendantRes.getChildren(); + do { + descendantRes = children.iterator().next(); + if (generatesComponent(descendantRes)) { + updateStatements.put(update, new AbstractMap.SimpleEntry<>(updateExp, new AbstractMap.SimpleEntry<>(outRes, descendantRes))); + updateExp = null; + break; + } + children = descendantRes.getChildren(); + } while (children != null && children.size() == 1); + } + // Replace the type of the state field. + Type fieldType = getImplStateType(outRes, langSpec); + if (updateExp instanceof Term) { + ((Term) updateExp).setType(fieldType); + for (Map.Entry varEnt: ((Term) updateExp).getVariables().entrySet()) { + if (varEnt.getValue().getName().equals(fieldOfResourceState)) { + varEnt.getValue().setType(fieldType); + } + } + } else if (updateExp instanceof Variable) { + ((Variable) updateExp).setType(fieldType); + } + // 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]; + if (updateStatement.endsWith("\n")) { + updateStatement = updateStatement.substring(0, updateStatement.length() - 1); + } + } 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 (updateStatement.endsWith("\n")) { + updateStatement = updateStatement.substring(0, updateStatement.length() - 1); + } + } + 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(); + } + } + // add an update statement of the state of dst side resource. + if (numOfOutResourcesWithTheSameHierarchy == 1) { + update.addFirstStatement(updateStatement); + } else { + Term conditions = null; + int i = 1; + Map>> resourcePaths = ch.fillOutsideResourcePaths(out, getPushAccessor(platformSpec)); + 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 : ""), DataConstraintModel.typeString), + arg}); + if (conditions == null) { + conditions = condition; + } else { + conditions = new Term(DataConstraintModel.and, new Expression[] { + conditions, + condition}); + } + } + i++; + } + update.addFirstStatement(langSpec.getIfStatement(conditions, updateStatement)); + } + } + } 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 (inDegree > 1 + || (ch.getInputChannelMembers().size() == 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. + String cacheFieldName = langSpec.toVariableName(getComponentName(srcResPath.getResourceHierarchy(), langSpec)); + FieldDeclaration cacheField = langSpec.newFieldDeclaration( + srcResPath.getResourceStateType(), + cacheFieldName, + 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); + } + } + + // Update and initialize a field to refer to an outside input resource for PULL transfer. + if (platformSpec.isMonolithic()) { + // For a monolithic application. + Set outsideInputMembers = new HashSet<>(); + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.isOutside()) { + outsideInputMembers.add(cm); + } + } + if (outsideInputMembers.size() > 0) { + Map>> resourcePaths = null; + for (ChannelMember out1: ch.getOutputChannelMembers()) { + if (resourceNode.getInSideResources().contains(out1.getResource())) { + try { + resourcePaths = ch.fillOutsideResourcePaths(out1, getPullAccessor(platformSpec)); + } 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(platformSpec).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). + boolean hasUpdateMethodinvoked = false; + for (Edge resToCh2: resourceNode.getOutEdges()) { + DataFlowEdge dOut = (DataFlowEdge) resToCh2; + ChannelNode directDstChNode = (ChannelNode) resToCh2.getDestination(); + DataTransferChannel directDstCh = directDstChNode.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: directDstCh.getInputChannelMembers()) { + if (cm.getResource().equals(resourceNode.getOutSideResource(directDstCh))) { + if (cm.isOutside()) { + outsideInputResource2 = true; // Regarded as pull transfer. + } + in = cm; + } + if (cm.isOutside()) { + outsideInputMembers2.add(cm); + } + } + // Should take into account the channel hierarchy. + Set ancestorDstChannels = directDstChNode.getAncestors(); + Set descendantDstChannels = directDstChNode.getDescendants(); + Set outEdges = new HashSet<>(); + outEdges.addAll(directDstChNode.getOutEdges()); + for (ChannelNode ancestorDst: ancestorDstChannels) { + outEdges.addAll(ancestorDst.getOutEdges()); + } + for (ChannelNode descendantDst: descendantDstChannels) { + outEdges.addAll(descendantDst.getOutEdges()); + } + for (Edge chToRes2: outEdges) { + // For each data transfer to dstNode:ResourceNode. + ResourceNode dstNode = ((ResourceNode) chToRes2.getDestination()); + ChannelNode chNode2 = (ChannelNode) chToRes2.getSource(); + DataTransferChannel ch2 = chNode2.getChannel(); + // Check if the output resource is outside of the channel scope. + boolean outsideOutputResource2 = false; + ChannelMember out1 = null; + for (ChannelMember cm: ch2.getOutputChannelMembers()) { + if (dstNode.getInSideResources().contains(cm.getResource())) { + out1 = cm; + if (cm.isOutside()) { + outsideOutputResource2 = true; + break; + } + } + } + // Also take into account the channel hierarchy to determine push/pull transfer. + if (descendantDstChannels.contains(chNode2)) { + outsideOutputResource2 = true; // Regarded as (broadcasting) push transfer. + } + if (ancestorDstChannels.contains(chNode2)) { + outsideInputResource2 = true; // Regarded as (collecting) pull transfer. + } + if ((((PushPullAttribute) dOut.getAttribute()).getSelectedOption() == PushPullValue.PUSH && !outsideInputResource2) || outsideOutputResource2) { + // PUSH transfer + boolean addForStatement = false; + String forVarName = null; + if (descendantDstChannels.contains(chNode2)) { + // For hierarchical channels (broadcasting push transfer). + if (ch2.getSelectors() != null && ch2.getSelectors().size() > 0) { + Expression selExp = ch2.getSelectors().get(0).getExpression(); + Type selType = null; + if (selExp instanceof Variable) { + selType = ((Variable) selExp).getType(); + forVarName = ((Variable) selExp).getName(); + ChannelMember insideChMem = null; + for (ChannelMember cm :ch2.getInputChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + if (insideChMem == null) { + for (ChannelMember cm :ch2.getReferenceChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + } + if (insideChMem == null) { + for (ChannelMember cm :ch2.getOutputChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + } + ResourcePath insideResPath = insideChMem.getResource(); + while (insideResPath.getParent() != null && (insideResPath.getLastParam() == null || !insideResPath.getLastParam().equals(selExp))) { + insideResPath = insideResPath.getParent(); + } + insideResPath = insideResPath.getParent(); + if (insideResPath != null) { + String parent = null; + if (platformSpec.isMonolithic() + || insideResPath.getCommonPrefix(resourceNode.getOutSideResource(directDstCh)) != null + || !platformSpec.isDifferentTreesAsDifferentServices()) { + if (!platformSpec.isMonolithic() && generatesComponent(insideResPath.getResourceHierarchy())) { + Expression getter = getPullAccessor(platformSpec).getDirectStateAccessorFor(insideResPath, resourceNode.getOutSideResource(directDstCh)); + Term valueGetter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); + valueGetter.addChild(getter); + parent = valueGetter.toImplementation(new String[] {}); + } else { + parent = getPullAccessor(platformSpec).getDirectStateAccessorFor(insideResPath, resourceNode.getOutSideResource(directDstCh)).toImplementation(new String[] {}); + } + } else { + // for REST API + parent = langSpec.toVariableName(getComponentName(insideResPath.getResourceHierarchy(), langSpec)); + } + if (selType.equals(DataConstraintModel.typeInt)) { + // make a for loop (for a list) for broadcasting. + update.addStatement(langSpec.getForStatementForList(forVarName, parent)); + addForStatement = true; + } else if (selType.equals(DataConstraintModel.typeString)) { + // make a for loop (for a map) for broadcasting. + update.addStatement(langSpec.getForStatementForMap(forVarName, DataConstraintModel.typeString.getInterfaceTypeName(), parent)); + addForStatement = true; + } + if (!platformSpec.isMonolithic() + && insideResPath.getCommonPrefix(resourceNode.getOutSideResource(directDstCh)) == null + && platformSpec.isDifferentTreesAsDifferentServices()) { + // for REST API + Type parentResType = insideResPath.getResourceStateType(); + String parentResName = langSpec.toVariableName(getComponentName(insideResPath.getResourceHierarchy(), langSpec)); + String parentResPath = insideResPath.toResourcePath().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + generatePullDataTransfer(update, parentResName, parentResPath, parentResType, true, platformSpec, langSpec); + } + } + } else if (selExp instanceof Term) { + // not supported. + } + } + } + // Get the value of reference member to call the update method. + List>> refParams = new ArrayList<>(); + Map> referredResources = new HashMap<>(); + Set referredSet = referredResources.get(update); + for (ChannelMember rc: ch2.getReferenceChannelMembers()) { + ResourcePath ref = rc.getResource(); + if (referredSet == null) { + referredSet = new HashSet<>(); + referredResources.put(update, referredSet); + } + if (!resourceNode.getInSideResources().contains(ref)) { + String refVarName = langSpec.toVariableName(getComponentName(ref.getResourceHierarchy(), langSpec)); + Type refResourceType = ref.getResourceStateType(); + if (!referredSet.contains(ref)) { + referredSet.add(ref); + String[] sideEffects = new String[] {""}; + ResourcePath srcRes = in.getResource(); + if (!generatesComponent(srcRes.getResourceHierarchy())) { + srcRes = srcRes.getParent(); + } + if (!platformSpec.isMonolithic() + && (rc.isOutside() || (ref.getCommonPrefix(srcRes) == null && platformSpec.isDifferentTreesAsDifferentServices()))) { + List pathParams = new ArrayList<>(); + for (Expression pathExp: ref.getPathParams()) { + pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); + } + generatePullDataTransfer(update, refVarName, ref.getResourceHierarchy().toResourcePath(pathParams), refResourceType, false, platformSpec, langSpec); + } else { + Expression refGetter = getPullAccessor(platformSpec).getDirectStateAccessorFor(ref, srcRes); + String refExp = refGetter.toImplementation(sideEffects); + String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); + update.addStatement(sideEffects[0] + langSpec.getVariableDeclaration(refTypeName, refVarName) + + langSpec.getAssignment() + refExp + langSpec.getStatementDelimiter()); + } + } + refParams.add(new AbstractMap.SimpleEntry<>(ref.getResourceStateType(), + new AbstractMap.SimpleEntry<>(refVarName, + refVarName))); + } + } + List>> pathParams = new ArrayList<>(); + if (platformSpec.isMonolithic()) { + // Update fields to refer to outside resources. + ResourcePath filledOutsideResourcePath = null; + try { + Map>> resourcePaths = ch2.fillOutsideResourcePaths(out1, getPullAccessor(platformSpec)); + if (resourcePaths != null && resourcePaths.size() > 0) { + for (ChannelMember outsideMember: resourcePaths.keySet()) { + ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey(); + if (out1.equals(outsideMember)) { + filledOutsideResourcePath = outsidePath; + } + if (!generatesComponent(outsidePath.getResourceHierarchy())) { + outsidePath = outsidePath.getParent(); + } + String outsideResName = langSpec.toVariableName(getComponentName(outsidePath.getResourceHierarchy(), langSpec)); + Expression outsideExp = getPullAccessor(platformSpec).getDirectStateAccessorFor(outsidePath, null); + if (generatesComponent(outsidePath.getResourceHierarchy())) { + outsideExp = ((Term) outsideExp).getChild(0); + } + if (outsideExp instanceof Field) { + outsideExp = new Variable(((Field) outsideExp).getSymbol().getName(), ((Field) outsideExp).getType()); + } else if (outsideExp instanceof Term) { + for (Entry fieldEnt: ((Term) outsideExp).getSubTerms(Field.class).entrySet()) { + Position pos = fieldEnt.getKey(); + Field field = fieldEnt.getValue(); + Variable var = new Variable(field.getSymbol().getName(), field.getType()); + ((Term) outsideExp).replaceSubTerm(pos, var); + } + } + String[] sideEffects = new String[] {""}; + String outsideAccessor = outsideExp.toImplementation(sideEffects); + update.addStatement(langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter()); // change the reference field. + } + } + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork + | InvalidMessage | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + // Values of path parameters to call the update method. + if (filledOutsideResourcePath == null) { + filledOutsideResourcePath = out1.getResource(); + } + for (Expression pathParam: filledOutsideResourcePath.getPathParams()) { + if (pathParam instanceof Variable) { + Variable pathVar = (Variable) pathParam; + pathParams.add(new AbstractMap.SimpleEntry<>(pathVar.getType(), + new AbstractMap.SimpleEntry<>(pathVar.getName(), + pathVar.getName()))); + } else if (pathParam instanceof Constant) { + Constant pathVar = (Constant) pathParam; + pathParams.add(new AbstractMap.SimpleEntry<>(pathVar.getType(), + new AbstractMap.SimpleEntry<>(pathVar.getSymbol().getName(), + pathVar.getSymbol().getName()))); + } + } + } else { + // Values of path parameters to call the update method. + for (Expression pathParam: out1.getResource().getPathParams()) { + if (pathParam instanceof Variable) { + Variable pathVar = (Variable) pathParam; + pathParams.add(new AbstractMap.SimpleEntry<>(pathVar.getType(), + new AbstractMap.SimpleEntry<>(pathVar.getName(), + pathVar.getName()))); + } + } + } + // Values of channel parameters to call the update method. + List>> params = new ArrayList<>(); + for (Selector selector: ch2.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 to call the update method. + ResourceHierarchy srcRes2 = resourceNode.getResourceHierarchy(); + if (generatesComponent(srcRes2)) { + params.add(new AbstractMap.SimpleEntry<>(srcRes2.getResourceStateType(), + new AbstractMap.SimpleEntry<>(langSpec.toVariableName(srcRes2.getResourceName()), + langSpec.getFieldAccessor(fieldOfResourceState)))); + } else { + params.add(new AbstractMap.SimpleEntry<>(srcRes2.getResourceStateType(), + new AbstractMap.SimpleEntry<>(langSpec.toVariableName(srcRes2.getResourceName()), + langSpec.getFieldAccessor(langSpec.toVariableName(srcRes2.getResourceName()))))); + srcRes2 = srcRes2.getParent(); + } + params.addAll(refParams); + // Call the update method. + 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 + || (!platformSpec.isMonolithic() && in.getResource().getCommonPrefix(out1.getResource()) == null && platformSpec.isDifferentTreesAsDifferentServices())) { + // Inter-servces + if (!platformSpec.isMonolithic()) { + // REST API + RestApiSpecific restApiSpec = (RestApiSpecific) platformSpec; + String httpMethod = null; + if (out1.getStateTransition().isRightUnary()) { + httpMethod = "put"; + } else { + httpMethod = "post"; + } + String[] sideEffects = new String[] {""}; + List pathParamsUrl = new ArrayList<>(); + for (Expression pathExp: out1.getResource().getPathParams()) { + pathParamsUrl.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); + } + String resName = langSpec.toVariableName(resComponentName); + if (inDegree <= 1) { + resName = null; + } + Map>> filledPaths = null; + try { + filledPaths = ch2.fillOutsideResourcePaths(out1, getPushAccessor(platformSpec)); + } catch (ParameterizedIdentifierIsFutureWork + | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage + | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + String dstPath = null; + if (filledPaths != null && filledPaths.get(out1) != null) { + ResourcePath filledDstPath = filledPaths.get(out1).getKey(); + dstPath = filledDstPath.toResourcePath().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + } else { + dstPath = dstRes.toResourcePath(pathParamsUrl); + } + // Call the update method. + if (!hasUpdateMethodinvoked) { + // The first call to an update method in this method + update.addStatement(restApiSpec.getHttpMethodParamsConstructionStatement(srcRes2.getResourceName(), params, true)); + update.addStatement(langSpec.getVariableDeclaration(DataConstraintModel.typeString.getInterfaceTypeName(), "result") + + langSpec.getAssignment() + restApiSpec.getHttpMethodCallStatement(restApiSpec.getBaseURL(), dstPath, resName, httpMethod)); + hasUpdateMethodinvoked = true; + } else { + // After the second time of call to update methods in this method + update.addStatement(restApiSpec.getHttpMethodParamsConstructionStatement(srcRes2.getResourceName(), params, false)); + update.addStatement("result" + langSpec.getAssignment() + restApiSpec.getHttpMethodCallStatement(restApiSpec.getBaseURL(), dstPath, resName, httpMethod)); + } + } else { + // Use the reference field to refer to outside destination resource. + List args = new ArrayList<>(); + for (Map.Entry> paramEnt: pathParams) { + args.add(paramEnt.getValue().getValue()); + } + for (Map.Entry> paramEnt: params) { + args.add(paramEnt.getValue().getValue()); + } + update.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, args) + + langSpec.getStatementDelimiter()); // this.dst.updateDstFromSrc(value, refParams); + } + } else { + // Intra-service + // The destination resource is not outside. + List args = new ArrayList<>(); + for (Map.Entry> paramEnt: pathParams) { + args.add(paramEnt.getValue().getValue()); + } + for (Map.Entry> paramEnt: params) { + args.add(paramEnt.getValue().getValue()); + } + if (srcRes2 != dstRes) { + update.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, args) + + langSpec.getStatementDelimiter()); // this.dst.updateDstFromSrc(value, refParams); + } else { + update.addStatement(langSpec.getMethodInvocation(updateMethodName, args) + + langSpec.getStatementDelimiter()); // this.updateDstFromSrc(value, refParams); + } + } + if (addForStatement) { + // Close the for loop + update.addStatement(langSpec.getEndForStatement(forVarName)); + } + } + } + if (outsideInputMembers2.size() > 0) { + if (!generatesComponent(resourceNode.getResourceHierarchy())) { + // srcRes2 does not have a component. + ResourcePath srcRes2 = resourceNode.getOutSideResource(directDstCh); + for (Edge chToRes2: outEdges) { + ChannelNode chNode2 = (ChannelNode) chToRes2.getSource(); + DataTransferChannel ch2 = chNode2.getChannel(); + for (ChannelMember out2: ch2.getOutputChannelMembers()) { + if (!generatesComponent(out2.getResource().getResourceHierarchy())) { + // Also dstRes2 does not have a component. + ResourcePath dstRes2 = out2.getResource(); + if (srcRes2.getParent().equals(dstRes2.getParent())) { + Map>> resourcePaths = null; + try { + resourcePaths = ch2.fillOutsideResourcePaths(out2, getPullAccessor(platformSpec)); + if (resourcePaths != null && resourcePaths.size() > 0) { + for (ChannelMember outsideMember: outsideInputMembers2) { + for (ChannelMember dependedMember: resourcePaths.get(outsideMember).getValue()) { + if (dependedMember.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(platformSpec).getDirectStateAccessorFor(outsidePath, null); + if (generatesComponent(outsidePath.getResourceHierarchy())) { + outsideExp = ((Term) outsideExp).getChild(0); + } + 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, TypeDeclaration rootComponent, DataTransferModel model, Map priorMemberForInputChannel, IPlatformSpecific platformSpec, 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 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 out: ((DataTransferChannel) ch).getOutputChannelMembers()) { + if (resourceNode.getInSideResources().contains(out.getResource())) { + Expression message = out.getStateTransition().getMessageExpression(); + MethodDeclaration input = null; + MethodDeclaration mainInputAccessor = null; + MethodDeclaration rootInputAccessor = null; + if (message instanceof Term) { + // Declare an input method in this component. + ArrayList resInputParams = new ArrayList<>(); + ArrayList mainInputParams = new ArrayList<>(); + ArrayList rootInputParams = new ArrayList<>(); + String resourcePath = null; + if (!platformSpec.isMonolithic()) { + resourcePath = getInputMethodResourcePathAndPathParams(out.getResource(), rootInputParams, platformSpec, langSpec); // 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. + 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())); + } + if (!platformSpec.isMonolithic() && !resourcePath.contains("{" + var.getName()+ "}")) { + VariableDeclaration param = langSpec.newVariableDeclaration(var.getType(), var.getName()); + ((RestApiSpecific) platformSpec).addFormParamAnnotation(param, var.getName()); + rootInputParams.add(param); + } + } + } + if (platformSpec.isMonolithic() + || (resourceNode.getResourceHierarchy().getParent() != null && resourceNode.getResourceHierarchy().getParent().getParent() != null)) { + 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. (for monolithic application) + if (platformSpec.hasMain()) { + String messageSymbol = ((Term) message).getSymbol().getImplName(); + mainInputAccessor = getMethod(mainComponent, messageSymbol); + if (mainInputAccessor == null) { + mainInputAccessor = langSpec.newMethodDeclaration(messageSymbol, false, null, mainInputParams); + mainComponent.addMethod(mainInputAccessor); + } else { + // Add type to a parameter without type. + if (mainInputAccessor.getParameters() != null) { + for (VariableDeclaration param: mainInputAccessor.getParameters()) { + if (param.getType() == null) { + for (VariableDeclaration p: mainInputParams) { + if (param.getName().equals(p.getName()) && p.getType() != null) { + param.setType(p.getType()); + } + } + } + } + } + } + } + + // For the root resource. (for REST API) + if (!platformSpec.isMonolithic()) { + if (priorMemberForInputChannel.get(ch) == null || out == priorMemberForInputChannel.get(ch)) { + // If out is the receiver of the input event. + priorMemberForInputChannel.put(ch, out); + String messageSymbol = ((Term) message).getSymbol().getImplName(); + rootInputAccessor = declareInputAccessorInTheRootResource(messageSymbol, rootInputParams, out, resourcePath, + rootComponent, platformSpec, langSpec); + if (input == null) { + input = rootInputAccessor; + rootInputAccessor = null; + } + } + } + } else if (message instanceof Variable) { + // Declare an input method in this component. + ArrayList resInputParams = new ArrayList<>(); + ArrayList mainInputParams = new ArrayList<>(); + int v = 1; + if (out.getResource().getLastParam() != null) { + Expression param = out.getResource().getLastParam(); + if (param instanceof Variable) { + Variable var = (Variable) param; + resInputParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + } else if (param instanceof Term) { + Term var = (Term) param; + resInputParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v)); + mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + if (out.getResource().getParent() != null) { + for (Expression param: out.getResource().getParent().getPathParams()) { + if (param instanceof Variable) { + Variable var = (Variable) param; + mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + } else if (param instanceof Term) { + Term var = (Term) param; + mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + } + if (platformSpec.isMonolithic() + || (resourceNode.getResourceHierarchy().getParent() != null && resourceNode.getResourceHierarchy().getParent().getParent() != null)) { + 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. (for monolithic application) + if (platformSpec.hasMain()) { + String messageSymbol = ((Variable) message).getName(); + mainInputAccessor = getMethod(mainComponent, messageSymbol, mainInputParams); + if (mainInputAccessor == null) { + if (mainInputParams.size() == 0) { + mainInputAccessor = langSpec.newMethodDeclaration(messageSymbol, null); + } else { + mainInputAccessor = langSpec.newMethodDeclaration(messageSymbol, false, null, mainInputParams); + } + mainComponent.addMethod(mainInputAccessor); + } + } + + // For the root resource. (for REST API) + if (!platformSpec.isMonolithic()) { + if (priorMemberForInputChannel.get(ch) == null || out == priorMemberForInputChannel.get(ch)) { + // If out is the receiver of the input event. + priorMemberForInputChannel.put(ch, out); + ArrayList rootInputParams = new ArrayList<>(); + String resourcePath = getGetterResourcePathAndPathParams(out.getResource(), rootInputParams, platformSpec, langSpec); + if (resourcePath.indexOf('/') > 0) { + resourcePath = resourcePath.substring(resourcePath.indexOf('/')); + } else { + resourcePath = ""; + } + String messageSymbol = ((Variable) message).getName(); + rootInputAccessor = declareInputAccessorInTheRootResource(messageSymbol, rootInputParams, out, resourcePath, + rootComponent, platformSpec, langSpec); + if (input == null) { + input = rootInputAccessor; + rootInputAccessor = null; + } + } + } + } + + // Add an invocation to the accessor method. + if (mainInputAccessor != null) { + if (platformSpec.hasMain()) { + // For an application with a main component, the reference resource is accessed from the main component. + for (ChannelMember rc: ((DataTransferChannel) ch).getReferenceChannelMembers()) { + // For each reference channel member, get the current state of the reference resource by pull data transfer. + ResourcePath ref = rc.getResource(); + if (!out.getResource().equals(ref)) { + String refVarName = ref.getLeafResourceName(); + Expression refGetter = getPullAccessor(platformSpec).getDirectStateAccessorFor(ref, null); + String[] sideEffects = new String[] {""}; + String refExp = refGetter.toImplementation(sideEffects); + String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); + mainInputAccessor.addFirstStatement(sideEffects[0] + langSpec.getVariableDeclaration(refTypeName, refVarName) + + langSpec.getAssignment() + refExp + langSpec.getStatementDelimiter()); + } + } + } + Expression resExp = getPullAccessor(platformSpec).getDirectStateAccessorFor(out.getResource(), null); + List args = new ArrayList<>(); + if (resExp instanceof Term) { + // to access the parent + if (((Term) resExp).getChildren().size() > 1 && ((Term) resExp).getChild(1) instanceof Variable) { + args.add(((Variable)((Term) resExp).getChild(1)).getName()); + } + resExp = ((Term) resExp).getChild(0); + } + String resourceAccess = resExp.toImplementation(new String[] {""}); + // Values of channel parameters. + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + 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()); + } + } + } + mainInputAccessor.addStatement(langSpec.getMethodInvocation(resourceAccess, input.getName(), args) + langSpec.getStatementDelimiter()); + } + + if (input != null) { + // Add a statement to update the state field to the input method. + try { + Expression updateExp = ((DataTransferChannel) ch).deriveUpdateExpressionOf(out, getRefAccessor(platformSpec)).getKey(); + // Replace Json constructor with a constructor of a descendant resource. + ResourceHierarchy outRes = out.getResource().getResourceHierarchy(); + if (outRes.getChildren().size() == 1 && outRes.getChildren().iterator().next().getNumParameters() > 0) { + ResourceHierarchy descendantRes = outRes; + Set children = descendantRes.getChildren(); + do { + descendantRes = children.iterator().next(); + if (generatesComponent(descendantRes)) { + inputStatements.put(input, new AbstractMap.SimpleEntry<>(updateExp, new AbstractMap.SimpleEntry<>(outRes, descendantRes))); + updateExp = null; + break; + } + children = descendantRes.getChildren(); + } while (children != null && children.size() == 1); + } + // 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]; + if (updateStatement.endsWith("\n")) { + updateStatement = updateStatement.substring(0, updateStatement.length() - 1); + } + } 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 (updateStatement.endsWith("\n")) { + updateStatement = updateStatement.substring(0, updateStatement.length() - 1); + } + } + 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); + } + } + } + if (!platformSpec.hasMain()) { + // For an application with no main component, the reference resource is accessed from each resource. + for (ChannelMember rc: ((DataTransferChannel) ch).getReferenceChannelMembers()) { + // For each reference channel member, get the current state of the reference side resource by pull data transfer. + ResourcePath ref = rc.getResource(); + if (!out.getResource().equals(ref)) { + String refResourceName = ref.getLeafResourceName(); + Type refResourceType = ref.getResourceStateType(); + String[] sideEffects = new String[] {""}; + ResourcePath dstRes = out.getResource(); + if (!generatesComponent(dstRes.getResourceHierarchy())) { + dstRes = dstRes.getParent(); + } + if (!platformSpec.isMonolithic() + && (rc.isOutside() || (ref.getCommonPrefix(dstRes) == null && platformSpec.isDifferentTreesAsDifferentServices()))) { + List pathParams = new ArrayList<>(); + for (Expression pathExp: ref.getPathParams()) { + pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); + } + generatePullDataTransfer(input, refResourceName, ref.getResourceHierarchy().toResourcePath(pathParams), refResourceType, true, platformSpec, langSpec); + } else { + Expression refGetter = getPullAccessor(platformSpec).getDirectStateAccessorFor(ref, dstRes); + String refExp = refGetter.toImplementation(sideEffects); + String refTypeName = refResourceType.getInterfaceTypeName(); + input.addFirstStatement(sideEffects[0] + langSpec.getVariableDeclaration(refTypeName, refResourceName) + + langSpec.getAssignment() + refExp + langSpec.getStatementDelimiter()); + } + } + } + } + + if (rootInputAccessor != null) { + // In the root resource + // The expression of the receiver (resource) of the input method. + ResourcePath outResPath = new ResourcePath(out.getResource()); + for (int i = 0; i < outResPath.getPathParams().size(); i++) { + Parameter pathParam = new Parameter(rootInputAccessor.getParameters().get(i).getName()); + outResPath.replacePathParam(i, pathParam, null); + } + Expression resExp = getPullAccessor(platformSpec).getDirectStateAccessorFor(outResPath, outResPath.getRoot()); + List args = new ArrayList<>(); + if (resExp instanceof Term) { + // to access the parent + if (((Term) resExp).getChildren().size() > 1 && ((Term) resExp).getChild(1) instanceof Variable) { + args.add(((Variable)((Term) resExp).getChild(1)).getName()); + } + resExp = ((Term) resExp).getChild(0); + } + String resourceAccess = resExp.toImplementation(new String[] {""}); + // Values of channel parameters. + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + args.add(selVar.getName()); + } + } + // Values of message parameters. + if (message instanceof Term) { + for (Variable mesVar: message.getVariables().values()) { + args.add(mesVar.getName()); + } + } + rootInputAccessor.addStatement(langSpec.getMethodInvocation(resourceAccess, input.getName(), args) + langSpec.getStatementDelimiter()); + if (input != null && input.getThrows() != null && ((RestApiSpecific) platformSpec).hasJsonException(input)) { + ((RestApiSpecific) platformSpec).addJsonException(rootInputAccessor); + } + } + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork + | InvalidMessage | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + + // Add an invocation to an update method (for a chain of update method invocations). + boolean hasUpdateMethodinvoked = false; + for (Edge resToCh: resourceNode.getOutEdges()) { + DataFlowEdge dOut = (DataFlowEdge) resToCh; + ChannelNode directDstChNode = (ChannelNode) resToCh.getDestination(); + DataTransferChannel directDstCh = directDstChNode.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: directDstCh.getInputChannelMembers()) { + if (resourceNode.getOutSideResources().contains(cm.getResource())) { + if (cm.isOutside()) { + outsideInputResource2 = true; // Regarded as pull transfer. + } + in = cm; + } + if (cm.isOutside()) { + outsideInputMembers2.add(cm); + } + } + // Should take into account the channel hierarchy. + Set ancestorDstChannels = directDstChNode.getAncestors(); + Set descendantDstChannels = directDstChNode.getDescendants(); + Set outEdges = new HashSet<>(); + outEdges.addAll(directDstChNode.getOutEdges()); + for (ChannelNode ancestorDst: ancestorDstChannels) { + outEdges.addAll(ancestorDst.getOutEdges()); + } + for (ChannelNode descendantDst: descendantDstChannels) { + outEdges.addAll(descendantDst.getOutEdges()); + } + for (Edge chToRes: outEdges) { + // For each data transfer to dstNode:ResourceNode. + ResourceNode dstNode = ((ResourceNode) chToRes.getDestination()); + ChannelNode chNode2 = (ChannelNode) chToRes.getSource(); + DataTransferChannel ch2 = chNode2.getChannel(); + // 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; + } + } + } + // Also take into account the channel hierarchy to determine push/pull transfer. + if (descendantDstChannels.contains(chNode2)) { + outsideOutputResource2 = true; // Regarded as (broadcasting) push transfer. + } + if (ancestorDstChannels.contains(chNode2)) { + outsideInputResource2 = true; // Regarded as (collecting) pull transfer. + } + if ((((PushPullAttribute) dOut.getAttribute()).getSelectedOption() == PushPullValue.PUSH && !outsideInputResource2) || outsideOutputResource2) { + // PUSH transfer + + // Calculate in-degree (PUSH transfer) of the destination resource. + Set inEdges = new HashSet<>(); + inEdges.addAll(directDstChNode.getInEdges()); + for (ChannelNode ancestorSrc: ancestorDstChannels) { + inEdges.addAll(ancestorSrc.getInEdges()); + } + for (ChannelNode descendantSrc: descendantDstChannels) { + inEdges.addAll(descendantSrc.getInEdges()); + } + int inDegree = 0; + for (Edge resToCh2: inEdges) { + DataFlowEdge df =(DataFlowEdge) resToCh2; + if (((PushPullAttribute) df.getAttribute()).getSelectedOption() == PushPullValue.PUSH) { + inDegree++; + } + } + boolean addForStatement = false; + String forVarName = null; + if (descendantDstChannels.contains(chNode2)) { + // For hierarchical channels (broadcasting push transfer). + if (ch2.getSelectors() != null && ch2.getSelectors().size() > 0) { + Expression selExp = ch2.getSelectors().get(0).getExpression(); + Type selType = null; + if (selExp instanceof Variable) { + selType = ((Variable) selExp).getType(); + forVarName = ((Variable) selExp).getName(); + ChannelMember insideChMem = null; + for (ChannelMember cm :ch2.getInputChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + if (insideChMem == null) { + for (ChannelMember cm :ch2.getReferenceChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + } + if (insideChMem == null) { + for (ChannelMember cm :ch2.getOutputChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + } + ResourcePath insideResPath = insideChMem.getResource(); + while (insideResPath.getParent() != null && (insideResPath.getLastParam() == null || !insideResPath.getLastParam().equals(selExp))) { + insideResPath = insideResPath.getParent(); + } + insideResPath = insideResPath.getParent(); + if (insideResPath != null) { + String parent = null; + if (platformSpec.isMonolithic() + || insideResPath.getCommonPrefix(resourceNode.getOutSideResource(directDstCh)) != null + || !platformSpec.isDifferentTreesAsDifferentServices()) { + if (!platformSpec.isMonolithic() && generatesComponent(insideResPath.getResourceHierarchy())) { + Expression getter = getPullAccessor(platformSpec).getDirectStateAccessorFor(insideResPath, resourceNode.getOutSideResource(directDstCh)); + Term valueGetter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); + valueGetter.addChild(getter); + parent = valueGetter.toImplementation(new String[] {}); + } else { + parent = getPullAccessor(platformSpec).getDirectStateAccessorFor(insideResPath, resourceNode.getOutSideResource(directDstCh)).toImplementation(new String[] {}); + } + } else { + // for REST API + parent = langSpec.toVariableName(getComponentName(insideResPath.getResourceHierarchy(), langSpec)); + } + if (selType.equals(DataConstraintModel.typeInt)) { + // make a for loop (for a list) for broadcasting. + input.addStatement(langSpec.getForStatementForList(forVarName, parent)); + addForStatement = true; + } else if (selType.equals(DataConstraintModel.typeString)) { + // make a for loop (for a map) for broadcasting. + input.addStatement(langSpec.getForStatementForMap(forVarName, DataConstraintModel.typeString.getInterfaceTypeName(), parent)); + addForStatement = true; + } + if (!platformSpec.isMonolithic() + && insideResPath.getCommonPrefix(resourceNode.getOutSideResource(directDstCh)) == null + && platformSpec.isDifferentTreesAsDifferentServices()) { + // for REST API + Type parentResType = insideResPath.getResourceStateType(); + String parentResName = langSpec.toVariableName(getComponentName(insideResPath.getResourceHierarchy(), langSpec)); + String parentResPath = insideResPath.toResourcePath().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + generatePullDataTransfer(input, parentResName, parentResPath, parentResType, true, platformSpec, langSpec); + } + } + } else if (selExp instanceof Term) { + // not supported. + } + } + } + // Get the value of reference member to call the update method. + List>> refParams = new ArrayList<>(); + Map> referredResources = new HashMap<>(); + Set referredSet = referredResources.get(input); + for (ChannelMember rc: ch2.getReferenceChannelMembers()) { + ResourcePath ref = rc.getResource(); + if (referredSet == null) { + referredSet = new HashSet<>(); + referredResources.put(input, referredSet); + } + if (!resourceNode.getOutSideResources().contains(ref)) { + String refVarName = langSpec.toVariableName(getComponentName(ref.getResourceHierarchy(), langSpec)); + if (!referredSet.contains(ref)) { + referredSet.add(ref); + ResourcePath srcRes = in.getResource(); + if (!generatesComponent(srcRes.getResourceHierarchy())) { + srcRes = srcRes.getParent(); + } + Expression refGetter = getPullAccessor(platformSpec).getDirectStateAccessorFor(ref, srcRes); + 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()); + } + refParams.add(new AbstractMap.SimpleEntry<>(ref.getResourceStateType(), + new AbstractMap.SimpleEntry<>(refVarName, + refVarName))); + } + } + List>> pathParams = new ArrayList<>(); + if (platformSpec.isMonolithic()) { + // Update fields to refer to outside resources. + ResourcePath filledOutsideResourcePath = null; + try { + Map>> resourcePaths = ch2.fillOutsideResourcePaths(out2, getPullAccessor(platformSpec)); + if (resourcePaths != null && resourcePaths.size() > 0) { + for (ChannelMember outsideMember: resourcePaths.keySet()) { + ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey(); + if (out2.equals(outsideMember)) { + filledOutsideResourcePath = outsidePath; + } + if (!generatesComponent(outsidePath.getResourceHierarchy())) { + outsidePath = outsidePath.getParent(); + } + String outsideResName = langSpec.toVariableName(getComponentName(outsidePath.getResourceHierarchy(), langSpec)); + Expression outsideExp = getPullAccessor(platformSpec).getDirectStateAccessorFor(outsidePath, null); + if (generatesComponent(outsidePath.getResourceHierarchy())) { + outsideExp = ((Term) outsideExp).getChild(0); + } + if (outsideExp instanceof Field) { + outsideExp = new Variable(((Field) outsideExp).getSymbol().getName(), ((Field) outsideExp).getType()); + } else if (outsideExp instanceof Term) { + for (Entry fieldEnt: ((Term) outsideExp).getSubTerms(Field.class).entrySet()) { + Position pos = fieldEnt.getKey(); + Field field = fieldEnt.getValue(); + Variable var = new Variable(field.getSymbol().getName(), field.getType()); + ((Term) outsideExp).replaceSubTerm(pos, var); + } + } + String[] sideEffects = new String[] {""}; + String outsideAccessor = outsideExp.toImplementation(sideEffects); + input.addStatement(langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter()); // change the reference field. + } + } + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork + | InvalidMessage | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + // Values of path parameters to call the update method. + if (filledOutsideResourcePath == null) { + filledOutsideResourcePath = out2.getResource(); + } + for (Expression pathParam: filledOutsideResourcePath.getPathParams()) { + if (pathParam instanceof Variable) { + Variable pathVar = (Variable) pathParam; + pathParams.add(new AbstractMap.SimpleEntry<>(pathVar.getType(), + new AbstractMap.SimpleEntry<>(pathVar.getName(), + pathVar.getName()))); + } else if (pathParam instanceof Constant) { + Constant pathVar = (Constant) pathParam; + pathParams.add(new AbstractMap.SimpleEntry<>(pathVar.getType(), + new AbstractMap.SimpleEntry<>(pathVar.getSymbol().getName(), + pathVar.getSymbol().getName()))); + } + } + } else { + // Values of path parameters to call the update method. + for (Expression pathParam: out2.getResource().getPathParams()) { + if (pathParam instanceof Variable) { + Variable pathVar = (Variable) pathParam; + pathParams.add(new AbstractMap.SimpleEntry<>(pathVar.getType(), + new AbstractMap.SimpleEntry<>(pathVar.getName(), + pathVar.getName()))); + } + } + } + // Values of channel parameters to call the update method. + List>> params = new ArrayList<>(); + for (Selector selector: ch2.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 to call the update method. + ResourceHierarchy srcRes = resourceNode.getResourceHierarchy(); + if (generatesComponent(srcRes)) { + params.add(new AbstractMap.SimpleEntry<>(srcRes.getResourceStateType(), + new AbstractMap.SimpleEntry<>(langSpec.toVariableName(srcRes.getResourceName()), + langSpec.getFieldAccessor(fieldOfResourceState)))); + } else { + params.add(new AbstractMap.SimpleEntry<>(srcRes.getResourceStateType(), + new AbstractMap.SimpleEntry<>(langSpec.toVariableName(srcRes.getResourceName()), + langSpec.getFieldAccessor(langSpec.toVariableName(srcRes.getResourceName()))))); + srcRes = srcRes.getParent(); + } + params.addAll(refParams); + // Call the update method. + 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 + || (!platformSpec.isMonolithic() && in.getResource().getCommonPrefix(out2.getResource()) == null && platformSpec.isDifferentTreesAsDifferentServices())) { + // Inter-servces + if (!platformSpec.isMonolithic()) { + // REST API + RestApiSpecific restApiSpec = (RestApiSpecific) platformSpec; + String httpMethod = null; + if (out2.getStateTransition().isRightUnary()) { + httpMethod = "put"; + } else { + httpMethod = "post"; + } + String[] sideEffects = new String[] {""}; + List pathParamsUrl = new ArrayList<>(); + for (Expression pathExp: out2.getResource().getPathParams()) { + pathParamsUrl.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); + } + String resName2 = langSpec.toVariableName(resComponentName); + if (inDegree <= 1) { + resName2 = null; + } + Map>> filledPaths = null; + try { + filledPaths = ch2.fillOutsideResourcePaths(out2, getPushAccessor(platformSpec)); + } catch (ParameterizedIdentifierIsFutureWork + | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage + | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + String dstPath = null; + if (filledPaths != null && filledPaths.get(out2) != null) { + ResourcePath filledDstPath = filledPaths.get(out2).getKey(); + dstPath = filledDstPath.toResourcePath().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + } else { + dstPath = dstRes.toResourcePath(pathParamsUrl); + } + // Call the update method. + if (!hasUpdateMethodinvoked) { + // The first call to an update method in this method + input.addStatement(restApiSpec.getHttpMethodParamsConstructionStatement(srcRes.getResourceName(), params, true)); + input.addStatement(langSpec.getVariableDeclaration(DataConstraintModel.typeString.getInterfaceTypeName(), "result") + + langSpec.getAssignment() + restApiSpec.getHttpMethodCallStatement(restApiSpec.getBaseURL(), dstPath, resName2, httpMethod)); + hasUpdateMethodinvoked = true; + if (component != null && !((RestApiSpecific) platformSpec).hasHttpClientFieldDeclaration(component)) { + // Declare a client field to connect to the destination resource of push transfer. + ((RestApiSpecific) platformSpec).addHttpClientFieldDeclaration(component); + } + } else { + // After the second time of call to update methods in this method + input.addStatement(restApiSpec.getHttpMethodParamsConstructionStatement(srcRes.getResourceName(), params, false)); + input.addStatement("result" + langSpec.getAssignment() + restApiSpec.getHttpMethodCallStatement(restApiSpec.getBaseURL(), dstPath, resName2, httpMethod)); + } + } else { + // Use the reference field to refer to outside destination resource. + List args = new ArrayList<>(); + for (Map.Entry> paramEnt: pathParams) { + args.add(paramEnt.getValue().getValue()); + } + for (Map.Entry> paramEnt: params) { + args.add(paramEnt.getValue().getValue()); + } + input.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, args) + + langSpec.getStatementDelimiter()); // this.dst.updateDstFromSrc(value, refParams); + } + } else { + // Intra-service + // The destination resource is not outside. + List args = new ArrayList<>(); + for (Map.Entry> paramEnt: pathParams) { + args.add(paramEnt.getValue().getValue()); + } + for (Map.Entry> paramEnt: params) { + args.add(paramEnt.getValue().getValue()); + } + if (srcRes != dstRes) { + input.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, args) + + langSpec.getStatementDelimiter()); // this.dst.updateDstFromSrc(value, refParams); + } else { + input.addStatement(langSpec.getMethodInvocation(updateMethodName, args) + + langSpec.getStatementDelimiter()); // this.updateDstFromSrc(value, refParams); + } + } + if (addForStatement) { + // Close the for loop. + input.addStatement(langSpec.getEndForStatement(forVarName)); + } + } + } + + // Update and initialize a field to refer to an outside input resource for PULL transfer. + if (platformSpec.isMonolithic()) { + // For a monolithic application. + if (outsideInputMembers2.size() > 0) { + if (!generatesComponent(resourceNode.getResourceHierarchy())) { + ResourcePath srcRes2 = resourceNode.getOutSideResource(directDstCh); + for (ChannelMember out2: directDstCh.getOutputChannelMembers()) { + if (!generatesComponent(out2.getResource().getResourceHierarchy())) { + ResourcePath dstRes2 = out2.getResource(); + if (srcRes2.getParent().equals(dstRes2.getParent())) { + Map>> resourcePaths = null; + try { + resourcePaths = directDstCh.fillOutsideResourcePaths(out2, getPullAccessor(platformSpec)); + if (resourcePaths != null && resourcePaths.size() > 0) { + for (ChannelMember outsideMember: outsideInputMembers2) { + for (ChannelMember dependedMember: resourcePaths.get(outsideMember).getValue()) { + if (dependedMember.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(platformSpec).getDirectStateAccessorFor(outsidePath, null); + if (generatesComponent(outsidePath.getResourceHierarchy())) { + outsideExp = ((Term) outsideExp).getChild(0); + } + 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); + } + + protected void declareGetterAccessorInTheRootResource(ResourceNode resourceNode, TypeDeclaration rootComponent, + IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { + if (resourceNode.getResourceHierarchy().getParent() != null) { + // For a non-root resource + MethodDeclaration getterAccessor = null; + List mainGetterParams = new ArrayList<>(); + String resourcePath = getGetterResourcePathAndPathParams(resourceNode.getPrimaryResourcePath(), mainGetterParams, platformSpec, langSpec); + if (resourcePath.indexOf('/') > 0) { + resourcePath = resourcePath.substring(resourcePath.indexOf('/')); + } else { + resourcePath = ""; + } + if (mainGetterParams.size() > 0) { + getterAccessor = langSpec.newMethodDeclaration(getterPrefix + getComponentName(resourceNode.getResourceHierarchy(), langSpec) + methoNameOfResourceState, + false, + getImplStateType(resourceNode.getResourceHierarchy(), langSpec), + mainGetterParams); + } else { + getterAccessor = langSpec.newMethodDeclaration(getterPrefix + getComponentName(resourceNode.getResourceHierarchy(), langSpec) + methoNameOfResourceState, + getImplStateType(resourceNode.getResourceHierarchy(), langSpec)); + } + getterAccessor.setBody(new Block()); + ResourcePath resPath = new ResourcePath(resourceNode.getPrimaryResourcePath()); + for (int i = 0; i < mainGetterParams.size(); i++) { + Parameter pathParam = new Parameter(mainGetterParams.get(i).getName()); + resPath.replacePathParam(i, pathParam, null); + } + Expression getState = getPullAccessor(platformSpec).getDirectStateAccessorFor(resPath, resPath.getRoot()); + getterAccessor.getBody().addStatement(langSpec.getReturnStatement(getState.toImplementation(new String[] {null})) + langSpec.getStatementDelimiter()); + if (!platformSpec.isMonolithic()) { + ((RestApiSpecific) platformSpec).addGetAnnotations(getterAccessor); + if (resourcePath.length() > 0) { + ((RestApiSpecific) platformSpec).addPathAnnotation(getterAccessor, resourcePath); + } + } + rootComponent.addMethod(getterAccessor); + } + } + + protected void declareUpdateAccessorInTheRootResource(ResourceNode resourceNode, String updateMethodName, + DataTransferChannel ch, ChannelMember cm, ResourcePath srcResPath, ResourcePath dstResPath, TypeDeclaration rootComponent, + int inDegree, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { + ArrayList parameters; + VariableDeclaration param; + parameters = new ArrayList<>(); + String resourcePath = getUpdateResourcePathAndPathParams(dstResPath, parameters, true, platformSpec, langSpec); // Path parameters to identify the self resource. + ResourcePath resPath = new ResourcePath(dstResPath); + for (int i = 0; i < parameters.size(); i++) { + Parameter pathParam = new Parameter(parameters.get(i).getName()); + resPath.replacePathParam(i, pathParam, null); + } + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + VariableDeclaration chParam = langSpec.newVariableDeclaration(selVar.getType(), selVar.getName()); + if (!platformSpec.isMonolithic()) { + ((RestApiSpecific) platformSpec).addFormParamAnnotation(chParam, selVar.getName()); + } + parameters.add(chParam); // A channel parameter to specify the context of the collaboration. + } + } + Type srcType = srcResPath.getResourceStateType(); + String srcResName = langSpec.toVariableName(getComponentName(srcResPath.getResourceHierarchy(), langSpec)); + param = langSpec.newVariableDeclaration(srcType, srcResName); + if (!platformSpec.isMonolithic()) ((RestApiSpecific) platformSpec).addFormParamAnnotation(param, srcResName); + parameters.add(param); // The state of the source resource to carry the data-flow. + for (ResourcePath refRes: ch.getReferenceResources()) { + if (!refRes.equals(resourceNode.getInSideResource(ch))) { + String refName = langSpec.toVariableName(getComponentName(refRes.getResourceHierarchy(), langSpec)); + param = langSpec.newVariableDeclaration(refRes.getResourceStateType(), refName); + if (!platformSpec.isMonolithic()) { + ((RestApiSpecific) platformSpec).addFormParamAnnotation(param, refName); + } + parameters.add(param); + } + } + MethodDeclaration updateAccessor = langSpec.newMethodDeclaration(updateMethodName, false, null, parameters); + if (!platformSpec.isMonolithic()) { + if (isPut(cm)) { + ((RestApiSpecific) platformSpec).addPutAnnotations(updateAccessor); + } else { + if (!isDelete(cm)) { + ((RestApiSpecific) platformSpec).addPostAnnotations(updateAccessor); + } else { + ((RestApiSpecific) platformSpec).addDeleteAnnotations(updateAccessor); + } + } + } + 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 += "/" + langSpec.toVariableName(srcResName); + } + if (!platformSpec.isMonolithic()) ((RestApiSpecific) platformSpec).addPathAnnotation(updateAccessor, resourcePath); + + // To make the accessor call the update method. + Expression resExp = getPullAccessor(platformSpec).getDirectStateAccessorFor(resPath, resPath.getRoot()); + List args = new ArrayList<>(); + if (resExp instanceof Term) { + // to access the parent + if (((Term) resExp).getChildren().size() > 1 && ((Term) resExp).getChild(1) instanceof Variable) { + args.add(((Variable)((Term) resExp).getChild(1)).getName()); + } + resExp = ((Term) resExp).getChild(0); + } + String resourceAccess = resExp.toImplementation(new String[] {""}); + for (VariableDeclaration var: updateAccessor.getParameters()) { + args.add(var.getName()); + } + updateAccessor.addStatement(langSpec.getMethodInvocation(resourceAccess, updateMethodName, args) + langSpec.getStatementDelimiter()); + rootComponent.addMethod(updateAccessor); + } + + protected MethodDeclaration declareInputAccessorInTheRootResource(String inputMethodName, + ArrayList rootInputParams, ChannelMember cm, String resourcePath, + TypeDeclaration rootComponent, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { + MethodDeclaration rootInputAccessor; + rootInputAccessor = langSpec.newMethodDeclaration(inputMethodName, false, null, rootInputParams); + if (!platformSpec.isMonolithic()) { + if (isPut(cm)) { + ((RestApiSpecific) platformSpec).addPutAnnotations(rootInputAccessor); + } else { + if (!isDelete(cm)) { + ((RestApiSpecific) platformSpec).addPostAnnotations(rootInputAccessor); + } else { + ((RestApiSpecific) platformSpec).addDeleteAnnotations(rootInputAccessor); + } + } + if (resourcePath.length() > 0) { + ((RestApiSpecific) platformSpec).addPathAnnotation(rootInputAccessor, resourcePath); + } + } + rootComponent.addMethod(rootInputAccessor); + return rootInputAccessor; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/generators/DataTransferMethodAnalyzer.java b/AlgebraicDataflowArchitectureModel/src/generators/DataTransferMethodAnalyzer.java index 8e68103..e2ae5d3 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/DataTransferMethodAnalyzer.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/DataTransferMethodAnalyzer.java @@ -1,45 +1,44 @@ package generators; -import java.util.HashSet; - -import models.*; -import models.algebra.*; -import models.dataConstraintModel.ChannelMember; -import models.dataConstraintModel.DataConstraintModel; +import models.Edge; import models.dataFlowModel.*; +import java.util.HashSet; + /** * Algorithm to analyze data transfer methods selected by a user - * + * * @author Nitta * */ 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. - * + * * @param graph a data flow graph (in/out) */ static public void decideToStoreResourceStates(DataFlowGraph graph) { reachableNodes.clear(); - for (Node n : graph.getNodes()) { + for (ResourceNode n : graph.getResourceNodes()) { ResourceNode resource = (ResourceNode) n; trackNode(resource); } } - + static private void trackNode(ResourceNode resource) { if (reachableNodes.contains(resource)) 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()).getSelectedOption() != 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..660f67a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/generators/ILanguageSpecific.java @@ -0,0 +1,96 @@ +package generators; + +import code.ast.*; +import models.algebra.Expression; +import models.algebra.Term; +import models.algebra.Type; + +import java.util.List; + +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 getSelfExp(); + + 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 getForStatementForList(String varName, String list); + + String getForStatementForCollection(String varName, String varType, String collection); + + String getForStatementForMap(String varName, String varType, String map); + + String getEndForStatement(); + + String getEndForStatement(String varName); + + String toComponentName(String name); + + String toVariableName(String name); + + String getMainComponentName(); + + String getAssignment(); + + String getStatementDelimiter(); + + String getStringDelimiter(); + + String getOpeningScoreDelimiter(); + + String getClosingScoreDelimiter(); + + String getValueToStringExp(String typeName, String valueExp); + + String getStringToValueExp(String typeName, String strExp); + + String getPairExp(String first, String second); + + String getFirstEntryFromMapExp(String map); + + String getTupleGet(String tupleExp, int idx, int length); + + String getDecomposedTuple(String tupleExp, VariableDeclaration tupleVar, List vars); + + boolean isValueType(Type type); + + boolean isVoidType(Type type); +} diff --git a/AlgebraicDataflowArchitectureModel/src/generators/IPlatformSpecific.java b/AlgebraicDataflowArchitectureModel/src/generators/IPlatformSpecific.java new file mode 100644 index 0000000..3c833c0 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/generators/IPlatformSpecific.java @@ -0,0 +1,9 @@ +package generators; + +public interface IPlatformSpecific { + boolean hasMain(); + + boolean isMonolithic(); + + boolean isDifferentTreesAsDifferentServices(); +} diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JavaCodeGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/JavaCodeGenerator.java index 314e81a..fe84596 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/JavaCodeGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/JavaCodeGenerator.java @@ -1,45 +1,19 @@ package generators; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - - -import code.ast.Block; -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 code.ast.*; import models.Edge; import models.Node; -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.ResourcePath; -import models.dataFlowModel.DataTransferModel; -import models.dataFlowModel.DataTransferChannel; +import models.algebra.*; +import models.dataConstraintModel.*; +import models.dataFlowModel.*; import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor; -import models.dataFlowModel.PushPullAttribute; -import models.dataFlowModel.PushPullValue; -import models.dataFlowModel.DataFlowEdge; -import models.dataFlowModel.DataFlowGraph; -import models.dataFlowModel.ResourceNode; -import models.dataFlowModel.StoreAttribute; + +import java.util.*; +import java.util.Map.Entry; /** * Generator for plain Java prototypes - * + * * @author Nitta * */ @@ -47,178 +21,763 @@ public static final Type typeVoid = new Type("Void", "void"); private static String defaultMainTypeName = "Main"; static String mainTypeName = defaultMainTypeName; - + public static String getMainTypeName() { return mainTypeName; } - + public static void setMainTypeName(String mainTypeName) { JavaCodeGenerator.mainTypeName = mainTypeName; } - + public static void resetMainTypeName() { 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).getInterfaceTypeName() + ">", 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) { + if (res.getParent() == null) return true; + if (res.getChildren() == null || res.getChildren().size() == 0) return false; + if (res.getNumParameters() > 0 && res.getChildren().size() == 1 && res.getChildren().iterator().next().getNumParameters() > 0) + return false; + if (res.getChildren().size() == 1 && res.getChildren().iterator().next().getNumParameters() > 0 + && (res.getChildren().iterator().next().getChildren() == null || res.getChildren().iterator().next().getChildren().size() == 0)) + return false; + return true; +// return res.getParent() == null || !(res.getChildren() == null || res.getChildren().size() == 0); + } + static public ArrayList doGenerate(DataFlowGraph graph, DataTransferModel model) { ArrayList codes = new ArrayList<>(); - ArrayList resources = determineResourceOrder(graph); - - TypeDeclaration mainType = new TypeDeclaration(mainTypeName); - CompilationUnit mainCU = new CompilationUnit(mainType); + 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> descendantGetters = new HashMap<>(); + List> constructorParams = new ArrayList<>(); + + 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()).getSelectedOption() == 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()).getSelectedOption() != 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, "new " + childTypeName + "()")); + } + } + } + } + } + + // 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); + } + if (component != null) { + // (#1) Declare the getter methods in this resource to obtain descendant resources. (complementary to #2) + Set descendants = descendantGetters.get(resourceNode.getResourceHierarchy()); + if (descendants == null) { + descendants = new HashSet<>(); + descendantGetters.put(resourceNode.getResourceHierarchy(), descendants); + } + for (ResourceNode child : resourceNode.getChildren()) { + // A descendant of the child may generate a component. + List params = new ArrayList<>(); + int v = 1; + ResourceNode descendant = child; + Set childNodes; + do { + Expression param = descendant.getPrimaryResourcePath().getLastParam(); + if (param != null) { + if (param instanceof Variable) { + Variable var = (Variable) param; + params.add(new VariableDeclaration(var.getType(), var.getName())); + } else if (param instanceof Term) { + Term var = (Term) param; + params.add(new VariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + if (generatesComponent(descendant.getResourceHierarchy())) { + // If the descendant generates a component. + if (!descendants.contains(descendant.getResourceHierarchy())) { + descendants.add(descendant.getResourceHierarchy()); + String descendantCompName = getComponentName(descendant.getResourceHierarchy()); + Type descendantType = new Type(descendantCompName, descendantCompName); + MethodDeclaration descendantGetter = null; + if (params.size() == 0) { + descendantGetter = new MethodDeclaration("get" + descendantCompName, descendantType); + } else { + descendantGetter = new MethodDeclaration("get" + descendantCompName, false, descendantType, params); + } + component.addMethod(descendantGetter); + } + break; + } + childNodes = descendant.getChildren(); + } while (childNodes != null && childNodes.size() == 1 && (descendant = childNodes.iterator().next()) != null); } } } - 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 and reference fields 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()).getSelectedOption() != 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)))); + } + } + + // (#2) Declare the getter method to obtain the resource state in an ancestor component. (complementary to #1) + if (component == null) { + // No component is created for this resource. + ResourceNode ancestorNode = resourceNode; + Stack ancestors = new Stack<>(); + do { + ancestors.push(ancestorNode); + ancestorNode = ancestorNode.getParent(); + } while (!generatesComponent(ancestorNode.getResourceHierarchy())); + List getterParams = new ArrayList<>(); + int v = 1; + while (ancestors.size() > 0) { + ResourceNode curAncestor = ancestors.pop(); + Expression param = curAncestor.getPrimaryResourcePath().getLastParam(); + if (param instanceof Variable) { + Variable var = (Variable) param; + getterParams.add(new VariableDeclaration(var.getType(), var.getName())); + } else if (param instanceof Term) { + Term var = (Term) param; + getterParams.add(new VariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + String getterName = "get" + getComponentName(resourceNode.getResourceHierarchy()); + boolean bExists = false; + for (Map.Entry entry : getters) { + ResourceHierarchy r = entry.getKey(); + MethodDeclaration m = entry.getValue(); + if (r == ancestorNode.getResourceHierarchy() && m.getName().equals(getterName) + && (m.getParameters() == null ? 0 : m.getParameters().size()) == getterParams.size()) { + bExists = true; + break; + } + } + if (!bExists) { + 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<>(ancestorNode.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; + ChannelNode directDstChNode = (ChannelNode) re.getDestination(); + DataTransferChannel directDstCh = directDstChNode.getChannel(); + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm : directDstCh.getInputChannelMembers()) { + if (cm.getResource().equals(resourceNode.getOutSideResource(directDstCh)) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + // Should take into account the channel hierarchy. + Set ancestorDstChannels = directDstChNode.getAncestors(); + Set descendantDstChannels = directDstChNode.getDescendants(); + Set outEdges = new HashSet<>(); + outEdges.addAll(directDstChNode.getOutEdges()); + for (ChannelNode ancestorDst : ancestorDstChannels) { + outEdges.addAll(ancestorDst.getOutEdges()); + } + for (ChannelNode descendantDst : descendantDstChannels) { + outEdges.addAll(descendantDst.getOutEdges()); + } + for (Edge chToRes : outEdges) { + ResourceHierarchy dstRes = ((ResourceNode) chToRes.getDestination()).getResourceHierarchy(); + ChannelNode chNode = (ChannelNode) chToRes.getSource(); + DataTransferChannel ch = chNode.getChannel(); + // 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; + } + } + // Also take into account the channel hierarchy to determine push/pull transfer. + if (descendantDstChannels.contains(chNode)) { + outsideOutputResource = true; // Regarded as (broadcasting) push transfer. + } + if (ancestorDstChannels.contains(chNode)) { + outsideInputResource = true; // Regarded as (collecting) pull transfer. + } + if ((((PushPullAttribute) re.getAttribute()).getSelectedOption() == 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 called by other resources for push data transfer + // and reference fields for pull data transfer. + for (Edge chToRes : resourceNode.getInEdges()) { + ChannelNode directSrcChannel = (ChannelNode) chToRes.getSource(); + DataTransferChannel ch = directSrcChannel.getChannel(); + // Should take into account the channel hierarchy. + Set ancestorSrcChannels = directSrcChannel.getAncestors(); + Set descendantSrcChannels = directSrcChannel.getDescendants(); + Set inEdges = new HashSet<>(); + inEdges.addAll(directSrcChannel.getInEdges()); + for (ChannelNode ancestorSrc : ancestorSrcChannels) { + inEdges.addAll(ancestorSrc.getInEdges()); + } + for (ChannelNode descendantSrc : descendantSrcChannels) { + inEdges.addAll(descendantSrc.getInEdges()); + } + for (Edge resToCh : inEdges) { + // For each data transfer from srcResPath:ResourcePath to resourceNode:ResourceNode. + DataFlowEdge re = (DataFlowEdge) resToCh; + ChannelNode indirectSrcChNode = (ChannelNode) re.getDestination(); + DataTransferChannel indirectSrcCh = indirectSrcChNode.getChannel(); + ResourcePath srcResPath = ((ResourceNode) re.getSource()).getOutSideResource(indirectSrcCh); + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm : indirectSrcCh.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; + 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; + } + } + } + // Also take into account the channel hierarchy to determine push/pull transfer. + if (ancestorSrcChannels.contains(indirectSrcChNode)) { + outsideOutputResource = true; // Regarded as (broadcasting) push transfer. + } + if (descendantSrcChannels.contains(indirectSrcChNode)) { + outsideInputResource = true; // Regarded as (collecting) pull transfer. + } + String srcResName = getComponentName(srcResPath.getResourceHierarchy()); + ResourcePath srcRes2 = srcResPath; + String srcResName2 = srcResName; + if (!generatesComponent(srcResPath.getResourceHierarchy())) { + srcRes2 = srcResPath.getParent(); + srcResName2 = getComponentName(srcRes2.getResourceHierarchy()); + } + if ((((PushPullAttribute) re.getAttribute()).getSelectedOption() != 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 : ""); + VariableDeclaration pathParam = new VariableDeclaration(pathVar.getType(), varName); + params.add(pathParam); // A path parameter to identify the self resource. + } else if (exp instanceof Term) { + Term pathVar = (Term) exp; + String varName = "self" + (v > 1 ? v : ""); + 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(srcResPath.getResourceStateType(), toVariableName(getComponentName(srcResPath.getResourceHierarchy())))); // 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(), toVariableName(getComponentName(ref.getResourceHierarchy())))); + } + } + 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. + boolean existsField = false; + for (FieldDeclaration field : component.getFields()) { + if (field.getName().equals(refVar.getName())) { + existsField = true; + break; + } + } + if (!existsField) { + 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()) { + 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 +785,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))); - } - - // 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 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) { + 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")); @@ -275,70 +912,230 @@ block.addStatement("this.right = right;"); constructor.setBody(block); type.addMethod(constructor); - - for(FieldDeclaration field : type.getFields()) { + + for (FieldDeclaration field : type.getFields()) { MethodDeclaration getter = new MethodDeclaration( - "get" + field.getName().substring(0,1).toUpperCase() + field.getName().substring(1), - new Type("Double","T")); + "get" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1), + new Type("Double", "T")); getter.setBody(new Block()); getter.getBody().addStatement("return " + field.getName() + ";"); type.addMethod(getter); } - - CompilationUnit cu = new CompilationUnit(type); + + CompilationUnit cu = new CompilationUnit(type); cu.addImport(new ImportDeclaration("java.util.*")); codes.add(cu); isCreatedPair = true; } } - - // 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[] {""}); - } else { - if (DataConstraintModel.typeList.isAncestorOf(stateType)) { - initializer = "new " + resId.getResourceStateType().getImplementationTypeName() + "()"; - } else if (DataConstraintModel.typeMap.isAncestorOf(stateType)) { - initializer = "new " + resId.getResourceStateType().getImplementationTypeName() + "()"; + + private static Map> getDependedRootComponentGraph(DataTransferModel model) { + Map> dependedComponentGraph = new HashMap<>(); + for (Channel ch : model.getChannels()) { + Set inRes = new HashSet<>(); + Set outRes = new HashSet<>(); + getDependedRootComponentGraphSub(ch, inRes, outRes, true); + 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); + } + if (!out.getRoot().equals(in.getRoot())) { + dependings.add(in.getRoot()); + } + } + } } } + return dependedComponentGraph; + } + + private static void getDependedRootComponentGraphSub(Channel ch, Set inRes, Set outRes, boolean isRoot) { + DataTransferChannel dtCh = (DataTransferChannel) ch; + for (ChannelMember cm : dtCh.getChannelMembers()) { + if (!isRoot && !cm.isOutside()) { + outRes.add(cm.getResource().getResourceHierarchy()); // dependency to a descendant channel resource. + } + if (cm.isOutside()) { + outRes.add(cm.getResource().getResourceHierarchy()); // dependency to an outside resource. + } else { + inRes.add(cm.getResource().getResourceHierarchy()); // dependency from an inside resource. + } + } + for (Channel childCh : ch.getChildren()) { + getDependedRootComponentGraphSub(childCh, inRes, outRes, false); + } + } + + private static 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()).getSelectedOption() == 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()).getSelectedOption() != 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++; + } + ResourcePath resPath = new ResourcePath(resourceNode.getPrimaryResourcePath()); + for (int i = 0; i < mainGetterParams.size(); i++) { + Parameter pathParam = new Parameter(mainGetterParams.get(i).getName()); + resPath.replacePathParam(i, pathParam, null); + } + 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(resPath, 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) { + initializer = DataConstraintModel.getDefaultValue(stateType); + } return initializer; } - + static public ArrayList getCodes(ArrayList codeTree) { ArrayList codes = new ArrayList<>(); for (TypeDeclaration type : codeTree) { @@ -383,93 +1180,258 @@ } 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); + } + // use the cached value as the current state + return new Field(toVariableName(getComponentName(targetRes.getResourceHierarchy())), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + return new Parameter(toVariableName(getComponentName(targetRes.getResourceHierarchy())), + 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(target.getResourceName(), - target.getResourceStateType() != null ? target.getResourceStateType() - : DataConstraintModel.typeInt); - } - - @Override - public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { - return new Parameter(target.getResourceName(), - target.getResourceStateType() != null ? target.getResourceStateType() + return new Parameter(toVariableName(getComponentName(targetRes.getResourceHierarchy())), + 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)) { + 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); + } + } + + @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); + } + // Get the next state by invoking a getter method. + 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 + if (fromRes.isAncestorOf(targetRes)) { + Stack pathStack = new Stack<>(); + ResourcePath curPath = targetRes; + do { + pathStack.push(curPath); + curPath = curPath.getParent(); + } while (!curPath.equals(fromRes)); + // iterate from the fromRes resource + return getRelativePath(targetRes, pathStack); + } + if (generatesComponent(targetRes.getResourceHierarchy())) { + Term getter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); + getter.addChild(new Field(toVariableName(getComponentName(targetRes.getResourceHierarchy())), targetRes.getResourceStateType())); + return getter; + } else { + return new Field(toVariableName(getComponentName(targetRes.getResourceHierarchy())), targetRes.getResourceStateType()); + } + } else { + // (#3) access from the outside of the hierarchy (must be kept consistent with #4) + Stack pathStack = new Stack<>(); + ResourcePath curPath = targetRes; + do { + pathStack.push(curPath); + curPath = curPath.getParent(); + } while (curPath != null); + // iterate from the root resource + return getRelativePath(targetRes, pathStack); + } + } + + private Expression getRelativePath(ResourcePath targetRes, Stack pathStack) { + ResourcePath curPath; + Term getter = null; + int arity = 2; + boolean doesChainInvocations = true; + while (!pathStack.empty()) { + curPath = pathStack.pop(); + String typeName = getComponentName(curPath.getResourceHierarchy()); + if (getter == null && generatesComponent(curPath.getResourceHierarchy())) { + // root resource + String fieldName = toVariableName(typeName); + getter = new Field(fieldName, new Type(typeName, typeName)); + } else { + if (generatesComponent(curPath.getResourceHierarchy())) { + if (arity == 2) { + Term newGetter = new Term(new Symbol("get" + typeName, -1, Symbol.Type.METHOD)); + newGetter.addChild(getter); + if (curPath.getResourceHierarchy().getNumParameters() > 0) { + Expression param = curPath.getLastParam(); + if (param != null) { + newGetter.addChild(param); + newGetter.getSymbol().setArity(2); + } + } + getter = newGetter; + } else { + // add the last path parameter. + if (curPath.getResourceHierarchy().getNumParameters() > 0) { + Expression param = curPath.getLastParam(); + if (param != null) { + getter.getSymbol().setArity(arity); + getter.addChild(param); + } + } + } + arity = 2; + doesChainInvocations = true; + } else { + // to get a descendant resource directly. (e.g, .todos.{year}.{month}.{day}.{id} ==> .getTodos().getTodo(year, month, day, id)) + if (doesChainInvocations) { + Term newGetter = new Term(new Symbol("get" + typeName, -1, Symbol.Type.METHOD)); + newGetter.addChild(getter); + getter = newGetter; + doesChainInvocations = false; + } + if (curPath.getResourceHierarchy().getNumParameters() > 0) { + // may change the symbol name + getter.getSymbol().changeName("get" + typeName); + // add a path parameter. + Expression param = curPath.getLastParam(); + if (param != null) { + getter.getSymbol().setArity(arity); + getter.addChild(param); + arity++; + } + } + } + } + } + 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", - target.getResourceStateType() != null ? target.getResourceStateType() + 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(target.getResourceName(), target.getResourceStateType())); - return getter; + return new Parameter(toVariableName(getComponentName(targetRes.getResourceHierarchy())), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); } - + @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(); + return new Parameter(toVariableName(getComponentName(targetRes.getResourceHierarchy())), + 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(toVariableName(getComponentName(targetRes.getResourceHierarchy())), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); } }; } diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java index 86cd28a..eac7652 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java @@ -1,261 +1,1238 @@ package generators; -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 code.ast.CompilationUnit; import code.ast.MethodDeclaration; import code.ast.TypeDeclaration; +import code.ast.VariableDeclaration; import models.Edge; -import models.Node; -import models.algebra.Expression; -import models.algebra.InvalidMessage; -import models.algebra.ParameterizedIdentifierIsFutureWork; -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.ResourcePath; -import models.dataFlowModel.DataTransferModel; -import models.dataFlowModel.DataTransferChannel; +import models.algebra.*; +import models.dataConstraintModel.*; +import models.dataFlowModel.*; import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor; -import models.dataFlowModel.PushPullAttribute; -import models.dataFlowModel.PushPullValue; -import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork; -import models.dataFlowModel.DataFlowEdge; -import models.dataFlowModel.DataFlowGraph; -import models.dataFlowModel.ResourceNode; -import models.dataFlowModel.StoreAttribute; + +import java.util.*; +import java.util.Map.Entry; 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<>(); - for (CompilationUnit code: codes) { - for (TypeDeclaration type: code.types()) { - typeMap.put(type.getTypeName().substring(0,1).toLowerCase() + type.getTypeName().substring(1), type); + Map componentMap = new HashMap<>(); + for (CompilationUnit code : codes) { + for (TypeDeclaration component : code.types()) { + componentMap.put(component.getTypeName(), component); } } // Generate the body of each update or getter method. 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 + ";"; + Map> referredResources = new HashMap<>(); + for (Edge e : graph.getEdges()) { + DataFlowEdge resToCh = (DataFlowEdge) e; + if (!resToCh.isChannelToResource()) { + PushPullAttribute pushPull = (PushPullAttribute) resToCh.getAttribute(); + ResourceNode src = (ResourceNode) resToCh.getSource(); + ChannelNode directDstChNode = (ChannelNode) resToCh.getDestination(); + DataTransferChannel directDstCh = directDstChNode.getChannel(); + // Should take into account the channel hierarchy. + Set ancestorDstChannels = directDstChNode.getAncestors(); + Set descendantDstChannels = directDstChNode.getDescendants(); + Set outEdges = new HashSet<>(); + outEdges.addAll(directDstChNode.getOutEdges()); + for (ChannelNode ancestorDst : ancestorDstChannels) { + outEdges.addAll(ancestorDst.getOutEdges()); + } + for (ChannelNode descendantDst : descendantDstChannels) { + outEdges.addAll(descendantDst.getOutEdges()); + } + for (Edge chToRes : outEdges) { + 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); + ChannelNode chNode = (ChannelNode) chToRes.getSource(); + DataTransferChannel ch = chNode.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); + // Check if the output resource is outside of the channel scope. + boolean outsideOutputResource = out.isOutside(); + // Take into account the channel hierarchy. + if (descendantDstChannels.contains(chNode)) { + outsideOutputResource = true; // Regarded as (broadcasting) push transfer. } - } - if (dst.getIndegree() > 1) { - // update a cash of src side resource (when incoming edges are multiple) - String cashStatement = "this." + srcResourceName + " = " + srcResourceName + ";"; - if (update.getBody() == null || !update.getBody().getStatements().contains(cashStatement)) { - update.addFirstStatement(cashStatement); - } - } - MethodDeclaration getter = getGetterMethod(dstType); - if (((StoreAttribute) dst.getAttribute()).isStored()) { - // returns the current state stored in a field. - if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { - Type resourceType = dst.getResource().getResourceStateType(); - if (model.isPrimitiveType(resourceType)) { - getter.addStatement("return value;"); + if (ancestorDstChannels.contains(chNode)) { + outsideInputResource = true; // Regarded as (collecting) pull transfer. + } + if ((pushPull.getSelectedOption() == 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) + Term unifiedMassage = null; + if (directDstCh != ch) { + unifiedMassage = directDstCh.fillOutsideResourcePaths(out, JavaCodeGenerator.pushAccessor, null).getValue(); } - 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 + ";"); + Expression updateExp = null; + if (ch.getReferenceChannelMembers().size() == 0) { + Term message = ch.fillOutsideResourcePaths(out, JavaCodeGenerator.pushAccessor, null).getValue(); + if (unifiedMassage == null) { + unifiedMassage = message; + } else { + unifiedMassage = (Term) unifiedMassage.unify(message); + } + updateExp = ch.deriveUpdateExpressionOf(out, unifiedMassage, JavaCodeGenerator.pushAccessor); + } 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); + } + Term message = ch.fillOutsideResourcePaths(out, JavaCodeGenerator.pushAccessor, inputResourceToStateAccessor).getValue(); + if (unifiedMassage == null) { + unifiedMassage = message; + } else { + unifiedMassage = (Term) unifiedMassage.unify(message); + } + updateExp = ch.deriveUpdateExpressionOf(out, unifiedMassage, JavaCodeGenerator.pushAccessor); } - refParams += ", " + refVarName; + // Replace Json constructor with a constructor of a descendant resource. + ResourceHierarchy outRes = out.getResource().getResourceHierarchy(); + if (outRes.getChildren().size() == 1 && outRes.getChildren().iterator().next().getNumParameters() > 0) { + ResourceHierarchy descendantRes = outRes.getChildren().iterator().next(); + Set children; + do { + if (JavaCodeGenerator.generatesComponent(descendantRes)) break; + children = descendantRes.getChildren(); + } while (children != null && children.size() == 1 && (descendantRes = children.iterator().next()) != null); + Type descendantStateType = descendantRes.getResourceStateType(); + String descendantComponentName = JavaCodeGenerator.getComponentName(descendantRes); + TypeDeclaration descendantComponent = componentMap.get(descendantComponentName); + if (DataConstraintModel.typeJson.isAncestorOf(descendantStateType)) { + replaceJsonTermWithConstructorInvocation(updateExp, descendantStateType, descendantComponentName, descendantComponent); + } + } + // Replace the type of the state field. + Type fieldType = JavaCodeGenerator.getImplStateType(outRes); + if (updateExp instanceof Term) { + ((Term) updateExp).setType(fieldType); + for (Map.Entry varEnt : ((Term) updateExp).getVariables().entrySet()) { + if (varEnt.getValue().getName().equals("value")) { + varEnt.getValue().setType(fieldType); + } + } + } else if (updateExp instanceof Variable) { + ((Variable) updateExp).setType(fieldType); + } + // 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]; + if (updateStatement.endsWith("\n")) { + updateStatement = updateStatement.substring(0, updateStatement.length() - 1); + } + } 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 (updateStatement.endsWith("\n")) { + updateStatement = updateStatement.substring(0, updateStatement.length() - 1); + } + } + 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 + ";"; + } + } + // 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 : ""), DataConstraintModel.typeString), + 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); + // Calculate in-degree (PUSH transfer) of the destination resource. + int inDegree = 0; + Set inEdges = new HashSet<>(); + inEdges.addAll(chNode.getInEdges()); + for (ChannelNode ancestor : chNode.getAncestors()) { + inEdges.addAll(ancestor.getInEdges()); + } + for (ChannelNode descendant : chNode.getDescendants()) { + inEdges.addAll(descendant.getInEdges()); + } + for (Edge resToCh2 : inEdges) { + DataFlowEdge df = (DataFlowEdge) resToCh2; + if (((PushPullAttribute) df.getAttribute()).getSelectedOption() == PushPullValue.PUSH) { + inDegree++; + } + } + if (inDegree > 1 + || (inDegree == 1 && directDstCh.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 : directDstCh.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 caller update methods + for (MethodDeclaration srcUpdate : getUpdateMethods(srcComponent, srcResName)) { + ResourcePath dstRes = out.getResource(); + // Get the value of reference member to call the update method. + String refParams = ""; + Set referredSet = referredResources.get(srcUpdate); + for (ChannelMember rc : ch.getReferenceChannelMembers()) { + ResourcePath ref = rc.getResource(); + if (referredSet == null) { + referredSet = new HashSet<>(); + referredResources.put(srcUpdate, referredSet); + } + if (!dst.getInSideResources().contains(ref)) { + String refVarName = JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(ref.getResourceHierarchy())); + if (!referredSet.contains(ref)) { + referredSet.add(ref); + ResourcePath srcRes = in.getResource(); + if (!JavaCodeGenerator.generatesComponent(srcRes.getResourceHierarchy())) { + srcRes = srcRes.getParent(); + } + Expression refGetter = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(ref, srcRes); + String[] sideEffects = new String[]{""}; + String refExp = refGetter.toImplementation(sideEffects); + String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); + srcUpdate.addStatement(sideEffects[0] + refTypeName + " " + refVarName + " = " + refExp + ";"); + } + refParams += ", " + refVarName; + } + } + // Update fields to refer to outside resources. + ResourcePath filledOutsideResourcePath = null; + 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 (out.equals(outsideMember)) { + filledOutsideResourcePath = outsidePath; + } + if (!JavaCodeGenerator.generatesComponent(outsidePath.getResourceHierarchy())) { + outsidePath = outsidePath.getParent(); + } + String outsideResName = JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(outsidePath.getResourceHierarchy())); + Expression outsideExp = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(outsidePath, null); + if (JavaCodeGenerator.generatesComponent(outsidePath.getResourceHierarchy())) { + outsideExp = ((Term) outsideExp).getChild(0); + } + if (outsideExp instanceof Field) { + outsideExp = new Variable(((Field) outsideExp).getSymbol().getName(), ((Field) outsideExp).getType()); + } else if (outsideExp instanceof Term) { + for (Entry fieldEnt : ((Term) outsideExp).getSubTerms(Field.class).entrySet()) { + Position pos = fieldEnt.getKey(); + Field field = fieldEnt.getValue(); + Variable var = new Variable(field.getSymbol().getName(), field.getType()); + ((Term) outsideExp).replaceSubTerm(pos, var); + } + } + String[] sideEffects = new String[]{""}; + String outsideAccessor = outsideExp.toImplementation(sideEffects); + srcUpdate.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // change the reference field. + } + } + // Values of path parameters to call the update method. + if (filledOutsideResourcePath == null) { + filledOutsideResourcePath = dstRes; + } + String pathParams = ""; + for (Expression pathParam : filledOutsideResourcePath.getPathParams()) { + if (pathParam instanceof Variable) { + Variable pathVar = (Variable) pathParam; + pathParams += pathVar.getName() + ", "; + } else if (pathParam instanceof Constant) { + Constant pathVar = (Constant) pathParam; + pathParams += pathVar.getSymbol().getName() + ", "; + } + } + // Values of channel parameters to call the update method. + String chParams = ""; + for (Selector selector : ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + chParams += selVar.getName() + ", "; + } + } + // Value of the source side (input side) resource. + String srcFieldName = "value"; + if (!JavaCodeGenerator.generatesComponent(src.getResourceHierarchy())) { + srcFieldName = JavaCodeGenerator.toVariableName(srcResourceName); + } + // Call the update method. + String updateMethodName = null; + if (JavaCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { + updateMethodName = "updateFrom" + srcResourceName; + } else { + updateMethodName = "update" + dstResourceName + "From" + 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 + ");"); + } + if (descendantDstChannels.contains(chNode)) { + // For hierarchical channels (broadcasting push transfer). + if (ch.getSelectors() != null && ch.getSelectors().size() > 0) { + Expression selExp = ch.getSelectors().get(0).getExpression(); + Type selType = null; + String forVarName = null; + if (selExp instanceof Variable) { + selType = ((Variable) selExp).getType(); + forVarName = ((Variable) selExp).getName(); + ChannelMember insideChMem = null; + for (ChannelMember cm : ch.getInputChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + if (insideChMem == null) { + for (ChannelMember cm : ch.getReferenceChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + } + if (insideChMem == null) { + for (ChannelMember cm : ch.getOutputChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + } + ResourcePath insideResPath = insideChMem.getResource(); + while (insideResPath.getParent() != null && (insideResPath.getLastParam() == null || !insideResPath.getLastParam().equals(selExp))) { + insideResPath = insideResPath.getParent(); + } + insideResPath = insideResPath.getParent(); + if (insideResPath != null) { + String parent = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(insideResPath, src.getOutSideResource(directDstCh)).toImplementation(new String[]{}); + if (selType.equals(DataConstraintModel.typeInt)) { + // make a for loop (for a list) for broadcasting. + srcUpdate.addFirstStatement("for (int " + forVarName + " = 0; " + forVarName + " < " + parent + ".size(); " + forVarName + "++) {"); + srcUpdate.addStatement("}"); + } else if (selType.equals(DataConstraintModel.typeString)) { + // make a for loop (for a map) for broadcasting. + srcUpdate.addFirstStatement("for (String " + forVarName + ": " + parent + ".keySet()) {"); + srcUpdate.addStatement("}"); + } + } + } else if (selExp instanceof Term) { + // not supported. + } + } + } + } + // For caller input methods + for (MethodDeclaration srcInput : getInputMethods(srcComponent, src, model)) { + ResourcePath dstRes = out.getResource(); + // Get the value of reference member to call the update method. + String refParams = ""; + Set referredSet = referredResources.get(srcInput); + for (ChannelMember rc : ch.getReferenceChannelMembers()) { + ResourcePath ref = rc.getResource(); + if (referredSet == null) { + referredSet = new HashSet<>(); + referredResources.put(srcInput, referredSet); + } + if (!dst.getInSideResources().contains(ref)) { + String refVarName = JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(ref.getResourceHierarchy())); + if (!referredSet.contains(ref)) { + referredSet.add(ref); + ResourcePath srcRes = in.getResource(); + if (!JavaCodeGenerator.generatesComponent(srcRes.getResourceHierarchy())) { + srcRes = srcRes.getParent(); + } + Expression refGetter = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(ref, srcRes); + String[] sideEffects = new String[]{""}; + String refExp = refGetter.toImplementation(sideEffects); + String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); + srcInput.addStatement(sideEffects[0] + refTypeName + " " + refVarName + " = " + refExp + ";"); + } + refParams += ", " + refVarName; + } + } + // Update fields to refer to outside resources. + ResourcePath filledOutsideResourcePath = null; + 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 (out.equals(outsideMember)) { + filledOutsideResourcePath = outsidePath; + } + if (!JavaCodeGenerator.generatesComponent(outsidePath.getResourceHierarchy())) { + outsidePath = outsidePath.getParent(); + } + String outsideResName = JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(outsidePath.getResourceHierarchy())); + Expression outsideExp = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(outsidePath, null); + if (JavaCodeGenerator.generatesComponent(outsidePath.getResourceHierarchy())) { + outsideExp = ((Term) outsideExp).getChild(0); + } + if (outsideExp instanceof Field) { + outsideExp = new Variable(((Field) outsideExp).getSymbol().getName(), ((Field) outsideExp).getType()); + } else if (outsideExp instanceof Term) { + for (Entry fieldEnt : ((Term) outsideExp).getSubTerms(Field.class).entrySet()) { + Position pos = fieldEnt.getKey(); + Field field = fieldEnt.getValue(); + Variable var = new Variable(field.getSymbol().getName(), field.getType()); + ((Term) outsideExp).replaceSubTerm(pos, var); + } + } + String[] sideEffects = new String[]{""}; + String outsideAccessor = outsideExp.toImplementation(sideEffects); + srcInput.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // change the reference field. + } + } + // Values of path parameters to call the update method. + if (filledOutsideResourcePath == null) { + filledOutsideResourcePath = dstRes; + } + String pathParams = ""; + for (Expression pathParam : filledOutsideResourcePath.getPathParams()) { + if (pathParam instanceof Variable) { + Variable pathVar = (Variable) pathParam; + pathParams += pathVar.getName() + ", "; + } else if (pathParam instanceof Constant) { + Constant pathVar = (Constant) pathParam; + pathParams += pathVar.getSymbol().getName() + ", "; + } + } + // Values of channel parameters to call the update method. + String chParams = ""; + for (Selector selector : ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + chParams += selVar.getName() + ", "; + } + } + // Call the update method. + 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) { + 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 + ");"); + } + if (descendantDstChannels.contains(chNode)) { + // For hierarchical channels (broadcasting push transfer). + if (ch.getSelectors() != null && ch.getSelectors().size() > 0) { + Expression selExp = ch.getSelectors().get(0).getExpression(); + Type selType = null; + String varName = null; + if (selExp instanceof Variable) { + selType = ((Variable) selExp).getType(); + varName = ((Variable) selExp).getName(); + ChannelMember insideChMem = null; + for (ChannelMember cm : ch.getInputChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + if (insideChMem == null) { + for (ChannelMember cm : ch.getReferenceChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + } + if (insideChMem == null) { + for (ChannelMember cm : ch.getOutputChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + } + ResourcePath insideResPath = insideChMem.getResource(); + while (insideResPath.getParent() != null && (insideResPath.getLastParam() == null || !insideResPath.getLastParam().equals(selExp))) { + insideResPath = insideResPath.getParent(); + } + insideResPath = insideResPath.getParent(); + if (insideResPath != null) { + String parent = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(insideResPath, src.getOutSideResource(directDstCh)).toImplementation(new String[]{}); + if (selType.equals(DataConstraintModel.typeInt)) { + // make a for loop (for a list) for broadcasting. + srcInput.addFirstStatement("for (int " + varName + " = 0; " + varName + " < " + parent + ".size(); " + varName + "++) {"); + srcInput.addStatement("}"); + } else if (selType.equals(DataConstraintModel.typeString)) { + // make a for loop (for a map) for broadcasting. + srcInput.addFirstStatement("for (String " + varName + ": " + parent + ".keySet()) {"); + srcInput.addStatement("}"); + } + } + } else if (selExp instanceof Term) { + // not supported. + } + } + } + } + } else if ((pushPull.getSelectedOption() != 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) { + // The first time to fill the getter method's body. + // Data transfer on the same channel hierarchy. + String[] sideEffects = new String[]{""}; + boolean isContainedPush = false; + Map inputResourceToStateAccessor = new HashMap<>(); + for (Edge chToRes2 : dst.getInEdges()) { + 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()).getSelectedOption() == 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 + } + Map.Entry>>, Term> resourcePathsAndMessage; + + // Construct the base message. + if (!isContainedPush) { + // All incoming edges are in PULL-style. + resourcePathsAndMessage = ch.fillOutsideResourcePaths(out, JavaCodeGenerator.pullAccessor, null); + } else { + // At least one incoming edge is in PUSH-style. + resourcePathsAndMessage = ch.fillOutsideResourcePaths(out, JavaCodeGenerator.pullAccessor, inputResourceToStateAccessor); + } + Map>> resourcePaths = resourcePathsAndMessage.getKey(); + Term messageTerm = resourcePathsAndMessage.getValue(); + // Data transfer from the descendant channel hierarchies. + Stack> channelItrStack = new Stack<>(); + DataTransferChannel curChannel = ch; + if (curChannel.getChildren() != null && curChannel.getChildren().size() > 0) { + // retrieve descendant channels recursively. + Iterator chItr = curChannel.getChildren().iterator(); + do { + if (!chItr.hasNext()) { + chItr = channelItrStack.pop(); + } else { + curChannel = (DataTransferChannel) chItr.next(); + // generate pull data transfers. + Set chMems = new HashSet<>(curChannel.getInputChannelMembers()); + chMems.addAll(curChannel.getReferenceChannelMembers()); + for (ChannelMember cm2 : chMems) { + if (resourcePaths == null || !resourcePaths.keySet().contains(cm2)) { + // not a depending channel member. + ResourcePath src2 = cm2.getResource(); + Type srcResType2 = src2.getResourceStateType(); + String srcResName2 = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(src2.getResourceHierarchy())); + String srcGetter = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(src2, dst.getInSideResource(curChannel)).toImplementation(new String[]{}); + getter.addStatement(srcResType2.getInterfaceTypeName() + " " + srcResName2 + " = " + srcGetter + ";"); + } else { + // a depending channel member. + ResourcePath src2 = resourcePaths.get(cm2).getKey(); + // get outside src2 resource state by pull data transfer. + if (cm2.isOutside() || src2.getCommonPrefix(dst.getInSideResource(curChannel)) == null) { + // generate a pull data transfer from a depending in/ref resource. + Type srcResType2 = src2.getResourceStateType(); + String srcResName2 = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(src2.getResourceHierarchy())); + String dependingGetter = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(src2, dst.getInSideResource(curChannel)).toImplementation(new String[]{}); + getter.addStatement(srcResType2.getInterfaceTypeName() + " " + srcResName2 + " = " + dependingGetter + ";"); + } + } + } + // collect the message constraints by a descendant channel. + List varsForSideEffects = new ArrayList<>(); + int v = 0; + resourcePathsAndMessage = curChannel.fillOutsideResourcePaths(out, JavaCodeGenerator.pullAccessor, null); + if (resourcePathsAndMessage != null) { + resourcePaths = resourcePathsAndMessage.getKey(); + Term messageTermSub = resourcePathsAndMessage.getValue(); + for (Entry fieldEnt : ((Term) messageTermSub).getSubTerms(Field.class).entrySet()) { + Position pos = fieldEnt.getKey(); + Field field = fieldEnt.getValue(); + Variable var = new Variable(field.getSymbol().getName(), field.getType()); + ((Term) messageTermSub).replaceSubTerm(pos, var); + } + for (Map.Entry subTermEnt : messageTermSub.getSubTerms(Term.class).entrySet()) { + Term subTerm = subTermEnt.getValue(); + if (!(subTerm instanceof Constant) && subTerm.getSymbol().isImplWithSideEffect()) { + Variable var = new Variable("v" + v, subTerm.getType()); + varsForSideEffects.add(var); + v++; + // Add a side effect statement within the loop + Position pos = new Position(); + pos.addHeadOrder(0); + subTerm.replaceSubTerm(pos, var); + sideEffects = new String[]{""}; + String curState = messageTermSub.toImplementation(sideEffects); + getter.addStatement(sideEffects[0].replaceAll("\n", "")); + // Cancel the side effects in the return value. + pos = subTermEnt.getKey(); + messageTermSub.replaceSubTerm(pos, var); + } + } + if (messageTerm == null) { + messageTerm = messageTermSub; + } else { + messageTerm = (Term) messageTerm.unify(messageTermSub); + } + if (messageTerm == null) { + throw new UnificationFailed(); + } + } + // enclosed by a for loop + Expression selExp = curChannel.getSelectors().get(0).getExpression(); + Type selType = null; + String varName = null; + if (selExp instanceof Variable) { + selType = ((Variable) selExp).getType(); + varName = ((Variable) selExp).getName(); + ChannelMember insideChMem = null; + for (ChannelMember cm2 : curChannel.getInputChannelMembers()) { + if (!cm2.isOutside()) { + insideChMem = cm2; + break; + } + } + if (insideChMem == null) { + for (ChannelMember cm2 : curChannel.getReferenceChannelMembers()) { + if (!cm2.isOutside()) { + insideChMem = cm2; + break; + } + } + } + ResourcePath insideResPath = insideChMem.getResource(); + while (insideResPath.getParent() != null && (insideResPath.getLastParam() == null || !insideResPath.getLastParam().equals(selExp))) { + insideResPath = insideResPath.getParent(); + } + insideResPath = insideResPath.getParent(); + if (insideResPath != null) { + String parent = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(insideResPath, dst.getInSideResource(ch)).toImplementation(new String[]{}); + if (selType.equals(DataConstraintModel.typeInt)) { + // make a for loop (for a list) for data collecting. + getter.addFirstStatement("for (int " + varName + " = 0; " + varName + " < " + parent + ".size(); " + varName + "++) {"); + } else if (selType.equals(DataConstraintModel.typeString)) { + // make a for loop (for a map) for data collecting. + getter.addFirstStatement("for (String " + varName + ": " + parent + ".keySet()) {"); + } + } + } + // initialize the variables to hold side effects within the loop. + for (Variable var : varsForSideEffects) { + getter.addFirstStatement(var.getType().getInterfaceTypeName() + " " + var.getName() + " = new " + var.getType().getImplementationTypeName() + "();"); + } + // end of the loop + getter.addStatement("}"); + if (curChannel.getChildren() != null && curChannel.getChildren().size() > 0) { + channelItrStack.push(chItr); + chItr = curChannel.getChildren().iterator(); + } + } + } while (!channelItrStack.isEmpty()); + } + // generate a return statement. + sideEffects = new String[]{""}; + String curState = ch.deriveUpdateExpressionOf(out, messageTerm, JavaCodeGenerator.pullAccessor).toImplementation(sideEffects); + getter.addStatement(sideEffects[0] + "return " + curState + ";"); + } + if (outsideInputResource) { + // Update fields to refer to outside resources. + 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 dependedMembers = resourcePaths.get(outsideMember).getValue(); + for (ChannelMember dependedMember : dependedMembers) { + ResourcePath dependedRes = dependedMember.getResource(); + ResourceNode dependedNode = null; + PushPullAttribute pushPull2 = null; + for (Edge resToCh2 : resToCh.getDestination().getInEdges()) { + if (((ResourceNode) resToCh2.getSource()).getOutSideResources().contains(dependedRes)) { + dependedNode = (ResourceNode) resToCh2.getSource(); + pushPull2 = (PushPullAttribute) resToCh.getAttribute(); + } + } + TypeDeclaration dependedComponent = null; + if (JavaCodeGenerator.generatesComponent(dependedRes.getResourceHierarchy())) { + String dependedResourceName = JavaCodeGenerator.getComponentName(dependedRes.getResourceHierarchy()); + dependedComponent = componentMap.get(dependedResourceName); + } else { + String dependedParentResourceName = JavaCodeGenerator.getComponentName(dependedRes.getParent().getResourceHierarchy()); + dependedComponent = componentMap.get(dependedParentResourceName); + } + Expression outsideExp = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(outsidePath, null); + if (JavaCodeGenerator.generatesComponent(outsidePath.getResourceHierarchy())) { + outsideExp = ((Term) outsideExp).getChild(0); + } + if (dstComponent == dependedComponent) { + // In the common parent. + if (dependedNode != null) { // Inspect further dependency. + for (Edge chToRes2 : dependedNode.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(dependedComponent, 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(dependedComponent); + constructor.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // initialize the reference field. + } else { + boolean isPush = true; + for (Edge resToCh2 : chToRes2.getSource().getInEdges()) { + if (((PushPullAttribute) resToCh2.getAttribute()).getSelectedOption() != 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(dependedRes.getResourceHierarchy())) { + update = getUpdateMethod(dependedComponent, null, JavaCodeGenerator.getComponentName(dependingResSrc.getResourceHierarchy())); + } else { + String dependingResName = JavaCodeGenerator.getComponentName(dependedRes.getResourceHierarchy()); + update = getUpdateMethod(dependedComponent, dependingResName, JavaCodeGenerator.getComponentName(dependingResSrc.getResourceHierarchy())); + } + update.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // change the reference field. + } + // Update constructor. + MethodDeclaration constructor = getConstructor(dependedComponent); + constructor.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // initialize the reference field. + } + } + } + } + } else { + if (pushPull2.getSelectedOption() == PushPullValue.PUSH) { + // In an update method of the destination component. + MethodDeclaration update = null; + if (JavaCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { + update = getUpdateMethod(dstComponent, null, JavaCodeGenerator.getComponentName(dependedRes.getResourceHierarchy())); + } else { + String dstResName = JavaCodeGenerator.getComponentName(dst.getResourceHierarchy()); + update = getUpdateMethod(dstComponent, dstResName, JavaCodeGenerator.getComponentName(dependedRes.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(); - if (model.isPrimitiveType(resourceType)) { - getter.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);"); + TypeDeclaration mainComponent = componentMap.get(JavaCodeGenerator.mainTypeName); + for (ResourceHierarchy resource : model.getResourceHierarchies()) { + String resourceName = JavaCodeGenerator.getComponentName(resource); + TypeDeclaration component = componentMap.get(resourceName); + if (JavaCodeGenerator.generatesComponent(resource)) { + 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)) { + // 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 = JavaCodeGenerator.getComponentName(child); + String fieldName = JavaCodeGenerator.toVariableName(childTypeName); + Term childGetter = null; + if (!JavaCodeGenerator.generatesComponent(child)) { + // 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[]{""}; + String returnValue = composer.toImplementation(sideEffects); + if (sideEffects[0] != null) { + stateGetter.addStatement(sideEffects[0] + "return " + returnValue + ";"); + } else { + stateGetter.addStatement("return " + returnValue + ";"); + } + } + } + } + } + + // (#4) descendant getter method (the implementation must be kept consistent with #3) + if (resource.getChildren().size() > 0) { + for (ResourceHierarchy child : resource.getChildren()) { + ResourceHierarchy parent = resource; + ResourceHierarchy descendant = child; + Set children; + Expression selector; + int params = 0; + if (DataConstraintModel.typeList.isAncestorOf(parent.getResourceStateType())) { + selector = new Field("value"); + params++; + } else if (DataConstraintModel.typeMap.isAncestorOf(parent.getResourceStateType())) { + selector = new Field("value"); + params++; + } else { + String fieldName = JavaCodeGenerator.getComponentName(descendant); + selector = new Field(JavaCodeGenerator.toVariableName(fieldName)); + } + do { + String methodName = JavaCodeGenerator.getComponentName(descendant); + MethodDeclaration descendantGetter = null; + for (MethodDeclaration getter : getGetterMethods(component, methodName)) { + if ((getter.getParameters() == null && params == 0) || (getter.getParameters() != null && getter.getParameters().size() == params)) { + descendantGetter = getter; + break; + } + } + if (descendantGetter != null) { + if (DataConstraintModel.typeList.isAncestorOf(parent.getResourceStateType())) { + Term newSelector = new Term(DataConstraintModel.get); + newSelector.addChild(selector); + newSelector.addChild(new Variable(descendantGetter.getParameters().get(descendantGetter.getParameters().size() - 1).getName())); + newSelector.setType(descendantGetter.getReturnType()); + selector = newSelector; + } else if (DataConstraintModel.typeMap.isAncestorOf(parent.getResourceStateType())) { + Term newSelector = new Term(DataConstraintModel.lookup); + newSelector.addChild(selector); + newSelector.addChild(new Variable(descendantGetter.getParameters().get(descendantGetter.getParameters().size() - 1).getName())); + newSelector.setType(descendantGetter.getReturnType()); + selector = newSelector; + } + if (descendantGetter != null && (descendantGetter.getBody() == null || descendantGetter.getBody().getStatements().size() == 0)) { + String[] sideEffects = new String[]{null}; + String returnValue = selector.toImplementation(sideEffects); + if (sideEffects[0] != null) descendantGetter.addStatement(sideEffects[0]); + descendantGetter.addStatement("return " + returnValue + ";"); + } + } + if (JavaCodeGenerator.generatesComponent(descendant)) { + // If the descendant generates a component. + break; + } + parent = descendant; + if (DataConstraintModel.typeList.isAncestorOf(parent.getResourceStateType())) { + params++; + } else if (DataConstraintModel.typeMap.isAncestorOf(parent.getResourceStateType())) { + params++; + } + children = descendant.getChildren(); + } while (children != null && children.size() == 1 && (descendant = children.iterator().next()) != null); + } } } - // 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); + } + + // 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 a descendant resource. + ResourceHierarchy outRes = out.getResource().getResourceHierarchy(); + if (outRes.getChildren().size() == 1 && outRes.getChildren().iterator().next().getNumParameters() > 0) { + ResourceHierarchy descendantRes = outRes.getChildren().iterator().next(); + Set children; + do { + if (JavaCodeGenerator.generatesComponent(descendantRes)) break; + children = descendantRes.getChildren(); + } while (children != null && children.size() == 1 && (descendantRes = children.iterator().next()) != null); + Type descendantStateType = descendantRes.getResourceStateType(); + String descendantComponentName = JavaCodeGenerator.getComponentName(descendantRes); + TypeDeclaration descendantComponent = componentMap.get(descendantComponentName); + if (DataConstraintModel.typeJson.isAncestorOf(descendantStateType)) { + replaceJsonTermWithConstructorInvocation(updateExp, descendantStateType, descendantComponentName, descendantComponent); + } + } + // Replace the type of the state field. + Type fieldType = JavaCodeGenerator.getImplStateType(outRes); + if (updateExp instanceof Term) { + ((Term) updateExp).setType(fieldType); + for (Map.Entry varEnt : ((Term) updateExp).getVariables().entrySet()) { + if (varEnt.getValue().getName().equals("value")) { + varEnt.getValue().setType(fieldType); + } + } + } else if (updateExp instanceof Variable) { + ((Variable) updateExp).setType(fieldType); + } + // 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]; + updateStatement = sideEffects[0]; + if (updateStatement.endsWith("\n")) { + updateStatement = updateStatement.substring(0, updateStatement.length() - 1); + } } else { updateStatement = sideEffects[0] + "this.value = " + newState + ";"; } 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 (updateStatement.endsWith("\n")) { + updateStatement = updateStatement.substring(0, updateStatement.length() - 1); + } + } + 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 + ");"); } } } @@ -263,43 +1240,129 @@ } } } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork - | InvalidMessage | UnificationFailed | ValueUndefined e1) { + | InvalidMessage | UnificationFailed | ValueUndefined e1) { e1.printStackTrace(); } return codes; } - - private static MethodDeclaration getUpdateMethod(TypeDeclaration type, TypeDeclaration from) { - for (MethodDeclaration m: type.getMethods()) { - if (m.getName().equals("update" + from.getTypeName())) return m; - } - return null; - } - - private static List getUpdateMethods(TypeDeclaration type) { - List updates = new ArrayList<>(); - for (MethodDeclaration m: type.getMethods()) { - if (m.getName().startsWith("update")) { - updates.add(m); + + private static void replaceJsonTermWithConstructorInvocation(Expression exp, Type replacedJsonType, String replacingClassName, TypeDeclaration descendantComponent) { + // Replace each json term in exp with the corresponding constructor invocation. + Type descendantType = new Type(replacingClassName, replacingClassName); + Map subTerms = ((Term) exp).getSubTerms(Term.class); + Iterator> termEntItr = subTerms.entrySet().iterator(); + while (termEntItr.hasNext()) { + Entry termEnt = termEntItr.next(); + Term jsonTerm = termEnt.getValue(); + if (jsonTerm.getType() != null) { + if (jsonTerm.getType().equals(replacedJsonType)) { + if (jsonTerm instanceof JsonTerm || jsonTerm.getSymbol().equals(DataConstraintModel.addMember)) { + String constructorInvocation = "new " + replacingClassName + "("; + MethodDeclaration descendantConstructor = getConstructor(descendantComponent); + String delimiter = ""; + if (descendantConstructor != null) { + for (VariableDeclaration var : descendantConstructor.getParameters()) { + // Extract the argument of each constructor parameter from jsonTerm. + JsonAccessor jsonMember = new JsonAccessor(DataConstraintModel.dot); + jsonMember.addChild(jsonTerm); + jsonMember.addChild(new Constant(var.getName(), DataConstraintModel.typeString)); + Expression param = jsonMember.reduce(); // Reduce {"name": "foo", age: 25}.name => "foo" + 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, descendantType)); + subTerms = ((Term) exp).getSubTerms(Term.class); + termEntItr = subTerms.entrySet().iterator(); + } else { + jsonTerm.setType(descendantType); + } + } else { + Type oldType = jsonTerm.getType(); + Type newType = new Type(oldType.getTypeName(), + oldType.getImplementationTypeName().replace(replacedJsonType.getInterfaceTypeName(), replacingClassName), + oldType.getInterfaceTypeName().replace(replacedJsonType.getInterfaceTypeName(), replacingClassName)); + for (Type parent : oldType.getParentTypes()) { + newType.addParentType(parent); + } + jsonTerm.setType(newType); + } } } - return updates; } - - private static MethodDeclaration getGetterMethod(TypeDeclaration type) { - for (MethodDeclaration m: type.getMethods()) { - if (m.getName().startsWith("get")) return m; + + private static MethodDeclaration getConstructor(TypeDeclaration component) { + for (MethodDeclaration m : component.getMethods()) { + if (m.isConstructor()) return m; } return null; } - private static Map> getIOChannelsAndMembers(ResourceNode resource, DataTransferModel model) { + 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 : 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 component, String resourceName) { + for (MethodDeclaration m : component.getMethods()) { + if (m.getName().startsWith("get" + resourceName)) return m; + } + return null; + } + + private static List getGetterMethods(TypeDeclaration component, String resourceName) { + List getters = new ArrayList<>(); + for (MethodDeclaration m : component.getMethods()) { + if (m.getName().equals("get" + resourceName)) { + getters.add(m); + } + } + return getters; + } + + 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())) { + for (ChannelMember out : ch.getOutputChannelMembers()) { + 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) { @@ -313,36 +1376,55 @@ } 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); + for (ChannelMember out : channel.getOutputChannelMembers()) { + if (resource.getInSideResources().contains(out.getResource())) { + MethodDeclaration input = getInputMethod(component, out, channel.getOutputChannelMembers().size()); inputs.add(input); } } } 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..ae17189 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/generators/JavaSpecific.java @@ -0,0 +1,368 @@ +package generators; + +import code.ast.*; +import models.algebra.Expression; +import models.algebra.Term; +import models.algebra.Type; +import models.algebra.Variable; +import models.dataConstraintModel.DataConstraintModel; + +import java.util.List; + +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 && !isConstructor) { + 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) { + initializer = DataConstraintModel.getDefaultValue(type); + } + return initializer; + } + + @Override + public boolean declareField() { + return true; + } + + @Override + public String getSelfExp() { + return self; + } + + @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 != null && 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 getForStatementForList(String varName, String list) { + return "for (int " + varName + getAssignment() + "0; " + varName + " < " + list + ".size(); " + varName + "++) {"; + } + + @Override + public String getForStatementForCollection(String varName, String varType, String collection) { + return "for (" + varType + " " + varName + ": " + collection + ") {"; + } + + @Override + public String getForStatementForMap(String varName, String varType, String map) { + return "for (" + varType + " " + varName + ": " + map + ".keySet()) {"; + } + + @Override + public String getEndForStatement() { + return "}"; + } + + @Override + public String getEndForStatement(String varName) { + return "}"; + } + + @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 getOpeningScoreDelimiter() { + return "{"; + } + + @Override + public String getClosingScoreDelimiter() { + return "}"; + } + + @Override + public String getValueToStringExp(String typeName, String valueExp) { + if (typeName.equals("int")) { + return "Integer.toString(" + valueExp + ")"; + } else if (typeName.equals("float")) { + return "Float.toString(" + valueExp + ")"; + } else if (typeName.equals("double")) { + return "Double.toString(" + valueExp + ")"; + } else if (typeName.equals("boolean")) { + return "Boolean.toString(" + valueExp + ")"; + } else { + return valueExp + ".toString()"; + } + } + + @Override + public String getStringToValueExp(String typeName, String strExp) { + if (typeName.equals("int")) { + return "Integer.parseInt(" + strExp + ")"; + } else if (typeName.equals("float")) { + return "Float.parseFloat(" + strExp + ")"; + } else if (typeName.equals("double")) { + return "Double.parseDouble(" + strExp + ")"; + } else if (typeName.equals("boolean")) { + return "Boolean.parseBoolean(" + strExp + ")"; + } else if (typeName.startsWith("ArrayList") || typeName.startsWith("List")) { + return "Arrays.asList(" + strExp + ".replace(\"[\",\"\").replace(\"]\",\"\").split(\",\",0))"; + } else { + return strExp; + } + } + + @Override + public String getPairExp(String first, String second) { + return getConstructorInvocation(DataConstraintModel.typeTuple.getImplementationTypeName() + "<>", List.of(first, second)); + } + + @Override + public String getFirstEntryFromMapExp(String map) { + return getMethodInvocation(map, "entrySet().iterator().next"); + } + + @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..33488cb 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/JerseyCodeGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/JerseyCodeGenerator.java @@ -1,220 +1,1142 @@ package generators; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Set; - -import code.ast.Annotation; -import code.ast.Block; -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 code.ast.*; import models.Edge; import models.Node; -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.ResourcePath; -import models.dataFlowModel.DataTransferModel; -import models.dataFlowModel.DataTransferChannel; +import models.algebra.*; +import models.dataConstraintModel.*; +import models.dataFlowModel.*; import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor; -import models.dataFlowModel.PushPullAttribute; -import models.dataFlowModel.PushPullValue; -import models.dataFlowModel.DataFlowEdge; -import models.dataFlowModel.DataFlowGraph; -import models.dataFlowModel.ResourceNode; -import models.dataFlowModel.StoreAttribute; + +import java.util.*; /** * Generator for Jersey prototypes - * + * * @author Nitta * */ 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; - + public static String getMainTypeName() { return mainTypeName; } - + public static void setMainTypeName(String mainTypeName) { JerseyCodeGenerator.mainTypeName = mainTypeName; } - + public static void resetMainTypeName() { 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).getInterfaceTypeName() + ">", 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) { + if (res.getParent() == null) return true; + if (res.getChildren() == null || res.getChildren().size() == 0) return false; + if (res.getNumParameters() > 0 && res.getChildren().size() == 1 && res.getChildren().iterator().next().getNumParameters() > 0) + return false; + if (res.getChildren().size() == 1 && res.getChildren().iterator().next().getNumParameters() > 0 + && (res.getChildren().iterator().next().getChildren() == null || res.getChildren().iterator().next().getChildren().size() == 0)) + return false; + return true; +// return res.getParent() == null || !(res.getChildren() == null || res.getChildren().size() == 0); + } + 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> descendantGetters = new HashMap<>(); + 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("jakarta.ws.rs.*")); + cu.addImport(new ImportDeclaration("jakarta.ws.rs.client.*")); + cu.addImport(new ImportDeclaration("jakarta.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")); } - MethodDeclaration update = new MethodDeclaration("update" + srcResName, false, typeVoid, vars); - for (ChannelMember cm: re.getChannel().getOutputChannelMembers()) { - if (cm.getResource() == rn.getResource()) { - if (cm.getStateTransition().isRightUnary()) { - update.addAnnotation(new Annotation("PUT")); + 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))); + // Add a parameter to initialize the state field to the constructor. +// Map nameToParam = constructorParams.get(res); +// if (nameToParam == null) { +// nameToParam = new HashMap<>(); +// constructorParams.put(resourceNode.getResourceHierarchy(), nameToParam); +// } +// String varName = "value"; +// 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 { - update.addAnnotation(new Annotation("POST")); + // 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, "new " + childTypeName + "()")); + } + } } } } - 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))); + + // 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")); } - type.addMethod(update); + component.addMethod(stateGetter); } - } - -// // 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 (component != null) { + // (#1) Declare the getter methods in this resource to obtain descendant resources. (complementary to #2) + Set descendants = descendantGetters.get(resourceNode.getResourceHierarchy()); + if (descendants == null) { + descendants = new HashSet<>(); + descendantGetters.put(resourceNode.getResourceHierarchy(), descendants); + } + for (ResourceNode child : resourceNode.getChildren()) { + // A descendant of the child may generate a component. + List pathParams = new ArrayList<>(); + int v = 1; + ResourceNode descendant = child; + Set childNodes; + do { + Expression pathParam = descendant.getPrimaryResourcePath().getLastParam(); + if (pathParam != null) { + 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++; + } + if (generatesComponent(descendant.getResourceHierarchy())) { + // If the descendant generates a component. + if (!descendants.contains(descendant.getResourceHierarchy())) { + descendants.add(descendant.getResourceHierarchy()); + String descendantCompName = getComponentName(descendant.getResourceHierarchy()); + Type descendantType = new Type(descendantCompName, descendantCompName); + MethodDeclaration descendantGetter = null; + if (pathParams.size() == 0) { + descendantGetter = new MethodDeclaration("get" + descendantCompName, descendantType); + } else { + descendantGetter = new MethodDeclaration("get" + descendantCompName, false, descendantType, pathParams); + } + component.addMethod(descendantGetter); + } + break; + } + childNodes = descendant.getChildren(); + } while (childNodes != null && childNodes.size() == 1 && (descendant = childNodes.iterator().next()) != null); + } + } + +// // 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; // } -// 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 + "\"")); + // 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)); + } + } + } + + // (#2) Declare the getter method to obtain the resource state in an ancestor component. (complementary to #1) + if (component == null) { + // No component is created for this resource. + ResourceNode ancestorNode = resourceNode; + Stack ancestors = new Stack<>(); + do { + ancestors.push(ancestorNode); + ancestorNode = ancestorNode.getParent(); + } while (!generatesComponent(ancestorNode.getResourceHierarchy())); + List pathParams = new ArrayList<>(); + int v = 1; + while (ancestors.size() > 0) { + ResourceNode curAncestor = ancestors.pop(); + Expression pathParam = curAncestor.getPrimaryResourcePath().getLastParam(); + 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 getterName = "get" + getComponentName(resourceNode.getResourceHierarchy()); + boolean bExists = false; + for (Map.Entry entry : getters) { + ResourceHierarchy r = entry.getKey(); + MethodDeclaration m = entry.getValue(); + if (r == ancestorNode.getResourceHierarchy() && m.getName().equals(getterName) + && (m.getParameters() == null ? 0 : m.getParameters().size()) == pathParams.size()) { + bExists = true; + break; + } + } + if (!bExists) { + 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<>(ancestorNode.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 = new ResourcePath(resourceNode.getPrimaryResourcePath()); + for (int i = 0; i < mainGetterParams.size(); i++) { + Parameter pathParam = new Parameter(mainGetterParams.get(i).getName()); + resPath.replacePathParam(i, pathParam, null); + } + 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 for push data transfer. + for (Edge resToCh : resourceNode.getOutEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + ChannelNode directDstChNode = (ChannelNode) re.getDestination(); + DataTransferChannel directDstCh = directDstChNode.getChannel(); + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm : directDstCh.getInputChannelMembers()) { + if (cm.getResource().equals(resourceNode.getOutSideResource(directDstCh)) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + // Should take into account the channel hierarchy. + Set ancestorDstChannels = directDstChNode.getAncestors(); + Set descendantDstChannels = directDstChNode.getDescendants(); + Set outEdges = new HashSet<>(); + outEdges.addAll(directDstChNode.getOutEdges()); + for (ChannelNode ancestorDst : ancestorDstChannels) { + outEdges.addAll(ancestorDst.getOutEdges()); + } + for (ChannelNode descendantDst : descendantDstChannels) { + outEdges.addAll(descendantDst.getOutEdges()); + } + for (Edge chToRes : outEdges) { + // For each data transfer to dstNode:ResourceNode. + ResourceNode dstNode = ((ResourceNode) chToRes.getDestination()); + ChannelNode chNode = (ChannelNode) chToRes.getSource(); + DataTransferChannel ch = chNode.getChannel(); + // Check if the output resource is outside of the channel scope. + boolean outsideOutputResource = false; + ChannelMember out = null; + for (ChannelMember cm : ch.getOutputChannelMembers()) { + if (dstNode.getInSideResources().contains(cm.getResource())) { + out = cm; + if (cm.isOutside()) { + outsideOutputResource = true; + break; + } + } + } + ResourcePath dstRes = out.getResource(); + // Also take into account the channel hierarchy to determine push/pull transfer. + if (descendantDstChannels.contains(chNode)) { + outsideOutputResource = true; // Regarded as (broadcasting) push transfer. + } + if (ancestorDstChannels.contains(chNode)) { + outsideInputResource = true; // Regarded as (collecting) pull transfer. + } + if (!bDeclareClientField && ((((PushPullAttribute) re.getAttribute()).getSelectedOption() == 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)); + } + } + } + } + } + } + // Declare update methods called by other resources for push data transfer + // and reference fields for pull data transfer. + for (Edge chToRes : resourceNode.getInEdges()) { + ChannelNode directSrcChannel = (ChannelNode) chToRes.getSource(); + DataTransferChannel ch = directSrcChannel.getChannel(); + // Should take into account the channel hierarchy. + Set ancestorSrcChannels = directSrcChannel.getAncestors(); + Set descendantSrcChannels = directSrcChannel.getDescendants(); + Set inEdges = new HashSet<>(); + inEdges.addAll(directSrcChannel.getInEdges()); + for (ChannelNode ancestorSrc : ancestorSrcChannels) { + inEdges.addAll(ancestorSrc.getInEdges()); + } + for (ChannelNode descendantSrc : descendantSrcChannels) { + inEdges.addAll(descendantSrc.getInEdges()); + } + for (Edge resToCh : inEdges) { + // For each data transfer from srcResPath:ResourcePath to resourceNode:ResourceNode. + DataFlowEdge re = (DataFlowEdge) resToCh; + ChannelNode indirectSrcChNode = (ChannelNode) re.getDestination(); + DataTransferChannel indirectSrcCh = indirectSrcChNode.getChannel(); + ResourcePath srcResPath = ((ResourceNode) re.getSource()).getOutSideResource(indirectSrcCh); + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm : indirectSrcCh.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; + 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; + } + } + } + // Also take into account the channel hierarchy to determine push/pull transfer. + if (ancestorSrcChannels.contains(indirectSrcChNode)) { + outsideOutputResource = true; // Regarded as (broadcasting) push transfer. + } + if (descendantSrcChannels.contains(indirectSrcChNode)) { + outsideInputResource = true; // Regarded as (collecting) pull transfer. + } + String srcResName = getComponentName(srcResPath.getResourceHierarchy()); + Type srcType = srcResPath.getResourceStateType(); + if (!generatesComponent(srcResPath.getResourceHierarchy())) { + srcResPath = srcResPath.getParent(); + } + if ((((PushPullAttribute) re.getAttribute()).getSelectedOption() != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { + // For pull transfer. + if (outsideInputResource || (resourceNode.getInSideResource(ch).getCommonPrefix(srcResPath) == 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() != srcResPath.getResourceHierarchy()) { + component.addField(srcRefField); + } + } else { + // No component is created for this resource. + if (resourceNode.getParent().getResourceHierarchy() != srcResPath.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(srcResPath) == 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))) { + String refName = toVariableName(getComponentName(refRes.getResourceHierarchy())); + param = new VariableDeclaration(refRes.getResourceStateType(), refName); + if (isRestAPI) param.addAnnotation(new Annotation("FormParam", "\"" + refName + "\"")); params.add(param); } - MethodDeclaration input = new MethodDeclaration( - ((Term) cm.getStateTransition().getMessageExpression()).getSymbol().getImplName(), - false, typeVoid, params); - if (cm.getStateTransition().isRightUnary()) { - input.addAnnotation(new Annotation("PUT")); - } else { - input.addAnnotation(new Annotation("POST")); + } + 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 or delete. + boolean isPut = false; + boolean isDelete = false; + for (ChannelMember cm : ch.getOutputChannelMembers()) { + if (resourceNode.getInSideResources().contains(cm.getResource())) { + if (cm.getStateTransition().isRightUnary()) { + isPut = true; + } else { + isPut = false; + Expression nextExp = cm.getStateTransition().getNextStateExpression(); + if (nextExp instanceof Term) { + Symbol rootSymbol = ((Term) nextExp).getSymbol(); + if (rootSymbol.equals(DataConstraintModel.delete) || rootSymbol.equals(DataConstraintModel.remove)) { + isDelete = true; + } else if (rootSymbol.equals(DataConstraintModel.cond)) { + Expression childExp = ((Term) nextExp).getChild(1); + if (childExp instanceof Term) { + rootSymbol = ((Term) childExp).getSymbol(); + if (rootSymbol.equals(DataConstraintModel.delete) || rootSymbol.equals(DataConstraintModel.remove)) { + isDelete = true; + } + } + childExp = ((Term) nextExp).getChild(2); + if (childExp instanceof Term) { + rootSymbol = ((Term) childExp).getSymbol(); + if (rootSymbol.equals(DataConstraintModel.delete) || rootSymbol.equals(DataConstraintModel.remove)) { + isDelete = true; + } + } + } +// HashMap subTerms = ((Term) nextExp).getSubTerms(Term.class); +// for (Term subTerm: subTerms.values()) { +// Symbol rootSymbol = subTerm.getSymbol(); +// if (rootSymbol.equals(DataConstraintModel.delete) || rootSymbol.equals(DataConstraintModel.remove)) { +// isDelete = true; +// break; +// } +// } + } + } } - 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 (isRestAPI) { + if (isPut) { + update.addAnnotation(new Annotation("PUT")); } else { - input.addAnnotation(new Annotation("POST")); + if (!isDelete) { + update.addAnnotation(new Annotation("POST")); + } else { + update.addAnnotation(new Annotation("DELETE")); + } } - type.addMethod(input); + } + // 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())) { + // 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 { + // No component is created for this resource. + fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), cacheField)); + } + 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))) { + String refName = toVariableName(getComponentName(refRes.getResourceHierarchy())); + param = new VariableDeclaration(refRes.getResourceStateType(), refName); + param.addAnnotation(new Annotation("FormParam", "\"" + refName + "\"")); + params.add(param); + } + } + MethodDeclaration updateAccessor = new MethodDeclaration(updateMethodName, false, typeVoid, params); + if (isPut) { + updateAccessor.addAnnotation(new Annotation("PUT")); + } else { + if (!isDelete) { + updateAccessor.addAnnotation(new Annotation("POST")); + } else { + updateAccessor.addAnnotation(new Annotation("DELETE")); + } + } + 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(), 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 { + boolean isDelete = false; + Expression nextExp = cm.getStateTransition().getNextStateExpression(); + if (nextExp instanceof Term) { + Symbol rootSymbol = ((Term) nextExp).getSymbol(); + if (rootSymbol.equals(DataConstraintModel.delete) || rootSymbol.equals(DataConstraintModel.remove)) { + isDelete = true; + } else if (rootSymbol.equals(DataConstraintModel.cond)) { + Expression childExp = ((Term) nextExp).getChild(1); + if (childExp instanceof Term) { + rootSymbol = ((Term) childExp).getSymbol(); + if (rootSymbol.equals(DataConstraintModel.delete) || rootSymbol.equals(DataConstraintModel.remove)) { + isDelete = true; + } + } + childExp = ((Term) nextExp).getChild(2); + if (childExp instanceof Term) { + rootSymbol = ((Term) childExp).getSymbol(); + if (rootSymbol.equals(DataConstraintModel.delete) || rootSymbol.equals(DataConstraintModel.remove)) { + isDelete = true; + } + } + } +// HashMap subTerms = ((Term) nextExp).getSubTerms(Term.class); +// for (Term subTerm: subTerms.values()) { +// Symbol rootSymbol = subTerm.getSymbol(); +// if (rootSymbol.equals(DataConstraintModel.delete) || rootSymbol.equals(DataConstraintModel.remove)) { +// isDelete = true; +// break; +// } +// } + } + if (!isDelete) { + inputAccessor.addAnnotation(new Annotation("POST")); + } else { + inputAccessor.addAnnotation(new Annotation("DELETE")); + } + } + if (resourcePath.length() > 0) { + inputAccessor.addAnnotation(new Annotation("Path", "\"" + resourcePath + "\"")); + } + Map nameToMethod = inputAccessors.get(resourceNode.getResourceHierarchy()); + if (nameToMethod == null) { + nameToMethod = new HashMap<>(); + inputAccessors.put(resourceNode.getResourceHierarchy(), nameToMethod); + } + nameToMethod.put(messageSymbol, 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 { + boolean isDelete = false; + Expression nextExp = cm.getStateTransition().getNextStateExpression(); + if (nextExp instanceof Term) { + Symbol rootSymbol = ((Term) nextExp).getSymbol(); + if (rootSymbol.equals(DataConstraintModel.delete) || rootSymbol.equals(DataConstraintModel.remove)) { + isDelete = true; + } else if (rootSymbol.equals(DataConstraintModel.cond)) { + Expression childExp = ((Term) nextExp).getChild(1); + if (childExp instanceof Term) { + rootSymbol = ((Term) childExp).getSymbol(); + if (rootSymbol.equals(DataConstraintModel.delete) || rootSymbol.equals(DataConstraintModel.remove)) { + isDelete = true; + } + } + childExp = ((Term) nextExp).getChild(2); + if (childExp instanceof Term) { + rootSymbol = ((Term) childExp).getSymbol(); + if (rootSymbol.equals(DataConstraintModel.delete) || rootSymbol.equals(DataConstraintModel.remove)) { + isDelete = true; + } + } + } +// HashMap subTerms = ((Term) nextExp).getSubTerms(Term.class); +// for (Term subTerm: subTerms.values()) { +// Symbol rootSymbol = subTerm.getSymbol(); +// if (rootSymbol.equals(DataConstraintModel.delete) || rootSymbol.equals(DataConstraintModel.remove)) { +// isDelete = true; +// break; +// } +// } + } + if (!isDelete) { + inputAccessor.addAnnotation(new Annotation("POST")); + } else { + inputAccessor.addAnnotation(new Annotation("DELETE")); + } + } + if (resourcePath.length() > 0) { + inputAccessor.addAnnotation(new Annotation("Path", "\"" + resourcePath + "\"")); + } + Map nameToMethod = inputAccessors.get(resourceNode.getResourceHierarchy()); + if (nameToMethod == null) { + nameToMethod = new HashMap<>(); + inputAccessors.put(resourceNode.getResourceHierarchy(), nameToMethod); + } + nameToMethod.put(messageSymbol, 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("jakarta.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)) { + for (MethodDeclaration inputAccessor : inputAccessors.get(res).values()) { + rootComponent.addMethod(inputAccessor); + } + } + } + } } // Declare the Pair class. boolean isCreatedPair = false; - for(Node n : resources) { + for (Node n : resources) { ResourceNode rn = (ResourceNode) n; - if(isCreatedPair) continue; - if(model.getType("Pair").isAncestorOf(rn.getResource().getResourceStateType())) { + if (isCreatedPair) continue; + 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")); @@ -227,48 +1149,183 @@ block.addStatement("this.right = right;"); constructor.setBody(block); type.addMethod(constructor); - - for(FieldDeclaration field : type.getFields()) { + + for (FieldDeclaration field : type.getFields()) { MethodDeclaration getter = new MethodDeclaration( - "get" + field.getName().substring(0,1).toUpperCase() + field.getName().substring(1), - new Type("Double","T")); + "get" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1), + new Type("Double", "T")); getter.setBody(new Block()); getter.getBody().addStatement("return " + field.getName() + ";"); type.addMethod(getter); } - + // MethodDeclaration toStr = new MethodDeclaration("toString", false, DataConstraintModel.typeString, null); // block = new Block(); // block.addStatement("return \"{\\\"\" + left + \"\\\":\\\"\" + right + \"\\\"}\";"); // toStr.setBody(block); // type.addMethod(toStr); - - CompilationUnit cu = new CompilationUnit(type); + + CompilationUnit cu = new CompilationUnit(type); cu.addImport(new ImportDeclaration("java.util.*")); codes.add(cu); isCreatedPair = true; } } - + 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[] {""}); - } else { - if (DataConstraintModel.typeList.isAncestorOf(stateType)) { - initializer = "new " + resId.getResourceStateType().getImplementationTypeName() + "()"; - } else if (DataConstraintModel.typeMap.isAncestorOf(stateType)) { - initializer = "new " + resId.getResourceStateType().getImplementationTypeName() + "()"; + + 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 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 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 (res.getInitialValue() != null) { + initializer = res.getInitialValue().toImplementation(new String[]{""}); + } else if (stateType != null) { + initializer = DataConstraintModel.getDefaultValue(stateType); + } return initializer; } - + static public ArrayList getCodes(ArrayList codeTree) { ArrayList codes = new ArrayList<>(); for (TypeDeclaration type : codeTree) { @@ -313,44 +1370,193 @@ } return codes; } - + 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(toVariableName(getComponentName(targetRes.getResourceHierarchy())), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + return new Parameter(toVariableName(getComponentName(targetRes.getResourceHierarchy())), + 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)) { + 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(toVariableName(getComponentName(targetRes.getResourceHierarchy())), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + if (from != null && !target.isOutside()) { + ResourcePath fromRes = from.getResource(); + if (targetRes.getCommonPrefix(fromRes) != null) { + return getDirectStateAccessorFor(targetRes, fromRes); + } + } + // Get the next state through a local variable which is to be initialized by a response of a GET API. + return new Parameter(toVariableName(getComponentName(targetRes.getResourceHierarchy())), + 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(toVariableName(getComponentName(targetRes.getResourceHierarchy())), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } else { + // (#3) access from an ancestor or outside of the hierarchy (must be kept consistent with #4) + 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 arity = 2; + boolean doesChainInvocations = true; + 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 { + if (generatesComponent(curPath.getResourceHierarchy())) { + if (arity == 2) { + Term newGetter = new Term(new Symbol("get" + typeName, -1, Symbol.Type.METHOD)); + newGetter.addChild(getter); + if (curPath.getResourceHierarchy().getNumParameters() > 0) { + Expression param = curPath.getLastParam(); + if (param != null) { + newGetter.addChild(param); + newGetter.getSymbol().setArity(2); + } + } + getter = newGetter; + } else { + // add the last path parameter. + if (curPath.getResourceHierarchy().getNumParameters() > 0) { + Expression param = curPath.getLastParam(); + if (param != null) { + getter.getSymbol().setArity(arity); + getter.addChild(param); + } + } + } + arity = 2; + doesChainInvocations = true; + } else { + // to get a descendant resource directly. (e.g, .todos.{year}.{month}.{day}.{id} ==> .getTodos().getTodo(year, month, day, id)) + if (doesChainInvocations) { + Term newGetter = new Term(new Symbol("get" + typeName, -1, Symbol.Type.METHOD)); + newGetter.addChild(getter); + getter = newGetter; + doesChainInvocations = false; + } + if (curPath.getResourceHierarchy().getNumParameters() > 0) { + // may change the symbol name + getter.getSymbol().changeName("get" + typeName); + // add a path parameter. + Expression param = curPath.getLastParam(); + if (param != null) { + getter.getSymbol().setArity(arity); + getter.addChild(param); + arity++; + } + } + } + } + } + + 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", - target.getResourceStateType() != null ? target.getResourceStateType() + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } // for reference channel member - return new Parameter(target.getResourceName(), - target.getResourceStateType() != null ? target.getResourceStateType() + return new Parameter(toVariableName(getComponentName(targetRes.getResourceHierarchy())), + 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(toVariableName(getComponentName(targetRes.getResourceHierarchy())), + 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..fdfd502 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java @@ -4,9 +4,12 @@ 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.Stack; import algorithms.TypeInference; import code.ast.CodeUtil; @@ -16,9 +19,14 @@ 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 +35,17 @@ import models.dataConstraintModel.Channel; import models.dataConstraintModel.ChannelMember; import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.JsonAccessor; +import models.dataConstraintModel.JsonTerm; +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 +57,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,271 +69,1299 @@ 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(); + ChannelNode directDstChNode = (ChannelNode) resToCh.getDestination(); + DataTransferChannel directDstCh = directDstChNode.getChannel(); + // Should take into account the channel hierarchy. + Set ancestorDstChannels = directDstChNode.getAncestors(); + Set descendantDstChannels = directDstChNode.getDescendants(); + Set outEdges = new HashSet<>(); + outEdges.addAll(directDstChNode.getOutEdges()); + for (ChannelNode ancestorDst: ancestorDstChannels) { + outEdges.addAll(ancestorDst.getOutEdges()); + } + for (ChannelNode descendantDst: descendantDstChannels) { + outEdges.addAll(descendantDst.getOutEdges()); + } + for (Edge chToRes: outEdges) { + // For each data transfer from src:ResourceNode to dst:ResourceNode. + 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(); + ChannelNode chNode = (ChannelNode) chToRes.getSource(); + DataTransferChannel ch = chNode.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: directDstCh.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(); + // Also take into account the channel hierarchy to determine push/pull transfer. + if (descendantDstChannels.contains(chNode)) { + outsideOutputResource = true; // Regarded as (broadcasting) push transfer. + } + if (ancestorDstChannels.contains(chNode)) { + outsideInputResource = true; // Regarded as (collecting) pull transfer. + } + if ((pushPull.getSelectedOption() == 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) + Term unifiedMassage = null; + if (directDstCh != ch) { + unifiedMassage = directDstCh.fillOutsideResourcePaths(out, JerseyCodeGenerator.pushAccessor, null).getValue(); + } + Expression updateExp = null; + if (ch.getReferenceChannelMembers().size() == 0) { + Term message = ch.fillOutsideResourcePaths(out, JerseyCodeGenerator.pushAccessor, null).getValue(); + if (unifiedMassage == null) { + unifiedMassage = message; + } else { + unifiedMassage = (Term) unifiedMassage.unify(message); + } + updateExp = ch.deriveUpdateExpressionOf(out, unifiedMassage, JerseyCodeGenerator.pushAccessor); + } else { + // if there exists one or more reference channel member. + HashMap inputResourceToStateAccessor = new HashMap<>(); + for (ChannelMember c: ch.getInputChannelMembers()) { + inputResourceToStateAccessor.put(c, JerseyCodeGenerator.pushAccessor); + } + for (ChannelMember c: ch.getReferenceChannelMembers()) { + inputResourceToStateAccessor.put(c, JerseyCodeGenerator.refAccessor); + } + Term message = ch.fillOutsideResourcePaths(out, JerseyCodeGenerator.pushAccessor, inputResourceToStateAccessor).getValue(); + if (unifiedMassage == null) { + unifiedMassage = message; + } else { + unifiedMassage = (Term) unifiedMassage.unify(message); + } + updateExp = ch.deriveUpdateExpressionOf(out, unifiedMassage, JerseyCodeGenerator.pushAccessor); + } + // Replace Json constructor with a constructor of a descendant resource. + ResourceHierarchy outRes = out.getResource().getResourceHierarchy(); + if (outRes.getChildren().size() == 1 && outRes.getChildren().iterator().next().getNumParameters() > 0) { + ResourceHierarchy descendantRes = outRes.getChildren().iterator().next(); + Set children; + do { + if (JerseyCodeGenerator.generatesComponent(descendantRes)) break; + children = descendantRes.getChildren(); + } while (children != null && children.size() == 1 && (descendantRes = children.iterator().next()) != null); + Type descendantStateType = descendantRes.getResourceStateType(); + String descendantComponentName = JerseyCodeGenerator.getComponentName(descendantRes); + TypeDeclaration descendantComponent = componentMap.get(descendantComponentName); + if (DataConstraintModel.typeJson.isAncestorOf(descendantStateType)) { + replaceJsonTermWithConstructorInvocation(updateExp, descendantStateType, descendantComponentName, descendantComponent); + } + } + // Replace the type of the state field. + Type fieldType = JerseyCodeGenerator.getImplStateType(outRes); + if (updateExp instanceof Term) { + ((Term) updateExp).setType(fieldType); + for (Map.Entry varEnt: ((Term) updateExp).getVariables().entrySet()) { + if (varEnt.getValue().getName().equals("value")) { + varEnt.getValue().setType(fieldType); + } + } + } else if (updateExp instanceof Variable) { + ((Variable) updateExp).setType(fieldType); + } + // 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]; + if (updateStatement.endsWith("\n")) { + updateStatement = updateStatement.substring(0, updateStatement.length() - 1); + } + } 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 (updateStatement.endsWith("\n")) { + updateStatement = updateStatement.substring(0, updateStatement.length() - 1); + } + } + 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 : ""), DataConstraintModel.typeString), + 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 (PUSH transfer) of the destination resource. + int inDegree = 0; + Set inEdges = new HashSet<>(); + inEdges.addAll(chNode.getInEdges()); + for (ChannelNode ancestor: chNode.getAncestors()) { + inEdges.addAll(ancestor.getInEdges()); + } + for (ChannelNode descendant: chNode.getDescendants()) { + inEdges.addAll(descendant.getInEdges()); + } + for (Edge resToCh2: inEdges) { + DataFlowEdge df =(DataFlowEdge) resToCh2; + if (((PushPullAttribute) df.getAttribute()).getSelectedOption() == PushPullValue.PUSH) { + inDegree++; + } + } + if (inDegree > 1 + || (inDegree == 1 && directDstCh.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. + ResourcePath outResPath = new ResourcePath(out.getResource()); + for (int i = 0; i < outResPath.getPathParams().size(); i++) { + Parameter pathParam = new Parameter(update.getParameters().get(i).getName()); + outResPath.replacePathParam(i, pathParam, null); + } + Expression resExp = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(outResPath, outResPath.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[] {""}); + int v = 0; + for (VariableDeclaration var: update2.getParameters()) { + if (v < out.getResource().getPathParams().size()) { + if (out.getResource().getPathParams().get(v) instanceof Variable) { + args += delimiter + ((Variable) out.getResource().getPathParams().get(v)).getName(); + } else if (out.getResource().getPathParams().get(v) instanceof Term) { + args += delimiter + "v" + (v + 1); + } + } 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 { + 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) + 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 caller update methods + 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))); + // Get the value of reference member to call the update method. + 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 = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(ref.getResourceHierarchy())); + Type refResourceType = ref.getResourceStateType(); + if (!referredSet.contains(ref)) { + referredSet.add(ref); + String[] sideEffects = new String[] {""}; + ResourcePath srcRes = in.getResource(); + if (!JerseyCodeGenerator.generatesComponent(srcRes.getResourceHierarchy())) { + srcRes = srcRes.getParent(); + } + if (rc.isOutside() || (ref.getCommonPrefix(srcRes) == null && JerseyCodeGenerator.differentTreesAsDifferentServices)) { + List pathParams = new ArrayList<>(); + for (Expression pathExp: ref.getPathParams()) { + pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); + } + generatePullDataTransfer(srcUpdate, refResourceName, ref.getResourceHierarchy().toResourcePath(pathParams), refResourceType); + } else { + 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; + } + Map>> filledPaths = ch.fillOutsideResourcePaths(out, JerseyCodeGenerator.pushAccessor); + String dstPath = null; + if (filledPaths != null && filledPaths.get(out) != null) { + ResourcePath filledDstPath = filledPaths.get(out).getKey(); + dstPath = filledDstPath.toResourcePath().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + } else { + dstPath = dstRes.getResourceHierarchy().toResourcePath(pathParams); + } + // Call the update method. + 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, dstPath, 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, dstPath, srcResName, httpMethod)); + } + if (descendantDstChannels.contains(chNode)) { + // For hierarchical channels (broadcasting push transfer). + if (ch.getSelectors() != null && ch.getSelectors().size() > 0) { + Expression selExp = ch.getSelectors().get(0).getExpression(); + Type selType = null; + String varName = null; + if (selExp instanceof Variable) { + selType = ((Variable) selExp).getType(); + varName = ((Variable) selExp).getName(); + ChannelMember insideChMem = null; + for (ChannelMember cm :ch.getInputChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + if (insideChMem == null) { + for (ChannelMember cm :ch.getReferenceChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + } + if (insideChMem == null) { + for (ChannelMember cm :ch.getOutputChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + } + ResourcePath insideResPath = insideChMem.getResource(); + while (insideResPath.getParent() != null && (insideResPath.getLastParam() == null || !insideResPath.getLastParam().equals(selExp))) { + insideResPath = insideResPath.getParent(); + } + insideResPath = insideResPath.getParent(); + String parent = null; + if (JerseyCodeGenerator.generatesComponent(insideResPath.getResourceHierarchy())) { + Expression getter = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(insideResPath, src.getPrimaryResourcePath()); + Term valueGetter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); + valueGetter.addChild(getter); + parent = valueGetter.toImplementation(new String[] {}); + } else { + parent = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(insideResPath, src.getPrimaryResourcePath()).toImplementation(new String[] {}); + } + if (insideResPath != null) { + if (selType.equals(DataConstraintModel.typeInt)) { + // make a for loop (for a list) for broadcasting. + srcUpdate.addFirstStatement("for (int " + varName + " = 0; " + varName +" < " + parent + ".size(); " + varName + "++) {"); + srcUpdate.addStatement("}"); + } else if (selType.equals(DataConstraintModel.typeString)) { + // make a for loop (for a map) for broadcasting. + srcUpdate.addFirstStatement("for (String " + varName + ": " + parent + ".keySet()) {"); + srcUpdate.addStatement("}"); + } + } + } else if (selExp instanceof Term) { + // not supported. + } + } + } + srcUpdate.addThrow("JsonProcessingException"); + } else { + // Intra-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 = ", "; + } + // Call the update method. + 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 caller input methods + 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))); + // Get the value of reference member to call the update method. + 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 = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(ref.getResourceHierarchy())); Type refResourceType = ref.getResourceStateType(); if (!referredSet.contains(ref)) { referredSet.add(ref); - generatePullDataTransfer(srcUpdate, refResourceName, refResourceType); + String[] sideEffects = new String[] {""}; + ResourcePath srcRes = in.getResource(); + if (!JerseyCodeGenerator.generatesComponent(srcRes.getResourceHierarchy())) { + srcRes = srcRes.getParent(); + } + if (rc.isOutside() || (ref.getCommonPrefix(srcRes) == null && JerseyCodeGenerator.differentTreesAsDifferentServices)) { + List pathParams = new ArrayList<>(); + for (Expression pathExp: ref.getPathParams()) { + pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); + } + generatePullDataTransfer(srcInput, refResourceName, ref.getResourceHierarchy().toResourcePath(pathParams), refResourceType); + } else { + Expression refGetter = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(ref, srcRes); + String refExp = refGetter.toImplementation(sideEffects); + String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); + srcInput.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-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; + } + Map>> filledPaths = ch.fillOutsideResourcePaths(out, JerseyCodeGenerator.pushAccessor); + String dstPath = null; + if (filledPaths != null && filledPaths.get(out) != null) { + ResourcePath filledDstPath = filledPaths.get(out).getKey(); + dstPath = filledDstPath.toResourcePath().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + } else { + dstPath = dstRes.getResourceHierarchy().toResourcePath(pathParams); + } + // Call the update method. + 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, dstPath, 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, dstPath, srcResName, httpMethod)); + } + if (descendantDstChannels.contains(chNode)) { + // For hierarchical channels (broadcasting push transfer). + if (ch.getSelectors() != null && ch.getSelectors().size() > 0) { + Expression selExp = ch.getSelectors().get(0).getExpression(); + Type selType = null; + String forVarName = null; + if (selExp instanceof Variable) { + selType = ((Variable) selExp).getType(); + forVarName = ((Variable) selExp).getName(); + ChannelMember insideChMem = null; + for (ChannelMember cm :ch.getInputChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + if (insideChMem == null) { + for (ChannelMember cm :ch.getReferenceChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + } + if (insideChMem == null) { + for (ChannelMember cm :ch.getOutputChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + } + ResourcePath insideResPath = insideChMem.getResource(); + while (insideResPath.getParent() != null && (insideResPath.getLastParam() == null || !insideResPath.getLastParam().equals(selExp))) { + insideResPath = insideResPath.getParent(); + } + insideResPath = insideResPath.getParent(); + String parent = null; + if (JerseyCodeGenerator.generatesComponent(insideResPath.getResourceHierarchy())) { + Expression getter = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(insideResPath, src.getPrimaryResourcePath()); + Term valueGetter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); + valueGetter.addChild(getter); + parent = valueGetter.toImplementation(new String[] {}); + } else { + parent = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(insideResPath, src.getPrimaryResourcePath()).toImplementation(new String[] {}); + } + if (insideResPath != null) { + if (selType.equals(DataConstraintModel.typeInt)) { + // make a for loop (for a list) for broadcasting. + srcInput.addFirstStatement("for (int " + forVarName + " = 0; " + forVarName +" < " + parent + ".size(); " + forVarName + "++) {"); + srcInput.addStatement("}"); + } else if (selType.equals(DataConstraintModel.typeString)) { + // make a for loop (for a map) for broadcasting. + srcInput.addFirstStatement("for (String " + forVarName + ": " + parent + ".keySet()) {"); + srcInput.addStatement("}"); + } + } + } else if (selExp instanceof Term) { + // not supported. + } + } + } + srcInput.addThrow("JsonProcessingException"); + } else { + // Intra-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 = ", "; + } + // Call the update method. + 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.getSelectedOption() != 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) { + // The first time to fill the getter method's body. + + // Data transfer on the same channel hierarchy. + String[] sideEffects = new String[] {""}; + // 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 = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(refRes.getResourceHierarchy())); + Type refResourceType = refRes.getResourceStateType(); + ResourcePath dstRes = out.getResource(); + if (!JerseyCodeGenerator.generatesComponent(dstRes.getResourceHierarchy())) { + dstRes = dstRes.getParent(); + } + if (rc.isOutside() || (refRes.getCommonPrefix(dstRes) == null && JerseyCodeGenerator.differentTreesAsDifferentServices)) { + List pathParams = new ArrayList<>(); + for (Expression pathExp: refRes.getPathParams()) { + sideEffects = new String[] {""}; + pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); + } + generatePullDataTransfer(getter, refResourceName, refRes.getResourceHierarchy().toResourcePath(pathParams), refResourceType); + } else { + Expression refGetter = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(refRes, dstRes); + sideEffects = new String[] {""}; + String refExp = refGetter.toImplementation(sideEffects); + String refTypeName = refResourceType.getInterfaceTypeName(); + getter.addFirstStatement(sideEffects[0] + refTypeName + " " + refResourceName + " = " + refExp + ";"); + } } - // 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"); + + // Construct the base message. + Map.Entry>>, Term> resourcePathsAndMessage = ch.fillOutsideResourcePaths(out, JerseyCodeGenerator.pullAccessor, null); + Map>> resourcePaths = resourcePathsAndMessage.getKey(); + Term messageTerm = resourcePathsAndMessage.getValue(); + + // Data transfer from path depending resource. + for (Entry>> pathEnt: resourcePaths.entrySet()) { + ChannelMember cm = pathEnt.getKey(); + ResourcePath src2 = pathEnt.getValue().getKey(); + // get outside src2 resource state by pull data transfer. + if (cm.isOutside() || src2.getCommonPrefix(dst.getInSideResource(ch)) == null) { + // Data transfer from an outside input resource is regarded as PULL transfer. + List pathParams = new ArrayList<>(); + for (Expression pathExp: src2.getPathParams()) { + sideEffects = new String[] {""}; + pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); + } + // generate a pull data transfer from a depending in/ref resource. + Type srcResourceType = src2.getResourceStateType(); + String srcResName2 = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(src2.getResourceHierarchy())); + String srcPath2 = src2.toResourcePath().replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + generatePullDataTransfer(getter, srcResName2, srcPath2, srcResourceType); + } + } + + // Data transfer from the descendant channel hierarchies. + Stack> channelItrStack = new Stack<>(); + DataTransferChannel curChannel = ch; + if (curChannel.getChildren() != null && curChannel.getChildren().size() > 0) { + // retrieve descendant channels recursively. + // (For each descendant channel, data transfer from every input resource is regarded as PULL transfer.) + Iterator chItr = curChannel.getChildren().iterator(); + do { + if (!chItr.hasNext()) { + chItr = channelItrStack.pop(); + } else { + curChannel = (DataTransferChannel) chItr.next(); + // generate pull data transfers. + Set chMems = new HashSet<>(curChannel.getInputChannelMembers()); + chMems.addAll(curChannel.getReferenceChannelMembers()); + for (ChannelMember cm2: chMems) { + if (resourcePaths == null || !resourcePaths.keySet().contains(cm2)) { + // not a depending channel member. + ResourcePath src2 = cm2.getResource(); + Type srcResType2 = src2.getResourceStateType(); + String srcResName2 = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(src2.getResourceHierarchy())); + String srcPath2 = src2.toResourcePath().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + generatePullDataTransfer(getter, srcResName2, srcPath2, srcResType2); + } else { + // a depending channel member. + ResourcePath src2 = resourcePaths.get(cm2).getKey(); + // get outside src2 resource state by pull data transfer. + if (cm2.isOutside() || src2.getCommonPrefix(dst.getInSideResource(curChannel)) == null) { + // generate a pull data transfer from a depending in/ref resource. + Type srcResType2 = src2.getResourceStateType(); + String srcResName2 = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(src2.getResourceHierarchy())); + String srcPath2 = src2.toResourcePath().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + generatePullDataTransfer(getter, srcResName2, srcPath2, srcResType2); + } + } + } + // collect the message constraints by a descendant channel. + List varsForSideEffects = new ArrayList<>(); + int v = 0; + resourcePathsAndMessage = curChannel.fillOutsideResourcePaths(out, JerseyCodeGenerator.pullAccessor, null); + if (resourcePathsAndMessage != null) { + resourcePaths = resourcePathsAndMessage.getKey(); + Term messageTermSub = resourcePathsAndMessage.getValue(); + for (Map.Entry subTermEnt: messageTermSub.getSubTerms(Term.class).entrySet()) { + Term subTerm = subTermEnt.getValue(); + if (!(subTerm instanceof Constant) && subTerm.getSymbol().isImplWithSideEffect()) { + Variable var = new Variable("v" + v, subTerm.getType()); + varsForSideEffects.add(var); + v++; + // Add a side effect statement within the loop + Position pos = new Position(); + pos.addHeadOrder(0); + subTerm.replaceSubTerm(pos, var); + sideEffects = new String[] {""}; + String curState = messageTermSub.toImplementation(sideEffects); + getter.addStatement(sideEffects[0].replaceAll("\n", "")); + // Cancel the side effects in the return value. + pos = subTermEnt.getKey(); + messageTermSub.replaceSubTerm(pos, var); + } + } + if (messageTerm == null) { + messageTerm = messageTermSub; + } else { + messageTerm = (Term) messageTerm.unify(messageTermSub); + } + if (messageTerm == null) { + throw new UnificationFailed(); + } + } + // enclosed by a for loop + Expression selExp = curChannel.getSelectors().get(0).getExpression(); + Type selType = null; + String varName = null; + if (selExp instanceof Variable) { + selType = ((Variable) selExp).getType(); + varName = ((Variable) selExp).getName(); + ChannelMember insideChMem = null; + for (ChannelMember cm2 :curChannel.getInputChannelMembers()) { + if (!cm2.isOutside()) { + insideChMem = cm2; + break; + } + } + if (insideChMem == null) { + for (ChannelMember cm2 :curChannel.getReferenceChannelMembers()) { + if (!cm2.isOutside()) { + insideChMem = cm2; + break; + } + } + } + ResourcePath insideResPath = insideChMem.getResource(); + while (insideResPath.getParent() != null && (insideResPath.getLastParam() == null || !insideResPath.getLastParam().equals(selExp))) { + insideResPath = insideResPath.getParent(); + } + insideResPath = insideResPath.getParent(); + String parent = null; + if (insideResPath != null) { + if (insideResPath.getCommonPrefix(dst.getInSideResource(ch)) != null) { + if (JerseyCodeGenerator.generatesComponent(insideResPath.getResourceHierarchy())) { + Expression parentGetter = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(insideResPath, dst.getInSideResource(ch)); + Term valueGetter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); + valueGetter.addChild(parentGetter); + parent = valueGetter.toImplementation(new String[] {}); + } else { + parent = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(insideResPath, dst.getInSideResource(ch)).toImplementation(new String[] {}); + } + } else { + parent = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(insideResPath.getResourceHierarchy())); + } + if (selType.equals(DataConstraintModel.typeInt)) { + // make a for loop (for a list) for data collecting. + getter.addFirstStatement("for (int " + varName + " = 0; " + varName +" < " + parent + ".size(); " + varName + "++) {"); + } else if (selType.equals(DataConstraintModel.typeString)) { + // make a for loop (for a map) for data collecting. + getter.addFirstStatement("for (String " + varName + ": " + parent + ".keySet()) {"); + } + if (insideResPath.getCommonPrefix(dst.getInSideResource(ch)) == null) { + Type parentResType = insideResPath.getResourceStateType(); + String parentResName = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(insideResPath.getResourceHierarchy())); + String parentResPath = insideResPath.toString().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + generatePullDataTransfer(getter, parentResName, parentResPath, parentResType); + } + } + } + // initialize the variables to hold side effects within the loop + for (Variable var: varsForSideEffects) { + getter.addFirstStatement(var.getType().getInterfaceTypeName() + " " + var.getName() + " = new " + var.getType().getImplementationTypeName() + "();"); + } + // end of the loop + getter.addStatement("}"); + if (curChannel.getChildren() != null && curChannel.getChildren().size() > 0) { + channelItrStack.push(chItr); + chItr = curChannel.getChildren().iterator(); + } + } + } while (!channelItrStack.isEmpty()); + } + // generate a return statement. + Expression curExp = ch.deriveUpdateExpressionOf(out, messageTerm, JerseyCodeGenerator.pullAccessor); + sideEffects = new String[] {""}; + String curState = curExp.toImplementation(sideEffects); + if (ch.getChildren() == null || ch.getChildren().size() == 0) { + getter.addStatement(sideEffects[0] + "return " + curState + ";"); + } else { + getter.addStatement("return " + curState + ";"); + } + } + // get src resource state by pull data transfer. + 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 (JavaCodeGenerator.generatesComponent(resource)) { + 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 (!JerseyCodeGenerator.generatesComponent(child)) { + // 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+ ";"); + } + } + } + } + } + + // (#4) descendant getter method (the implementation must be kept consistent with #3) + if (resource.getChildren().size() > 0) { + for (ResourceHierarchy child: resource.getChildren()) { + ResourceHierarchy parent = resource; + ResourceHierarchy descendant = child; + Set children; + Expression selector; + int params = 0; + if (DataConstraintModel.typeList.isAncestorOf(parent.getResourceStateType())) { + selector = new Field("value"); + params++; + } else if (DataConstraintModel.typeMap.isAncestorOf(parent.getResourceStateType())) { + selector = new Field("value"); + params++; + } else { + String fieldName = JerseyCodeGenerator.getComponentName(descendant); + selector = new Field(JerseyCodeGenerator.toVariableName(fieldName)); + } + do { + String methodName = JerseyCodeGenerator.getComponentName(descendant); + MethodDeclaration descendantGetter = null; + for (MethodDeclaration getter: getGetterMethods(component, methodName)) { + if ((getter.getParameters() == null && params == 0) || (getter.getParameters() != null && getter.getParameters().size() == params)) { + descendantGetter = getter; + break; + } + } + if (descendantGetter != null) { + if (DataConstraintModel.typeList.isAncestorOf(parent.getResourceStateType())) { + Term newSelector = new Term(DataConstraintModel.get); + newSelector.addChild(selector); + newSelector.addChild(new Variable(descendantGetter.getParameters().get(descendantGetter.getParameters().size() - 1).getName())); + newSelector.setType(descendantGetter.getReturnType()); + selector = newSelector; + } else if (DataConstraintModel.typeMap.isAncestorOf(parent.getResourceStateType())) { + Term newSelector = new Term(DataConstraintModel.lookup); + newSelector.addChild(selector); + newSelector.addChild(new Variable(descendantGetter.getParameters().get(descendantGetter.getParameters().size() - 1).getName())); + newSelector.setType(descendantGetter.getReturnType()); + selector = newSelector; + } + if (descendantGetter != null && (descendantGetter.getBody() == null || descendantGetter.getBody().getStatements().size() == 0)) { + String[] sideEffects = new String[] {null}; + String returnValue = selector.toImplementation(sideEffects); + if (sideEffects[0] != null) descendantGetter.addStatement(sideEffects[0]); + descendantGetter.addStatement("return " + returnValue + ";"); + } + } + if (JerseyCodeGenerator.generatesComponent(descendant)) { + // If the descendant generates a component. + break; + } + parent = descendant; + if (DataConstraintModel.typeList.isAncestorOf(parent.getResourceStateType())) { + params++; + } else if (DataConstraintModel.typeMap.isAncestorOf(parent.getResourceStateType())) { + params++; + } + children = descendant.getChildren(); + } while (children != null && children.size() == 1 && (descendant = children.iterator().next()) != null); + } + } } - // 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); + } + + // 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[] {""}; + ResourcePath dstRes = out.getResource(); + if (!JerseyCodeGenerator.generatesComponent(dstRes.getResourceHierarchy())) { + dstRes = dstRes.getParent(); + } + if (rc.isOutside() || (ref.getCommonPrefix(dstRes) == null && JerseyCodeGenerator.differentTreesAsDifferentServices)) { + List pathParams = new ArrayList<>(); + for (Expression pathExp: ref.getPathParams()) { + pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); + } + generatePullDataTransfer(input, refResourceName, ref.getResourceHierarchy().toResourcePath(pathParams), refResourceType); + } else { + 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 a descendant resource. + ResourceHierarchy outRes = out.getResource().getResourceHierarchy(); + if (outRes.getChildren().size() == 1 && outRes.getChildren().iterator().next().getNumParameters() > 0) { + ResourceHierarchy descendantRes = outRes.getChildren().iterator().next(); + Set children; + do { + if (JerseyCodeGenerator.generatesComponent(descendantRes)) break; + children = descendantRes.getChildren(); + } while (children != null && children.size() == 1 && (descendantRes = children.iterator().next()) != null); + Type descendantStateType = descendantRes.getResourceStateType(); + String descendantComponentName = JerseyCodeGenerator.getComponentName(descendantRes); + TypeDeclaration descendantComponent = componentMap.get(descendantComponentName); + if (DataConstraintModel.typeJson.isAncestorOf(descendantStateType)) { + replaceJsonTermWithConstructorInvocation(updateExp, descendantStateType, descendantComponentName, descendantComponent); + } + } + // Replace the type of the state field. + Type fieldType = JerseyCodeGenerator.getImplStateType(outRes); + if (updateExp instanceof Term) { + ((Term) updateExp).setType(fieldType); + for (Map.Entry varEnt: ((Term) updateExp).getVariables().entrySet()) { + if (varEnt.getValue().getName().equals("value")) { + varEnt.getValue().setType(fieldType); + } + } + } else if (updateExp instanceof Variable) { + ((Variable) updateExp).setType(fieldType); + } + // 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]; + if (updateStatement.endsWith("\n")) { + updateStatement = updateStatement.substring(0, updateStatement.length() - 1); + } } else { 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 (updateStatement.endsWith("\n")) { + updateStatement = updateStatement.substring(0, updateStatement.length() - 1); + } + } + 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) { + ResourcePath outResPath = new ResourcePath(out.getResource()); + for (int i = 0; i < outResPath.getPathParams().size(); i++) { + Parameter pathParam = new Parameter(inputAccessor.getParameters().get(i).getName()); + outResPath.replacePathParam(i, pathParam, null); + } + // The expression of the receiver (resource) of the input method. + Expression resExp = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(outResPath, outResPath.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[] {""}); + // 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 +1375,66 @@ return codes; } - private static void generatePullDataTransfer(MethodDeclaration methodBody, String fromResourceName, Type fromResourceType) { + private static void replaceJsonTermWithConstructorInvocation(Expression exp, Type replacedJsonType, String replacingClassName, TypeDeclaration descendantComponent) { + // Replace each json term in exp with the corresponding constructor invocation. + Type descendantType = new Type(replacingClassName, replacingClassName); + Map subTerms = ((Term) exp).getSubTerms(Term.class); + Iterator> termEntItr = subTerms.entrySet().iterator(); + while (termEntItr.hasNext()) { + Entry termEnt = termEntItr.next(); + Term jsonTerm = termEnt.getValue(); + if (jsonTerm.getType() != null) { + if (jsonTerm.getType().equals(replacedJsonType)) { + if (jsonTerm instanceof JsonTerm || jsonTerm.getSymbol().equals(DataConstraintModel.addMember)) { + String constructorInvocation = "new " + replacingClassName + "("; + MethodDeclaration descendantConstructor = getConstructor(descendantComponent); + if (descendantConstructor != null) { + String delimiter = ""; + for (VariableDeclaration var: descendantConstructor.getParameters()) { + // Extract the argument of each constructor parameter from jsonTerm. + JsonAccessor jsonMember = new JsonAccessor(DataConstraintModel.dot); + jsonMember.addChild(jsonTerm); + jsonMember.addChild(new Constant(var.getName(), DataConstraintModel.typeString)); + Expression param = jsonMember.reduce(); // Reduce {"name": "foo", age: 25}.name => "foo" + 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(); + } else { + jsonTerm.setType(descendantType); + } + } else { + Type oldType = jsonTerm.getType(); + Type newType = new Type(oldType.getTypeName(), + oldType.getImplementationTypeName().replace(replacedJsonType.getInterfaceTypeName(), replacingClassName), + oldType.getInterfaceTypeName().replace(replacedJsonType.getInterfaceTypeName(), replacingClassName)); + for (Type parent: oldType.getParentTypes()) { + newType.addParentType(parent); + } + jsonTerm.setType(newType); + } + } + } + } + + 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 +1473,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) { @@ -418,7 +1518,7 @@ || elmType == DataConstraintModel.typeLong || elmType == DataConstraintModel.typeFloat || elmType == DataConstraintModel.typeDouble) { - String elmVal = CodeUtil.getToValueExp(elmType.getImplementationTypeName(), elementBase + ".getKey()"); + String elmVal = new JavaSpecific().getStringToValueExp(elmType.getImplementationTypeName(), elementBase + ".getKey()"); decoded = decoded.replace("$x", "new AbstractMap.SimpleEntry<>(" + elmVal + ", $x)"); } else if (elmType == DataConstraintModel.typeString) { decoded = decoded.replace("$x", "new AbstractMap.SimpleEntry<>(" + elementBase + ".getKey(), $x)"); @@ -451,7 +1551,7 @@ || keyType == DataConstraintModel.typeDouble) { decoded += "for (String k: " + mapVal + ".keySet()) {\n"; decoded += "\t" + mapVar + ".put("; - keyVal = CodeUtil.getToValueExp(keyType.getImplementationTypeName(), "k"); + keyVal = new JavaSpecific().getStringToValueExp(keyType.getImplementationTypeName(), "k"); decoded += keyVal + ", " + mapVal + ".get(" + keyVal + ")" + ");\n"; decoded += "}"; } else if (keyType == DataConstraintModel.typeString) { @@ -489,7 +1589,7 @@ // typeTuple: {"1.0":2.0}, typePair: {"left": 1.0, "right":2.0} statements += "form.param(\"" + paramName + "\", new ObjectMapper().writeValueAsString(" + value + "));\n"; } else { - statements += "form.param(\"" + paramName + "\", " + CodeUtil.getToStringExp(paramType.getImplementationTypeName(), value) + ");\n"; + statements += "form.param(\"" + paramName + "\", " + new JavaSpecific().getValueToStringExp(paramType.getImplementationTypeName(), value) + ");\n"; } } if (isFirstCall) { @@ -516,37 +1616,64 @@ 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 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 List getGetterMethods(TypeDeclaration component, String resourceName) { + List getters = new ArrayList<>(); + for (MethodDeclaration m: component.getMethods()) { + if (m.getName().equals("get" + resourceName)) { + getters.add(m); + } + } + return getters; + } + + 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 +1688,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 +1703,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/generators/JerseySpecific.java b/AlgebraicDataflowArchitectureModel/src/generators/JerseySpecific.java new file mode 100644 index 0000000..1d61be4 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/generators/JerseySpecific.java @@ -0,0 +1,163 @@ +package generators; + +import algorithms.TypeInference; +import code.ast.*; +import models.algebra.Type; +import models.dataConstraintModel.DataConstraintModel; + +import java.util.List; +import java.util.Map; + +public class JerseySpecific extends RestApiSpecific { + public static final Type typeClient = new Type("Client", "Client"); + + public JerseySpecific() { + langSpec = new JavaSpecific(); + } + + @Override + public void addComponentAnnotations(TypeDeclaration component, String resourcePath) { + component.addAnnotation(new Annotation("Component")); + component.addAnnotation(new Annotation("Path", "\"/" + resourcePath + "\"")); + } + + @Override + public void addGetAnnotations(MethodDeclaration getMethod) { + getMethod.addAnnotation(new Annotation("Produces", "MediaType.APPLICATION_JSON")); + getMethod.addAnnotation(new Annotation("GET")); + } + + @Override + public void addPutAnnotations(MethodDeclaration putMethod) { + putMethod.addAnnotation(new Annotation("PUT")); + } + + @Override + public void addPostAnnotations(MethodDeclaration postMethod) { + postMethod.addAnnotation(new Annotation("POST")); + } + + @Override + public void addDeleteAnnotations(MethodDeclaration deleteMethod) { + deleteMethod.addAnnotation(new Annotation("DELETE")); + } + + @Override + public void addPathAnnotation(MethodDeclaration method, String resourcePath) { + method.addAnnotation(new Annotation("Path", "\"" + resourcePath + "\"")); + } + + @Override + public void addPathParamAnnotation(VariableDeclaration var, String paramName) { + var.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\"")); + } + + @Override + public void addFormParamAnnotation(VariableDeclaration var, String paramName) { + var.addAnnotation(new Annotation("FormParam", "\"" + paramName + "\"")); + } + + @Override + public void addPlatformSpecificImports(CompilationUnit cu) { + cu.addImport(new ImportDeclaration("jakarta.ws.rs.*")); + cu.addImport(new ImportDeclaration("jakarta.ws.rs.client.*")); + cu.addImport(new ImportDeclaration("jakarta.ws.rs.core.*")); +// 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")); + } + + @Override + public boolean hasHttpClientFieldDeclaration(TypeDeclaration component) { + for (FieldDeclaration field : component.getFields()) { + if (field.getType().equals(typeClient)) { + return true; + } + } + return false; + } + + @Override + public void addHttpClientFieldDeclaration(TypeDeclaration component) { + FieldDeclaration clientField = new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()"); + component.addField(clientField); + } + + @Override + public String getConversionFromJsonString(String fromStrVarName, String toVarName, String toVarType) { + return toVarType + toVarName + langSpec.getAssignment() + langSpec.getConstructorInvocation("ObjectMapper", null) + ".readValue(" + fromStrVarName + ", HashMap.class)"; + } + + @Override + public String getHttpMethodParamsConstructionStatement(String callerResourceName, List>> params, boolean isFirstCall) { + String statements = ""; + if (isFirstCall) { + statements += "Form "; + } + statements += "form" + langSpec.getAssignment() + langSpec.getConstructorInvocation("Form", null) + langSpec.getStatementDelimiter() + "\n"; + for (Map.Entry> param : params) { + Type paramType = param.getKey(); + String paramName = param.getValue().getKey(); + String value = param.getValue().getValue(); + if (DataConstraintModel.typeList.isAncestorOf(paramType)) { + Type compType = TypeInference.getListComponentType(paramType); + String wrapperType = DataConstraintModel.getWrapperType(compType); + if (wrapperType == null) { + statements += langSpec.getForStatementForCollection("i", compType.getInterfaceTypeName(), value) + "\n"; + } else { + statements += langSpec.getForStatementForCollection("i", wrapperType, value) + "\n"; + } + if (DataConstraintModel.typeTuple.isAncestorOf(compType) || DataConstraintModel.typePair.isAncestorOf(paramType) || DataConstraintModel.typeList.isAncestorOf(compType) || DataConstraintModel.typeMap.isAncestorOf(paramType)) { + statements += "\tform.param(\"" + paramName + "\", " + langSpec.getConstructorInvocation("ObjectMapper", null) + ".writeValueAsString(i))" + langSpec.getStatementDelimiter() + "\n"; // typeTuple: {"1.0":2.0}, typePair: {"left": 1.0, "right":2.0} + } else { + statements += "\tform.param(\"" + paramName + "\", i.toString())" + langSpec.getStatementDelimiter() + "\n"; + } + statements += langSpec.getEndForStatement("i") + "\n"; +// return "Entity entity = Entity.entity(" + paramName + ".toString(), MediaType.APPLICATION_JSON);"; + } else if (DataConstraintModel.typeTuple.isAncestorOf(paramType) || DataConstraintModel.typePair.isAncestorOf(paramType) || DataConstraintModel.typeMap.isAncestorOf(paramType)) { + // typeTuple: {"1.0":2.0}, typePair: {"left": 1.0, "right":2.0} + statements += "form.param(\"" + paramName + "\", " + langSpec.getConstructorInvocation("ObjectMapper", null) + ".writeValueAsString(" + value + "))" + langSpec.getStatementDelimiter() + "\n"; + } else { + statements += "form.param(\"" + paramName + "\", " + new JavaSpecific().getValueToStringExp(paramType.getImplementationTypeName(), value) + ")" + langSpec.getStatementDelimiter() + "\n"; + } + } + if (isFirstCall) { + statements += "Entity "; + } + statements += "entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED)" + langSpec.getStatementDelimiter(); + return statements; + } + + @Override + public String getHttpMethodCallStatement(String baseURL, String resourceName, String senderResName, String httpMethod) { + if (senderResName == null) { + return "client.target(\"" + baseURL + "\").path(\"/" + resourceName + "\").request()." + httpMethod + "(entity, String.class)" + langSpec.getStatementDelimiter(); + } else { + // For each source resource, a child resource is defined in the destination resource so that its state can be updated separately. + return "client.target(\"" + baseURL + "\").path(\"/" + resourceName + "/" + senderResName + "\").request()." + httpMethod + "(entity, String.class)" + langSpec.getStatementDelimiter(); + } + } + + @Override + public String getHttpMethodCallWithResponseStatement(String baseURL, String resourceName, String httpMethod, String responseTypeName) { + String responseShortTypeName = responseTypeName; + if (responseTypeName.contains("<")) { + responseShortTypeName = responseTypeName.substring(0, responseTypeName.indexOf("<")); + } + return "client.target(\"" + baseURL + "\").path(\"/" + resourceName + "\").request()." + httpMethod + "(" + responseShortTypeName + ".class)" + langSpec.getStatementDelimiter(); + } + + @Override + public void addJsonException(MethodDeclaration method) { + method.addThrow("JsonProcessingException"); + } + + @Override + public boolean hasJsonException(MethodDeclaration method) { + if (method.getThrows() == null || method.getThrows().getExceptions() == null) return false; + return method.getThrows().getExceptions().contains("JsonProcessingException"); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/generators/RestApiSpecific.java b/AlgebraicDataflowArchitectureModel/src/generators/RestApiSpecific.java new file mode 100644 index 0000000..8c1a355 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/generators/RestApiSpecific.java @@ -0,0 +1,68 @@ +package generators; + +import code.ast.CompilationUnit; +import code.ast.MethodDeclaration; +import code.ast.TypeDeclaration; +import code.ast.VariableDeclaration; +import models.algebra.Type; + +import java.util.List; +import java.util.Map.Entry; + +abstract public class RestApiSpecific implements IPlatformSpecific { + protected ILanguageSpecific langSpec = null; + + @Override + public boolean hasMain() { + return false; + } + + @Override + public boolean isMonolithic() { + return false; + } + + @Override + public boolean isDifferentTreesAsDifferentServices() { + return true; + } + + abstract public void addComponentAnnotations(TypeDeclaration component, String resourcePath); + + abstract public void addGetAnnotations(MethodDeclaration getMethod); + + abstract public void addPutAnnotations(MethodDeclaration putMethod); + + abstract public void addPostAnnotations(MethodDeclaration postMethod); + + abstract public void addDeleteAnnotations(MethodDeclaration deleteMethod); + + abstract public void addPathAnnotation(MethodDeclaration method, String resourcePath); + + abstract public void addPathParamAnnotation(VariableDeclaration var, String paramName); + + abstract public void addFormParamAnnotation(VariableDeclaration var, String paramName); + + abstract public void addPlatformSpecificImports(CompilationUnit cu); + + abstract public boolean hasHttpClientFieldDeclaration(TypeDeclaration component); + + abstract public void addHttpClientFieldDeclaration(TypeDeclaration component); + + abstract public String getConversionFromJsonString(String fromStrVarName, String toVarName, String toVarType); + + abstract public String getHttpMethodParamsConstructionStatement(String callerResourceName, List>> params, boolean isFirstCall); + + abstract public String getHttpMethodCallStatement(String baseURL, String resourceName, String senderResName, String httpMethod); + + abstract public String getHttpMethodCallWithResponseStatement(String baseURL, String resourceName, String httpMethod, String responseTypeName); + + abstract public void addJsonException(MethodDeclaration method); + + abstract public boolean hasJsonException(MethodDeclaration method); + + public String getBaseURL() { + return "http://localhost:8080"; + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/generators/StandaloneSpecific.java b/AlgebraicDataflowArchitectureModel/src/generators/StandaloneSpecific.java new file mode 100644 index 0000000..081edf3 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/generators/StandaloneSpecific.java @@ -0,0 +1,19 @@ +package generators; + +public class StandaloneSpecific implements IPlatformSpecific { + + @Override + public boolean hasMain() { + return true; + } + + @Override + public boolean isMonolithic() { + return true; + } + + @Override + public boolean isDifferentTreesAsDifferentServices() { + return false; // meaningless + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/DirectedGraph.java b/AlgebraicDataflowArchitectureModel/src/models/DirectedGraph.java index 42b7208..f57532c 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/DirectedGraph.java +++ b/AlgebraicDataflowArchitectureModel/src/models/DirectedGraph.java @@ -28,7 +28,7 @@ nodes.remove(node); node.clearInEdges(); node.clearOutEdges(); - for (Edge edge: edges) { + for (Edge edge : edges) { if (edge.getSource().equals(node)) { edges.remove(edge); } else if (edge.getDestination().equals(node)) { @@ -43,7 +43,7 @@ public void setEdges(Set edges) { this.edges = edges; - for (Edge edge: edges) { + for (Edge edge : edges) { if (!nodes.contains(edge.getSource())) nodes.add(edge.getSource()); if (!nodes.contains(edge.getDestination())) nodes.add(edge.getDestination()); edge.getSource().addOutEdge(edge); diff --git a/AlgebraicDataflowArchitectureModel/src/models/Edge.java b/AlgebraicDataflowArchitectureModel/src/models/Edge.java index 9ea2037..1a522f1 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/Edge.java +++ b/AlgebraicDataflowArchitectureModel/src/models/Edge.java @@ -25,11 +25,11 @@ public void setDestination(Node destination) { this.destination = destination; } - + public EdgeAttribute getAttribute() { return attribute; } - + public void setAttribute(EdgeAttribute attribute) { this.attribute = attribute; } diff --git a/AlgebraicDataflowArchitectureModel/src/models/EdgeAttribute.java b/AlgebraicDataflowArchitectureModel/src/models/EdgeAttribute.java index ea6cdec..765fe59 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/EdgeAttribute.java +++ b/AlgebraicDataflowArchitectureModel/src/models/EdgeAttribute.java @@ -1,5 +1,4 @@ package models; public class EdgeAttribute { - } diff --git a/AlgebraicDataflowArchitectureModel/src/models/Node.java b/AlgebraicDataflowArchitectureModel/src/models/Node.java index 3fa1e28..8c92924 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/Node.java +++ b/AlgebraicDataflowArchitectureModel/src/models/Node.java @@ -1,11 +1,12 @@ package models; +import java.util.Collection; import java.util.HashSet; import java.util.Set; public class Node implements Cloneable { - private Set inEdges = null; - private Set outEdges = null; + protected Collection inEdges = null; + protected Collection outEdges = null; private NodeAttribute attribute; public Node() { @@ -13,7 +14,7 @@ outEdges = new HashSet<>(); } - public Set getInEdges() { + public Collection getInEdges() { return inEdges; } @@ -21,7 +22,7 @@ this.inEdges = inEdges; } - public Set getOutEdges() { + public Collection getOutEdges() { return outEdges; } @@ -44,11 +45,11 @@ public void removeOutEdge(Edge edge) { outEdges.remove(edge); } - + public void clearInEdges() { inEdges.clear(); } - + public void clearOutEdges() { outEdges.clear(); } @@ -63,7 +64,7 @@ public Set getPredecessors() { Set predecessors = new HashSet(); - for (Edge edge: inEdges) { + for (Edge edge : inEdges) { predecessors.add(edge.getSource()); } return predecessors; @@ -71,16 +72,16 @@ public Set getSuccessors() { Set successors = new HashSet(); - for (Edge edge: outEdges) { + for (Edge edge : outEdges) { successors.add(edge.getDestination()); } return successors; } - + public NodeAttribute getAttribute() { return attribute; } - + public void setAttribute(NodeAttribute attribute) { this.attribute = attribute; } diff --git a/AlgebraicDataflowArchitectureModel/src/models/NodeAttribute.java b/AlgebraicDataflowArchitectureModel/src/models/NodeAttribute.java index 7d1ded2..538d3f7 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/NodeAttribute.java +++ b/AlgebraicDataflowArchitectureModel/src/models/NodeAttribute.java @@ -1,5 +1,4 @@ package models; public class NodeAttribute { - } diff --git a/AlgebraicDataflowArchitectureModel/src/models/algebra/Constant.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/Constant.java index 440145e..fe8de06 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/algebra/Constant.java +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Constant.java @@ -3,14 +3,14 @@ import java.util.ArrayList; public class Constant extends Term { - + public Constant(String value) { super(new Symbol(value, 0), new ArrayList()); } public Constant(String value, Type type) { - super(new Symbol(value, 0), new ArrayList()); - symbol.setSignature(new Type[] {type}); + super(new Symbol((type == null ? value : type.valueToRepresentation(value)), 0), new ArrayList()); + symbol.setSignature(new Type[]{type}); } public Constant(Symbol symbol) { @@ -25,7 +25,7 @@ @Override public Object clone() { - Constant c = new Constant(symbol); + Constant c = new Constant(symbol); c.setType(type); return c; } @@ -34,9 +34,16 @@ return symbol.getName(); } + public Object getValue() { + if (getType() != null) { + return getType().representationToValue(symbol.getName()); + } + return symbol.getName(); + } + 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..a6a9409 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/algebra/Expression.java +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Expression.java @@ -4,10 +4,28 @@ 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(); + public abstract HashMap getSubTerms(Class clazz); public HashMap getVariables() { @@ -16,6 +34,7 @@ /** * Get the implementation of this expression. + * * @param sideEffects an array with an optional implementation that should be written before the evaluation of this expression * @return the implementation to represent the value of this expression */ diff --git a/AlgebraicDataflowArchitectureModel/src/models/algebra/Field.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/Field.java index f51b3c4..9389de4 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/algebra/Field.java +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Field.java @@ -1,20 +1,20 @@ package models.algebra; -import java.util.ArrayList; - /** * A field in the implementation (regarded as a constant in the algebraic system) + * * @author Nitta * */ public class Field extends Constant { - + public Field(String name) { super(name); } public Field(String name, Type type) { - super(name, type); + super(name); + symbol.setSignature(new Type[]{type}); } public Field(Symbol symbol) { @@ -22,12 +22,12 @@ } public Type getType() { - if (symbol.getSignature().length >= 1) { + if (symbol.getSignature() != null && symbol.getSignature().length >= 1) { return symbol.getSignature()[0]; } return null; } - + @Override public boolean equals(Object another) { if (!(another instanceof Field)) return false; diff --git a/AlgebraicDataflowArchitectureModel/src/models/algebra/LambdaAbstraction.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/LambdaAbstraction.java new file mode 100644 index 0000000..c740729 --- /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/Parameter.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/Parameter.java index 739ab80..93d5286 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/algebra/Parameter.java +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Parameter.java @@ -2,17 +2,19 @@ /** * A parameter in the implementation (regarded as a constant in the algebraic system) + * * @author Nitta * */ public class Parameter extends Constant { - + public Parameter(String name) { super(name); } public Parameter(String name, Type type) { - super(name, type); + super(name); + symbol.setSignature(new Type[]{type}); } public Parameter(Symbol symbol) { @@ -20,7 +22,7 @@ } public Type getType() { - if (symbol.getSignature().length >= 1) { + if (symbol.getSignature() != null && symbol.getSignature().length >= 1) { return symbol.getSignature()[0]; } return null; diff --git a/AlgebraicDataflowArchitectureModel/src/models/algebra/Position.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/Position.java index 0a7c10b..a27449a 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/algebra/Position.java +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Position.java @@ -20,7 +20,7 @@ public int removeHeadOrder() { return orders.remove(0); } - + public List getOrders() { return orders; } @@ -29,6 +29,14 @@ 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..96b3290 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; @@ -48,6 +51,7 @@ public Symbol(String name, int arity, Type operatorType, IImplGenerator generator) { this.name = name; + this.implName = name; this.arity = arity; this.operatorType = operatorType; this.generator = generator; @@ -56,6 +60,7 @@ public Symbol(String name, int arity, Type operatorType, IImplGenerator generator, boolean bSideEffect) { this.name = name; + this.implName = name; this.arity = arity; this.operatorType = operatorType; this.generator = generator; @@ -66,18 +71,58 @@ } } + public Symbol(String name, int arity, ICalculator calculator) { + this.name = name; + this.implName = name; + this.arity = arity; + this.calculator = calculator; + } + + public Symbol(String name, int arity, Type operatorType, ICalculator calculator) { + this.name = name; + this.implName = name; + this.arity = arity; + this.operatorType = operatorType; + this.implOperatorType = operatorType; + this.calculator = calculator; + } + + public Symbol(String name, int arity, Type operatorType, IImplGenerator generator, ICalculator calculator) { + this.name = name; + this.implName = 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; } - + public int getArity() { return arity; } - + public String getName() { return name; } - + + public void changeName(String name) { + this.name = name; + this.implName = name; + } + public Type getOperatorType() { return operatorType; } @@ -89,31 +134,35 @@ 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; } - + public void setInverses(Symbol[] inverses) { this.inverses = inverses; } - + public models.algebra.Type[] getSignature() { return signature; } - + public void setSignature(models.algebra.Type[] signature) { this.signature = signature; } - + public String getImplName() { return implName; } - + public void setImplName(String implName) { this.implName = implName; } - + public Type getImplOperatorType() { return implOperatorType; } @@ -135,11 +184,11 @@ } public boolean isImplWithSideEffect() { - return (implOperatorType == Type.METHOD_WITH_SIDE_EFFECT - || implOperatorType == Type.LAMBDA_WITH_SIDE_EFFECT + return (implOperatorType == Type.METHOD_WITH_SIDE_EFFECT + || implOperatorType == Type.LAMBDA_WITH_SIDE_EFFECT || implOperatorType == Type.GENERATIVE_WITH_SIDE_EFFECT); } - + public void setImplOperatorType(Type implOperatorType) { this.implOperatorType = implOperatorType; } @@ -149,24 +198,37 @@ } public void setGenerator(IImplGenerator generator) { - this.generator = generator; + this.generator = generator; } /** * Generate the implementation of this symbol - * @param type the type of this symbol - * @param childrenImpl the implementations of the children + * + * @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 + * @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; + } + public boolean equals(Object another) { if (!(another instanceof Symbol)) return false; return name.equals(((Symbol) another).name) && arity == ((Symbol) another).arity; @@ -218,12 +280,18 @@ public interface IImplGenerator { /** * Generate the implementation - * @param type the type of this expression - * @param children the implementations of the children + * + * @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 + * @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..6ea96b7 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/algebra/Term.java +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Term.java @@ -1,25 +1,33 @@ 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) { super(); 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; } @@ -32,44 +40,62 @@ this.type = type; } - public Type getType() { + public Type getType() { if (type == null) { if (symbol.getSignature() == null) return null; return symbol.getSignature()[0]; } return type; } - + public boolean addChild(Expression child) { if (getArity() != -1 && children.size() >= getArity()) return false; 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; children.add(child); } + public void addChild(int n, Expression child, boolean bForced) { + if (!bForced && getArity() != -1 && children.size() >= getArity()) return; + children.add(n, child); + } + public Expression getChild(int n) { return children.get(n); } - public ArrayList getChildren() { + public List getChildren() { return children; } public HashMap getSubTerms(Class clazz) { HashMap subTerms = new HashMap<>(); - if (clazz == this.getClass()) { - subTerms.put(new Position(), (T) this); + Class thisClass = this.getClass(); + while (thisClass != null) { + if (clazz == thisClass) { + subTerms.put(new Position(), (T) this); + break; + } + thisClass = thisClass.getSuperclass(); } for (int i = 0; i < children.size(); i++) { - HashMap terms = children.get(i).getSubTerms(clazz); - for (Entry term: terms.entrySet()) { - Position pos = term.getKey(); - pos.addHeadOrder(i); - subTerms.put(pos, term.getValue()); + if (children.get(i) != null) { + HashMap terms = children.get(i).getSubTerms(clazz); + for (Entry term : terms.entrySet()) { + Position pos = term.getKey(); + pos.addHeadOrder(i); + subTerms.put(pos, term.getValue()); + } } } return subTerms; @@ -86,7 +112,7 @@ public Term substitute(Variable variable, Expression value) { Term newTerm = (Term) this.clone(); HashMap variables = getVariables(); - for (Entry varEnt: variables.entrySet()) { + for (Entry varEnt : variables.entrySet()) { if (varEnt.getValue().equals(variable)) { newTerm.replaceSubTerm(varEnt.getKey(), value); } @@ -109,6 +135,8 @@ @Override public Expression unify(Expression another) { if (another instanceof Variable) return (Expression) this.clone(); + if (this instanceof Constant) return (Expression) this.clone(); + if (another instanceof Constant) return (Expression) another.clone(); if (another instanceof Term) { Term anotherTerm = (Term) another; if (!symbol.equals(anotherTerm.symbol)) return null; @@ -120,7 +148,70 @@ return unifiedTerm; } else { return null; - } + } + } + + 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)) { + Expression newChild = ((Term) (child)).reduce(); + if (newChild != child) { + bReduced = true; + child = newChild; + } + } + 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 = (Term) this.clone(); + newTerm.children = newChildren; + return newTerm; + } } @Override @@ -143,7 +234,7 @@ @Override public boolean contains(Expression exp) { if (equals(exp)) return true; - for (Expression e: children) { + for (Expression e : children) { if (e.contains(exp)) return true; } return false; @@ -174,8 +265,12 @@ @Override public Object clone() { Term newTerm = new Term(symbol); - for (Expression e: children) { - newTerm.addChild((Expression) e.clone()); + for (Expression e : children) { + if (e != null) { + newTerm.addChild((Expression) e.clone()); + } else { + newTerm.addChild(null); + } } newTerm.type = type; return newTerm; @@ -185,7 +280,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++) { @@ -197,14 +292,18 @@ } else { String exp = symbol.toString() + "("; String delimiter = ""; - for (Expression e: children) { - exp += (delimiter + e.toString()); + for (Expression e : children) { + if (e != null) { + exp += (delimiter + e.toString()); + } else { + exp += (delimiter + "null"); + } delimiter = ","; } return exp + ")"; } } - + public String toImplementation(String[] sideEffects) { int[] implParamOrder = symbol.getImplParamOrder(); @@ -213,50 +312,64 @@ String component0 = components[0].replace("(", "").replace(")", ""); String[] params = component0.split(","); String exp = components[1]; + String receiver = ""; if (implParamOrder == null) { - for (int i = 0; i < params.length; i++) { + receiver = children.get(0).toImplementation(sideEffects); + exp = exp.replace(params[0], receiver); + for (int i = 1; i < params.length; i++) { exp = exp.replace(params[i], children.get(i).toImplementation(sideEffects)); } } else { - for (int i = 0; i < params.length; i++) { + receiver = children.get(implParamOrder[0]).toImplementation(sideEffects); + exp = exp.replace(params[0], receiver); + for (int i = 1; i < params.length; i++) { exp = exp.replace(params[i], children.get(implParamOrder[i]).toImplementation(sideEffects)); } } if (symbol.isImplWithSideEffect()) { sideEffects[0] = sideEffects[0] + exp + ";\n"; - if (implParamOrder == null) { - exp = children.get(0).toImplementation(new String[] {""}); - } else { - exp = children.get(implParamOrder[0]).toImplementation(new String[] {""}); - } + exp = receiver; } 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++) { - String childSideEffect[] = new String[] {""}; - childrenImpl[i] = children.get(i).toImplementation(childSideEffect); + 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] = 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 + exp = childrenImpl[0]; // the value of this term } return exp; } else { for (int i = 0; i < children.size(); i++) { - String childSideEffect[] = new String[] {""}; - childrenImpl[i] = children.get(implParamOrder[i]).toImplementation(childSideEffect); + 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] = 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 + exp = childrenImpl[0]; // the value of this term } return exp; } @@ -265,12 +378,19 @@ if (implParamOrder == null) { return "(" + children.get(0).toImplementation(sideEffects) + symbol.toImplementation() + children.get(1).toImplementation(sideEffects) + ")"; } else { - return "(" + children.get(implParamOrder[0]).toImplementation(sideEffects) + symbol.toImplementation() + children.get(implParamOrder[1]).toImplementation(sideEffects) + ")"; + 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; + String receiver = ""; + if (children.get(0) != null) { + receiver = children.get(0).toImplementation(sideEffects); + exp = receiver + "." + symbol.toImplementation() + "("; + } else { + exp = symbol.toImplementation() + "("; + } String delimiter = ""; for (int i = 1; i < children.size(); i++) { Expression e = children.get(i); @@ -280,11 +400,12 @@ exp += ")"; if (symbol.isImplWithSideEffect()) { sideEffects[0] = sideEffects[0] + exp + ";\n"; - exp = children.get(0).toImplementation(new String[] {""}); + exp = receiver; } return exp; } else { - String exp = children.get(implParamOrder[0]).toImplementation(sideEffects) + "." + symbol.toImplementation() + "("; + String receiver = children.get(implParamOrder[0]).toImplementation(sideEffects); + String exp = receiver + "." + symbol.toImplementation() + "("; String delimiter = ""; for (int i = 1; i < children.size(); i++) { Expression e = children.get(implParamOrder[i]); @@ -294,7 +415,7 @@ exp += ")"; if (symbol.isImplWithSideEffect()) { sideEffects[0] = sideEffects[0] + exp + ";\n"; - exp = children.get(implParamOrder[0]).toImplementation(new String[] {""}); + exp = receiver; } return exp; } @@ -302,7 +423,7 @@ if (implParamOrder == null) { String exp = symbol.toImplementation() + "("; String delimiter = ""; - for (Expression e: children) { + for (Expression e : children) { exp += (delimiter + e.toImplementation(sideEffects)); delimiter = ","; } diff --git a/AlgebraicDataflowArchitectureModel/src/models/algebra/Type.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/Type.java index 50b0285..a2d6a35 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/algebra/Type.java +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Type.java @@ -9,32 +9,32 @@ 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); } - + public String getTypeName() { return typeName; } @@ -62,24 +62,44 @@ public List getParentTypes() { return parentTypes; } - + public void addParentType(Type parentType) { parentTypes.add(parentType); } - + public void replaceParentType(Type oldParentType, Type newParentType) { parentTypes.set(parentTypes.indexOf(oldParentType), newParentType); } public boolean isAncestorOf(Type another) { if (this.equals(another)) return true; - if (another.getParentTypes() == null) return false; - for (Type anothersParentType: another.getParentTypes()) { + if (another == null || another.getParentTypes() == null) return false; + for (Type anothersParentType : another.getParentTypes()) { if (isAncestorOf(anothersParentType)) return true; } return false; } - + + public String valueToRepresentation(Object value) { + if (value instanceof String) return (String) value; + return value.toString(); + } + + public Object representationToValue(String representation) { + return representation; + } + + @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/algebra/Variable.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/Variable.java index fa0095b..4652c54 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/algebra/Variable.java +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Variable.java @@ -10,13 +10,13 @@ super(); this.name = name; } - + public Variable(String name, Type type) { super(); this.name = name; this.type = type; } - + public String getName() { return name; } @@ -24,11 +24,11 @@ public Type getType() { return type; } - + public void setType(Type type) { this.type = type; } - + @Override public HashMap getSubTerms(Class clazz) { HashMap subTerms = new HashMap<>(); @@ -48,18 +48,18 @@ public Expression unify(Expression another) { return (Expression) another.clone(); } - + @Override public Expression getInverseMap(Expression outputValue, Position targetPos) { if (targetPos.isEmpty()) return outputValue; return null; } - + @Override public boolean contains(Expression exp) { return equals(exp); } - + @Override public boolean equals(Object another) { if (!(another instanceof Variable)) return false; diff --git a/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/CallEdge.java b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/CallEdge.java new file mode 100644 index 0000000..ade5382 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/CallEdge.java @@ -0,0 +1,17 @@ +package models.controlFlowModel; + +import models.Edge; +import models.dataFlowModel.PushPullValue; + +public class CallEdge extends Edge { + private final PushPullValue selectedOption; + + public CallEdge(ObjectNode src, ObjectNode dst, PushPullValue selectedOption) { + super(src, dst); + this.selectedOption = selectedOption; + } + + public PushPullValue getSelectedOption() { + return this.selectedOption; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/CallEdgeAttribute.java b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/CallEdgeAttribute.java new file mode 100644 index 0000000..0dd0992 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/CallEdgeAttribute.java @@ -0,0 +1,84 @@ +package models.controlFlowModel; + +import com.mxgraph.model.mxCell; +import models.EdgeAttribute; +import models.dataFlowModel.PushPullValue; + +/** + * {@link CallEdgeAttribute} represents a set of attributes for a call edge. + */ +public class CallEdgeAttribute extends EdgeAttribute { + private CallEdge callEdge = null; + private ObjectNode originalSrcObjNode = null; + private mxCell srcCell = null; + private mxCell dstCell = null; + + public CallEdgeAttribute(CallEdge callEdge, ObjectNode originalSrcObjNode, final mxCell srcCell, final mxCell dstCell) { + this.callEdge = callEdge; + this.callEdge.setAttribute(this); + + this.originalSrcObjNode = originalSrcObjNode; + + this.srcCell = srcCell; + this.dstCell = dstCell; + } + + public CallEdgeAttribute(CallEdge callEdge, final mxCell srcCell, final mxCell dstCell) { + this.callEdge = callEdge; + this.callEdge.setAttribute(this); + + this.srcCell = srcCell; + this.dstCell = dstCell; + } + + public CallEdge getCallEdge() { + return callEdge; + } + + public ObjectNode getOriginalSourceObjectNode() { + return originalSrcObjNode; + } + + public PushPullValue getSelectedOption() { + return callEdge.getSelectedOption(); + } + + public ObjectNode getSourceObjectNode() { + if (!(callEdge.getSource() instanceof ObjectNode)) + throw new ClassCastException("sourceNode isn't type of "); + return (ObjectNode) callEdge.getSource(); + } + + public ObjectNode getDestinationObjectNode() { + if (!(callEdge.getDestination() instanceof ObjectNode)) + throw new ClassCastException("destinationNode isn't type of "); + return (ObjectNode) callEdge.getDestination(); + } + + public mxCell getSourceCell() { + return srcCell; + } + + public mxCell getDestinationCell() { + return dstCell; + } + + public void setDestinationCell(final mxCell dstCell) { + this.dstCell = dstCell; + } + + /** + * Return a calling order of the call edge + */ + @Override + public String toString() { + String value = ""; + + ObjectNode srcObjNode = (ObjectNode) callEdge.getSource(); + if (srcObjNode.getOutEdges().size() >= 2) { + int order = srcObjNode.getOutEdgeCallOrder(callEdge) + 1; + value += "[" + order + "]"; + } + return value; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/CallGraph.java b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/CallGraph.java new file mode 100644 index 0000000..eddbd86 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/CallGraph.java @@ -0,0 +1,122 @@ +package models.controlFlowModel; + +import models.DirectedGraph; +import models.Node; +import models.dataFlowModel.PushPullValue; +import models.dataFlowModel.ResourceNode; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * {@link CallGraph} is a directed graph that represents the control flow between resources + */ +public class CallGraph extends DirectedGraph { + protected Map statefulObjMap; + + public CallGraph() { + statefulObjMap = new HashMap<>(); + } + + /** + * Add a node to the graph. + * + * @param node The node to be added. + */ + public void addNode(Node node) { + if (node instanceof ResourceNode) { + ResourceNode resNode = (ResourceNode) node; + StatefulObjectNode objNode = statefulObjMap.get(resNode); + if (objNode == null) { + objNode = new StatefulObjectNode(resNode); + statefulObjMap.put(resNode, objNode); + super.addNode(objNode); + } + } else if (node instanceof StatefulObjectNode) { + StatefulObjectNode objNode = (StatefulObjectNode) node; + if (statefulObjMap.get(objNode.getResource()) == null) { + statefulObjMap.put(objNode.getResource(), objNode); + super.addNode(objNode); + } + } else { + super.addNode(node); + } + } + + /** + * Add new call edge from {@code srcResNode} to {@code dstResNode} with PUSH / PULL selection. + * + * @param srcResNode The resource node the edge starts from. + * @param dstResNode The resource node the edge ends at. + * @param selectedOption PUSH / PULL + */ + public void addEdge(ResourceNode srcResNode, ResourceNode dstResNode, PushPullValue selectedOption) { + addNode(srcResNode); + addNode(dstResNode); + addEdge(new CallEdge(getStatefulObjectNode(srcResNode), getStatefulObjectNode(dstResNode), selectedOption)); + } + + /** + * Insert an edge into the graph. + * + * @param srcObjNode The source node of the edge to be inserted + * @param dstObjNode The destination node of the edge to be inserted + * @param selectedOption PUSH or PULL + * @param n The calling order of the edge + */ + public void insertEdge(ObjectNode srcObjNode, ObjectNode dstObjNode, PushPullValue selectedOption, int n) { + CallEdge edge = new CallEdge(srcObjNode, dstObjNode, selectedOption); + simpleAddEdge(edge); + addNode(srcObjNode); + addNode(dstObjNode); + srcObjNode.insertOutEdge(edge, n); + dstObjNode.addInEdge(edge); + } + + public void addAncestorNodes(ResourceNode node) { + addNode(node); + + ResourceNode parent = node.getParent(); + StatefulObjectNode statefulObjectNode = statefulObjMap.get(node); + if (parent != null) { + addAncestorNodes(parent); + + StatefulObjectNode parentStatefulObjectNode = statefulObjMap.get(parent); + parentStatefulObjectNode.addChild(statefulObjectNode); + } + } + + public void addDescendantNodes(ResourceNode node) { + addNode(node); + + for (ResourceNode child : node.getChildren()) { + addDescendantNodes(child); + + StatefulObjectNode statefulObjectNode = statefulObjMap.get(node); + statefulObjectNode.addChild(statefulObjMap.get(child)); + } + } + + public StatefulObjectNode getStatefulObjectNode(ResourceNode resNode) { + return statefulObjMap.get(resNode); + } + + public Set getRootNodes() { + Set roots = new HashSet<>(); + for (Node node : getNodes()) { + if (node instanceof StatefulObjectNode) { + StatefulObjectNode statefulObjectNode = (StatefulObjectNode) node; + if (statefulObjectNode.getResource().getResourceHierarchy().getParent() == null) { + roots.add(node); + } + } else { + if (node.getIndegree() == 0) { + roots.add(node); + } + } + } + return roots; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/CompositeCallEdgeAttribute.java b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/CompositeCallEdgeAttribute.java new file mode 100644 index 0000000..f509408 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/CompositeCallEdgeAttribute.java @@ -0,0 +1,24 @@ +package models.controlFlowModel; + +import java.util.ArrayList; +import java.util.List; + +/** + * 制御フローを表す2本以上のエッジを統合したときの情報
+ * e.g.) "仲介者への依存"等で2本の制御フローのエッジが1本になったときなど + */ +public class CompositeCallEdgeAttribute extends CallEdgeAttribute { + private CallEdgeAttribute currentEdgeAttr = null; + private List mergedCallEdgeAttrs = null; + + public CompositeCallEdgeAttribute(final CallEdgeAttribute callEdgeAttr) { + super(callEdgeAttr.getCallEdge(), callEdgeAttr.getOriginalSourceObjectNode(), callEdgeAttr.getSourceCell(), callEdgeAttr.getDestinationCell()); + currentEdgeAttr = (currentEdgeAttr == null) ? callEdgeAttr : null; + } + + public void mergeCallEdgeAttribute(final CallEdgeAttribute mergedCallEdgeAttr) { + if (currentEdgeAttr == null) return; + if (mergedCallEdgeAttrs == null) mergedCallEdgeAttrs = new ArrayList(); + mergedCallEdgeAttrs.add(mergedCallEdgeAttr); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/ControlFlowDelegator.java b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/ControlFlowDelegator.java new file mode 100644 index 0000000..96d4543 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/ControlFlowDelegator.java @@ -0,0 +1,215 @@ +package models.controlFlowModel; + +import models.Edge; +import models.dataFlowModel.PushPullValue; + +import java.util.ArrayList; +import java.util.List; + +public class ControlFlowDelegator { + /** + * CFD適用可能範囲を探索する + * + * @param callEdge + * @return CFD適用可能なノードのリスト + */ + public List searchDelegatableNodes(final CallEdge callEdge) { + List nodes = new ArrayList<>(); + + // 1. adding parentNode + ObjectNode delegatingNode = (ObjectNode) callEdge.getDestination(); + ObjectNode parentNode = (ObjectNode) callEdge.getSource(); + + if (parentNode == null || delegatingNode == null) + throw new NullPointerException("parentNode is null."); + if (!(parentNode instanceof ObjectNode && delegatingNode instanceof ObjectNode)) + throw new ClassCastException("callEdge.getSource() is not ObjectNode"); + + // if the relation of "delegatingNode" to "parentNode" is 1 : 1 ? + // then return an empty list. + if (isRootNode(parentNode) && !hasChildrenNode(parentNode)) return nodes; + + // 2. collecting for each transfer method has nodes in the common area. + collectCommonTransferNodes(nodes, parentNode, delegatingNode); + + // 3. if the transfer method is PUSH-style, + // then search delegatable area. + collectParentNodesOnPushTransfer(nodes, delegatingNode, parentNode); + + return nodes; + } + + /** + * DoMが適用可能な仲介者オブジェクトの探索 + * + * @param callEdge + */ + public List searchDependableMediatorNodes(final CallEdge callEdge) { + List nodes = new ArrayList<>(); + + // If the transfer method is PULL-style, + // then search dependable mediator object. + collectDependableObjectNodeOnPullTransfer(nodes, callEdge); + + return nodes; + } + + /** + * CFDを実行する. + * + * @param delegatingEdge 委譲するコントロールフロー + * @param dstObjNode 呼び出し元となるノード + */ + public void delegateCallEdge(CallEdge delegatingEdge, final ObjectNode dstObjNode) { + ObjectNode srcObjNode = (ObjectNode) delegatingEdge.getDestination(); + if (srcObjNode == null) throw new ClassCastException(); + + delegatingEdge.getSource().removeOutEdge(delegatingEdge); + srcObjNode.removeInEdge(delegatingEdge); + + // Reconnecting the edge to the new source object. + delegatingEdge.setDestination(srcObjNode); + delegatingEdge.setSource(dstObjNode); + + srcObjNode.addInEdge(delegatingEdge); + dstObjNode.addOutEdge(delegatingEdge); + } + + /** + * PUSH/PULLで共通するCFD適用可能なノードを再帰的に探索する + * + * @param nodes 適用可能なノードのリスト + * @param curObjNode + * @param delegatingObjNode + */ + private void collectCommonTransferNodes(List nodes, ObjectNode curObjNode, final ObjectNode delegatingObjNode) { + if (!hasChildrenNode(curObjNode)) return; + + for (Edge e : curObjNode.getOutEdges()) { + ObjectNode foundNode = (ObjectNode) e.getDestination(); + + if (foundNode.equals(delegatingObjNode)) continue; + + nodes.add(foundNode); + collectCommonTransferNodes(nodes, foundNode, delegatingObjNode); + } + } + + /** + * 順序が必要なPUSHのコントロールフローグラフ上のCFD適用可能範囲を再帰的に探索する. + * (自身の親となるノードを再帰的に探索する) + * + * @param nodes + * @param curObjNode + * @param parentDelegatingNode + */ + private void collectParentNodesOnPushTransfer(List nodes, ObjectNode curObjNode, final ObjectNode parentDelegatingNode) { + if (isRootNode(curObjNode)) return; + if (isInEdgesConversingToNode(curObjNode)) return; + + ObjectNode parentObjNode = (ObjectNode) curObjNode.getInEdge(0).getSource(); + if (parentObjNode == null) return; + + if (!parentDelegatingNode.equals(parentObjNode)) + nodes.add(parentObjNode); + + int inEdgeCallOrder = parentObjNode.getOutEdgeCallOrder(curObjNode.getInEdge(0)); + for (Edge edge : parentObjNode.getOutEdges()) { + if (!(edge instanceof CallEdge)) continue; + + int callOrder = parentObjNode.getOutEdgeCallOrder((CallEdge) edge); + if (inEdgeCallOrder < callOrder) collectChildrenNodesOnPushTransfer(nodes, (CallEdge) edge); + } + + collectParentNodesOnPushTransfer(nodes, parentObjNode, parentDelegatingNode); + } + + /** + * (あるコントロールフローについて, その呼び出し順よりも若いノードを, 呼び出し先を辿ることで再帰的に探索する) + * + * @param nodes + * @param callEdge + */ + private void collectChildrenNodesOnPushTransfer(List nodes, CallEdge callEdge) { + ObjectNode dstObjNode = (ObjectNode) callEdge.getDestination(); + if (dstObjNode == null) return; + + nodes.add(dstObjNode); + + if (!hasChildrenNode(dstObjNode)) return; + + for (Edge e : dstObjNode.getOutEdges()) { + CallEdge edge = (CallEdge) e; + if (edge == null) continue; + + ObjectNode foundNode = (ObjectNode) e.getDestination(); + if (foundNode == null) continue; + if (nodes.contains(foundNode)) continue; + + collectChildrenNodesOnPushTransfer(nodes, edge); + } + } + + /** + * Add mediator object nodes collected from the given call edge to the given {@code nodes} + * + * @param nodes list of nodes which the collected nodes added to + * @param callEdge the call edge which maybe contain a mediator objects in it + */ + private void collectDependableObjectNodeOnPullTransfer(List nodes, CallEdge callEdge) { + if (callEdge.getSelectedOption() != PushPullValue.PULL) return; + + // エッジの両端が状態を持つノード(リソース)か? + StatefulObjectNode srcObjNode = (callEdge.getSource() instanceof StatefulObjectNode) + ? (StatefulObjectNode) callEdge.getSource() + : null; + + StatefulObjectNode dstObjNode = (callEdge.getDestination() instanceof StatefulObjectNode) + ? (StatefulObjectNode) callEdge.getDestination() + : null; + + if (srcObjNode == null || dstObjNode == null) return; + + // 呼び出し先が仲介者オブジェクトか? + for (Edge inEdge : dstObjNode.getInEdges()) { + CallEdge inCallEdge = (CallEdge) inEdge; + if (inCallEdge == null) continue; + + // 状態を持たないオブジェクトか?(仲介者オブジェクトか?) + ObjectNode inSrcObjNode = (inCallEdge.getSource() instanceof ObjectNode) + ? (ObjectNode) inCallEdge.getSource() + : null; + if (inSrcObjNode == null) continue; + + if (inSrcObjNode instanceof StatefulObjectNode) continue; + if (inSrcObjNode instanceof EventChannelObjectNode) continue; + + nodes.add(inSrcObjNode); + } + } + + /** + * Indicates whether the given object node is in the root or not. + * + * @param node the object node to be checked + * @return {@code true} if the given object node is in the root, otherwise {@code false} + */ + private boolean isRootNode(final ObjectNode node) { + return node.getInEdges().isEmpty(); + } + + /** + * Indicates whether the given object node has child nodes. + * + * @param node the object node to be checked + * @return {@code true} if the given object node has child nodes, otherwise {@code false} + */ + private boolean hasChildrenNode(final ObjectNode node) { + if (node.getOutEdges().size() < 1) return false; + return true; + } + + private boolean isInEdgesConversingToNode(final ObjectNode node) { + return (1 < node.getInEdges().size()); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/ControlFlowGraph.java b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/ControlFlowGraph.java new file mode 100644 index 0000000..a569946 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/ControlFlowGraph.java @@ -0,0 +1,182 @@ +package models.controlFlowModel; + +import generators.CodeGenerator; +import models.DirectedGraph; +import models.Edge; +import models.Node; +import models.algebra.Expression; +import models.algebra.Term; +import models.algebra.Variable; +import models.dataConstraintModel.Channel; +import models.dataConstraintModel.ChannelMember; +import models.dataFlowModel.*; + +import java.util.*; + +public class ControlFlowGraph extends DirectedGraph implements IFlowGraph { + private final DataFlowGraph dataFlowGraph; + private final CallGraph pushCallGraph; + private final CallGraph pullCallGraph; + + public ControlFlowGraph(DataFlowGraph dataFlowGraph, DataTransferModel model) { + this.dataFlowGraph = dataFlowGraph; + this.pushCallGraph = new CallGraph(); + this.pullCallGraph = new CallGraph(); + + for (Edge edge : dataFlowGraph.getEdges()) { + if (((DataFlowEdge) edge).isChannelToResource()) { + continue; + } + DataFlowEdge resourceToChannelEdge = (DataFlowEdge) edge; + PushPullAttribute edgeAttribute = ((PushPullAttribute) resourceToChannelEdge.getAttribute()); + ChannelNode middleChannelNode = (ChannelNode) resourceToChannelEdge.getDestination(); + + // Should take into account the channel hierarchy. + Set ancestorDstChannels = middleChannelNode.getAncestors(); + Set descendantDstChannels = middleChannelNode.getDescendants(); + Set outEdges = new HashSet<>(); + outEdges.addAll(middleChannelNode.getOutEdges()); + for (ChannelNode ancestorDst : ancestorDstChannels) { + outEdges.addAll(ancestorDst.getOutEdges()); + } + for (ChannelNode descendantDst : descendantDstChannels) { + outEdges.addAll(descendantDst.getOutEdges()); + } + for (Edge channelToResourceEdge : outEdges) { + ResourceNode sourceNode = (ResourceNode) resourceToChannelEdge.getSource(); + ResourceNode destinationNode = (ResourceNode) channelToResourceEdge.getDestination(); + if (!CodeGenerator.generatesComponent(sourceNode.getResourceHierarchy())) { // sourceNode is a leaf node + sourceNode = sourceNode.getParent(); // change sourceNode to its parent (It must correspond to a component resource) + } + if (!CodeGenerator.generatesComponent(destinationNode.getResourceHierarchy())) { // destinationNode is a leaf node + destinationNode = destinationNode.getParent(); // change destinationNode to its parent (It must correspond to a component resource) + } + + if (sourceNode.getResourceHierarchy().equals(destinationNode.getResourceHierarchy())) { + continue; + } + + if (edgeAttribute.getOptions().get(0) == PushPullValue.PUSH) { + createEdgeChain(pushCallGraph, sourceNode, destinationNode, PushPullValue.PUSH); + } else { + createEdgeChain(pullCallGraph, destinationNode, sourceNode, PushPullValue.PULL); + } + } + } + + for (Channel channel : model.getInputChannels()) { + DataTransferChannel eventChannel = (DataTransferChannel) channel; + EventChannelObjectNode sourceNode = new EventChannelObjectNode(eventChannel); + + for (ChannelMember member : eventChannel.getChannelMembers()) { + // Set up source node name + if (sourceNode.getName() == null) { + Expression exp = member.getStateTransition().getMessageExpression(); + if (exp instanceof Term) { + sourceNode.setName(((Term) exp).getSymbol().getName()); + } else if (exp instanceof Variable) { + sourceNode.setName(((Variable) exp).getName()); + } + } + + for (ResourceNode resourceNode : dataFlowGraph.getResourceNodes(member.getResource().getResourceHierarchy())) { + if (!CodeGenerator.generatesComponent(resourceNode.getResourceHierarchy())) { + resourceNode = resourceNode.getParent(); + } + createEdgeChain(pushCallGraph, sourceNode, resourceNode, PushPullValue.PUSH); + } + } + } + } + + public DataFlowGraph getDataFlowGraph() { + return dataFlowGraph; + } + + public CallGraph getPushCallGraph() { + return pushCallGraph; + } + + public CallGraph getPullCallGraph() { + return pullCallGraph; + } + + @Override + public Map> getAllComponentNodes() { + Map> allNodeSets = new HashMap<>(); + for (Node n : pushCallGraph.getNodes()) { + Set nodeSet = new HashSet<>(); + nodeSet.add(n); + allNodeSets.put(n, nodeSet); + } + for (Node n : pullCallGraph.getNodes()) { + if (n instanceof StatefulObjectNode) { + ResourceNode resNode = ((StatefulObjectNode) n).getResource(); + Set nodeSet = null; + if (pushCallGraph.getStatefulObjectNode(resNode) != null) { + // Merge a stateful object node in the push call graph and that in the pull call graph. + nodeSet = allNodeSets.get(pushCallGraph.getStatefulObjectNode(resNode)); + } else { + nodeSet = new HashSet<>(); + } + nodeSet.add(n); + allNodeSets.put(n, nodeSet); + } else { + Set nodeSet = new HashSet<>(); + nodeSet.add(n); + allNodeSets.put(n, nodeSet); + } + } + return allNodeSets; + } + + // TODO: エッジだけでけくノードも一緒に追加する + private void createEdgeChain(CallGraph callGraph, ResourceNode sourceNode, ResourceNode destinationNode, PushPullValue option) { + callGraph.addAncestorNodes(sourceNode); + callGraph.addDescendantNodes(sourceNode); + callGraph.addAncestorNodes(destinationNode); + callGraph.addDescendantNodes(destinationNode); + + Deque path = new ArrayDeque<>(); + ResourceNode currentNode = destinationNode; + while (currentNode != null && currentNode != sourceNode) { + path.push(currentNode); + currentNode = currentNode.getParent(); + } + path.push(sourceNode); + + // path = [destination, parent, ..., root of destination, source] + while (!path.isEmpty()) { + ResourceNode from = path.pop(); + ResourceNode to = path.peek(); + if (to == null) { + continue; + } + callGraph.addEdge(from, to, option); + } + } + + private void createEdgeChain(CallGraph callGraph, EventChannelObjectNode sourceNode, ResourceNode destinationNode, PushPullValue option) { + callGraph.addAncestorNodes(destinationNode); + callGraph.addDescendantNodes(destinationNode); + + Deque path = new ArrayDeque<>(); + + StatefulObjectNode currentNode = callGraph.getStatefulObjectNode(destinationNode); + while (currentNode != null) { + path.push(currentNode); + currentNode = callGraph.getStatefulObjectNode(currentNode.getResource().getParent()); + } + path.push(sourceNode); + + // path = [destination, parent, ..., root of destination, source] + while (!path.isEmpty()) { + ObjectNode from = path.pop(); + ObjectNode to = path.peek(); + if (to == null) { + continue; + } + callGraph.insertEdge(from, to, option, 0); // TODO: Set a call order properly + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/EventChannelObjectNode.java b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/EventChannelObjectNode.java new file mode 100644 index 0000000..f9011bb --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/EventChannelObjectNode.java @@ -0,0 +1,25 @@ +package models.controlFlowModel; + +import models.dataFlowModel.DataTransferChannel; + +public class EventChannelObjectNode extends ObjectNode { + DataTransferChannel eventChannel = null; + + public EventChannelObjectNode(DataTransferChannel ioChannel) { + super(null); + this.eventChannel = ioChannel; + } + + public EventChannelObjectNode(String name, DataTransferChannel ioChannel) { + super(name); + this.eventChannel = ioChannel; + } + + public DataTransferChannel getIOChannel() { + return eventChannel; + } + + public void setIOChannel(DataTransferChannel ioChannel) { + this.eventChannel = ioChannel; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/ObjectNode.java b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/ObjectNode.java new file mode 100644 index 0000000..7af6a3b --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/ObjectNode.java @@ -0,0 +1,88 @@ +package models.controlFlowModel; + +import models.Edge; +import models.Node; + +import java.util.ArrayList; +import java.util.List; + +public class ObjectNode extends Node { + protected String name = ""; + + public ObjectNode(String name) { + inEdges = new ArrayList<>(); + outEdges = new ArrayList<>(); + + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + /** + * Get the specific edge that connects to the input port of the node. + * + * @param i the index of the edge in the input edges + * @return the edge + */ + public CallEdge getInEdge(int i) { + return (CallEdge) ((List) inEdges).get(i); + } + + /** + * Get the specific edge that connects to the output port of the node. + * + * @param i the index of the edge in the output edges + * @return the edge + */ + public CallEdge getOutEdge(int i) { + return (CallEdge) ((List) outEdges).get(i); + } + + public CallEdge findEdgeInInEdges(final CallEdge edge) { + for (Edge e : inEdges) { + if (e instanceof CallEdge) return (CallEdge) e; + } + return null; + } + + public void insertOutEdge(CallEdge edge, int n) { + ((List) outEdges).add(n, edge); + } + + public int getChildrenNum() { + return outEdges.size(); + } + + public int getOutEdgeCallOrder(final CallEdge callEdge) { + for (int i = 0; i < outEdges.size(); i++) { + if (callEdge.equals(getOutEdge(i))) return i; + } + return -1; + } + + public ObjectNode getChildren(int i) { + return (ObjectNode) ((List) outEdges).get(i).getDestination(); + } + + /** + * Change the call order of specified output edge. + * + * @param curOrder Current call order + * @param newCallOrder New call order + */ + public void sortOutEdgesByCallOrder(final int curOrder, final int newCallOrder) { + ArrayList edges = ((ArrayList) outEdges); + Edge selectedEdge = ((List) outEdges).get(curOrder); + + Edge tempEdge = ((List) outEdges).get(newCallOrder - 1); + + edges.set(newCallOrder - 1, selectedEdge); + edges.set(curOrder, tempEdge); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/ObjectNodeAttribute.java b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/ObjectNodeAttribute.java new file mode 100644 index 0000000..0930054 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/ObjectNodeAttribute.java @@ -0,0 +1,90 @@ +package models.controlFlowModel; + +import models.NodeAttribute; + +/** + * {@link ObjectNodeAttribute} represents an attribute that the linked object node in a control-flow graph has. + */ +public class ObjectNodeAttribute extends NodeAttribute { + private final ObjectNode objectNode; + private String shapeStyle = ""; + + public ObjectNodeAttribute(final ObjectNode objectNode) { + this.objectNode = objectNode; + this.objectNode.setAttribute(this); + + // Setting a shape style of cell + if (objectNode instanceof StatefulObjectNode) { + shapeStyle = "shape=ellipse;perimeter=ellipsePerimeter;"; + } else if (objectNode instanceof EventChannelObjectNode) { + shapeStyle = "shape=rectangle;perimeter=rectanglePerimeter;"; + } else { + shapeStyle = "shape=hexagon;perimeter=hexagonPerimeter;"; + } + + // Setting a name of a cell + if (objectNode.name != null) return; +// if (objectNode.name.isEmpty()) return; + + if (objectNode instanceof StatefulObjectNode) { + objectNode.name = objectNode.getName(); + } else if (objectNode instanceof EventChannelObjectNode) { + objectNode.name = "entryPoint"; + } + } + + /** + * Get an object node that the attribute is linked to. + * + * @return an object node that the attribute is linked to. + */ + public ObjectNode getObjectNode() { + return objectNode; + } + + public String getDefaultStyle() { + String style = ";"; + + return shapeStyle + style; + } + + /** + * Get the color of a selected cell. + */ + public String getEnableStyle() { + String style = "fillColor=#7fffd4;"; + style += "strokeColor=#66cdaa;"; + style += "strokeWidth=2;"; + + return shapeStyle + style; + } + + /** + * Get the color of an unselected cell. + */ + public String getDisableStyle() { + String style = "fillColor=#999999"; + + return shapeStyle + style; + } + + /** + * Get the color of a cell that can be delegated. + */ + public String getDelegatingStyle() { + String style = "strokeWidth=4;"; + style += "strokeColor=#4169e;"; + + return shapeStyle + style; + } + + /** + * Get the cell's name that displayed on GUI + */ + @Override + public String toString() { + return objectNode instanceof StatefulObjectNode + ? ((StatefulObjectNode) objectNode).getResource().getResourceName() + : objectNode.getName(); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/StatefulObjectNode.java b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/StatefulObjectNode.java new file mode 100644 index 0000000..1c0015a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/StatefulObjectNode.java @@ -0,0 +1,28 @@ +package models.controlFlowModel; + +import java.util.HashSet; +import java.util.Set; + +import models.dataFlowModel.ResourceNode; + +public class StatefulObjectNode extends ObjectNode { + private ResourceNode resource; + private Set children = new HashSet<>(); + + public StatefulObjectNode(ResourceNode resource) { + super(resource.getResourceName()); + this.resource = resource; + } + + public ResourceNode getResource() { + return resource; + } + + public void addChild(ObjectNode child) { + children.add(child); + } + + public Set getChildren() { + return children; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Channel.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Channel.java index 8bf74da..218cf80 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Channel.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Channel.java @@ -1,84 +1,180 @@ package models.dataConstraintModel; -import java.util.HashSet; -import java.util.Set; - +import models.algebra.Expression; import models.algebra.Variable; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + 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<>(); - for (Variable var: variables) { + 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)); } channelMembers = new HashSet<>(); } - + public String getChannelName() { 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; + child.refreshOutside(); + } + + 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); } - + + public List getAllSelectorVariables() { + List allSelectorVariables = new ArrayList<>(); + if (parent != null) allSelectorVariables.addAll(parent.getAllSelectorVariables()); + for (Selector sel : selectors) { + allSelectorVariables.add(sel.getExpression()); + } + return allSelectorVariables; + } + public Set getChannelMembers() { return channelMembers; } - + 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); + } + } + if (isNative) { + channelMember.getResource().getResourceHierarchy().setNative(true); } } - public void removeChannelMember(ResourcePath id) { - for (ChannelMember cm: channelMembers) { - if (cm.getResource() == id) { + public void refreshOutside() { + for (ChannelMember channelMember : channelMembers) { + 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 { + channelMember.setOutside(false); + } + } else { + if (channelMember.getResource().getPathParams().size() == 0) { + channelMember.setOutside(false); + } else { + channelMember.setOutside(true); + } + } + } + } + + public void removeChannelMember(ResourcePath res) { + for (ChannelMember cm : channelMembers) { + if (cm.getResource() == res) { channelMembers.remove(cm); break; } } } - + public Set getResources() { Set resources = new HashSet<>(); - for (ChannelMember member: channelMembers) { + for (ChannelMember member : channelMembers) { resources.add(member.getResource()); } return resources; } + public boolean isNative() { + return isNative; + } + + public void setNative(boolean isNative) { + this.isNative = isNative; + for (ChannelMember member : channelMembers) { + member.getResource().getResourceHierarchy().setNative(isNative); + } + } + public String toString() { return channelName; } diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ChannelMember.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ChannelMember.java index 190a49f..b6dc4ee 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ChannelMember.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ChannelMember.java @@ -1,58 +1,50 @@ package models.dataConstraintModel; -import java.util.ArrayList; -import java.util.List; - 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() { return resourcePath; } - + public void setResource(ResourcePath resourcePath) { 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; } - + public void setStateTransition(StateTransition stateTransition) { 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.toString() + "(" + stateTransition.getCurStateExpression() + "," + stateTransition.getMessageExpression() + ")"; } - return resourcePath.getResourceName() + "(" - + stateTransition.getCurStateExpression() + "," - + stateTransition.getMessageExpression() + ")" - + " == " + stateTransition.getNextStateExpression(); + return resourcePath.toString() + "(" + + stateTransition.getCurStateExpression() + "," + + stateTransition.getMessageExpression() + ")" + + " = " + stateTransition.getNextStateExpression(); } } diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/DataConstraintModel.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/DataConstraintModel.java index 35b2aa7..6421a5c 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/DataConstraintModel.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/DataConstraintModel.java @@ -1,16 +1,15 @@ package models.dataConstraintModel; -import java.util.Collection; -import java.util.HashMap; - -import models.algebra.Symbol; -import models.algebra.Type; +import models.algebra.*; import parser.Parser; +import java.util.*; + 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"); @@ -18,7 +17,21 @@ public static final Type typeFloat = new Type("Float", "float", typeInt); public static final Type typeDouble = new Type("Double", "double", typeFloat); public static final Type typeBoolean = new Type("Bool", "boolean"); - public static final Type typeString = new Type("Str", "String"); + public static final Type typeString = new Type("Str", "String") { + public String valueToRepresentation(Object value) { + if (value instanceof String) { + return Parser.DOUBLE_QUOT + (String) value + Parser.DOUBLE_QUOT; + } + return value.toString(); + } + + public Object representationToValue(String representation) { + if (representation.startsWith(Parser.DOUBLE_QUOT) && representation.endsWith(Parser.DOUBLE_QUOT)) { + return representation.substring(1, representation.length() - 1); + } + return representation; + } + }; public static final Type typeList = new Type("List", "ArrayList", "List"); public static final Type typeListInt = new Type("List", "ArrayList<>", "List", typeList); public static final Type typeListStr = new Type("List", "ArrayList<>", "List", typeList); @@ -27,22 +40,730 @@ 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 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 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).getClass() == Constant.class)) { + return null; + } + if (!(args.get(1).getClass() == Constant.class)) { + 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() != null && arg1.getType() != null) { + 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); + } else if (arg0.getType().equals(typeString) || arg1.getType().equals(typeString)) { + return new Constant((String) arg0.getValue() + (String) arg1.getValue(), typeString); + } + } + if (sArg0.contains(Parser.DOT) || sArg1.contains(Parser.DOT)) { + return new Constant(Double.toString(Double.parseDouble(sArg0) + Double.parseDouble(sArg1)), typeDouble); + } else { + return new Constant(Integer.toString(Integer.parseInt(sArg0) + Integer.parseInt(sArg1)), typeInt); + } + } + }); + 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).getClass() == Constant.class)) { + return null; + } + if (!(args.get(1).getClass() == Constant.class)) { + 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() != null && arg1.getType() != null) { + 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); + } + } + if (sArg0.contains(Parser.DOT) || sArg1.contains(Parser.DOT)) { + return new Constant(Double.toString(Double.parseDouble(sArg0) * Double.parseDouble(sArg1)), typeDouble); + } else { + return new Constant(Integer.toString(Integer.parseInt(sArg0) * Integer.parseInt(sArg1)), typeInt); + } + } + }); + 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).getClass() == Constant.class)) { + return null; + } + if (!(args.get(1).getClass() == Constant.class)) { + 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() != null && arg1.getType() != null) { + 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); + } + } + if (sArg0.contains(Parser.DOT) || sArg1.contains(Parser.DOT)) { + return new Constant(Double.toString(Double.parseDouble(sArg0) - Double.parseDouble(sArg1)), typeDouble); + } else { + return new Constant(Integer.toString(Integer.parseInt(sArg0) - Integer.parseInt(sArg1)), typeInt); + } + } + }); + 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).getClass() == Constant.class)) { + return null; + } + if (!(args.get(1).getClass() == Constant.class)) { + 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() != null && arg1.getType() != null) { + 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); + } + } + if (sArg0.contains(Parser.DOT) || sArg1.contains(Parser.DOT)) { + return new Constant(Double.toString(Double.parseDouble(sArg0) / Double.parseDouble(sArg1)), typeDouble); + } else { + return new Constant(Integer.toString(Integer.parseInt(sArg0) / Integer.parseInt(sArg1)), typeInt); + } + } + }); + 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).getClass() == Constant.class)) { + return null; + } + if (!(args.get(1).getClass() == Constant.class)) { + 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() != null && arg1.getType() != null) { + 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).getClass() == Constant.class)) { + return null; + } + Constant arg = (Constant) args.get(0); + String sArg = arg.getSymbol().toString(); + if (arg.getType() != null) { + 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); + } + } + if (sArg.contains(Parser.DOT)) { + return new Constant(Double.toString(-Double.parseDouble(sArg)), typeDouble); + } else { + return new Constant(Integer.toString(-Integer.parseInt(sArg)), typeInt); + } + } + }); + public static final Symbol eq = new Symbol(Parser.EQ, 2, Symbol.Type.INFIX, new Symbol.IImplGenerator() { + @Override + public String generate(Type type, Type[] childrenTypes, String[] children, String[] childrenSideEffects, String[] sideEffect) { + for (String s : childrenSideEffects) { + sideEffect[0] += s; + } + if (childrenTypes[0] != null && childrenTypes[0].equals(typeString) + && childrenTypes[1] != null && childrenTypes[1].equals(typeString)) { + return children[0] + ".equals(" + children[1] + ")"; + } + return "(" + children[0] + "==" + children[1] + ")"; + } + }, new Symbol.ICalculator() { + @Override + public Expression calculate(List args) { + if (!(args.get(0).getClass() == Constant.class)) { + return null; + } + if (!(args.get(1).getClass() == Constant.class)) { + 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() == null || arg1.getType() == null) { + if (sArg0.contains(Parser.DOT) || sArg1.contains(Parser.DOT)) { + result = Double.parseDouble(sArg0) == Double.parseDouble(sArg1); + } else { + result = Integer.parseInt(sArg0) == Integer.parseInt(sArg1); + } + } + 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)); + } else if (arg0.getType().equals(typeString) || arg1.getType().equals(typeString)) { + result = sArg0.toString().equals(sArg1.toString()); + } + if (result) { + return new Constant(true_); + } else { + return new Constant(false_); + } + } + }); + public static final Symbol neq = new Symbol(Parser.NEQ, 2, Symbol.Type.INFIX, new Symbol.IImplGenerator() { + @Override + public String generate(Type type, Type[] childrenTypes, String[] children, String[] childrenSideEffects, String[] sideEffect) { + for (String s : childrenSideEffects) { + sideEffect[0] += s; + } + if (childrenTypes[0] != null && childrenTypes[0].equals(typeString) + && childrenTypes[1] != null && childrenTypes[1].equals(typeString)) { + return "!" + children[0] + ".equals(" + children[1] + ")"; + } + return "(" + children[0] + "!=" + children[1] + ")"; + } + }, new Symbol.ICalculator() { + @Override + public Expression calculate(List args) { + if (!(args.get(0).getClass() == Constant.class)) { + return null; + } + if (!(args.get(1).getClass() == Constant.class)) { + 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() == null || arg1.getType() == null) { + if (sArg0.contains(Parser.DOT) || sArg1.contains(Parser.DOT)) { + result = Double.parseDouble(sArg0) != Double.parseDouble(sArg1); + } else { + result = Integer.parseInt(sArg0) != Integer.parseInt(sArg1); + } + } + 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)); + } else if (arg0.getType().equals(typeString) || arg1.getType().equals(typeString)) { + result = !(sArg0.toString().equals(sArg1.toString())); + } + 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).getClass() == Constant.class)) { + return null; + } + if (!(args.get(1).getClass() == Constant.class)) { + 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() == null || arg1.getType() == null) { + if (sArg0.contains(Parser.DOT) || sArg1.contains(Parser.DOT)) { + result = Double.parseDouble(sArg0) > Double.parseDouble(sArg1); + } else { + result = Integer.parseInt(sArg0) > Integer.parseInt(sArg1); + } + } + 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).getClass() == Constant.class)) { + return null; + } + if (!(args.get(1).getClass() == Constant.class)) { + 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() == null || arg1.getType() == null) { + if (sArg0.contains(Parser.DOT) || sArg1.contains(Parser.DOT)) { + result = Double.parseDouble(sArg0) < Double.parseDouble(sArg1); + } else { + result = Integer.parseInt(sArg0) < Integer.parseInt(sArg1); + } + } + 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).getClass() == Constant.class)) { + return null; + } + if (!(args.get(1).getClass() == Constant.class)) { + 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() == null || arg1.getType() == null) { + if (sArg0.contains(Parser.DOT) || sArg1.contains(Parser.DOT)) { + result = Double.parseDouble(sArg0) >= Double.parseDouble(sArg1); + } else { + result = Integer.parseInt(sArg0) >= Integer.parseInt(sArg1); + } + } + 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).getClass() == Constant.class)) { + return null; + } + if (!(args.get(1).getClass() == Constant.class)) { + 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() == null || arg1.getType() == null) { + if (sArg0.contains(Parser.DOT) || sArg1.contains(Parser.DOT)) { + result = Double.parseDouble(sArg0) <= Double.parseDouble(sArg1); + } else { + result = Integer.parseInt(sArg0) <= Integer.parseInt(sArg1); + } + } + 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).getClass() == Constant.class)) { + return null; + } + if (!(args.get(1).getClass() == Constant.class)) { + 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() == null || arg1.getType() == null) return null; + 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).getClass() == Constant.class)) { + return null; + } + if (!(args.get(1).getClass() == Constant.class)) { + 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() == null || arg1.getType() == null) return null; + 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).getClass() == Constant.class)) { + return null; + } + Constant arg0 = (Constant) args.get(0); + String sArg0 = arg0.getSymbol().toString(); + boolean result = false; + if (arg0.getType() == null) return null; + 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); // Don't calculate here (Calculated in simulator). + public static final Symbol remove = new Symbol("remove", 2, Symbol.Type.PREFIX, "remove", Symbol.Type.METHOD_WITH_SIDE_EFFECT); // Don't calculate here (Calculated in simulator). 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 nil = new Symbol("nil", 0, Symbol.Type.PREFIX, new Symbol.IImplGenerator() { + public static final Symbol length = new Symbol("length", 1, Symbol.Type.PREFIX, "($x)->$x.size()", Symbol.Type.LAMBDA, new Symbol.ICalculator() { @Override - public String generate(Type type, String[] children, String[] childrenSideEffects, String[] sideEffect) { + public Expression calculate(List args) { + if (args.get(0).getClass() == Constant.class && ((Constant) args.get(0)).getSymbol().equals(nil)) { + return new Constant("0", typeInt); + } + if (args.get(0) instanceof Term) { + Term term = (Term) args.get(0); + Type type = term.getType(); + if (typeList.isAncestorOf(type)) { + int len = 0; + while (term.getSymbol().equals(DataConstraintModel.append)) { + len++; + term = (Term) term.getChild(0); + } + if (term instanceof ListTerm) { + len += ((ListTerm) term).getChildren().size(); + return new Constant(Integer.toString(len), typeInt); + } + } else if (typeMap.isAncestorOf(type)) { + HashSet keySet = new HashSet<>(); + while (term.getSymbol().equals(DataConstraintModel.insert)) { + if (term.getChild(1).getClass() == Constant.class) { + keySet.add((String) ((Constant) term.getChild(1)).getValue()); + } + term = (Term) term.getChild(0); + } + if (term instanceof MapTerm) { + keySet.addAll(((MapTerm) term).keySet()); + return new Constant(Integer.toString(keySet.size()), typeInt); + } + } else if (typeJson.isAncestorOf(type)) { + HashSet keySet = new HashSet<>(); + while (term.getSymbol().equals(DataConstraintModel.addMember)) { + if (term.getChild(1).getClass() == Constant.class) { + keySet.add((String) ((Constant) term.getChild(1)).getValue()); + } + term = (Term) term.getChild(0); + } + if (term instanceof JsonTerm) { + keySet.addAll(((JsonTerm) term).keySet()); + return new Constant(Integer.toString(keySet.size()), typeInt); + } + } else if (typeString.isAncestorOf(type)) { + int len = 0; + ArrayDeque strTerms = new ArrayDeque<>(); + strTerms.add(term); + while (strTerms.size() > 0) { + term = strTerms.poll(); + type = term.getType(); + if (term.getSymbol().equals(DataConstraintModel.add)) { + strTerms.add((Term) term.getChild(0)); + strTerms.add((Term) term.getChild(1)); + } else if (term instanceof Constant && typeString.isAncestorOf(type)) { + len += ((String) ((Constant) term).getValue()).length(); + } + } + return new Constant(Integer.toString(len), typeInt); + } + } + return null; + } + }); + public static final Symbol get = new Symbol("get", 2, Symbol.Type.PREFIX, "get", Symbol.Type.METHOD, new Symbol.ICalculator() { + @Override + public Expression calculate(List args) { + if (args.get(1).getClass() == Constant.class && ((Constant) args.get(1)).getType().equals(typeInt)) { + int idx = Integer.parseInt(((Constant) args.get(1)).toString()); + if (args.get(0) instanceof Term) { + Term term = (Term) args.get(0); + Type type = term.getType(); + if (typeList.isAncestorOf(type)) { + Stack appendedChldren = new Stack<>(); + while (term.getSymbol().equals(DataConstraintModel.append)) { + if (!(term.getChild(0) instanceof Term)) { + return null; + } + appendedChldren.push(term.getChild(1)); + term = (Term) term.getChild(0); + } + if (term instanceof ListTerm) { + ListTerm listTerm = (ListTerm) term.clone(); + for (Expression child : appendedChldren) { + listTerm.append(child); + } + return listTerm.get(idx); + } + } + } + } + return null; + } + }); + public static final Symbol set = new Symbol("set", 3, Symbol.Type.PREFIX, "set", Symbol.Type.METHOD_WITH_SIDE_EFFECT); // Don't calculate here (Calculated in simulator). + 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).getClass() == Constant.class && ((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) { + String key; + if (args.get(1) instanceof Constant) { + key = (String) ((Constant) args.get(1)).getValue(); + } else { + key = args.get(1).toString(); + } + if (((MapTerm) term).keySet().contains(key)) { + 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) { + String key; + if (args.get(1) instanceof Constant) { + key = (String) ((Constant) args.get(1)).getValue(); + } else { + key = args.get(1).toString(); + } + if (((JsonTerm) term).keySet().contains(key)) { + return new Constant(true_); + } + return new Constant(false_); + } + } + } + return null; + } + }); + public static final Symbol indexOf = new Symbol("indexOf", 2, Symbol.Type.PREFIX, "indexOf", Symbol.Type.METHOD, new Symbol.ICalculator() { + @Override + public Expression calculate(List args) { + if (args.get(0) instanceof Term) { + Term term = (Term) args.get(0); + Type type = term.getType(); + if (typeList.isAncestorOf(type)) { + Stack appendedChldren = new Stack<>(); + while (term.getSymbol().equals(DataConstraintModel.append)) { + if (!(term.getChild(0) instanceof Term)) { + return null; + } + appendedChldren.push(term.getChild(1)); + term = (Term) term.getChild(0); + } + if (term instanceof ListTerm) { + int idx = 0; + ListTerm listTerm = (ListTerm) term; + for (Expression child : listTerm.getChildren()) { + if (child.equals(args.get(1))) { + return new Constant(Integer.toString(idx), typeInt); + } + idx++; + } + for (Expression child : appendedChldren) { + if (child.equals(args.get(1))) { + return new Constant(Integer.toString(idx), typeInt); + } + idx++; + } + } + } + } + return null; + } + }); + public static final Symbol nil = new Symbol("nil", 0, Symbol.Type.PREFIX, new Symbol.IImplGenerator() { + final int count[] = {0}; + + @Override + public String generate(Type type, Type[] childrenTypes, String[] children, String[] childrenSideEffects, String[] sideEffect) { String compType = ""; if (type != null) { String interfaceType = type.getInterfaceTypeName(); @@ -53,27 +774,42 @@ 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" + count[0]; + if (sideEffect[0] == null) { + sideEffect[0] = ""; + } + sideEffect[0] += interfaceType + " " + temp + " = " + "new " + implType + "<" + compType + ">();\n"; + count[0]++; + return temp; + } + } return "new ArrayList<" + compType + ">()"; } - }); + }, true); 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 = ""; + String impl = ""; impl += type.getInterfaceTypeName() + " " + temp + ";\n"; if (childrenSideEffects[0] != null && childrenSideEffects[0].length() > 0) impl += childrenSideEffects[0]; - impl += "if (" + children[0] + ") {\n"; - if (childrenSideEffects[1] != null && childrenSideEffects[1].length() > 0) impl += "\t" + childrenSideEffects[1]; - impl += "\t" + temp + " = " + children[1] + ";\n"; + impl += "if (" + childrenImpl[0] + ") {\n"; + if (childrenSideEffects[1] != null && childrenSideEffects[1].length() > 0) + impl += "\t" + childrenSideEffects[1]; + 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"; + if (childrenSideEffects[2] != null && childrenSideEffects[2].length() > 0) + impl += "\t" + childrenSideEffects[2]; + impl += "\t" + temp + " = " + childrenImpl[2] + ";\n"; impl += "}\n"; sideEffect[0] += impl; @@ -81,25 +817,22 @@ count[0]++; return temp; } + }, new Symbol.ICalculator() { + @Override + public Expression calculate(List args) { + if (!(args.get(0).getClass() == Constant.class)) 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) { - for (String s: childrenSideEffects) { + public String generate(Type type, Type[] childrenTypes, String[] childrenImpl, String[] childrenSideEffects, String[] sideEffect) { + for (String s : childrenSideEffects) { sideEffect[0] += s; } String impl = "new Pair<>(" + childrenImpl[0] + "," + childrenImpl[1] + ")"; @@ -108,8 +841,8 @@ }); 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) { - for (String s: childrenSideEffects) { + public String generate(Type type, Type[] childrenTypes, String[] childrenImpl, String[] childrenSideEffects, String[] sideEffect) { + for (String s : childrenSideEffects) { sideEffect[0] += s; } String impl = "new AbstractMap.SimpleEntry<>(" + childrenImpl[0] + "$x)"; @@ -124,67 +857,149 @@ public static final Symbol snd = new Symbol("snd", 1, Symbol.Type.PREFIX, "getValue", Symbol.Type.METHOD); 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}; + public static final Symbol insert = new Symbol("insert", 3, Symbol.Type.PREFIX, "put", Symbol.Type.METHOD_WITH_SIDE_EFFECT); // Don't calculate here (Calculated in simulator). + public static final Symbol delete = new Symbol("delete", 2, Symbol.Type.PREFIX, "remove", Symbol.Type.METHOD_WITH_SIDE_EFFECT); // Don't calculate here (Calculated in simulator). + public static final Symbol lookup = new Symbol("lookup", 2, Symbol.Type.PREFIX, "get", Symbol.Type.METHOD, new Symbol.ICalculator() { @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 Expression calculate(List args) { + if (args.get(1).getClass() == Constant.class && ((Constant) args.get(1)).getType().equals(typeString)) { + String key = (String) ((Constant) args.get(1)).getValue(); + if (args.get(0) instanceof Term) { + Term term = (Term) args.get(0); + Type type = term.getType(); + if (typeMap.isAncestorOf(type)) { + Stack> appendedChldren = new Stack<>(); + while (term.getSymbol().equals(DataConstraintModel.insert)) { + if (!(term.getChild(0) instanceof Term)) { + return null; + } + appendedChldren.push(new AbstractMap.SimpleEntry<>(term.getChild(0), term.getChild(1))); + term = (Term) term.getChild(0); + } + if (term instanceof MapTerm) { + MapTerm mapTerm = (MapTerm) term.clone(); + for (Map.Entry childEnt : appendedChldren) { + if (childEnt.getKey().getClass() == Constant.class) + mapTerm.insert((String) ((Constant) childEnt.getKey()).getValue(), childEnt.getValue()); + } + return mapTerm.get(key); + } + } + } + } + return null; } }); - + 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, new Symbol.ICalculator() { + @Override + public Expression calculate(List args) { + return new Constant(Double.toString(Math.PI), typeDouble); + } + }); + public static final Symbol E = new Symbol("E", 0, Symbol.Type.PREFIX, "Math.E", Symbol.Type.PREFIX, new Symbol.ICalculator() { + @Override + public Expression calculate(List args) { + return new Constant(Double.toString(Math.E), typeDouble); + } + }); + public static final Symbol sqrt = new Symbol("sqrt", 1, Symbol.Type.PREFIX, "Math.sqrt", Symbol.Type.PREFIX, new Symbol.ICalculator() { + @Override + public Expression calculate(List args) { + if (args.get(0).getClass() == Constant.class) { + if (((Constant) args.get(0)).getType().equals(typeDouble)) { + return new Constant(Double.toString(Math.sqrt(Double.parseDouble((String) ((Constant) args.get(0)).getValue()))), typeDouble); + } else if (((Constant) args.get(0)).getType().equals(typeFloat)) { + return new Constant(Float.toString((float) Math.sqrt(Float.parseFloat((String) ((Constant) args.get(0)).getValue()))), typeFloat); + } + } + return null; + } + }); + 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}); - mul.setInverses(new Symbol[] {div, div}); - sub.setInverses(new Symbol[] {add}); - 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}); - lt.setSignature(new Type[] {typeBoolean, null, null}); - ge.setSignature(new Type[] {typeBoolean, null, null}); - le.setSignature(new Type[] {typeBoolean, null, null}); - and.setSignature(new Type[] {typeBoolean, typeBoolean, typeBoolean}); - or.setSignature(new Type[] {typeBoolean, typeBoolean, typeBoolean}); - neg.setSignature(new Type[] {typeBoolean, typeBoolean}); - 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}); - right.setSignature(new Type[] {null, typePair}); - tuple.setSignature(new Type[] {typeTuple, null, null}); - tuple.setInverses(new Symbol[] {fst, snd}); - fst.setSignature(new Type[] {null, typeTuple}); - snd.setSignature(new Type[] {null, typeTuple}); - insert.setSignature(new Type[] {typeMap, typeMap, null, null}); - lookup.setSignature(new Type[] {null, typeMap, null}); + add.setInverses(new Symbol[]{sub, sub}); + mul.setInverses(new Symbol[]{div, div}); + sub.setInverses(new Symbol[]{add}); + div.setInverses(new Symbol[]{mul}); + minus.setInverses(new Symbol[]{minus}); + mod.setSignature(new Type[]{typeInt, null, null}); + eq.setSignature(new Type[]{typeBoolean, null, null}); + neq.setSignature(new Type[]{typeBoolean, null, null}); + gt.setSignature(new Type[]{typeBoolean, null, null}); + lt.setSignature(new Type[]{typeBoolean, null, null}); + ge.setSignature(new Type[]{typeBoolean, null, null}); + le.setSignature(new Type[]{typeBoolean, null, null}); + 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}); + indexOf.setSignature(new Type[]{typeInt, typeList, 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}); + pair.setSignature(new Type[]{typePair, null, null}); + pair.setInverses(new Symbol[]{left, right}); + left.setSignature(new Type[]{null, typePair}); + right.setSignature(new Type[]{null, typePair}); + 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, typeJson, 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 +1011,7 @@ addType(typePair); addType(typeTuple); addType(typeMap); + addType(typeJson); symbols = new HashMap<>(); addSymbol(add); addSymbol(mul); @@ -203,15 +1019,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 +1028,21 @@ addSymbol(and); addSymbol(or); addSymbol(neg); + addSymbol(cons); + addSymbol(append); + addSymbol(remove); + addSymbol(head); + addSymbol(tail); + addSymbol(length); + addSymbol(contains); + addSymbol(indexOf); + addSymbol(get); + addSymbol(set); + addSymbol(cond); + addSymbol(nil); + addSymbol(null_); addSymbol(true_); addSymbol(false_); - addSymbol(null_); addSymbol(pair); addSymbol(left); addSymbol(right); @@ -231,90 +1050,132 @@ 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 ResourceHierarchy getOrPutResourceHierarchy(ResourceHierarchy resourceHierarchy) { + String hierarchy = resourceHierarchy.toString(); + if (resourceHierarchies.get(hierarchy) != null) { + return resourceHierarchies.get(hierarchy); + } + resourceHierarchies.put(hierarchy, resourceHierarchy); + return resourceHierarchy; + } + + 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); + removeResourcePath(resourcePath); + } + + public void removeResourcePath(ResourcePath resourcePath) { + for (Channel ch : channels.values()) { + ch.removeChannelMember(resourcePath); + } + for (Channel ch : inputChannels.values()) { + ch.removeChannelMember(resourcePath); } } - + public Collection getChannels() { return channels.values(); } - + public Channel getChannel(String channelName) { return channels.get(channelName); } 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) { @@ -349,20 +1210,20 @@ } public boolean isPrimitiveType(Type type) { - if (type == typeInt - || type == typeLong - || type == typeFloat - || type == typeDouble - || type == typeBoolean) { + if (type == typeInt + || type == typeLong + || type == typeFloat + || type == typeDouble + || type == typeBoolean) { return true; } return false; } - + public static boolean isListType(Type type) { return typeList.isAncestorOf(type); } - + public static String getDefaultValue(Type type) { if (type == typeInt) { return "0"; @@ -380,13 +1241,36 @@ return "new " + type.getImplementationTypeName() + "()"; } + public static Expression getDefaultValueExpression(Type type) { + if (type == typeInt) { + return new Constant("0", typeInt); + } else if (type == typeLong) { + return new Constant("0L", typeLong); + } else if (type == typeFloat) { + return new Constant("0.0f", typeFloat); + } else if (type == typeDouble) { + return new Constant("0.0", typeDouble); + } else if (type == typeBoolean) { + return new Constant(false_); + } else if (type == typeString) { + return new Constant("", typeString); + } else if (type.isAncestorOf(typeList)) { + return new ListTerm(); + } else if (type.isAncestorOf(typeMap)) { + return new MapTerm(); + } else if (type.isAncestorOf(typeJson)) { + return new JsonTerm(); + } + return null; + } + @Override public String toString() { String out = ""; - for (Channel channel: ioChannels.values()) { + for (Channel channel : inputChannels.values()) { out += channel.toString(); } - for (Channel channel: channels.values()) { + for (Channel channel : channels.values()) { out += channel.toString(); } return out; @@ -395,19 +1279,19 @@ 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; + init += resource.toString() + " := " + initializer + "\n"; } } 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()) { + for (Channel channel : channels.values()) { out += channel.getSourceText(); } return out; diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonAccessor.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonAccessor.java new file mode 100644 index 0000000..51e9899 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonAccessor.java @@ -0,0 +1,215 @@ +package models.dataConstraintModel; + +import models.algebra.*; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class JsonAccessor extends Term { + public JsonAccessor(Symbol symbol) { + super(symbol); + } + + @Override + public Expression reduce() { + Expression reducedTerm = super.reduce(); + if (reducedTerm instanceof Term) { + if (symbol == 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); + } + } else if (symbol == DataConstraintModel.dotParam && getChildren().size() >= 2) { + // this term is `json.{param}`. + Expression expJson = getChild(0); + Expression expKey = getChild(1); + if (expKey instanceof Variable && expJson instanceof Term) { + reducedTerm = getValue((Term) expJson, (Variable) expKey); + } + } + } + return reducedTerm; + } + + private Expression getValue(Term json, Expression key) { + if (json instanceof JsonTerm) { + if (key instanceof Constant) { + return ((JsonTerm) json).get((Constant) key); + } else if (key instanceof Variable) { + return ((JsonTerm) json).get((Variable) key); + } + } + if (key instanceof Constant || key instanceof Variable) { + if (json.getSymbol().equals(DataConstraintModel.addMember) || json.getSymbol().equals(DataConstraintModel.set)) { + if (json.getChild(1).equals(key)) { + Expression value = json.getChild(2); + if (value == null) { + return new Constant(DataConstraintModel.null_); + } + return value; + } + if (json.getChild(0) == null || (json.getChild(0) instanceof Term && (((Term) json.getChild(0)).getSymbol().equals(DataConstraintModel.null_)) || ((Term) json.getChild(0)).getSymbol().equals(DataConstraintModel.nil))) + return null; + return getValue((Term) json.getChild(0), key); + } + } + return new Constant(DataConstraintModel.null_); + } + + private Expression getValue(Term json, Variable key) { + if (json instanceof JsonTerm) { + return ((JsonTerm) json).get(key); + } + if (json.getSymbol().equals(DataConstraintModel.addMember) || json.getSymbol().equals(DataConstraintModel.set)) { + if (json.getChild(1).equals(key)) { + Expression value = json.getChild(2); + if (value == null) { + return new Constant(DataConstraintModel.null_); + } + return value; + } + if (json.getChild(0) == null || (json.getChild(0) instanceof Term && (((Term) json.getChild(0)).getSymbol().equals(DataConstraintModel.null_)) || ((Term) json.getChild(0)).getSymbol().equals(DataConstraintModel.nil))) + return null; + return getValue((Term) json.getChild(0), key); + } + if (json.getSymbol().equals(DataConstraintModel.insert)) { + if (json.getChild(1).equals(key)) { + Expression value = json.getChild(2); + if (value == null) { + return new Constant(DataConstraintModel.null_); + } + return value; + } + if (json.getChild(0) == null || (json.getChild(0) instanceof Term && (((Term) json.getChild(0)).getSymbol().equals(DataConstraintModel.null_)) || ((Term) json.getChild(0)).getSymbol().equals(DataConstraintModel.nil))) + return null; + return getValue((Term) json.getChild(0), key); + } + return new Constant(DataConstraintModel.null_); + } + + @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 == DataConstraintModel.dot && getChildren().size() >= 2) { + // this term is `json.key`. + Expression expJson = getChild(0); + Expression expKey = getChild(1); + Type jsonType = null; + if (expJson instanceof Variable) { + jsonType = ((Variable) expJson).getType(); + } else if (expJson instanceof Term) { + jsonType = ((Term) expJson).getType(); + } + String keyName = null; + if (expKey instanceof Constant) { + keyName = (String) ((Constant) expKey).getValue(); + 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 if (jsonType instanceof JsonType) { + keySet.addAll(((JsonType) jsonType).getKeys()); + if (keySet.size() == 0) { + keySet.add(keyName); + } + } + for (String key : keySet) { + Term addMemberTerm = new Term(DataConstraintModel.addMember); // addMember(jsonTerm, key, v) + addMemberTerm.addChild(jsonTerm); + addMemberTerm.addChild(new Constant(key, DataConstraintModel.typeString)); + 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 == 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 = ((Variable) expKey).getType(); + } else if (expKey instanceof Term) { + keyType = ((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)) { + if (children.get(1) instanceof Constant) { + return children.get(0).toString() + symbol.toString() + (String) ((Constant) children.get(1)).getValue(); + } + 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..7183110 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonTerm.java @@ -0,0 +1,90 @@ +package models.dataConstraintModel; + +import models.algebra.*; +import parser.Parser; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +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) { + if (keyToIndex.get(key) == null) return null; + return getChild(keyToIndex.get(key)); + } + + public Expression get(Constant key) { + if (keyToIndex.get(key.getValue()) == null) return null; + return getChild(keyToIndex.get(key.getValue())); + } + + public Expression get(Variable key) { + if (keyToIndex.get(key.getName()) == null) return null; + return getChild(keyToIndex.get(key.getName())); + } + + @Override + public Expression unify(Expression another) { + if (another instanceof JsonTerm) { + JsonTerm anotherTerm = (JsonTerm) another; + JsonTerm unifiedTerm = new JsonTerm(); + Set keySet = new HashSet<>(); + keySet.addAll(this.keySet()); + keySet.addAll(anotherTerm.keySet()); + for (String key : keySet) { + if (this.keySet().contains(key)) { + if (anotherTerm.keySet().contains(key)) { + unifiedTerm.addMember(key, this.get(key).unify(anotherTerm.get(key))); + } else { + unifiedTerm.addMember(key, this.get(key)); + } + } else { + unifiedTerm.addMember(key, anotherTerm.get(key)); + } + } + return unifiedTerm; + } + return this; + } + + @Override + public Object clone() { + JsonTerm newTerm = new JsonTerm(); + for (Expression e : children) { + newTerm.addChild((Expression) e.clone()); + } + newTerm.keyToIndex = new HashMap(keyToIndex); + return newTerm; + } + + public String toString() { + String jsonStr = "{"; + String delim = ""; + for (String key : keyToIndex.keySet()) { + jsonStr += delim + Parser.DOUBLE_QUOT + key + Parser.DOUBLE_QUOT + ": " + 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..0bfb9b5 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonType.java @@ -0,0 +1,65 @@ +package models.dataConstraintModel; + +import models.algebra.Type; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +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..651d83e --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ListTerm.java @@ -0,0 +1,58 @@ +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 int size() { + return children.size(); + } + + @Override + public Expression unify(Expression another) { + if (another instanceof ListTerm) { + ListTerm anotherTerm = (ListTerm) another; + if (children.size() != anotherTerm.children.size()) return null; + ListTerm unifiedTerm = new ListTerm(); + for (int i = 0; i < children.size(); i++) { + unifiedTerm.addChild(children.get(i).unify(anotherTerm.children.get(i))); + } + return unifiedTerm; + } + return this; + } + + @Override + public Object clone() { + ListTerm newTerm = new ListTerm(); + for (Expression e : children) { + newTerm.addChild((Expression) e.clone()); + } + return newTerm; + } + + 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..4f3875c --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/MapTerm.java @@ -0,0 +1,87 @@ +package models.dataConstraintModel; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Symbol; +import models.algebra.Term; +import parser.Parser; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +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) { + if (keyToIndex.get(key.getValue()) == null) return null; + return getChild(keyToIndex.get(key.getValue())); + } + + @Override + public Expression unify(Expression another) { + if (another instanceof MapTerm) { + MapTerm anotherTerm = (MapTerm) another; + MapTerm unifiedTerm = new MapTerm(); + Set keySet = new HashSet<>(); + keySet.addAll(this.keySet()); + keySet.addAll(anotherTerm.keySet()); + for (String key : keySet) { + if (this.keySet().contains(key)) { + if (anotherTerm.keySet().contains(key)) { + unifiedTerm.insert(key, this.get(key).unify(anotherTerm.get(key))); + } else { + unifiedTerm.insert(key, this.get(key)); + } + } else { + unifiedTerm.insert(key, anotherTerm.get(key)); + } + } + return unifiedTerm; + } + return this; + } + + @Override + public Object clone() { + MapTerm newTerm = new MapTerm(); + for (Expression e : children) { + newTerm.addChild((Expression) e.clone()); + } + newTerm.keyToIndex = new HashMap(keyToIndex); + return newTerm; + } + + public String toString() { + String mapStr = "{"; + String delim = ""; + for (String key : keyToIndex.keySet()) { + mapStr += delim + Parser.DOUBLE_QUOT + key + Parser.DOUBLE_QUOT + ": " + 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..8684cb5 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourceHierarchy.java @@ -0,0 +1,257 @@ +package models.dataConstraintModel; + +import models.algebra.Expression; +import models.algebra.Term; +import models.algebra.Type; +import models.algebra.Variable; + +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +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; + protected boolean isNative = false; + + 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; + if (this.children == null) this.children = new HashSet<>(); + 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; + if (this.children == null) this.children = new HashSet<>(); + 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 boolean isNative() { + return isNative; + } + + public void setNative(boolean isNative) { + this.isNative = isNative; + } + + 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 { + Map.Entry lastParam = pathParams.get(pathParams.size() - 1); + pathParams = pathParams.subList(0, pathParams.size() - 1); + if (lastParam.getValue() == null) { + return parent.toString(pathParams) + ".{" + lastParam.getKey() + "}"; + } + return parent.toString(pathParams) + ".{" + lastParam.getKey() + "=" + lastParam.getValue() + "}"; + } + } + + 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..b96c5fa 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourcePath.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourcePath.java @@ -1,70 +1,202 @@ package models.dataConstraintModel; import models.algebra.Expression; -import models.algebra.Term; -import models.algebra.Type; +import models.algebra.Symbol; -public class ResourcePath { - private String resourceName = null; - private Type resourceStateType = null; - private int numParameters = 0; - private Expression initialValue = null; - protected String initText = null; +import java.util.*; + +public class ResourcePath extends Symbol { + protected ResourcePath parent = null; + protected ResourceHierarchy resourceHierarchy = null; + protected List> pathParams = null; - public ResourcePath(String resourceName, int numParameters) { - this.resourceName = resourceName; - this.numParameters = numParameters; + public ResourcePath(String fullResourceName) { + super(fullResourceName); + this.parent = null; + this.resourceHierarchy = new ResourceHierarchy(null, fullResourceName); + this.pathParams = new ArrayList<>(); } - - public ResourcePath(String resourceName, Type resourceStateType, int numParameters) { - this.resourceName = resourceName; - this.resourceStateType = resourceStateType; - this.numParameters = numParameters; + + public ResourcePath(String fullResourceName, ResourceHierarchy resourceHierarchy) { + super(fullResourceName); + this.parent = null; + this.resourceHierarchy = resourceHierarchy; + this.pathParams = new ArrayList<>(); } - - public String getResourceName() { - return resourceName; + + public ResourcePath(ResourcePath parent, String leafResourceName) { + super(parent.toString() + "." + leafResourceName); + this.parent = parent; + this.resourceHierarchy = new ResourceHierarchy(parent.getResourceHierarchy(), leafResourceName); + this.pathParams = parent.getPathParamsAndConstraints(); + } + + public ResourcePath(ResourcePath parent, String leafResourceName, ResourceHierarchy resourceHierarchy) { + super(parent.toString() + "." + leafResourceName); + this.parent = parent; + this.resourceHierarchy = resourceHierarchy; + this.pathParams = parent.getPathParamsAndConstraints(); + } + + 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.getPathParamsAndConstraints()); + this.pathParams.add(new AbstractMap.SimpleEntry<>(exp, null)); + } + + public ResourcePath(ResourcePath parent, Expression exp, Expression constraint) { + super(parent.toString() + ".{" + exp + "=" + constraint + "}"); + this.parent = parent; + this.resourceHierarchy = new ResourceHierarchy(parent.getResourceHierarchy(), exp); + this.pathParams = new ArrayList<>(parent.getPathParamsAndConstraints()); + this.pathParams.add(new AbstractMap.SimpleEntry<>(exp, constraint)); + } + + public ResourcePath(ResourcePath parent, Expression exp, ResourceHierarchy resourceHierarchy) { + super(parent.toString() + ".{" + exp + "}"); + this.parent = parent; + this.resourceHierarchy = resourceHierarchy; + this.pathParams = new ArrayList<>(parent.getPathParamsAndConstraints()); + this.pathParams.add(new AbstractMap.SimpleEntry<>(exp, null)); + } + + public ResourcePath(ResourcePath parent, Expression exp, Expression constraint, ResourceHierarchy resourceHierarchy) { + super(parent.toString() + ".{" + exp + "=" + constraint + "}"); + this.parent = parent; + this.resourceHierarchy = resourceHierarchy; + this.pathParams = new ArrayList<>(parent.getPathParamsAndConstraints()); + this.pathParams.add(new AbstractMap.SimpleEntry<>(exp, constraint)); + } + + public ResourcePath(ResourcePath another) { + super(another.name); + if (another.parent != null) { + this.parent = new ResourcePath(another.parent); + } else { + this.parent = null; + } + this.resourceHierarchy = another.resourceHierarchy; + this.pathParams = new ArrayList<>(another.getPathParamsAndConstraints()); + } + + public ResourceHierarchy getResourceHierarchy() { + return resourceHierarchy; + } + + 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() { + List params = new ArrayList<>(); + for (Map.Entry paramEnt : this.pathParams) { + params.add(paramEnt.getKey()); + } + return params; + } + + public List> getPathParamsAndConstraints() { + return pathParams; + } + + public void setPathParams(List> pathParams) { + this.pathParams = pathParams; + } + + public void addPathParam(Expression pathParam) { + pathParams.add(new AbstractMap.SimpleEntry<>(pathParam, null)); + } + + public void addPathParamWithConstraint(Expression pathParam, Expression pathConstraint) { + pathParams.add(new AbstractMap.SimpleEntry<>(pathParam, pathConstraint)); + } + + public void replacePathParam(int i, Expression pathParam, Expression pathConstraint) { + if (i < pathParams.size()) { + pathParams.set(i, new AbstractMap.SimpleEntry<>(pathParam, pathConstraint)); + parent.replacePathParam(i, pathParam, pathConstraint); } } - public Expression getInitialValue() { - return initialValue; - } - - public void setInitialValue(Expression initialValue) { - this.initialValue = initialValue; - } - - public void setInitText(String initText) { - this.initText = initText; + public boolean endsWithParam() { + if (resourceHierarchy.getNumParameters() > 0) return true; + return false; } - public String getInitText() { - return initText; - } - - public boolean equals(Object another) { - if (!(another instanceof ResourcePath)) return false; - return resourceName.equals(((ResourcePath) another).resourceName); + public Expression getLastParam() { + if (endsWithParam()) { + return pathParams.get(pathParams.size() - 1).getKey(); + } + return null; } - public int hashCode() { - return resourceName.hashCode(); + public Map.Entry getLastParamAndConstraint() { + if (endsWithParam()) { + return pathParams.get(pathParams.size() - 1); + } + return null; + } + + public ResourcePath getParent() { + return parent; + } + + public ResourcePath getRoot() { + if (parent == null) return this; + return parent.getRoot(); + } + + 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 boolean isAncestorOf(ResourcePath another) { + Set ancestors = new HashSet<>(); + while (another != null) { + ancestors.add(another); + another = another.getParent(); + } + return ancestors.contains(this); + } + + public String toString() { + return resourceHierarchy.toString(pathParams); + } + + public String toResourcePath() { + List params = new ArrayList<>(); + String[] sideEffects = new String[]{""}; + for (Map.Entry param : pathParams) { + params.add("{" + param.getKey().toString() + "}"); + } + return resourceHierarchy.toResourcePath(params); } } diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Selector.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Selector.java index a29f8e3..0609bc2 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..89d9bcb 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/StateTransition.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/StateTransition.java @@ -1,16 +1,12 @@ package models.dataConstraintModel; +import models.algebra.*; +import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork; + import java.util.ArrayList; import java.util.HashMap; import java.util.Map.Entry; - -import models.algebra.Expression; -import models.algebra.InvalidMessage; -import models.algebra.Position; -import models.algebra.Term; -import models.algebra.ValueUndefined; -import models.algebra.Variable; -import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork; +import java.util.Set; public class StateTransition { private Expression curStateExpression = null; @@ -32,32 +28,46 @@ public void setNextStateExpression(Expression nextStateExpression) { this.nextStateExpression = nextStateExpression; } - + public Expression getMessageExpression() { return messageExpression; } - + public void setMessageExpression(Expression messageExpression) { this.messageExpression = messageExpression; } 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 Expression deriveMessageConstraintFor(Expression curStateValue, Expression nextStateValue) throws InvalidMessage, ResolvingMultipleDefinitionIsFutureWork { + + 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, Set substitutedPositions) throws InvalidMessage, ResolvingMultipleDefinitionIsFutureWork { HashMap> bindings = new HashMap<>(); Expression curStateTerm = getCurStateExpression(); - HashMap curStateVars = curStateTerm.getVariables(); - for (Entry curStateVarEnt: curStateVars.entrySet()) { + HashMap curStateVars = curStateTerm.getVariables(); + for (Entry curStateVarEnt : curStateVars.entrySet()) { Variable var = curStateVarEnt.getValue(); 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(); @@ -68,9 +78,9 @@ } Expression nextStateTerm = (Expression) getNextStateExpression().clone(); - for (Variable var: bindings.keySet()) { + for (Variable var : bindings.keySet()) { HashMap vars2 = nextStateTerm.getVariables(); - for (Variable var2: vars2.values()) { + for (Variable var2 : vars2.values()) { if (var.equals(var2) && bindings.get(var).size() == 1) { if (nextStateTerm instanceof Term) { nextStateTerm = ((Term) nextStateTerm).substitute(var, bindings.get(var).get(0)); @@ -82,11 +92,14 @@ } HashMap nextStateVars = nextStateTerm.getVariables(); - for (Entry nextStateVarEnt: nextStateVars.entrySet()) { + for (Entry nextStateVarEnt : nextStateVars.entrySet()) { Variable var = nextStateVarEnt.getValue(); 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,27 +110,22 @@ } 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(); - HashMap curStateVars = curStateTerm.getVariables(); - for (Entry curStateVarEnt: curStateVars.entrySet()) { + HashMap curStateVars = curStateTerm.getVariables(); + for (Entry curStateVarEnt : curStateVars.entrySet()) { Variable var = curStateVarEnt.getValue(); 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,28 +136,45 @@ } 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; } - - public Expression deriveNextStateExpressionFor(Expression curStateValue, Term concreteMessage) + + public Expression deriveNextStateExpressionFor(Expression curStateValue, Term concreteMessage) throws ResolvingMultipleDefinitionIsFutureWork, ValueUndefined { HashMap bindings = new HashMap<>(); - + Expression curStateTerm = getCurStateExpression(); HashMap curStateVars = curStateTerm.getVariables(); - for (Entry curStateVarEnt: curStateVars.entrySet()) { + for (Entry curStateVarEnt : curStateVars.entrySet()) { Variable var = curStateVarEnt.getValue(); 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); } @@ -158,7 +183,7 @@ Expression messageTerm = getMessageExpression(); HashMap messageVars = messageTerm.getVariables(); if (concreteMessage != null) { - for (Entry messageVarEnt: messageVars.entrySet()) { + for (Entry messageVarEnt : messageVars.entrySet()) { Variable var = messageVarEnt.getValue(); Position varPos = messageVarEnt.getKey(); Expression valueCalc = concreteMessage.getSubTerm(varPos); @@ -174,10 +199,13 @@ nextStateTerm = bindings.get((Variable) nextStateTerm); if (nextStateTerm == null) throw new ValueUndefined(); } else { - for (Variable var: bindings.keySet()) { + for (Variable var : bindings.keySet()) { 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..2dbb8af --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/StateTransitionTerm.java @@ -0,0 +1,9 @@ +package models.dataConstraintModel; + +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..ee28087 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ChannelNode.java @@ -0,0 +1,60 @@ +package models.dataFlowModel; + +import models.Node; + +import java.util.HashSet; +import java.util.Set; + +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 Set getAncestors() { + if (parent == null) { + return new HashSet<>(); + } + Set ancestors = parent.getAncestors(); + ancestors.add(parent); + return ancestors; + } + + public Set getDescendants() { + if (children == null) { + return new HashSet<>(); + } + Set descendants = new HashSet<>(); + for (ChannelNode child : children) { + descendants.addAll(child.getDescendants()); + descendants.add(child); + } + return descendants; + } + + 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..e6ad2df 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataFlowEdge.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataFlowEdge.java @@ -1,20 +1,25 @@ package models.dataFlowModel; -import models.*; +import models.Edge; public class DataFlowEdge extends Edge { - protected DataTransferChannel channel = null; - - public DataFlowEdge(ResourceNode src, ResourceNode dst, DataTransferChannel channel) { + protected boolean channelToResource = false; + + 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..7f88b82 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataFlowGraph.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataFlowGraph.java @@ -1,40 +1,194 @@ package models.dataFlowModel; -import java.util.HashMap; -import java.util.Map; - import models.DirectedGraph; +import models.Node; +import models.dataConstraintModel.ResourceHierarchy; import models.dataConstraintModel.ResourcePath; -public class DataFlowGraph extends DirectedGraph { - protected Map nodeMap = null; +import java.util.*; + +public class DataFlowGraph extends DirectedGraph implements IFlowGraph { + 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 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); + + 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)); } - ResourceNode dstNode = nodeMap.get(out); - if (dstNode == null) { - dstNode = new ResourceNode(out); - addNode(dstNode); - nodeMap.put(out, dstNode); + if (node != null) return node; + node = new ResourceNode(parent, inSideResource, inSideChannel, true); + addNode(node); + if (parent == null) { + rootResourceNodes.add(node); + } else { + parent.addChild(node); } - addEdge(new DataFlowEdge(srcNode, dstNode, dfChannelGen)); + 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); + } + + @Override + public Map> getAllComponentNodes() { + Map> allNodeSets = new HashMap<>(); + for (Node n : getResourceNodes()) { + Set nodeSet = new HashSet<>(); + nodeSet.add(n); + allNodeSets.put(n, nodeSet); + } + return allNodeSets; + } + + 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 Collection getChannelNodes() { + return channelNodeMap.values(); + } + + public Set getRootResourceNodes() { + return rootResourceNodes; + } + + public Set getRootChannelNodes() { + return rootChannelNodes; + } + + /** + * Adds an edge from a resource node to a channel node. + * + * @param srcNode A resource node to be added as the source node of the edge. + * @param dstNode A channel node to be added as the destination node of the edge. + * @return The newly created edge. + */ + public DataFlowEdge addEdge(ResourceNode srcNode, ChannelNode dstNode) { + DataFlowEdge edge = new DataFlowEdge(srcNode, dstNode); + addEdge(edge); + return edge; + } + + /** + * Adds an edge from a channel node to a resource node. + * + * @param srcNode A channel node to be added as the source node of the edge. + * @param dstNode A resource node to be added as the destination node of the edge. + * @return The newly created 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..d304108 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferChannel.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferChannel.java @@ -1,19 +1,14 @@ package models.dataFlowModel; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Set; +import models.algebra.*; +import models.dataConstraintModel.Channel; +import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.ResourcePath; +import models.dataConstraintModel.Selector; +import parser.Parser; -import models.algebra.Expression; -import models.algebra.InvalidMessage; -import models.algebra.Parameter; -import models.algebra.ParameterizedIdentifierIsFutureWork; -import models.algebra.Position; -import models.algebra.Term; -import models.algebra.UnificationFailed; -import models.algebra.ValueUndefined; -import models.algebra.Variable; -import models.dataConstraintModel.*; +import java.util.*; +import java.util.Map.Entry; public class DataTransferChannel extends Channel { protected Set inputChannelMembers = null; @@ -27,15 +22,38 @@ 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; } + public Set getAllInputChannelMembers() { + Set allInputChannelMembers = new HashSet<>(); + allInputChannelMembers.addAll(inputChannelMembers); + for (Channel child : children) { + allInputChannelMembers.addAll(((DataTransferChannel) child).getAllInputChannelMembers()); + } + return allInputChannelMembers; + } + public void setInputChannelMembers(Set inputChannelMembers) { this.inputChannelMembers = inputChannelMembers; } - public void addInputChannelMember(ChannelMember inputChannelMember) { + private void addInputChannelMember(ChannelMember inputChannelMember) { inputChannelMembers.add(inputChannelMember); } @@ -47,7 +65,7 @@ this.outputChannelMembers = outputChannelMembers; } - public void addOutputChannelMember(ChannelMember outputChannelMember) { + private void addOutputChannelMember(ChannelMember outputChannelMember) { outputChannelMembers.add(outputChannelMember); } @@ -59,41 +77,41 @@ 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) { - for (ChannelMember cm: inputChannelMembers) { + for (ChannelMember cm : inputChannelMembers) { if (cm.getResource() == id) { inputChannelMembers.remove(cm); super.removeChannelMember(id); return; } } - for (ChannelMember cm: outputChannelMembers) { + for (ChannelMember cm : outputChannelMembers) { if (cm.getResource() == id) { outputChannelMembers.remove(cm); super.removeChannelMember(id); return; } } - for (ChannelMember cm: referenceChannelMembers) { + for (ChannelMember cm : referenceChannelMembers) { if (cm.getResource() == id) { referenceChannelMembers.remove(cm); super.removeChannelMember(id); @@ -104,7 +122,7 @@ public Set getInputResources() { Set inputResources = new HashSet<>(); - for (ChannelMember member: inputChannelMembers) { + for (ChannelMember member : inputChannelMembers) { inputResources.add(member.getResource()); } return inputResources; @@ -112,7 +130,7 @@ public Set getOutputResources() { Set outputResources = new HashSet<>(); - for (ChannelMember member: outputChannelMembers) { + for (ChannelMember member : outputChannelMembers) { outputResources.add(member.getResource()); } return outputResources; @@ -120,7 +138,7 @@ public Set getReferenceResources() { Set referenceResources = new HashSet<>(); - for (ChannelMember member: referenceChannelMembers) { + for (ChannelMember member : referenceChannelMembers) { referenceResources.add(member.getResource()); } return referenceResources; @@ -128,6 +146,7 @@ /** * Derive the update expression of the state of the target channel member. + * * @param targetMember a channel member whose state is to be updated * @return the derived update expression * @throws ParameterizedIdentifierIsFutureWork @@ -140,10 +159,10 @@ IResourceStateAccessor defaultStateAccessor = new IResourceStateAccessor() { HashMap curStateParams = new HashMap<>(); 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); @@ -151,80 +170,296 @@ } return curStateParam; } - + @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. - * @param targetMember a channel member whose state is to be updated - * @param stateAccessor a resource state accessor - * @return the derived update expression + * 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 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) + + /** + * 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 push/pull resource state accessor + * @param inputResourceToStateAccessor push/pull resource state accessors (if null, stateAccessor is used.) + * @return the derived update calculation and the unified message + * @throws ParameterizedIdentifierIsFutureWork + * @throws ResolvingMultipleDefinitionIsFutureWork + * @throws InvalidMessage + * @throws UnificationFailed + * @throws ValueUndefined + */ + 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 { + if (!getOutputChannelMembers().contains(targetMember)) return null; + return fillOutsideResourcePaths(targetMember, stateAccessor, null).getKey(); + } + + /** + * 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 + * @param inputResourceToStateAccessor push/pull resource state accessors (if null, stateAccessor is used.) + * @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.Entry>>, Term> fillOutsideResourcePaths(ChannelMember targetMember, IResourceStateAccessor stateAccessor, Map inputResourceToStateAccessor) + throws ParameterizedIdentifierIsFutureWork, ResolvingMultipleDefinitionIsFutureWork, InvalidMessage, UnificationFailed, ValueUndefined { + 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 new AbstractMap.SimpleEntry<>(resourcePaths, unifiedMessage); + } + + /** + * Get the dependency from the values of the 'path parameters' of a channel member to the state values of other channel members. + * + * @return a map from a depending channel member to depended channel members + */ + 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) && otherCm != cm) { + 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) && otherCm != cm) { + 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) && otherCm != cm) { + 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); + for (ChannelMember inputMember : getInputChannelMembers()) { + 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); + for (ChannelMember referenceMember : getReferenceChannelMembers()) { + Expression messageConstraintByReference = calcMessageConstraintForReferenceMember(referenceMember, targetMember, stateAccessor, inputResourceToStateAccessor, substitutedPositionsInMessageFromChannels); messageConstraints.add((Term) messageConstraintByReference); } - + // Unify message constraints Term unifiedMessage = null; - for (Term messageContraint: messageConstraints) { + for (Term messageContraint : messageConstraints) { if (unifiedMessage == null) { unifiedMessage = messageContraint; } else { @@ -234,40 +469,227 @@ } } } - - // 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)) { + 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, Expression 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.getPathParamsAndConstraints(); + for (int i = 0; i < filledResourcePath.getPathParams().size(); i++) { + Expression pathParam = dstParams.get(i).getKey(); + Expression pathValue = dstParams.get(i).getValue(); + if (pathParam instanceof Variable) { + if (pathValue == null) { + if (bindings.get((Variable) pathParam) != null) { + filledResourcePath.replacePathParam(i, bindings.get((Variable) pathParam).getValue(), null); // 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 the path parameter has a constraint. + if (pathValue instanceof Term) { + Map pathValueVars = ((Term) pathValue).getVariables(); + for (Variable var : bindings.keySet()) { + if (pathValueVars.values().contains(var)) { // var is a subterm of a path parameter + pathValue = ((Term) pathValue).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 (!(pathValue instanceof Constant)) { + pathValue = ((Term) pathValue).reduce(); + } + filledResourcePath.replacePathParam(i, pathValue, null); // Replace a path parameter with the substituted term. + } + } + } 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(); + } + filledResourcePath.replacePathParam(i, pathParam, null); // 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 (Map.Entry pathParamEnt : resourcePath.getPathParamsAndConstraints()) { + Expression pathParam = pathParamEnt.getKey(); + 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 public String toString() { - String channelSource = "channel " + getChannelName() + " {\n"; - for (ChannelMember inputMember: inputChannelMembers) { - channelSource += "\t in " + inputMember + "\n"; + String channelSource = ""; + if (isNative()) { + channelSource += Parser.NATIVE + " "; } - for (ChannelMember refMember: referenceChannelMembers) { - channelSource += "\t ref " + refMember + "\n"; + if (parent == null) { + channelSource += Parser.CHANNEL + " " + getChannelName(); + } else { + channelSource += Parser.SUB_CHANNEL + " " + getChannelName(); } - for (ChannelMember outputMember: outputChannelMembers) { - channelSource += "\t out " + outputMember + "\n"; + if (getSelectors().size() > 0) { + channelSource += "("; + String delimitor = ""; + for (Selector selector : getSelectors()) { + channelSource += delimitor + selector.getExpression().toString(); + delimitor = ", "; + } + channelSource += ")"; + } + channelSource += " {\n"; + for (ChannelMember inputMember : inputChannelMembers) { + channelSource += "\t " + Parser.IN + " " + inputMember + "\n"; + } + for (ChannelMember refMember : referenceChannelMembers) { + channelSource += "\t " + Parser.REF + " " + refMember + "\n"; + } + for (ChannelMember outputMember : outputChannelMembers) { + channelSource += "\t " + Parser.OUT + " " + outputMember + "\n"; + } + for (Channel childCh : getChildren()) { + channelSource += childCh.toString(); } channelSource += "}\n"; return channelSource; } 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..8b7878c 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferModel.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferModel.java @@ -1,31 +1,304 @@ package models.dataFlowModel; -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 { +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class DataTransferModel extends DataConstraintModel { public DataFlowGraph getDataFlowGraph() { DataFlowGraph dataFlowGraph = new DataFlowGraph(); - 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); - } - } + Map>> channelLocalResMap = new HashMap<>(); + for (Channel channel : getChannels()) { + 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()) { + addReferenceResources(dataFlowGraph, channel, null, channelLocalResMap); + } + 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 addReferenceResources(DataFlowGraph dataFlowGraph, Channel dstChannel, ChannelNode parentChannelNode, + Map>> channelLocalResMap) { + DataTransferChannel dstDfChannel = (DataTransferChannel) dstChannel; + ChannelNode dstChannelNode = dataFlowGraph.addChannelNode(parentChannelNode, dstDfChannel); + for (ResourcePath srcRes : dstDfChannel.getReferenceResources()) { + 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); + } else { + if (chLocalResNodes == null) { + // There is no channel-local ResourcePath. + // Then, create a new channel-local ResourceNode. + ResourceNode srcResNode = addResourceNodes(dataFlowGraph, srcRes, dstDfChannel, channelLocalResMap, false); + } else { + boolean bExists = false; + for (Channel ch : chLocalResNodes.keySet()) { + Set nodes = chLocalResNodes.get(ch); + for (ResourceNode node : nodes) { + ResourcePath r = node.getOutSideResource((DataTransferChannel) ch); + if (r.toString().equals(srcRes.toString())) { + bExists = true; // There exists the same ResourecPath within some channel. + break; + } + } + } + if (!bExists) { + // Create a new channel-local ResourceNode. + ResourceNode srcResNode = addResourceNodes(dataFlowGraph, srcRes, dstDfChannel, channelLocalResMap, false); + } + } + } + } + for (Channel childChannel : dstDfChannel.getChildren()) { + addReferenceResources(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/IFlowGraph.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/IFlowGraph.java new file mode 100644 index 0000000..78ce600 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/IFlowGraph.java @@ -0,0 +1,10 @@ +package models.dataFlowModel; + +import models.Node; + +import java.util.Map; +import java.util.Set; + +public interface IFlowGraph { + public Map> getAllComponentNodes(); +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ModelExtension.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ModelExtension.java index d8aa4af..7ab0850 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ModelExtension.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ModelExtension.java @@ -10,7 +10,7 @@ private static Symbol.Memento mergeMem; private static Symbol.Memento extractFaceDownMem; private static Symbol.Memento sortByKeyMem; - + public static void extendModel(DataTransferModel model) { Symbol floor = model.getSymbol("floor"); floorMem = null; @@ -23,16 +23,16 @@ sumMem = null; if (sum != null) { sumMem = sum.createMemento(); - final int[] count = new int[] {0}; + 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(); if (interfaceType.contains("<")) { compType = interfaceType.substring(interfaceType.indexOf("<") + 1, interfaceType.lastIndexOf(">")); - } + } } count[0]++; String impl = compType + " " + "temp_sum" + count[0] + " = 0;\n"; @@ -44,7 +44,7 @@ } }); sum.setImplOperatorType(Symbol.Type.GENERATIVE); - sum.setSignature(new Type[] {DataConstraintModel.typeInt, DataConstraintModel.typeList}); + sum.setSignature(new Type[]{DataConstraintModel.typeInt, DataConstraintModel.typeList}); // sum.setImplName("stream().mapToInt(x->x).sum"); // sum.setImplOperatorType(Symbol.Type.METHOD); } @@ -53,11 +53,11 @@ if (merge != null) { mergeMem = merge.createMemento(); merge.setArity(2); - final int[] count = new int[] {0}; + 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) { @@ -87,7 +87,7 @@ impl += "\t\t\tt2 = i2.next();\n"; impl += "\t\t}\n"; impl += "\t\tif (t1 == null || (t2 != null && t1" + idxGetter + " < t2" + idxGetter + ")) {\n"; - impl += "\t\t\ttemp_l" + count[0] +".add(t2);\n"; + impl += "\t\t\ttemp_l" + count[0] + ".add(t2);\n"; impl += "\t\t\tt2 = null;\n"; impl += "\t\t} else {\n"; impl += "\t\t\ttemp_l" + count[0] + ".add(t1);\n"; @@ -100,8 +100,8 @@ } }); merge.setImplOperatorType(Symbol.Type.GENERATIVE); - merge.setSignature(new Type[] {DataConstraintModel.typeList, DataConstraintModel.typeList, DataConstraintModel.typeList}); - } + merge.setSignature(new Type[]{DataConstraintModel.typeList, DataConstraintModel.typeList, DataConstraintModel.typeList}); + } Symbol extractFaceDown = model.getSymbol("extractFaceDown"); extractFaceDownMem = null; if (extractFaceDown != null) { @@ -109,24 +109,24 @@ extractFaceDown.setArity(1); extractFaceDown.setGenerator(new Symbol.IImplGenerator() { @Override - public String generate(Type type, String[] children, String[] childrenSideEffects, String[] sideEffect) { - return children[0]+".stream().filter(item -> item.getValue()==false).collect(Collectors.toList())"; + 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())"; } }); extractFaceDown.setImplOperatorType(Symbol.Type.GENERATIVE); - extractFaceDown.setSignature(new Type[] {DataConstraintModel.typeList, null}); + extractFaceDown.setSignature(new Type[]{DataConstraintModel.typeList, null}); } Symbol sortByKey = model.getSymbol("sortByKey"); sortByKeyMem = null; - if(sortByKey != null) { + if (sortByKey != null) { sortByKeyMem = sortByKey.createMemento(); 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"; + String temp_sort = "temp_sort"; if (type != null) { String interfaceType = type.getInterfaceTypeName(); if (interfaceType.contains("<")) { @@ -136,27 +136,27 @@ if (implType.indexOf('<') >= 0) { implType = implType.substring(0, implType.indexOf('<')); } - - } - for (String s: childrenSideEffects) { + + } + for (String s : childrenSideEffects) { sideEffect[0] += s; } - temp_sort=children[0]+".sort(Comparator.comparing("+compType+"::getKey));\n"; + temp_sort = children[0] + ".sort(Comparator.comparing(" + compType + "::getKey));\n"; return temp_sort; } }); - sortByKey.setSignature(new Type[] {DataConstraintModel.typeList, DataConstraintModel.typeList}); + sortByKey.setSignature(new Type[]{DataConstraintModel.typeList, DataConstraintModel.typeList}); sortByKey.setImplOperatorType(Symbol.Type.GENERATIVE); } } public static void recoverModel(DataTransferModel model) { - Symbol floor = model.getSymbol("floor"); + Symbol floor = model.getSymbol("floor"); if (floor != null) floor.setMemento(floorMem); Symbol sum = model.getSymbol("sum"); if (sum != null) sum.setMemento(sumMem); Symbol merge = model.getSymbol("merge"); - if (merge != null) merge.setMemento(mergeMem); + if (merge != null) merge.setMemento(mergeMem); Symbol extractFaceDown = model.getSymbol("extractFaceDown"); if (extractFaceDown != null) extractFaceDown.setMemento(extractFaceDownMem); Symbol sortByKey = model.getSymbol("sortByKey"); diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/PushPullAttribute.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/PushPullAttribute.java index 598894a..575b015 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/PushPullAttribute.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/PushPullAttribute.java @@ -1,11 +1,11 @@ package models.dataFlowModel; +import models.EdgeAttribute; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import models.EdgeAttribute; - public class PushPullAttribute extends EdgeAttribute { private List options; @@ -13,22 +13,26 @@ options = new ArrayList<>(); } - public PushPullAttribute(PushPullValue[] options) { + public PushPullAttribute(PushPullValue[] options) { this.options = new ArrayList<>(Arrays.asList(options)); } - + public List getOptions() { return options; } - + + public PushPullValue getSelectedOption() { + return options.get(0); + } + public void setOptions(List options) { this.options = options; } - + public void addOption(PushPullValue option) { options.add(option); } - + public void removeOption(PushPullValue option) { options.remove(option); } @@ -37,6 +41,13 @@ this.options.retainAll(options); } + public boolean selectOption(PushPullValue option) { + if (!options.contains(option)) return false; + options.remove(option); + options.add(0, option); + return true; + } + public String[] getOptionStrings() { String[] optionString = new String[options.size()]; for (int i = 0; i < options.size(); i++) { diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/PushPullValue.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/PushPullValue.java index 28307c6..a9d2fc8 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/PushPullValue.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/PushPullValue.java @@ -7,14 +7,14 @@ public String toString() { switch (this) { - case PUSHorPULL: - return "PUSH/PULL"; - case PUSH: - return "PUSH"; - case PULL: - return "PULL"; - default: - return ""; + case PUSHorPULL: + return "PUSH/PULL"; + case PUSH: + return "PUSH"; + case PULL: + return "PULL"; + default: + return ""; } } } diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ReferenceEdge.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ReferenceEdge.java new file mode 100644 index 0000000..b6ac7ed --- /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..96557b8 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ResourceNode.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ResourceNode.java @@ -1,30 +1,197 @@ package models.dataFlowModel; import models.Node; +import models.algebra.Expression; +import models.algebra.Type; +import models.dataConstraintModel.ResourceHierarchy; import models.dataConstraintModel.ResourcePath; +import models.dataConstraintModel.Selector; + +import java.util.*; public class ResourceNode extends Node { - protected ResourcePath resourcePath = null; + protected ResourceNode parent = null; + protected Set children = null; + protected ResourceHierarchy resourceHierarchy = null; + protected ResourcePath primaryResourcePath = null; // for temporal use at the modeling stage + protected Map inSide = null; + protected Map outSide = null; + protected List selectors = null; + + public ResourceNode(ResourceNode parent, ResourcePath primaryResourcePath) { + this.parent = parent; + this.children = new HashSet<>(); + this.inSide = new HashMap<>(); + this.outSide = new HashMap<>(); + this.resourceHierarchy = primaryResourcePath.getResourceHierarchy(); + this.primaryResourcePath = primaryResourcePath; + this.selectors = new ArrayList<>(); + if (resourceHierarchy.getNumParameters() > 0) { + List pathParams = primaryResourcePath.getPathParams(); + selectors.add(new Selector(pathParams.get(pathParams.size() - 1))); + } + } + + 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 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 ResourceHierarchy getResourceHierarchy() { + return resourceHierarchy; + } + + public String getResourceName() { + return resourceHierarchy.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 (primaryResourcePath != null) return primaryResourcePath; + 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 Set getInSideChannels() { + return inSide.keySet(); + } + + public Collection getOutSideResources() { + return outSide.values(); + } + + public ResourcePath getOutSideResource(DataTransferChannel channel) { + return outSide.get(channel); + } + + public Set getOutSideChannels() { + return outSide.keySet(); + } + + public void addInSideResource(DataTransferChannel channel, ResourcePath inResource) { + primaryResourcePath = null; + inSide.put(channel, inResource); + } + + public void addOutSideResource(DataTransferChannel channel, ResourcePath outResource) { + primaryResourcePath = null; + outSide.put(channel, outResource); + } + + public List getSelectors() { + return selectors; + } + + public Selector getLastSelector() { + return selectors.get(resourceHierarchy.getTotalNumParameters() - 1); + } + + public List getAllSelectors() { + List selectors = new ArrayList<>(); + if (parent != null) { + selectors.addAll(parent.getAllSelectors()); + } + selectors.addAll(this.selectors); + return selectors; + } - public ResourceNode(ResourcePath resourcePath) { - this.resourcePath = resourcePath; - } - - public ResourcePath getResource() { - return resourcePath; - } - - 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.getResourceName(); - } +// 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..c00a207 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/visualModel/FormulaChannel.java +++ b/AlgebraicDataflowArchitectureModel/src/models/visualModel/FormulaChannel.java @@ -1,25 +1,24 @@ package models.visualModel; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; - import models.algebra.Expression; import models.algebra.Symbol; import models.algebra.Term; import models.algebra.Variable; import models.dataConstraintModel.ChannelMember; -import models.dataConstraintModel.DataConstraintModel; import models.dataConstraintModel.ResourcePath; import models.dataConstraintModel.StateTransition; import models.dataFlowModel.DataTransferChannel; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + public class FormulaChannel extends DataTransferChannel { private Symbol defaultOperator = null; private String formula = null; private Expression formulaRhs = null; - + public FormulaChannel(String channelName, Symbol defaultOperator) { super(channelName); this.defaultOperator = defaultOperator; @@ -32,30 +31,30 @@ // 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())); - formulaRhs = newTerm; + 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); } } - + public void addChannelMemberAsOutput(ChannelMember channelMember) { // StateTransition st = new StateTransition(); // st.setCurStateExpression(new Variable(channelMember.getIdentifierTemplate().getResourceName() + "1")); @@ -63,19 +62,19 @@ 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) { - setFormulaTerm(formulaRhs); + setFormulaTerm(formulaRhs); } } - + public Symbol getDefaultOperator() { return defaultOperator; } - + public void setDefaultOperator(Symbol defaultOperator) { this.defaultOperator = defaultOperator; } @@ -94,14 +93,14 @@ Map curStates = new HashMap<>(); Map nextStates = new HashMap<>(); Map resToNextVar = new HashMap<>(); - for (ChannelMember cm: this.getInputChannelMembers()) { + 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); nextStates.put(id, nextVar); - for (Variable var: variables) { + for (Variable var : variables) { if (var.getName().equals(resName)) { resToNextVar.put(var, nextVar); break; @@ -110,13 +109,13 @@ } Symbol update = new Symbol("update"); update.setArity(resToNextVar.keySet().size()); - for (ChannelMember cm: getInputChannelMembers()) { + for (ChannelMember cm : getInputChannelMembers()) { ResourcePath id = cm.getResource(); StateTransition st = new StateTransition(); st.setCurStateExpression(curStates.get(id)); st.setNextStateExpression(nextStates.get(id)); Term message = new Term(update); - for (Variable var: resToNextVar.values()) { + for (Variable var : resToNextVar.values()) { message.addChild(var); } st.setMessageExpression(message); @@ -127,19 +126,19 @@ rhs = resToNextVar.get((Variable) rhs); } else if (rhs instanceof Term) { formulaRhs = rhs; - for (Variable var: resToNextVar.keySet()) { + for (Variable var : resToNextVar.keySet()) { rhs = ((Term) rhs).substitute(var, resToNextVar.get(var)); } } - for (ChannelMember cm: getOutputChannelMembers()) { + 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); Term message = new Term(update); - for (Variable var: resToNextVar.values()) { + for (Variable var : resToNextVar.values()) { message.addChild(var); } st.setMessageExpression(message); diff --git a/AlgebraicDataflowArchitectureModel/src/parser/Parser.java b/AlgebraicDataflowArchitectureModel/src/parser/Parser.java index 876ee6f..62d9718 100644 --- a/AlgebraicDataflowArchitectureModel/src/parser/Parser.java +++ b/AlgebraicDataflowArchitectureModel/src/parser/Parser.java @@ -1,40 +1,26 @@ package parser; -import java.awt.image.DataBufferDouble; +import models.algebra.*; +import models.dataConstraintModel.*; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.DataTransferModel; +import parser.exceptions.*; + import java.io.BufferedReader; import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; -import models.algebra.Constant; -import models.algebra.Expression; -import models.algebra.Symbol; -import models.algebra.Term; -import models.algebra.Type; -import models.algebra.Variable; -import models.dataConstraintModel.ChannelMember; -import models.dataConstraintModel.ResourcePath; -import models.dataConstraintModel.StateTransition; -import models.dataFlowModel.DataTransferModel; -import models.dataFlowModel.DataTransferChannel; -import parser.exceptions.ExpectedAssignment; -import parser.exceptions.ExpectedChannel; -import parser.exceptions.ExpectedChannelName; -import parser.exceptions.ExpectedEquals; -import parser.exceptions.ExpectedInOrOutOrRefKeyword; -import parser.exceptions.ExpectedLeftCurlyBracket; -import parser.exceptions.ExpectedRHSExpression; -import parser.exceptions.ExpectedRightBracket; -import parser.exceptions.ExpectedStateTransition; -import parser.exceptions.WrongLHSExpression; -import parser.exceptions.WrongRHSExpression; - -public class Parser { +public class Parser { protected TokenStream stream; - + 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 = "for"; + 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,29 +29,43 @@ 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 static final String DOUBLE_QUOT = "\""; + public Parser(final TokenStream stream) { this.stream = stream; } - - public Parser(final BufferedReader reader) { + + public Parser(final BufferedReader reader) { this.stream = new TokenStream(); try { String line; @@ -78,137 +78,162 @@ } } - public DataTransferModel doParse() - throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedInOrOutOrRefKeyword, ExpectedStateTransition, ExpectedEquals, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment { + public DataTransferModel doParse() + throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedRightCurlyBracket, + ExpectedInOrOutOrRefOrSubKeyword, ExpectedStateTransition, ExpectedEquals, + ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, WrongPathExpression, WrongJsonExpression, ExpectedColon, ExpectedDoubleQuotation { return parseDataFlowModel(); } - - public DataTransferModel parseDataFlowModel() - throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedInOrOutOrRefKeyword, ExpectedStateTransition, ExpectedEquals, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment { + + public DataTransferModel parseDataFlowModel() + throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedRightCurlyBracket, + ExpectedInOrOutOrRefOrSubKeyword, ExpectedStateTransition, ExpectedEquals, + ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, WrongPathExpression, WrongJsonExpression, ExpectedColon, ExpectedDoubleQuotation { DataTransferModel model = new DataTransferModel(); DataTransferChannel channel; while ((channel = parseChannel(model)) != null) { - if (channel.getInputChannelMembers().size() == 0) { - model.addIOChannel(channel); + if (channel.getAllInputChannelMembers().size() == 0) { + model.addInputChannel(channel); } else { model.addChannel(channel); } } return model; } - - public DataTransferChannel parseChannel(DataTransferModel model) - throws - ExpectedLeftCurlyBracket, ExpectedRightBracket, ExpectedAssignment, - ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, - ExpectedChannel, ExpectedChannelName, ExpectedInOrOutOrRefKeyword, - ExpectedStateTransition, ExpectedEquals - { + + public DataTransferChannel parseChannel(DataTransferModel model) + throws ExpectedLeftCurlyBracket, ExpectedRightBracket, ExpectedRightCurlyBracket, ExpectedAssignment, + ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, + ExpectedChannel, ExpectedChannelName, ExpectedInOrOutOrRefOrSubKeyword, + ExpectedStateTransition, ExpectedEquals, WrongPathExpression, WrongJsonExpression, ExpectedColon, ExpectedDoubleQuotation { if (!stream.hasNext()) return null; if (stream.checkNext().equals(RIGHT_CURLY_BRACKET)) return null; - - String channelOrInitKeyword = stream.next(); - if (!channelOrInitKeyword.equals(CHANNEL)) { - if (!channelOrInitKeyword.equals(INIT)) throw new ExpectedChannel(stream.getLine()); + + boolean isNative = false; + String channelOrInitOrNativeKeyword = stream.next(); + if (channelOrInitOrNativeKeyword.equals(NATIVE)) { + // A native channel + isNative = true; + channelOrInitOrNativeKeyword = stream.next(); + } + if (!channelOrInitOrNativeKeyword.equals(CHANNEL) && !channelOrInitOrNativeKeyword.equals(SUB_CHANNEL)) { + if (!channelOrInitOrNativeKeyword.equals(INIT)) throw new ExpectedChannel(stream.getLine()); parseInit(model); - channelOrInitKeyword = stream.next(); + channelOrInitOrNativeKeyword = stream.next(); + if (channelOrInitOrNativeKeyword.equals(NATIVE)) { + // A native channel + isNative = true; + channelOrInitOrNativeKeyword = stream.next(); + } } if (!stream.hasNext()) throw new ExpectedChannelName(stream.getLine()); - + String channelName = stream.next(); if (channelName.equals(LEFT_CURLY_BRACKET)) throw new ExpectedChannelName(stream.getLine()); - + int fromLine = stream.getLine(); DataTransferChannel channel = new DataTransferChannel(channelName); + if (isNative) { + channel.setNative(true); + } String leftBracket = stream.next(); + if (leftBracket.equals(LEFT_BRACKET)) { + // has selectors + String rightBracket = null; + do { + String selector = stream.next(); + Variable var = parseVariable(stream, model, selector); + channel.addSelector(var); + rightBracket = stream.next(); + } while (rightBracket.equals(COMMA)); + 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 - { + + public void parseInit(DataTransferModel model) + throws ExpectedLeftCurlyBracket, ExpectedAssignment, ExpectedRHSExpression, WrongRHSExpression, + ExpectedRightBracket, ExpectedRightCurlyBracket, WrongPathExpression, WrongJsonExpression, ExpectedColon, ExpectedDoubleQuotation { 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(); if (!colon.equals(COLON)) throw new ExpectedAssignment(stream.getLine()); if (!stream.hasNext()) throw new ExpectedAssignment(stream.getLine()); String equals = stream.next(); if (!equals.equals(ASSIGNMENT)) throw new ExpectedAssignment(stream.getLine()); - + int toLine = stream.getLine(); Expression rightTerm = null; - if (!stream.hasNext()) throw new ExpectedRHSExpression(stream.getLine()); - rightTerm = parseTerm(stream, model); + if (!stream.hasNext()) throw new ExpectedRHSExpression(stream.getLine()); + 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 - { + + public ChannelMember parseChannelMember(DataTransferModel model, final String inOrOutOrRef) + throws ExpectedRightBracket, ExpectedRightCurlyBracket, ExpectedStateTransition, ExpectedEquals, + ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, WrongPathExpression, WrongJsonExpression, ExpectedColon, ExpectedDoubleQuotation { 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; - + if (!inOrOutOrRef.equals(REF)) { if (!stream.hasNext()) throw new ExpectedEquals(stream.getLine()); String equals = stream.next(); if (!equals.equals(EQUALS)) throw new ExpectedEquals(stream.getLine()); - - if (!stream.hasNext()) throw new ExpectedRHSExpression(stream.getLine()); - rightTerm = parseTerm(stream, model); + + if (!stream.hasNext()) throw new ExpectedRHSExpression(stream.getLine()); + rightTerm = parseTerm(stream, model); 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)); @@ -227,7 +252,7 @@ if (messageTerm.getSymbol().getSignature() == null && messageTerm.getChildren().size() > 0) { Type[] signature = new Type[messageTerm.getChildren().size() + 1]; int i = 1; - for (Expression e: messageTerm.getChildren()) { + for (Expression e : messageTerm.getChildren()) { if (e instanceof Variable && ((Variable) e).getType() != null) { signature[i] = ((Variable) e).getType(); } @@ -238,28 +263,43 @@ } return channelMember; } - - public Expression parseTerm(TokenStream stream, DataTransferModel model) - throws ExpectedRightBracket - { + + public Expression parseTerm(TokenStream stream, DataTransferModel model) throws ExpectedRightBracket, WrongJsonExpression, ExpectedColon, ExpectedDoubleQuotation { ArrayList expressions = new ArrayList<>(); ArrayList operators = new ArrayList<>(); String operator = null; - for (;;) { - String leftBracketOrMinus = stream.next(); - if (leftBracketOrMinus.equals(LEFT_BRACKET)) { + for (; ; ) { + 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 if (leftBracketOrMinusOrNeg.equals(DOUBLE_QUOT)) { + symbolName = DOUBLE_QUOT + stream.next() + DOUBLE_QUOT; + String doubleQuot = stream.next(); + if (!doubleQuot.equals(DOUBLE_QUOT)) throw new ExpectedDoubleQuotation(stream.getLine()); } else { - symbolName = leftBracketOrMinus; + symbolName = leftBracketOrMinusOrNeg; } Expression exp = null; if (stream.checkNext() != null && stream.checkNext().equals(LEFT_BRACKET)) { @@ -272,7 +312,8 @@ Term term = new Term(symbol); int arity = 0; do { - stream.next(); // LEFT_BRACKET or COMMA + stream.next(); // LEFT_BRACKET or COMMA + if (stream.checkNext().equals(RIGHT_BRACKET)) break; arity++; Expression subTerm = parseTerm(stream, model); term.addChild(subTerm, true); @@ -283,38 +324,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(DOUBLE_QUOT) && symbolName.endsWith(DOUBLE_QUOT)) { + // a string value + exp = new Constant(symbolName.substring(1, symbolName.length() - 1), 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 +366,137 @@ 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 + 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 + while (operator.equals(DOT)) { + Expression exp = expressions.remove(expressions.size() - 1); + 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 (literalOrLeftCurlyBracket.equals(LEFT_CURLY_BRACKET)) { + term = new JsonAccessor(DataTransferModel.dotParam); + } else { + term = new JsonAccessor(DataTransferModel.dot); + } + term.addChild(exp); + term.addChild(paramTerm); + expressions.add(term); + operator = stream.checkNext(); + if (operator == null) break; + if (operator.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); + } + term.setType(type); + break; + } + } + if (operator == null) { + 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 { + break; + } } else { break; } - stream.next(); // an arithmetic operator } if (expressions.size() == 1) { // no arithmetic operators @@ -343,13 +506,41 @@ ArrayList addSubs = new ArrayList<>(); Expression first = expressions.get(0); int i = 1; - for (Symbol op: operators) { + 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); @@ -361,7 +552,7 @@ if (first != null) monomials.add(first); Expression firstMonomial = monomials.get(0); i = 1; - for (Symbol op: addSubs) { + for (Symbol op : addSubs) { Expression secondMonomial = monomials.get(i); Term term = new Term(op); term.addChild(firstMonomial); @@ -369,100 +560,266 @@ 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) { - if(token == null) return false; - if(specificTokenName == null) return false; +// 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 parseJsonTerm(TokenStream stream, DataTransferModel model) throws ExpectedRightBracket, WrongJsonExpression, ExpectedColon, ExpectedDoubleQuotation { + JsonTerm jsonTerm = new JsonTerm(); + while (stream.checkNext() != null && !stream.checkNext().equals(RIGHT_CURLY_BRACKET)) { + if (stream.checkNext() == null || !stream.checkNext().equals(DOUBLE_QUOT)) + throw new ExpectedDoubleQuotation(stream.getLine()); + String doubleQuot = stream.next(); + String key = stream.next(); + if (stream.checkNext() == null || !stream.checkNext().equals(DOUBLE_QUOT)) + throw new ExpectedDoubleQuotation(stream.getLine()); + doubleQuot = stream.next(); + if (stream.checkNext() == null || !stream.checkNext().equals(COLON)) + throw new ExpectedColon(stream.getLine()); + String colon = stream.next(); + Expression value = parseTerm(stream, model); + jsonTerm.addMember(key, value); + 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, ExpectedDoubleQuotation { + ListTerm listTerm = new ListTerm(); + listTerm.setType(DataTransferModel.typeList); + while (stream.checkNext() != null && !stream.checkNext().equals(RIGHT_SQUARE_BRACKET)) { + Expression element = parseTerm(stream, model); + listTerm.addChild(element); + if (stream.checkNext() == null || !stream.checkNext().equals(COMMA)) break; + String comma = stream.next(); + } + return listTerm; + } + + public 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, ExpectedDoubleQuotation { + 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)); + hierarchy = model.getOrPutResourceHierarchy(hierarchy); + return hierarchy; + } + + public ResourcePath parseResourcePath(TokenStream stream, DataTransferModel model) + throws ExpectedRightBracket, ExpectedRightCurlyBracket, WrongPathExpression, WrongJsonExpression, ExpectedColon, ExpectedDoubleQuotation { + 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) throw new ExpectedRightCurlyBracket(stream.getLine()); + Expression paramConstraint = null; + if (rightCurlyBracket.equals(EQUALS)) { + // Path constraint + paramConstraint = parseTerm(stream, model); + rightCurlyBracket = stream.next(); + if (rightCurlyBracket == null) throw new ExpectedRightCurlyBracket(stream.getLine()); + } + if (!rightCurlyBracket.equals(RIGHT_CURLY_BRACKET)) + throw new ExpectedRightCurlyBracket(stream.getLine()); + if (paramConstraint == null) { + path = new ResourcePath(path, paramTerm); + } else { + path = new ResourcePath(path, paramTerm, paramConstraint); + } + } 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; - + public TokenStream() { line = 0; n = 0; } - + public void addLine(String line) { lines.add(line); line = line.trim(); - tokens.add( - splitBy( - splitBy( - splitBy( - splitBy( - splitBy( - splitBy( - splitBy( - splitBy( - 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)); + ArrayList tokenList = splitByDoubleQuotation(line); + tokenList = splitBy(tokenList, ADD, ADD_REGX); + tokenList = splitBy(tokenList, MUL, MUL_REGX); + tokenList = splitBy(tokenList, SUB, SUB_REGX); + tokenList = splitBy(tokenList, DIV, DIV_REGX); + tokenList = splitBy(tokenList, MOD, MOD); + tokenList = splitBy(tokenList, EQ, EQ); + tokenList = splitBy(tokenList, NEQ, NEQ); + tokenList = splitBy(tokenList, GE, GE); + tokenList = splitBy(tokenList, LE, LE); + tokenList = splitBy(tokenList, GT, GT); + tokenList = splitBy(tokenList, LT, LT); + tokenList = splitBy(tokenList, AND, AND); + tokenList = splitBy(tokenList, OR, OR_REGX); + tokenList = splitBy(tokenList, NEG, NEG); + tokenList = splitBy(tokenList, DOT, DOT_REGX); + tokenList = splitBy(tokenList, COMMA, COMMA); + tokenList = splitBy(tokenList, COLON, COLON); + tokenList = splitBy(tokenList, LEFT_BRACKET, LEFT_BRACKET_REGX); + tokenList = splitBy(tokenList, RIGHT_BRACKET, RIGHT_BRACKET_REGX); + tokenList = splitBy(tokenList, EQUALS, EQUALS); + tokenList = splitBy(tokenList, LEFT_CURLY_BRACKET, LEFT_CURLY_BRACKET_REGX); + tokenList = splitBy(tokenList, RIGHT_CURLY_BRACKET, RIGHT_CURLY_BRACKET_REGX); + tokenList = splitBy(tokenList, LEFT_SQUARE_BRACKET, LEFT_SQUARE_BRACKET_REGX); + tokenList = splitBy(tokenList, RIGHT_SQUARE_BRACKET, RIGHT_SQUARE_BRACKET_REGX); + tokens.add(tokenList); } - - 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; } - + + private ArrayList splitByDoubleQuotation(String line) { + ArrayList newTokens = new ArrayList<>(); + String[] tokens = line.split(DOUBLE_QUOT); + boolean fFirstToken = true; + for (int i = 0; i < tokens.length; i++) { + String token = tokens[i]; + if (!fFirstToken) { + newTokens.add(new Token(DOUBLE_QUOT, true)); + } + if (!fFirstToken || token.length() > 0) { + if (i % 2 == 0) { + for (String t : token.split("[ \t]")) { + newTokens.add(new Token(t)); + } + } else { + // string literal + newTokens.add(new Token(token, true)); + } + } + fFirstToken = false; + } + if (line.endsWith(DOUBLE_QUOT)) { + newTokens.add(new Token(DOUBLE_QUOT, true)); + } + return newTokens; + } + public String next() { if (line >= tokens.size()) return null; while (n >= tokens.get(line).size()) { @@ -470,11 +827,11 @@ 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; } - + public String checkNext() { if (line >= tokens.size()) return null; while (n >= tokens.get(line).size()) { @@ -482,9 +839,9 @@ n = 0; if (line >= tokens.size()) return null; } - return tokens.get(line).get(n); + return tokens.get(line).get(n).getTokenStr(); } - + public boolean hasNext() { if (line >= tokens.size()) return false; while (n >= tokens.get(line).size()) { @@ -494,11 +851,11 @@ } return true; } - + public int getLine() { return line; } - + public String getSourceText(int from, int to) { String text = ""; for (int l = from; l <= to; l++) { @@ -507,4 +864,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..d4f7efa 100644 --- a/AlgebraicDataflowArchitectureModel/src/parser/ParserDTRAM.java +++ b/AlgebraicDataflowArchitectureModel/src/parser/ParserDTRAM.java @@ -1,36 +1,19 @@ package parser; -import java.io.BufferedReader; -import java.io.IOException; - +import application.editor.Stage; import com.mxgraph.model.mxCell; import com.mxgraph.model.mxGeometry; import com.mxgraph.model.mxIGraphModel; import com.mxgraph.view.mxCellState; import com.mxgraph.view.mxGraph; import com.mxgraph.view.mxGraphView; - import models.dataFlowModel.DataTransferModel; -import parser.exceptions.ExpectedAssignment; -import parser.exceptions.ExpectedChannel; -import parser.exceptions.ExpectedChannelName; -import parser.exceptions.ExpectedEquals; -import parser.exceptions.ExpectedFormulaChannel; -import parser.exceptions.ExpectedGeometry; -import parser.exceptions.ExpectedInOrOutOrRefKeyword; -import parser.exceptions.ExpectedIoChannel; -import parser.exceptions.ExpectedLeftCurlyBracket; -import parser.exceptions.ExpectedModel; -import parser.exceptions.ExpectedNode; -import parser.exceptions.ExpectedRHSExpression; -import parser.exceptions.ExpectedResource; -import parser.exceptions.ExpectedRightBracket; -import parser.exceptions.ExpectedStateTransition; -import parser.exceptions.WrongLHSExpression; -import parser.exceptions.WrongRHSExpression; +import parser.exceptions.*; + +import java.io.BufferedReader; public class ParserDTRAM extends Parser { - + private static final String MODEL_GROUP = "model"; private static final String GEOMETRY_GROUP = "geometry"; private static final String GEOMETORY_NODE = "node"; @@ -38,136 +21,168 @@ private static final String CHANNEL_NODE = "c"; private static final String FORMULA_CHANNEL_NODE = "fc"; private static final String IO_CHANNEL_NODE = "ioc"; - - /**-------------------------------------------------------------------------------- + + /** + * -------------------------------------------------------------------------------- * [Constructor] - /**-------------------------------------------------------------------------------- - * + * /**-------------------------------------------------------------------------------- + * * @param stream */ public ParserDTRAM(final TokenStream stream) { super(stream); } - /**-------------------------------------------------------------------------------- - * + + /** + * -------------------------------------------------------------------------------- + * * @param reader */ public ParserDTRAM(final BufferedReader reader) { super(reader); } - - /**-------------------------------------------------------------------------------- + + /** + * -------------------------------------------------------------------------------- * [public] - /**-------------------------------------------------------------------------------- - * + * /**-------------------------------------------------------------------------------- + * * @param reader + * @throws WrongJsonExpression + * @throws ExpectedColon + * @throws ExpectedDoubleQuotation */ - public DataTransferModel doParseModel() - throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedInOrOutOrRefKeyword, ExpectedStateTransition, ExpectedEquals, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, ExpectedModel, ExpectedGeometry { + public DataTransferModel doParseModel() + throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedInOrOutOrRefOrSubKeyword, ExpectedStateTransition, ExpectedEquals, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, ExpectedModel, ExpectedGeometry, ExpectedRightCurlyBracket, WrongPathExpression, WrongJsonExpression, ExpectedColon, ExpectedDoubleQuotation { DataTransferModel model = getParsedModel(); return model; } - - /**-------------------------------------------------------------------------------- - * + + /** + * -------------------------------------------------------------------------------- + * * @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{ - + public void doParseGeometry(mxGraph graph) + throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedInOrOutOrRefOrSubKeyword, ExpectedStateTransition, ExpectedEquals, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, ExpectedModel, ExpectedGeometry, ExpectedNode, ExpectedResource, ExpectedFormulaChannel, ExpectedIoChannel { + parseGeometry(graph); } - - /**-------------------------------------------------------------------------------- + + /** + * -------------------------------------------------------------------------------- * [private] - /**-------------------------------------------------------------------------------- - * + * /**-------------------------------------------------------------------------------- + * * @param stream + * @throws WrongJsonExpression + * @throws ExpectedColon + * @throws ExpectedDoubleQuotation */ 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, ExpectedDoubleQuotation { + if (!stream.hasNext()) throw new NullPointerException(); - + String modelKeyword = stream.next(); if (!modelKeyword.equals(MODEL_GROUP)) throw new ExpectedModel(stream.getLine()); if (!stream.hasNext()) throw new ExpectedModel(stream.getLine()); - + String leftBracket = stream.next(); if (!leftBracket.equals(LEFT_CURLY_BRACKET)) throw new ExpectedLeftCurlyBracket(stream.getLine()); - + DataTransferModel model = parseDataFlowModel(); - + String rightBracket = stream.next(); - if(!rightBracket.equals(RIGHT_CURLY_BRACKET))throw new ExpectedRightBracket(stream.getLine()); - + if (!rightBracket.equals(RIGHT_CURLY_BRACKET)) throw new ExpectedRightBracket(stream.getLine()); + return model; } - - /**-------------------------------------------------------------------------------- - * change graph's geometries from "DTRAM" file. + + /** + * -------------------------------------------------------------------------------- + * change graph's geometries from "DTRAM" file. + * * @param stream * @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 { - - if (!isMatchKeyword(stream.next(), GEOMETRY_GROUP)) throw new ExpectedGeometry(stream.getLine()); - - if (!isMatchKeyword(stream.next(), LEFT_CURLY_BRACKET)) throw new ExpectedLeftCurlyBracket(stream.getLine()); - + throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedInOrOutOrRefOrSubKeyword, ExpectedStateTransition, ExpectedEquals, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, ExpectedModel, ExpectedGeometry, ExpectedNode, ExpectedResource, ExpectedFormulaChannel, ExpectedIoChannel { + + if (!doesMatchToKeyword(stream.next(), GEOMETRY_GROUP)) throw new ExpectedGeometry(stream.getLine()); + + if (!doesMatchToKeyword(stream.next(), LEFT_CURLY_BRACKET)) + throw new ExpectedLeftCurlyBracket(stream.getLine()); + String node = stream.next(); while (node.equals(GEOMETORY_NODE)) { - + String rOrFcOrIocOrC = stream.next(); if (!rOrFcOrIocOrC.equals(RESOURCE_NODE) && !rOrFcOrIocOrC.equals(FORMULA_CHANNEL_NODE) && !rOrFcOrIocOrC.equals(CHANNEL_NODE) && !rOrFcOrIocOrC.equals(IO_CHANNEL_NODE)) throw new ExpectedNode(stream.getLine()); - + 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); - + Object root = graph.getDefaultParent(); + mxCell nodeLayer = (mxCell) ((mxCell) root).getChildAt(Stage.NODE_LAYER); + mxCell dataFlowLayer = (mxCell) ((mxCell) root).getChildAt(Stage.DATA_FLOW_LAYER); mxIGraphModel graphModel = graph.getModel(); - for (int i = 0; i < graph.getModel().getChildCount(root); i++) { - - Object cell = graph.getModel().getChildAt(root, i); + for (int i = 0; i < graph.getModel().getChildCount(nodeLayer); i++) { + + Object cell = graph.getModel().getChildAt(nodeLayer, i); if (!graph.getModel().isVertex(cell)) continue; - + mxGeometry geom = (mxGeometry) ((mxCell) cell).getGeometry().clone(); mxGraphView view = graph.getView(); mxCellState state = view.getState(cell); - + if (!name.equals(state.getLabel())) continue; - + + geom.setX(xC); + geom.setY(yC); + graphModel.setGeometry(cell, geom); + } + for (int i = 0; i < graph.getModel().getChildCount(dataFlowLayer); i++) { + + Object cell = graph.getModel().getChildAt(dataFlowLayer, i); + if (!graph.getModel().isVertex(cell)) continue; + + mxGeometry geom = (mxGeometry) ((mxCell) cell).getGeometry().clone(); + mxGraphView view = graph.getView(); + mxCellState state = view.getState(cell); + + if (!name.equals(state.getLabel())) continue; + geom.setX(xC); geom.setY(yC); graphModel.setGeometry(cell, geom); } node = stream.next(); } - + if (!node.equals(RIGHT_CURLY_BRACKET)) throw new ExpectedRightBracket(stream.getLine()); } } diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedAssignment.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedAssignment.java index 90a6c3e..07dd16c 100644 --- a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedAssignment.java +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedAssignment.java @@ -1,7 +1,7 @@ package parser.exceptions; public class ExpectedAssignment extends ParseException { - + public ExpectedAssignment(int line) { super(line); } diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedChannel.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedChannel.java index 385248a..f9ba6c5 100644 --- a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedChannel.java +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedChannel.java @@ -1,9 +1,9 @@ package parser.exceptions; public class ExpectedChannel extends ParseException { - + public ExpectedChannel(int line) { super(line); } - + } diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedChannelName.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedChannelName.java index 72d0336..d12043f 100644 --- a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedChannelName.java +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedChannelName.java @@ -1,9 +1,9 @@ package parser.exceptions; public class ExpectedChannelName extends ParseException { - + public ExpectedChannelName(int line) { super(line); } - + } diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedColon.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedColon.java new file mode 100644 index 0000000..f6753f2 --- /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/ExpectedDoubleQuotation.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedDoubleQuotation.java new file mode 100644 index 0000000..434bec3 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedDoubleQuotation.java @@ -0,0 +1,8 @@ +package parser.exceptions; + +public class ExpectedDoubleQuotation extends ParseException { + + public ExpectedDoubleQuotation(int line) { + super(line); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedEquals.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedEquals.java index ed322d8..c19d9d1 100644 --- a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedEquals.java +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedEquals.java @@ -1,9 +1,9 @@ package parser.exceptions; public class ExpectedEquals extends ParseException { - + public ExpectedEquals(int line) { super(line); } - + } diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedFormulaChannel.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedFormulaChannel.java index e1e9f2b..415e2e6 100644 --- a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedFormulaChannel.java +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedFormulaChannel.java @@ -1,9 +1,9 @@ package parser.exceptions; public class ExpectedFormulaChannel extends ParseException { - + public ExpectedFormulaChannel(int line) { super(line); } - + } diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedGeometry.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedGeometry.java index 1521507..e82f993 100644 --- a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedGeometry.java +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedGeometry.java @@ -1,9 +1,9 @@ package parser.exceptions; public class ExpectedGeometry extends ParseException { - + public ExpectedGeometry(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..1e8da66 --- /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/ExpectedIoChannel.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedIoChannel.java index fba4cc7..62fac8f 100644 --- a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedIoChannel.java +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedIoChannel.java @@ -1,9 +1,9 @@ package parser.exceptions; public class ExpectedIoChannel extends ParseException { - + public ExpectedIoChannel(int line) { super(line); } - + } diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedLeftCurlyBracket.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedLeftCurlyBracket.java index 30f92b0..94c49a0 100644 --- a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedLeftCurlyBracket.java +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedLeftCurlyBracket.java @@ -1,9 +1,9 @@ package parser.exceptions; public class ExpectedLeftCurlyBracket extends ParseException { - + public ExpectedLeftCurlyBracket(int line) { super(line); } - + } diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedModel.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedModel.java index 222522a..16a5b88 100644 --- a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedModel.java +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedModel.java @@ -1,9 +1,9 @@ package parser.exceptions; public class ExpectedModel extends ParseException { - + public ExpectedModel(int line) { super(line); } - + } diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedNode.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedNode.java index b3af50c..dfc3c61 100644 --- a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedNode.java +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedNode.java @@ -1,9 +1,9 @@ package parser.exceptions; public class ExpectedNode extends ParseException { - + public ExpectedNode(int line) { super(line); } - + } diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedRHSExpression.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedRHSExpression.java index 994bf77..e8939a8 100644 --- a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedRHSExpression.java +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedRHSExpression.java @@ -1,9 +1,9 @@ package parser.exceptions; public class ExpectedRHSExpression extends ParseException { - + public ExpectedRHSExpression(int line) { super(line); } - + } diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedResource.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedResource.java index dd5f00a..7465413 100644 --- a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedResource.java +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedResource.java @@ -1,9 +1,9 @@ package parser.exceptions; public class ExpectedResource extends ParseException { - + public ExpectedResource(int line) { super(line); } - + } diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedRightBracket.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedRightBracket.java index 627c57b..41b8a62 100644 --- a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedRightBracket.java +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedRightBracket.java @@ -1,9 +1,9 @@ package parser.exceptions; public class ExpectedRightBracket extends ParseException { - + public ExpectedRightBracket(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..26dd9bf --- /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/ExpectedStateTransition.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedStateTransition.java index 02d27b4..0c0adc8 100644 --- a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedStateTransition.java +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedStateTransition.java @@ -1,9 +1,9 @@ package parser.exceptions; public class ExpectedStateTransition extends ParseException { - + public ExpectedStateTransition(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..8637624 --- /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/WrongLHSExpression.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/WrongLHSExpression.java index 0ed087a..289a610 100644 --- a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/WrongLHSExpression.java +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/WrongLHSExpression.java @@ -1,9 +1,9 @@ package parser.exceptions; public class WrongLHSExpression extends ParseException { - + public WrongLHSExpression(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..7436430 --- /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/parser/exceptions/WrongRHSExpression.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/WrongRHSExpression.java index 0a82a6c..f97c49b 100644 --- a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/WrongRHSExpression.java +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/WrongRHSExpression.java @@ -1,9 +1,9 @@ package parser.exceptions; public class WrongRHSExpression extends ParseException { - + public WrongRHSExpression(int line) { super(line); } - + } diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/webService/ExpectedBaseURL.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/webService/ExpectedBaseURL.java index 1136029..a091095 100644 --- a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/webService/ExpectedBaseURL.java +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/webService/ExpectedBaseURL.java @@ -2,7 +2,7 @@ import parser.exceptions.ParseException; -public class ExpectedBaseURL extends ParseException{ +public class ExpectedBaseURL extends ParseException { public ExpectedBaseURL(int line) { super(line); diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/webService/ExpectedResources.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/webService/ExpectedResources.java index ec01cf1..45229eb 100644 --- a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/webService/ExpectedResources.java +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/webService/ExpectedResources.java @@ -2,7 +2,7 @@ import parser.exceptions.ParseException; -public class ExpectedResources extends ParseException{ +public class ExpectedResources extends ParseException { public ExpectedResources(int line) { super(line); diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/webService/ExpectedWebService.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/webService/ExpectedWebService.java index 29e8557..91d8ab9 100644 --- a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/webService/ExpectedWebService.java +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/webService/ExpectedWebService.java @@ -2,10 +2,10 @@ import parser.exceptions.ParseException; -public class ExpectedWebService extends ParseException{ +public class ExpectedWebService extends ParseException { public ExpectedWebService(int line) { super(line); } - + } diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/ChannelState.java b/AlgebraicDataflowArchitectureModel/src/simulator/ChannelState.java new file mode 100644 index 0000000..cb50a86 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/ChannelState.java @@ -0,0 +1,167 @@ +package simulator; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.dataFlowModel.DataTransferChannel; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * State of a channel, which represents the map from the values of the depended channel selectors to those of the depending channel selectors + * + * @author Nitta + * + */ +public class ChannelState implements Cloneable { + 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 new HashMap<>(); + } + 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); + } + + /** + * Add a value of a depending channel selector + * + * @param channelValues the values of depended channel selectors + * @param dependingVariable a depending channel selector + * @param itsValue its new value + */ + public void addDependingParamAndValue(List channelValues, Expression dependingVariable, Expression itsValue) { + if (referenceStructure == null) { + referenceStructure = new ReferenceStructure(); + } + referenceStructure.addDependingParamAndValue(channelValues, dependingVariable, itsValue); + } + + public Object clone() { + ChannelState newChannelState = new ChannelState(channel); + if (referenceStructure != null) + newChannelState.referenceStructure = (ReferenceStructure) referenceStructure.clone(); + return newChannelState; + } + + private static class ReferenceStructure implements Cloneable { + 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 new HashMap<>(); + } + } + 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); + } + + public Object clone() { + ReferenceStructure newReferenceStructure = new ReferenceStructure(); + if (dependingParamAndValues != null) { + newReferenceStructure.dependingParamAndValues = new HashMap<>(dependingParamAndValues); + } + if (referenceStructure != null) { + newReferenceStructure.referenceStructure = new HashMap<>(); + for (Map.Entry refEnt : referenceStructure.entrySet()) { + newReferenceStructure.referenceStructure.put(refEnt.getKey(), (ReferenceStructure) refEnt.getValue().clone()); + } + } + return newReferenceStructure; + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/Event.java b/AlgebraicDataflowArchitectureModel/src/simulator/Event.java new file mode 100644 index 0000000..79ef282 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/Event.java @@ -0,0 +1,664 @@ +package simulator; + +import models.algebra.*; +import models.dataConstraintModel.Channel; +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; + +import java.util.*; + +/** + * Event occurred at a channel + * + * @author Nitta + * + */ +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 childEvents = new HashSet<>(); + private Map>>> channelSelectorToInputOrReferenceResourcePathParam = new HashMap<>(); + private Map>>> channelSelectorToOutputResourcePathParam = new HashMap<>(); + + /** + * Constructor for an input event channel + * + * @param channel an input event channel + * @param message an message for the channel + * @param outputResPath an output side resource path of the channel + * @param outputResource an output side resource of the channel + */ + 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 the values of the channel selectors from the output resource. + List channelSelectors = channel.getAllSelectors(); + for (Selector sel : channelSelectors) { + Map.Entry> paramIdxAndPos = channelSelectorToOutputResourcePathParam.get(sel).get(outputResPath); + Resource ancestor = outputResource; + int idx = outputResPath.getPathParams().size(); + while (ancestor != null) { + if (ancestor.getResourceHierarchy().getNumParameters() > 0) { + idx--; + if (idx == paramIdxAndPos.getKey()) break; + } + ancestor = ancestor.getParent(); + } + if (ancestor != null) + channelSelectorAndValues.add(new AbstractMap.SimpleEntry<>(sel, ancestor.getParameter())); + } + } + + /** + * Constructor for a non-event channel + * + * @param channel a non-event channel + * @param inputResPath an input side resource path of the channel + * @param inputResource an input side resource of the channel + */ + public Event(DataTransferChannel channel, ResourcePath inputResPath, Resource inputResource) { + this.channel = channel; + this.isInput = false; + this.inputResourcePath = inputResPath; + this.inputResource = inputResource; + connectChannelSelectorAndPathParameters(); + + // Extract the values of the channel selectors from the input resource. + List channelSelectors = channel.getAllSelectors(); + for (Selector sel : channelSelectors) { + if (inputResPath != null) { + Map.Entry> paramIdxAndPos = channelSelectorToInputOrReferenceResourcePathParam.get(sel).get(inputResPath); + if (paramIdxAndPos != null) { + Resource ancestor = inputResource; + int idx = inputResPath.getPathParams().size(); + while (ancestor != null) { + if (ancestor.getResourceHierarchy().getNumParameters() > 0) { + idx--; + if (idx == paramIdxAndPos.getKey()) break; + } + ancestor = ancestor.getParent(); + } + channelSelectorAndValues.add(new AbstractMap.SimpleEntry<>(sel, ancestor.getParameter())); + } + } + } + } + + /** + * Constructor for a non-event channel + * + * @param channel a non-event channel + * @param inputResPath an input side resource path of the channel + * @param inputResource an input side resource of the channel + * @param channelSelectorValues the values of depended channel selectors + * @param dependingVarToVal the values of depending channel selectors + */ + 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 the values of the channel selectors from channelSelectorValues. + 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) { + Set inputAndReferenceResPaths = new HashSet<>(channel.getInputResources()); + inputAndReferenceResPaths.addAll(channel.getReferenceResources()); + for (ResourcePath resPath : inputAndReferenceResPaths) { + for (int paramIdx = 0; paramIdx < resPath.getPathParams().size(); paramIdx++) { + Expression pathParam = resPath.getPathParams().get(paramIdx); + if (pathParam.contains(sel.getExpression())) { + for (Map.Entry posAndVar : pathParam.getVariables().entrySet()) { + Position p = posAndVar.getKey(); + Variable v = posAndVar.getValue(); + if (v.equals(sel.getExpression())) { + Map>> pathToIdxAndPos = channelSelectorToInputOrReferenceResourcePathParam.get(sel); + if (pathToIdxAndPos == null) { + pathToIdxAndPos = new HashMap<>(); + channelSelectorToInputOrReferenceResourcePathParam.put(sel, pathToIdxAndPos); + } + Map.Entry> idxAndPos = pathToIdxAndPos.get(resPath); + if (idxAndPos == null) { + idxAndPos = new AbstractMap.SimpleEntry<>(paramIdx, new HashSet<>()); + pathToIdxAndPos.put(resPath, idxAndPos); + } + idxAndPos.getValue().add(p); + } + } + } + } + } + for (ResourcePath resPath : channel.getOutputResources()) { + for (int paramIdx = 0; paramIdx < resPath.getPathParams().size(); paramIdx++) { + Expression pathParam = resPath.getPathParams().get(paramIdx); + if (pathParam.contains(sel.getExpression())) { + for (Map.Entry posAndVar : pathParam.getVariables().entrySet()) { + Position p = posAndVar.getKey(); + Variable v = posAndVar.getValue(); + if (v.equals(sel.getExpression())) { + Map>> pathToIdxAndPos = channelSelectorToOutputResourcePathParam.get(sel); + if (pathToIdxAndPos == null) { + pathToIdxAndPos = new HashMap<>(); + channelSelectorToOutputResourcePathParam.put(sel, pathToIdxAndPos); + } + Map.Entry> idxAndPos = pathToIdxAndPos.get(resPath); + if (idxAndPos == null) { + idxAndPos = new AbstractMap.SimpleEntry<>(paramIdx, new HashSet<>()); + pathToIdxAndPos.put(resPath, idxAndPos); + } + idxAndPos.getValue().add(p); + } + } + } + } + } + } + } + + 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 void addChild(Event child) { + childEvents.add(child); + } + + public Set getChildren() { + return childEvents; + } + + /** + * Construct channel message, collect descendant events of this event and update the values of the depending channel selectors + * + * @param resourceStateValueProvider a resourceStateValueProvider to provide current and next resource states + * @return calculated message constraint by this event + */ + public Expression constructMessageAndDescendantEvents(IResourceStateValueProvider resourceStateValueProvider, boolean doesUpdateDependingParameters) { + + try { + Map> substitutedPositionsInMessageFromChannels = new HashMap<>(); + return constructMessageAndDesdendantEvents(resourceStateValueProvider, substitutedPositionsInMessageFromChannels, doesUpdateDependingParameters); + } catch (ResolvingMultipleDefinitionIsFutureWork | InvalidMessage | UnificationFailed e) { + e.printStackTrace(); + } + return null; + } + + private Expression constructMessageAndDesdendantEvents(IResourceStateValueProvider resourceStateValueProvider, + Map> substitutedPositionsInMessageFromChannels, boolean doesUpdateDependingParameters) + throws InvalidMessage, ResolvingMultipleDefinitionIsFutureWork, UnificationFailed { + Expression unifiedMessage = null; + Expression messageConstraint = null; + if (message != null) { + unifiedMessage = (Term) message.clone(); + } + 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); + } + }; + + // 1. 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. + 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 = messageConstraint; + } else { + unifiedMessage = 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 = messageConstraint; + } else { + unifiedMessage = unifiedMessage.unify(messageConstraint); + if (unifiedMessage == null) { + throw new UnificationFailed(); + } + } + } + } else { + Set toResolve = new HashSet<>(); + Set resolved = new HashSet<>(); + for (Set depended : dependency.values()) { + toResolve.addAll(depended); + } + for (ChannelMember depending : dependency.keySet()) { + toResolve.remove(depending); + } + if ((messageConstraint = getMessage()) instanceof Term) { + unifiedMessage = messageConstraint; + } + for (ChannelMember leafMember : toResolve) { + 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 = messageConstraint; + } else { + unifiedMessage = unifiedMessage.unify(messageConstraint); + if (unifiedMessage == null) { + throw new UnificationFailed(); + } + } + } + resolved.addAll(toResolve); + toResolve.clear(); + + // 2. Calculate message constraints from remaining members on the channel member dependency graph. + for (; ; ) { + // Identify the channel members to resolve next. + 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. + if (doesUpdateDependingParameters) { + 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.getPathParamsAndConstraints().size(); i++) { + Expression var = unfilledResPath.getPathParamsAndConstraints().get(i).getKey(); + Expression val = filledResPath.getPathParamsAndConstraints().get(i).getKey(); + Expression constraint = unfilledResPath.getPathParamsAndConstraints().get(i).getValue(); + if (constraint != null && !constraint.equals(val)) { + // The value of the path parameter does not satisfy the constraint defined for the parameter. + return null; // Not to fire this event. + } + if (!channel.getAllSelectorVariables().contains(var)) { + // Update a depending channel parameter + updateDependingParameter(var, val); + } + } + } + + // Calculate message constraint + 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 = messageConstraint; + } else { + unifiedMessage = unifiedMessage.unify(messageConstraint); + if (unifiedMessage == null) { + throw new UnificationFailed(); + } + } + } + resolved.addAll(toResolve); + toResolve.clear(); + } + // For the channel members that are depended by and depend on no other member. + for (ChannelMember remainingMem : channel.getChannelMembers()) { + if (!resolved.contains(remainingMem)) { + resolved.add(remainingMem); + // Calculate message constraint + messageConstraint = null; + if (channel.getInputChannelMembers().contains(remainingMem)) { + // Calculate message constraint from an input state transition + messageConstraint = channel.calcMessageConstraintForInputMember(remainingMem, null, resouceStateAccessor, null, substitutedPositionsInMessageFromChannels); + } else if (channel.getReferenceChannelMembers().contains(remainingMem)) { + // Calculate message constraint from a reference state transition + messageConstraint = channel.calcMessageConstraintForReferenceMember(remainingMem, null, resouceStateAccessor, null, substitutedPositionsInMessageFromChannels); + } + if (messageConstraint != null) { + if (unifiedMessage == null) { + unifiedMessage = messageConstraint; + } else { + unifiedMessage = unifiedMessage.unify(messageConstraint); + if (unifiedMessage == null) { + throw new UnificationFailed(); + } + } + } + } + } + } + if (inputResource == null) return unifiedMessage; + + // 3. Propagate parent event message to collect child events and calculate message constraints from child channels. + Resource baseResource = inputResource; + while (baseResource.getResourceHierarchy().getNumParameters() == 0) { + if (baseResource.getParent() == null) break; + baseResource = baseResource.getParent(); + } + Expression parentEventMessage = (Expression) unifiedMessage.clone(); + for (Channel childChannel : channel.getChildren()) { + // Search the deepest input or reference side resource path in each child channel that matches the channel parameters. + ChannelMember inOrRef = null; + Set channelMembers = new HashSet<>(((DataTransferChannel) childChannel).getInputChannelMembers()); + channelMembers.addAll(((DataTransferChannel) childChannel).getReferenceChannelMembers()); + for (ChannelMember cm : channelMembers) { + if (!cm.isOutside()) { + ResourcePath resPath = cm.getResource(); + if (resPath.getPathParams().containsAll(childChannel.getAllSelectorVariables())) { + inOrRef = cm; + break; + } + } + } + if (inOrRef != null) { + // Collect events for all resources under this event's input resource that matches the deepest input side resource path. + for (Resource res : baseResource.getDescendants(inOrRef.getResource().getResourceHierarchy())) { + Event childEvent = new Event((DataTransferChannel) childChannel, inOrRef.getResource(), res); + childEvent.setMessage(parentEventMessage); + messageConstraint = childEvent.constructMessageAndDesdendantEvents(resourceStateValueProvider, substitutedPositionsInMessageFromChannels, true); + if (messageConstraint != null) { + childEvent.setMessage(messageConstraint); + if (unifiedMessage == null) { + unifiedMessage = messageConstraint; + } else { + unifiedMessage = unifiedMessage.unify(messageConstraint); + if (unifiedMessage == null) { + throw new UnificationFailed(); + } + } + childEvents.add(childEvent); + } + } + } else { + // Search the deepest output side resource path in each child channel that matches the channel parameters. + ChannelMember out = null; + for (ChannelMember cm : ((DataTransferChannel) childChannel).getOutputChannelMembers()) { + if (!cm.isOutside()) { + ResourcePath resPath = cm.getResource(); + if (resPath.getPathParams().containsAll(childChannel.getAllSelectorVariables())) { + out = cm; + break; + } + } + } + if (out != null) { + // Collect events for all resources under this event's input resource that matches the deepest output side resource path. + for (Resource res : baseResource.getDescendants(out.getResource().getResourceHierarchy())) { + Event childEvent = new Event((DataTransferChannel) childChannel, parentEventMessage, out.getResource(), res); + childEvent.constructMessageAndDesdendantEvents(resourceStateValueProvider, substitutedPositionsInMessageFromChannels, true); + childEvents.add(childEvent); + } + } + } + } + + return unifiedMessage; + } + + 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 ResourceIdentifier getResourceIdentifier(ResourcePath resPath) { + ResourceIdentifier resId = ResourceIdentifier.createFrom(resPath); + for (Map.Entry chParamEnt : channelSelectorAndValues) { + Selector sel = chParamEnt.getKey(); + Map>> inputPathToIdxAndPos = channelSelectorToInputOrReferenceResourcePathParam.get(sel); + if (inputPathToIdxAndPos != null) { + Map.Entry> pathParamEnt = inputPathToIdxAndPos.get(resPath); + if (pathParamEnt != null) { + Integer paramIdx = pathParamEnt.getKey(); + if (paramIdx != null) { + Expression pathParamExp = resId.getPathParams().get(paramIdx); + if (pathParamExp instanceof Variable) { + resId.setPathParam(paramIdx, chParamEnt.getValue()); + } + } + } + } + } + for (Map.Entry chParamEnt : channelSelectorAndValues) { + Selector sel = chParamEnt.getKey(); + Map>> outputPathToIdxAndPos = channelSelectorToOutputResourcePathParam.get(sel); + if (outputPathToIdxAndPos != null) { + Map.Entry> pathParamEnt = outputPathToIdxAndPos.get(resPath); + if (pathParamEnt != null) { + Integer paramIdx = pathParamEnt.getKey(); + if (paramIdx != null) { + Expression pathParamExp = resId.getPathParams().get(paramIdx); + if (pathParamExp instanceof Variable) { + 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)); + } + } + for (Map.Entry chParamEnt : channelSelectorAndValues) { + Selector sel = chParamEnt.getKey(); + Map>> inputPathToIdxAndPos = channelSelectorToInputOrReferenceResourcePathParam.get(sel); + if (inputPathToIdxAndPos != null) { + Map.Entry> pathParamEnt = inputPathToIdxAndPos.get(resPath); + if (pathParamEnt != null) { + Integer paramIdx = pathParamEnt.getKey(); + if (paramIdx != null) { + Expression pathParamExp = resId.getPathParams().get(paramIdx); + if (pathParamExp instanceof Term && sel.getExpression() instanceof Variable) { + pathParamExp = ((Term) pathParamExp).substitute((Variable) sel.getExpression(), chParamEnt.getValue()); + resId.setPathParam(paramIdx, ((Term) pathParamExp).reduce()); + } + } + } + } + } + for (Map.Entry chParamEnt : channelSelectorAndValues) { + Selector sel = chParamEnt.getKey(); + Map>> outputPathToIdxAndPos = channelSelectorToOutputResourcePathParam.get(sel); + if (outputPathToIdxAndPos != null) { + Map.Entry> pathParamEnt = outputPathToIdxAndPos.get(resPath); + if (pathParamEnt != null) { + Integer paramIdx = pathParamEnt.getKey(); + if (paramIdx != null) { + Expression pathParamExp = resId.getPathParams().get(paramIdx); + if (pathParamExp instanceof Term && sel.getExpression() instanceof Variable) { + pathParamExp = ((Term) pathParamExp).substitute((Variable) sel.getExpression(), chParamEnt.getValue()); + resId.setPathParam(paramIdx, ((Term) pathParamExp).reduce()); + } + } + } + } + } + return resId; + } + + public ResourceIdentifier getInputResourceIdentifier(ResourcePath inputResPath) { + ResourceIdentifier resId = ResourceIdentifier.createFrom(inputResPath); + for (Map.Entry chParamEnt : channelSelectorAndValues) { + Selector sel = chParamEnt.getKey(); + if (channelSelectorToInputOrReferenceResourcePathParam.get(sel) != null) { + Map.Entry> pathParamEnt = channelSelectorToInputOrReferenceResourcePathParam.get(sel).get(inputResPath); + if (pathParamEnt != null) { + Integer paramIdx = pathParamEnt.getKey(); + if (paramIdx != null) { + Expression pathParamExp = resId.getPathParams().get(paramIdx); + if (pathParamExp instanceof Variable) { + 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)); + } + } + for (Map.Entry chParamEnt : channelSelectorAndValues) { + Selector sel = chParamEnt.getKey(); + if (channelSelectorToInputOrReferenceResourcePathParam.get(sel) != null) { + Map.Entry> pathParamEnt = channelSelectorToInputOrReferenceResourcePathParam.get(sel).get(inputResPath); + if (pathParamEnt != null) { + Integer paramIdx = pathParamEnt.getKey(); + if (paramIdx != null) { + Expression pathParamExp = resId.getPathParams().get(paramIdx); + if (pathParamExp instanceof Term && sel.getExpression() instanceof Variable) { + pathParamExp = ((Term) pathParamExp).substitute((Variable) sel.getExpression(), chParamEnt.getValue()); + resId.setPathParam(paramIdx, ((Term) pathParamExp).reduce()); + } + } + } + } + } + return resId; + } + + public ResourceIdentifier getOutputResourceIdentifier(ResourcePath outputResPath) { + ResourceIdentifier resId = ResourceIdentifier.createFrom(outputResPath); + for (Map.Entry chParamEnt : channelSelectorAndValues) { + Selector sel = chParamEnt.getKey(); + if (channelSelectorToOutputResourcePathParam.get(sel) != null) { + Map.Entry> pathParamEnt = channelSelectorToOutputResourcePathParam.get(sel).get(outputResPath); + if (pathParamEnt != null) { + Integer paramIdx = pathParamEnt.getKey(); + if (paramIdx != null) { + Expression pathParamExp = resId.getPathParams().get(paramIdx); + if (pathParamExp instanceof Variable) { + resId.setPathParam(paramIdx, chParamEnt.getValue()); + } + } + } + } + } + for (Expression var : dependingParameters.keySet()) { + int paramIdx = resId.getPathParams().indexOf(var); + if (paramIdx >= 0) { + resId.setPathParam(paramIdx, dependingParameters.get(var)); + } + } + for (Map.Entry chParamEnt : channelSelectorAndValues) { + Selector sel = chParamEnt.getKey(); + if (channelSelectorToOutputResourcePathParam.get(sel) != null) { + Map.Entry> pathParamEnt = channelSelectorToOutputResourcePathParam.get(sel).get(outputResPath); + if (pathParamEnt != null) { + Integer paramIdx = pathParamEnt.getKey(); + if (paramIdx != null) { + Expression pathParamExp = resId.getPathParams().get(paramIdx); + if (pathParamExp instanceof Term && sel.getExpression() instanceof Variable) { + pathParamExp = ((Term) pathParamExp).substitute((Variable) sel.getExpression(), chParamEnt.getValue()); + resId.setPathParam(paramIdx, ((Term) pathParamExp).reduce()); + } + } + } + } + } + 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..7ab46cb --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/Resource.java @@ -0,0 +1,212 @@ +package simulator; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Type; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.ResourceHierarchy; +import simulator.states.*; + +import java.util.*; + +/** + * A runtime instance of a resource + * + * @author Nitta + * + */ +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(Resource resource) { + this.resourceHierarchy = resource.resourceHierarchy; + this.parent = resource.parent; + this.state = (ResourceState) resource.state.clone(); + this.parameter = resource.parameter; + } + + 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 (resType != null && DataConstraintModel.typeList.isAncestorOf(resType)) { + // List resource + state = new ListResourceState(); + } else if (resType != null && DataConstraintModel.typeMap.isAncestorOf(resType)) { + // Map resource + state = new MapResourceState(); + } else { + // Json resource + if (resType == null) { + resType = DataConstraintModel.typeJson; + resourceHierarchy.setResourceStateType(resType); + } + 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 { + Expression initValue = resourceHierarchy.getInitialValue(); + if (initValue != null && initValue instanceof Constant) { + state = new PrimitiveResourceState((Constant) resourceHierarchy.getInitialValue()); + } else { + state = new PrimitiveResourceState(null); + } + } + } + + 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() { + Map childrenMap = getChildrenMap(); + if (childrenMap == null) return null; + return childrenMap.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 Set getDescendants(ResourceHierarchy res) { + if (this.getResourceHierarchy().equals(res)) { + Set descendants = new HashSet<>(); + descendants.add(this); + return descendants; + } + if (!res.toString().startsWith(this.getResourceHierarchy().toString())) return new HashSet<>(); + Set descendants = new HashSet<>(); + for (Resource child : getChildren()) { + descendants.addAll(child.getDescendants(res)); + } + return descendants; + } + + 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..f2ee2fc --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/ResourceIdentifier.java @@ -0,0 +1,135 @@ +package simulator; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.dataConstraintModel.ResourceHierarchy; +import models.dataConstraintModel.ResourcePath; + +import java.util.AbstractMap; +import java.util.HashMap; +import java.util.Map; + +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, Expression param) { + if (paramIdx < pathParams.size()) { + pathParams.set(paramIdx, new AbstractMap.SimpleEntry<>(param, null)); + if (parent != null) { + ((ResourceIdentifier) parent).setPathParam(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 { + if (getLastParam() instanceof Constant) { + resId += (String) ((Constant) getLastParam()).getValue(); + } else { + resId += getLastParam().toString(); + } + } + return resId; + } + + public boolean isInstanceOf(ResourcePath resPath) { + ResourcePath resId = this; + while (resId != null && resPath != null) { + if (resId.getResourceHierarchy().getNumParameters() == 0) { + if (resPath.getResourceHierarchy().getNumParameters() != 0 || !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, (Expression) resPath.getLastParam().clone(), res); + } + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/Simulator.java b/AlgebraicDataflowArchitectureModel/src/simulator/Simulator.java new file mode 100644 index 0000000..e9128bf --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/Simulator.java @@ -0,0 +1,590 @@ +package simulator; + +import models.algebra.*; +import models.dataConstraintModel.*; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor; +import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork; +import simulator.Event.IResourceStateValueProvider; +import simulator.interfaces.INativeInitializer; +import simulator.interfaces.INativeReceiver; + +import java.util.*; + +/** + * Simulator to run a model + * + * @author Nitta + * + */ +public class Simulator { + private DataTransferModel model; + private SystemState curState; + private List systemReceivers = new ArrayList<>(); + private List systemInitializers = new ArrayList<>(); + 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 + Resource resource = new Resource(res); + curState.addResource(resource); + Expression initialValue = res.getInitialValue(); + if (initialValue != null) { + if (initialValue instanceof Term) { + initialValue = ((Term) initialValue).reduce(); + } + curState.updateResourceState(resource.getResourceIdentifier(), null, null, initialValue); + } else if (res.getResourceStateType() != null) { + initialValue = DataConstraintModel.getDefaultValueExpression(res.getResourceStateType()); + if (initialValue != null) { + curState.updateResourceState(resource.getResourceIdentifier(), null, null, initialValue); + } + } + } + } + for (Channel channel : model.getChannels()) { + curState.addChannel((DataTransferChannel) channel); + } + for (INativeInitializer initializer : systemInitializers) { + initializer.onInitFromModel(curState); + } + 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); + Expression message = inputEvent.constructMessageAndDescendantEvents(new ResourceStateValueProvider(curState, nextSystemState), true); + if (message != null) { + inputEvent.setMessage(message); + } + nextSystemState.addEvent(inputEvent); + + fireEvent(inputEvent, curState, nextSystemState); + + curState = nextSystemState; + for (INativeReceiver receiver : systemReceivers) { + receiver.onReceiveFromModel(inputEvent, nextSystemState); + } + return nextSystemState; + } + + public void addSystemReceiver(INativeReceiver receiver) { + systemReceivers.add(receiver); + } + + public void addSystemInitializer(INativeInitializer initializer) { + systemInitializers.add(initializer); + } + + 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.getResourceIdentifier(), receiver); + } + + public void removeSystemReceiver(INativeReceiver receiver) { + systemReceivers.remove(receiver); + } + + public void removeSystemInitializer(INativeInitializer initializer) { + systemInitializers.remove(initializer); + } + + 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.getResourceIdentifier()); + 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 { + final ChannelMember[] outTarget = new ChannelMember[1]; + final Variable[] outResVar = new Variable[1]; + IResourceStateAccessor resouceStateAccessor = new IResourceStateAccessor() { + @Override + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + if (target == outTarget[0]) + return outResVar[0]; // the current state of each output resource is not to be replaced with its value. + 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(); + } + }; + + // First, fire the all child events. + for (Event childEvent : event.getChildren()) { + fireEvent(childEvent, curSystemState, nextSystemState); + } + + // Fire this event. + DataTransferChannel channel = event.getChannel(); + if (channel.getOutputResources().size() > 0) { + // For each output resource, calculate the next state. + for (ChannelMember out : channel.getOutputChannelMembers()) { + // Calculate the next state expression. + Expression nextResState = null; + outTarget[0] = out; + outResVar[0] = new Variable(channel.getChannelName() + "$" + out.getResource().toString() + "$this"); // A special variable to represent the current state of each output resource. + if (event.getMessage() instanceof Variable) { + nextResState = channel.deriveUpdateExpressionOf(out, resouceStateAccessor).getKey(); + } else { + nextResState = channel.deriveUpdateExpressionOf(out, (Term) event.getMessage(), resouceStateAccessor); + } + // Substitute each channel selector in the expression to a value. + for (Map.Entry selestorAndVal : event.getChannelSelectorAndValues()) { + Expression selExp = selestorAndVal.getKey().getExpression(); + if (nextResState instanceof Term && selExp instanceof Variable) { + nextResState = ((Term) nextResState).substitute((Variable) selExp, selestorAndVal.getValue()); + } + } + // Update the resource state. + ResourceIdentifier outResId = event.getOutputResourceIdentifier(out.getResource()); + Expression curResState = null; + if (curSystemState.getResource(outResId) != null) { + curResState = curSystemState.getResource(outResId).getState().getValue(); + } + List updatedOutResIds = nextSystemState.updateResourceState(outResId, outResVar[0], curResState, nextResState); + if (updatedOutResIds != null) { + if (out.getResource().getResourceHierarchy().isNative()) { + // For a native output resource, neither ancestor nor descendants are changed. + for (Event nextEvent : getNextEvents(outResId, curSystemState, nextSystemState)) { + fireEvent(nextEvent, curSystemState, nextSystemState); + } + } else { + // When a normal resource is updated. + Set ancestors = new HashSet<>(); + for (ResourceIdentifier updatedOutResId : updatedOutResIds) { + while (updatedOutResId != null && !ancestors.contains(updatedOutResId)) { // In addition to the target state, its ancestors' states are also changed. + ancestors.add(updatedOutResId); + 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, nextSystemState); + if (nativeReceivers.get(channel) != null) { + receiver = nativeReceivers.get(channel).get(event.getInputResource().getResourceIdentifier()); // receiver for the channel and resource + if (receiver != null) receiver.onReceiveFromModel(event, nextSystemState); + } + } + } + + private Set getNextEvents(ResourceIdentifier inResId, SystemState curSystemState, SystemState nextSystemState) { + IResourceStateValueProvider resourceStateValueProvider = new ResourceStateValueProvider(curSystemState, nextSystemState); + Set nextEvents = new HashSet<>(); + for (Channel ch : model.getChannels()) { + DataTransferChannel channel = (DataTransferChannel) ch; + ChannelState nextChannelState = nextSystemState.getChannelState(channel); + if (nextChannelState == null) { + nextChannelState = new ChannelState(channel); + nextSystemState.updateChannelState(channel, nextChannelState); + } + 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. + boolean isInputResourceDepended = false; + for (ChannelMember dependedMem : invDependency.keySet()) { + if (inResPath == dependedMem.getResource()) { + // 1) If some depending resources are to be updated by the update of an 'depended' input resource. + if (doesSatifsyPathConstraints(inResPath, inResId)) { + Event nextEvent = new Event(channel, inResPath, nextSystemState.getResource(inResId)); + Expression message = nextEvent.constructMessageAndDescendantEvents(resourceStateValueProvider, true); + if (message != null) { + nextEvent.setMessage(message); + nextSystemState.updateChannelState(channel, nextChannelState); + List channelSelValues = nextEvent.getChannelSelectorValues(); + for (Map.Entry paramEnt : nextEvent.getDependingParameters().entrySet()) { + nextChannelState.addDependingParamAndValue(channelSelValues, paramEnt.getKey(), paramEnt.getValue()); + } + nextEvents.add(nextEvent); + } + } + isInputResourceDepended = true; + } + } + boolean isInputResourceDepending = false; + for (ChannelMember dependingMem : dependency.keySet()) { + if (inResPath == dependingMem.getResource()) { + isInputResourceDepending = true; + } + } + if (!isInputResourceDepended && !isInputResourceDepending) { + if (doesSatifsyPathConstraints(inResPath, inResId)) { + Event nextEvent = new Event(channel, inResPath, nextSystemState.getResource(inResId)); + Expression message = nextEvent.constructMessageAndDescendantEvents(resourceStateValueProvider, true); + if (message != null) { + nextEvent.setMessage(message); + nextEvents.add(nextEvent); + } + } + } + for (ChannelMember dependingMem : dependency.keySet()) { + if (inResPath == dependingMem.getResource()) { + // 2) If a 'depending' resource is directly updated. + ResourcePath filledResPath = inResId; + ResourcePath unfilledResPath = inResPath; + // Extract the values of path parameters from the updated resource identifier. + Map selectorVarToVal = new HashMap<>(); + Map dependingVarToVal = new HashMap<>(); + boolean doesSatisfyConstraint = true; + for (int i = 0; i < unfilledResPath.getPathParamsAndConstraints().size(); i++) { + Expression var = unfilledResPath.getPathParamsAndConstraints().get(i).getKey(); + Expression val = filledResPath.getPathParamsAndConstraints().get(i).getKey(); + Expression constraint = unfilledResPath.getPathParamsAndConstraints().get(i).getValue(); + if (constraint != null && !constraint.equals(val)) { + // The value of the path parameter does not satisfy the constraint defined for the parameter. + doesSatisfyConstraint = false; + break; + } + if (channel.getAllSelectorVariables().contains(var)) { + selectorVarToVal.put(var, val); + } else { + dependingVarToVal.put(var, val); + } + } + if (doesSatisfyConstraint) { + List> dependedChannelSelectorValues = nextChannelState.getDependedChannelSelectorValues(dependingVarToVal); + if (dependedChannelSelectorValues != null) { + for (List channelSelectorValues : dependedChannelSelectorValues) { + // 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); + Expression message = nextEvent.constructMessageAndDescendantEvents(resourceStateValueProvider, false); + if (message != null) { + nextEvent.setMessage(message); + nextEvents.add(nextEvent); + } + } + } + } + } + } + } + } + } + // 3) If a resource in a descendant channel is directly updated. + ResourcePath inResPath = null; + for (ResourcePath path : channel.getInputResources()) { + if (path.getPathParams().containsAll(channel.getAllSelectorVariables())) { + inResPath = path; + } + } + if (inResPath != null) { + for (Map.Entry, Event> childEventEnt : collectDescendantEvents(channel, channel, inResId, nextSystemState, resourceStateValueProvider)) { + Event childEvent = childEventEnt.getValue(); + nextEvents.add(childEvent); + } + } + } + return nextEvents; + } + + private List, Event>> collectDescendantEvents(DataTransferChannel rootChannel, DataTransferChannel channel, ResourceIdentifier inResId, + SystemState nextSystemState, IResourceStateValueProvider resourceStateValueProvider) { + List, Event>> childEvents = new ArrayList<>(); + ChannelState nextChannelState = nextSystemState.getChannelState(rootChannel); + for (Channel childCh : channel.getChildren()) { + DataTransferChannel childChannel = (DataTransferChannel) childCh; + Map> dependency = childChannel.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 childInResPath : childChannel.getInputResources()) { + if (inResId.isInstanceOf(childInResPath)) { + boolean isInputResourceDepended = false; + for (ChannelMember dependedMem : invDependency.keySet()) { + if (childInResPath == dependedMem.getResource()) { + // 1) If some depending resources are to be updated by the update of an 'depended' input resource. + if (doesSatifsyPathConstraints(childInResPath, inResId)) { + Event childEvent = new Event(childChannel, childInResPath, nextSystemState.getResource(inResId)); + Expression message = childEvent.constructMessageAndDescendantEvents(resourceStateValueProvider, true); + if (message != null) { + childEvent.setMessage(message); + List childChannelSelValues = childEvent.getChannelSelectorValues(); + for (Map.Entry paramEnt : childEvent.getDependingParameters().entrySet()) { + nextChannelState.addDependingParamAndValue(childChannelSelValues, paramEnt.getKey(), paramEnt.getValue()); + } + if (childChannelSelValues.size() >= childChannel.getSelectors().size()) { + List channelSelectorValues = new ArrayList<>(childChannelSelValues); + for (int i = 0; i < childChannel.getSelectors().size(); i++) { + channelSelectorValues.remove(childChannelSelValues.size() - 1 - i); + } + childEvents.add(new AbstractMap.SimpleEntry<>(channelSelectorValues, childEvent)); + } + } + } + isInputResourceDepended = true; + } + } + boolean isInputResourceDepending = false; + for (ChannelMember dependingMem : dependency.keySet()) { + if (childInResPath == dependingMem.getResource()) { + isInputResourceDepending = true; + } + } + if (!isInputResourceDepended && !isInputResourceDepending) { + if (doesSatifsyPathConstraints(childInResPath, inResId)) { + Event childEvent = new Event(childChannel, childInResPath, nextSystemState.getResource(inResId)); + Expression message = childEvent.constructMessageAndDescendantEvents(resourceStateValueProvider, true); + if (message != null) { + childEvent.setMessage(message); + List childChannelSelValues = childEvent.getChannelSelectorValues(); + for (Map.Entry paramEnt : childEvent.getDependingParameters().entrySet()) { + nextChannelState.addDependingParamAndValue(childChannelSelValues, paramEnt.getKey(), paramEnt.getValue()); + } + if (childChannelSelValues.size() >= childChannel.getSelectors().size()) { + List channelSelectorValues = new ArrayList<>(childChannelSelValues); + for (int i = 0; i < childChannel.getSelectors().size(); i++) { + channelSelectorValues.remove(childChannelSelValues.size() - 1 - i); + } + childEvents.add(new AbstractMap.SimpleEntry<>(channelSelectorValues, childEvent)); + } + } + } + } + for (ChannelMember dependingMem : dependency.keySet()) { + if (childInResPath == dependingMem.getResource()) { + // 2) If a 'depending' resource is directly updated. + ResourcePath filledResPath = inResId; + ResourcePath unfilledResPath = childInResPath; + // Extract the values of path parameters from the updated resource identifier. + Map selectorVarToVal = new HashMap<>(); + Map dependingVarToVal = new HashMap<>(); + boolean doesSatisfyConstraint = true; + for (int i = 0; i < unfilledResPath.getPathParamsAndConstraints().size(); i++) { + Expression var = unfilledResPath.getPathParamsAndConstraints().get(i).getKey(); + Expression val = filledResPath.getPathParamsAndConstraints().get(i).getKey(); + Expression constraint = unfilledResPath.getPathParamsAndConstraints().get(i).getValue(); + if (constraint != null && !constraint.equals(val)) { + // The value of the path parameter does not satisfy the constraint defined for the parameter. + doesSatisfyConstraint = false; + break; + } + if (channel.getAllSelectorVariables().contains(var)) { + selectorVarToVal.put(var, val); + } else { + dependingVarToVal.put(var, val); + } + } + if (doesSatisfyConstraint) { + List> dependedChannelSelectorValues = nextChannelState.getDependedChannelSelectorValues(dependingVarToVal); + if (dependedChannelSelectorValues != null) { + for (List childChannelSelectorValues : dependedChannelSelectorValues) { + // 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 (!childChannelSelectorValues.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 childEvent = new Event(childChannel, childInResPath, nextSystemState.getResource(inResId), childChannelSelectorValues, dependingVarToVal); + Expression message = childEvent.constructMessageAndDescendantEvents(resourceStateValueProvider, false); + if (message != null) { + childEvent.setMessage(message); + if (childChannelSelectorValues.size() >= childChannel.getSelectors().size()) { + List channelSelectorValues = new ArrayList<>(childChannelSelectorValues); + for (int i = 0; i < childChannel.getSelectors().size(); i++) { + channelSelectorValues.remove(childChannelSelectorValues.size() - 1 - i); + } + childEvents.add(new AbstractMap.SimpleEntry<>(channelSelectorValues, childEvent)); + } + } + } + } + } + } + } + } + } + } + // 3) If a resource in a descendant channel is directly updated. + childEvents.addAll(collectDescendantEvents(rootChannel, childChannel, inResId, nextSystemState, resourceStateValueProvider)); + } + List, Event>> events = new ArrayList<>(); + ResourcePath inResPath = null; + for (ResourcePath path : channel.getInputResources()) { + if (path.getPathParams().containsAll(channel.getAllSelectorVariables())) { + inResPath = path; + } + } + for (Map.Entry, Event> childEventEnt : childEvents) { + List channelSelectorValues = childEventEnt.getKey(); + Event childEvent = childEventEnt.getValue(); + Map dependingVarToVal = nextChannelState.getDependingParamAndValues(channelSelectorValues); + Event event = new Event((DataTransferChannel) channel, inResPath, nextSystemState.getResource(inResId), channelSelectorValues, dependingVarToVal); + event.addChild(childEvent); + if (channelSelectorValues.size() >= channel.getSelectors().size()) { + List parentChannelSelectorValues = new ArrayList<>(channelSelectorValues); + for (int i = 0; i < channel.getSelectors().size(); i++) { + parentChannelSelectorValues.remove(channelSelectorValues.size() - 1 - i); + } + events.add(new AbstractMap.SimpleEntry<>(parentChannelSelectorValues, event)); + } + } + return events; + } + + private boolean doesSatifsyPathConstraints(ResourcePath resPath, ResourceIdentifier resId) { + for (int i = 0; i < resPath.getPathParamsAndConstraints().size(); i++) { + Expression val = resId.getPathParamsAndConstraints().get(i).getKey(); + Expression constraint = resPath.getPathParamsAndConstraints().get(i).getValue(); + if (constraint != null) { + String valStr = val.toString(); + if (val instanceof Constant) { + valStr = (String) ((Constant) val).getValue(); + } + String constStr = constraint.toString(); + if (constraint instanceof Constant) { + constStr = (String) ((Constant) constraint).getValue(); + } + if (!constStr.equals(valStr)) { + // The value of the path parameter does not satisfy the constraint defined for the parameter. + return false; // Not to fire this event. + } + } + } + return true; + } + + private static class ResourceStateValueProvider implements IResourceStateValueProvider { + SystemState curSystemState; + SystemState nextSystemState; + + public ResourceStateValueProvider(SystemState curSystemState, SystemState nextSystemState) { + this.curSystemState = curSystemState; + this.nextSystemState = nextSystemState; + } + + @Override + public Expression getCurrentStateValueOf(ResourceIdentifier resId) { + if (curSystemState.getResource(resId) == null) return null; + return curSystemState.getResource(resId).getState().getValue(); + } + + @Override + public Expression getNextStateValueOf(ResourceIdentifier resId) { + if (nextSystemState.getResource(resId) == null) return null; + return nextSystemState.getResource(resId).getState().getValue(); + } + } + + ; +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/SystemState.java b/AlgebraicDataflowArchitectureModel/src/simulator/SystemState.java new file mode 100644 index 0000000..1a8324d --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/SystemState.java @@ -0,0 +1,1048 @@ +package simulator; + +import models.algebra.*; +import models.dataConstraintModel.*; +import models.dataFlowModel.DataTransferChannel; +import simulator.states.*; + +import java.util.*; + +/** + * The whole state of a running model + * + * @author Nitta + * + */ +public class SystemState { + private Set rootResources = new HashSet<>(); + private Map channelStates = new HashMap<>(); + private List events = new ArrayList<>(); + + public SystemState() { + } + + public SystemState(SystemState prevState) { + for (Resource resource : prevState.getRootResources()) { + rootResources.add(new Resource(resource)); + } + for (Map.Entry channelEnt : prevState.getChannelStates().entrySet()) { + if (channelEnt.getValue() != null) { + channelStates.put(channelEnt.getKey(), (ChannelState) channelEnt.getValue().clone()); + } else { + channelStates.put(channelEnt.getKey(), null); + } + } + } + + 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 curResVar a variable to represent the current state of the resource (may be replaced with resCurStateVal) + * @param resCurStateVal the value of the current state of the resource + * @param resNextStateVal the value of the new state of the resource (may contain curResVar) + * @return the identifier of the deepest resource created by this update. + */ + public List updateResourceState(ResourceIdentifier resourceIdentifier, Variable curResVar, Expression resCurStateVal, Expression resNextStateVal) { + Type resType = resourceIdentifier.getResourceStateType(); + if (resNextStateVal instanceof Term) { + Term termValue = (Term) resNextStateVal; + if (termValue.getSymbol().equals(DataConstraintModel.cond)) { + // If resNextStateVal is a conditional term, then calculates it here. + Expression condExp = termValue.getChild(0); + if (condExp instanceof Term) { + condExp = ((Term) condExp).substitute(curResVar, resCurStateVal); + condExp = ((Term) condExp).reduce(); + } + List condArgs = new ArrayList<>(); + condArgs.add(condExp); + condArgs.add(termValue.getChild(1)); + condArgs.add(termValue.getChild(2)); + resNextStateVal = DataConstraintModel.cond.calculate(condArgs); + if (resNextStateVal != null) { + return updateResourceState(resourceIdentifier, curResVar, resCurStateVal, resNextStateVal); + } + return null; + } + } + if (resType != null && DataConstraintModel.typeList.isAncestorOf(resType)) { + if (resNextStateVal instanceof Constant) { + } else if (resNextStateVal instanceof ListTerm) { + ListTerm listValue = (ListTerm) resNextStateVal; + Resource res = getResource(resourceIdentifier); + ResourceState state = res.getState(); + if (state instanceof ListResourceState) { + List createdResources = new ArrayList<>(); + ((ListResourceState) state).clearChildStates(); + for (int i = 0; i < listValue.size(); i++) { + Expression childExp = new Constant(Integer.toString(i), DataConstraintModel.typeInt); + ResourceHierarchy childResourceHierarchy = null; + if (resourceIdentifier.getResourceHierarchy().getChildren() != null && resourceIdentifier.getResourceHierarchy().getChildren().size() > 0) { + childResourceHierarchy = resourceIdentifier.getResourceHierarchy().getChildren().iterator().next(); + } else { + Type childResType = null; + if (listValue.get(i) instanceof Variable) { + childResType = ((Variable) listValue.get(i)).getType(); + } else if (listValue.get(i) instanceof Term) { + childResType = ((Term) listValue.get(i)).getType(); + } + childResourceHierarchy = new ResourceHierarchy(resourceIdentifier.getResourceHierarchy(), 1, childResType); + } + ResourceIdentifier childResourceIdentifier = new ResourceIdentifier(resourceIdentifier, childExp, childResourceHierarchy); + Map.Entry> childInfo = createResourceState(childResourceIdentifier, curResVar, resCurStateVal, listValue.get(i)); + ((ListResourceState) state).addChildState(childInfo.getKey()); + createdResources.addAll(childInfo.getValue()); + } + return createdResources; + } + return null; + } else if (resNextStateVal instanceof Term) { + Term listValue = (Term) resNextStateVal; + if (listValue.getSymbol().equals(DataConstraintModel.append)) { + List createdResources = new ArrayList<>(); + Resource res = getResource(resourceIdentifier); + ResourceState state = res.getState(); + if (listValue.getChild(0) instanceof Term) { + if (listValue.getChild(0) instanceof Constant && ((Constant) listValue.getChild(0)).getSymbol().equals(DataConstraintModel.nil)) { + // If the child list is nil. + ResourceState parentState = res.getParent().getState(); + ResourceState newState = new ListResourceState(); + if (parentState instanceof CompositeResourceState) { + ((CompositeResourceState) parentState).replaceChildState(state, newState); + } + state = newState; + res.changeState(state); + } else { + Expression childList = ((Term) listValue.getChild(0)).reduce(); + if (childList instanceof ListTerm) { + // If the child list is a list literal. + List childInfo = updateResourceState(resourceIdentifier, curResVar, resCurStateVal, childList); // one more recursion. + if (childInfo != null) createdResources.addAll(childInfo); + } else if (childList instanceof Term) { + // There may or may not be the current state of the resource (curResVar) under the child. + List childInfo = updateResourceState(resourceIdentifier, curResVar, resCurStateVal, listValue.getChild(0)); + if (childInfo != null) createdResources.addAll(childInfo); + } + } + } + Expression childExp = null; + if (state instanceof ListResourceState) { + childExp = new Constant(Integer.toString(((ListResourceState) state).getChildStates().size()), DataConstraintModel.typeInt); + } else if (state instanceof PrimitiveResourceState && ((PrimitiveResourceState) state).getValue().getSymbol().equals(DataConstraintModel.nil)) { + // If the value of state is nil. + childExp = new Constant("0", DataConstraintModel.typeInt); + 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 { + Type childResType = null; + if (listValue.getChild(1) instanceof Variable) { + childResType = ((Variable) listValue.getChild(1)).getType(); + } else if (listValue.getChild(1) instanceof Term) { + childResType = ((Term) listValue.getChild(1)).getType(); + } + childResourceHierarchy = new ResourceHierarchy(resourceIdentifier.getResourceHierarchy(), 1, childResType); + } + ResourceIdentifier childResourceIdentifier = new ResourceIdentifier(resourceIdentifier, childExp, childResourceHierarchy); + Map.Entry> childInfo = createResourceState(childResourceIdentifier, curResVar, resCurStateVal, listValue.getChild(1)); + ((ListResourceState) state).addChildState(childInfo.getKey()); + createdResources.addAll(childInfo.getValue()); + return createdResources; + } else if (listValue.getSymbol().equals(DataConstraintModel.set)) { + Expression childIdxExp = listValue.getChild(1); + if (childIdxExp instanceof Term) { + childIdxExp = ((Term) childIdxExp).substitute(curResVar, resCurStateVal); + childIdxExp = ((Term) childIdxExp).reduce(); + } + if (childIdxExp instanceof Constant) { + List createdResources = new ArrayList<>(); + Resource res = getResource(resourceIdentifier); + ResourceState state = res.getState(); + if (listValue.getChild(0) instanceof Term) { + if (listValue.getChild(0) instanceof Constant && ((Constant) listValue.getChild(0)).getSymbol().equals(DataConstraintModel.nil)) { + // If the child list is nil. + ResourceState parentState = res.getParent().getState(); + ResourceState newState = new ListResourceState(); + if (parentState instanceof CompositeResourceState) { + ((CompositeResourceState) parentState).replaceChildState(state, newState); + } + state = newState; + res.changeState(state); + } else { + Expression childList = ((Term) listValue.getChild(0)).reduce(); + if (childList instanceof ListTerm) { + // If the child list is a list literal. + List childInfo = updateResourceState(resourceIdentifier, curResVar, resCurStateVal, childList); // one more recursion. + if (childInfo != null) createdResources.addAll(childInfo); + } else if (childList instanceof Term) { + // There may or may not be the current state of the resource (curResVar) under the child. + List childInfo = updateResourceState(resourceIdentifier, curResVar, resCurStateVal, listValue.getChild(0)); + if (childInfo != null) createdResources.addAll(childInfo); + } + } + } + ResourceHierarchy childResourceHierarchy = null; + if (resourceIdentifier.getResourceHierarchy().getChildren() != null && resourceIdentifier.getResourceHierarchy().getChildren().size() > 0) { + childResourceHierarchy = resourceIdentifier.getResourceHierarchy().getChildren().iterator().next(); + } else { + Type childResType = null; + if (listValue.getChild(2) instanceof Variable) { + childResType = ((Variable) listValue.getChild(2)).getType(); + } else if (listValue.getChild(2) instanceof Term) { + childResType = ((Term) listValue.getChild(2)).getType(); + } + childResourceHierarchy = new ResourceHierarchy(resourceIdentifier.getResourceHierarchy(), 1, childResType); + } + ResourceIdentifier childResourceIdentifier = new ResourceIdentifier(resourceIdentifier, childIdxExp, childResourceHierarchy); + Map.Entry> childInfo = createResourceState(childResourceIdentifier, curResVar, resCurStateVal, listValue.getChild(2)); + ((ListResourceState) state).replaceChildState(Integer.parseInt(childIdxExp.toString()), childInfo.getKey()); + createdResources.addAll(childInfo.getValue()); + return createdResources; + } + } else if (listValue.getSymbol().equals(DataConstraintModel.remove)) { + Expression childIdxExp = listValue.getChild(1); + if (childIdxExp instanceof Term) { + childIdxExp = ((Term) childIdxExp).substitute(curResVar, resCurStateVal); + childIdxExp = ((Term) childIdxExp).reduce(); + } + if (childIdxExp instanceof Constant) { + List createdResources = new ArrayList<>(); + Resource res = getResource(resourceIdentifier); + ResourceState state = res.getState(); + if (listValue.getChild(0) instanceof Term) { + if (listValue.getChild(0) instanceof Constant && ((Constant) listValue.getChild(0)).getSymbol().equals(DataConstraintModel.nil)) { + // If the child list is nil. + ResourceState parentState = res.getParent().getState(); + ResourceState newState = new ListResourceState(); + if (parentState instanceof CompositeResourceState) { + ((CompositeResourceState) parentState).replaceChildState(state, newState); + } + state = newState; + res.changeState(state); + } else { + Expression childList = ((Term) listValue.getChild(0)).reduce(); + if (childList instanceof ListTerm) { + // If the child list is a list literal. + List childInfo = updateResourceState(resourceIdentifier, curResVar, resCurStateVal, childList); // one more recursion. + if (childInfo != null) createdResources.addAll(childInfo); + } else if (childList instanceof Term) { + // There may or may not be the current state of the resource (curResVar) under the child. + List childInfo = updateResourceState(resourceIdentifier, curResVar, resCurStateVal, listValue.getChild(0)); + if (childInfo != null) createdResources.addAll(childInfo); + } + } + } + ((ListResourceState) state).removeChildState(Integer.parseInt(childIdxExp.toString())); + createdResources.add(resourceIdentifier); + return createdResources; + } + } + } + } else if (resType != null && DataConstraintModel.typeMap.isAncestorOf(resType)) { + if (resNextStateVal instanceof Constant) { + } else if (resNextStateVal instanceof JsonTerm) { + JsonTerm mapValue = (JsonTerm) resNextStateVal; + Resource res = getResource(resourceIdentifier); + ResourceState state = res.getState(); + if (state instanceof MapResourceState) { + List createdResources = new ArrayList<>(); + ((MapResourceState) state).clearChildStates(); + for (String key : mapValue.keySet()) { + Expression childExp = new Constant(key, DataConstraintModel.typeString); + ResourceHierarchy childResourceHierarchy = null; + if (resourceIdentifier.getResourceHierarchy().getChildren() != null && resourceIdentifier.getResourceHierarchy().getChildren().size() > 0) { + childResourceHierarchy = resourceIdentifier.getResourceHierarchy().getChildren().iterator().next(); + } else { + Type childResType = null; + if (mapValue.get(key) instanceof Variable) { + childResType = ((Variable) mapValue.get(key)).getType(); + } else if (mapValue.get(key) instanceof Term) { + childResType = ((Term) mapValue.get(key)).getType(); + } + childResourceHierarchy = new ResourceHierarchy(resourceIdentifier.getResourceHierarchy(), 1, childResType); + } + ResourceIdentifier childResourceIdentifier = new ResourceIdentifier(resourceIdentifier, childExp, childResourceHierarchy); + Map.Entry> childInfo = createResourceState(childResourceIdentifier, curResVar, resCurStateVal, mapValue.get(key)); + ((MapResourceState) state).addChildState(key, childInfo.getKey()); + createdResources.addAll(childInfo.getValue()); + } + return createdResources; + } + return null; + } else if (resNextStateVal instanceof MapTerm) { + MapTerm mapValue = (MapTerm) resNextStateVal; + Resource res = getResource(resourceIdentifier); + ResourceState state = res.getState(); + if (state instanceof MapResourceState) { + ((MapResourceState) state).clearChildStates(); + List createdResources = new ArrayList<>(); + for (String key : mapValue.keySet()) { + Expression childExp = new Constant(key, DataConstraintModel.typeString); + ResourceHierarchy childResourceHierarchy = null; + if (resourceIdentifier.getResourceHierarchy().getChildren() != null && resourceIdentifier.getResourceHierarchy().getChildren().size() > 0) { + childResourceHierarchy = resourceIdentifier.getResourceHierarchy().getChildren().iterator().next(); + } else { + Type childResType = null; + if (mapValue.get(key) instanceof Variable) { + childResType = ((Variable) mapValue.get(key)).getType(); + } else if (mapValue.get(key) instanceof Term) { + childResType = ((Term) mapValue.get(key)).getType(); + } + childResourceHierarchy = new ResourceHierarchy(resourceIdentifier.getResourceHierarchy(), 1, childResType); + } + ResourceIdentifier childResourceIdentifier = new ResourceIdentifier(resourceIdentifier, childExp, childResourceHierarchy); + Map.Entry> childInfo = createResourceState(childResourceIdentifier, curResVar, resCurStateVal, mapValue.get(key)); + ((MapResourceState) state).addChildState(key, childInfo.getKey()); + createdResources.addAll(childInfo.getValue()); + } + return createdResources; + } + return null; + } else if (resNextStateVal instanceof Term) { + Term mapValue = (Term) resNextStateVal; + if (mapValue.getSymbol().equals(DataConstraintModel.insert)) { + List createdResources = new ArrayList<>(); + Expression childKeyExp = mapValue.getChild(1); + if (childKeyExp instanceof Term) { + childKeyExp = ((Term) childKeyExp).substitute(curResVar, resCurStateVal); + childKeyExp = ((Term) childKeyExp).reduce(); + } + if (childKeyExp instanceof Constant) { + Resource res = getResource(resourceIdentifier); + ResourceState state = res.getState(); + if (mapValue.getChild(0) instanceof Term) { + if (mapValue.getChild(0) instanceof Constant && ((Constant) mapValue.getChild(0)).getSymbol().equals(DataConstraintModel.nil)) { + // If the child map 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); + } else { + Expression childMap = ((Term) mapValue.getChild(0)).reduce(); + if (childMap instanceof MapTerm) { + // If the child map is a map literal. + List childInfo = updateResourceState(resourceIdentifier, curResVar, resCurStateVal, childMap); // one more recursion + if (childInfo != null) createdResources.addAll(childInfo); + } else if (childMap instanceof Term) { + // There may or may not be the current state of the resource (curResVar) under the child. + List childInfo = updateResourceState(resourceIdentifier, curResVar, resCurStateVal, mapValue.getChild(0)); + if (childInfo != null) createdResources.addAll(childInfo); + } + } + } + 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 { + Type childResType = null; + if (mapValue.getChild(2) instanceof Variable) { + childResType = ((Variable) mapValue.getChild(2)).getType(); + } else if (mapValue.getChild(2) instanceof Term) { + childResType = ((Term) mapValue.getChild(2)).getType(); + } + childResourceHierarchy = new ResourceHierarchy(resourceIdentifier.getResourceHierarchy(), 1, childResType); + } + ResourceIdentifier childResourceIdentifier = new ResourceIdentifier(resourceIdentifier, childKeyExp, childResourceHierarchy); + String childId = (String) ((Constant) childKeyExp).getValue(); + Map.Entry> childInfo = createResourceState(childResourceIdentifier, curResVar, resCurStateVal, mapValue.getChild(2)); + ((MapResourceState) state).addChildState(childId, childInfo.getKey()); + createdResources.addAll(childInfo.getValue()); + return createdResources; + } + } else if (mapValue.getSymbol().equals(DataConstraintModel.delete)) { + Expression childKeyExp = mapValue.getChild(1); + if (childKeyExp instanceof Term) { + childKeyExp = ((Term) childKeyExp).substitute(curResVar, resCurStateVal); + childKeyExp = ((Term) childKeyExp).reduce(); + } + if (childKeyExp instanceof Constant) { + List createdResources = new ArrayList<>(); + Resource res = getResource(resourceIdentifier); + ResourceState state = res.getState(); + if (mapValue.getChild(0) instanceof Term) { + if (mapValue.getChild(0) instanceof Constant && ((Constant) mapValue.getChild(0)).getSymbol().equals(DataConstraintModel.nil)) { + // If the child map 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); + } else { + Expression childMap = ((Term) mapValue.getChild(0)).reduce(); + if (childMap instanceof MapTerm) { + // If the child map is a map literal. + List childInfo = updateResourceState(resourceIdentifier, curResVar, resCurStateVal, childMap); // one more recursion + if (childInfo != null) createdResources.addAll(childInfo); + } else if (childMap instanceof Term) { + // There may or may not be the current state of the resource (curResVar) under the child. + List childInfo = updateResourceState(resourceIdentifier, curResVar, resCurStateVal, mapValue.getChild(0)); + if (childInfo != null) createdResources.addAll(childInfo); + } + } + } + String childId = (String) ((Constant) childKeyExp).getValue(); + ((MapResourceState) state).removeChildState(childId); + createdResources.add(resourceIdentifier); + return createdResources; + } + } + } + } else if (resType != null && DataConstraintModel.typeJson.isAncestorOf(resType)) { + if (resNextStateVal instanceof Constant) { + } else if (resNextStateVal instanceof JsonTerm) { + JsonTerm jsonValue = (JsonTerm) resNextStateVal; + Resource res = getResource(resourceIdentifier); + ResourceState state = res.getState(); + if (state instanceof JsonResourceState) { + List createdResources = new ArrayList<>(); + for (String key : jsonValue.keySet()) { + ResourceHierarchy childResourceHierarchy = null; + for (ResourceHierarchy childRes : resourceIdentifier.getResourceHierarchy().getChildren()) { + if (childRes.getResourceName().equals(key)) { + childResourceHierarchy = childRes; + break; + } + } + if (childResourceHierarchy == null) { + Type childResType = null; + if (jsonValue.get(key) instanceof Variable) { + childResType = ((Variable) jsonValue.get(key)).getType(); + } else if (jsonValue.get(key) instanceof Term) { + childResType = ((Term) jsonValue.get(key)).getType(); + } + childResourceHierarchy = new ResourceHierarchy(resourceIdentifier.getResourceHierarchy(), key, childResType); + } + ResourceIdentifier childResourceIdentifier = new ResourceIdentifier(resourceIdentifier, key, childResourceHierarchy); + Map.Entry> childInfo = createResourceState(childResourceIdentifier, curResVar, resCurStateVal, jsonValue.get(key)); + ResourceState childState = childInfo.getKey(); + if (res.getChildrenMap() != null && res.getChildrenMap().get(key) != null) { + res.getChildrenMap().get(key).changeState(childState); + } + ((JsonResourceState) state).addChildState(key, childState); + createdResources.addAll(childInfo.getValue()); + } + return createdResources; + } + return null; + } else if (resNextStateVal instanceof Term) { + Term jsonValue = (Term) resNextStateVal; + Resource res = getResource(resourceIdentifier); + ResourceState state = res.getState(); + List createdResources = new ArrayList<>(); + while (jsonValue.getSymbol().equals(DataConstraintModel.addMember)) { + Expression childKeyExp = jsonValue.getChild(1); + if (childKeyExp instanceof Constant) { + String memberName = (String) ((Constant) childKeyExp).getValue(); + ResourceHierarchy childResourceHierarchy = null; + for (ResourceHierarchy childRes : resourceIdentifier.getResourceHierarchy().getChildren()) { + if (childRes.getResourceName().equals(memberName)) { + childResourceHierarchy = childRes; + break; + } + } + if (childResourceHierarchy == null) { + Type childResType = null; + if (jsonValue.getChild(2) instanceof Variable) { + childResType = ((Variable) jsonValue.getChild(2)).getType(); + } else if (jsonValue.getChild(2) instanceof Term) { + childResType = ((Term) jsonValue.getChild(2)).getType(); + } + childResourceHierarchy = new ResourceHierarchy(resourceIdentifier.getResourceHierarchy(), memberName, childResType); + } + ResourceIdentifier childResourceIdentifier = new ResourceIdentifier(resourceIdentifier, memberName, childResourceHierarchy); + Map.Entry> childInfo = createResourceState(childResourceIdentifier, curResVar, resCurStateVal, jsonValue.getChild(2)); + ((JsonResourceState) state).addChildState(memberName, childInfo.getKey()); + createdResources.addAll(childInfo.getValue()); + } + if (!(jsonValue.getChild(0) instanceof Term)) break; + jsonValue = (Term) jsonValue.getChild(0); + } + return createdResources; + } + } else { + if (curResVar != null && resCurStateVal != null) { + if (resNextStateVal instanceof Term) { + resNextStateVal = ((Term) resNextStateVal).substitute(curResVar, resCurStateVal); + resNextStateVal = ((Term) resNextStateVal).reduce(); + } + } + if (resNextStateVal instanceof Constant) { + Resource res = getResource(resourceIdentifier); + ResourceState state = null; + if (res != null) { + state = res.getState(); + ((PrimitiveResourceState) state).setValue((Constant) resNextStateVal); + } 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((String) ((Constant) resourceIdentifier.getLastParam()).getValue(), new PrimitiveResourceState((Constant) resNextStateVal)); + } else if (parentResType != null && DataConstraintModel.typeJson.isAncestorOf(parentResType)) { + JsonResourceState parentState = (JsonResourceState) getResource(parentResId).getState(); + parentState.addChildState(resourceIdentifier.getLeafResourceName(), new PrimitiveResourceState((Constant) resNextStateVal)); + } + } + List updatedResources = new ArrayList<>(); + updatedResources.add(resourceIdentifier); + return updatedResources; + } + } + return null; + } + + public Map.Entry> createResourceState(ResourceIdentifier resourceIdentifier, Variable curResVar, Expression resCurStateVal, Expression resStateValue) { + Type resType = resourceIdentifier.getResourceStateType(); + if (resType == null && 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 ListTerm) { + ListTerm listValue = (ListTerm) resStateValue; + ListResourceState state = new ListResourceState(); + Map.Entry> createInfo = new AbstractMap.SimpleEntry<>(state, new ArrayList<>()); + for (int i = 0; i < listValue.size(); i++) { + Expression childExp = new Constant(Integer.toString(i), DataConstraintModel.typeInt); + ResourceHierarchy childResourceHierarchy = null; + if (resourceIdentifier.getResourceHierarchy().getChildren() != null && resourceIdentifier.getResourceHierarchy().getChildren().size() > 0) { + childResourceHierarchy = resourceIdentifier.getResourceHierarchy().getChildren().iterator().next(); + } else { + Type childResType = null; + if (listValue.get(i) instanceof Variable) { + childResType = ((Variable) listValue.get(i)).getType(); + } else if (listValue.get(i) instanceof Term) { + childResType = ((Term) listValue.get(i)).getType(); + } + childResourceHierarchy = new ResourceHierarchy(resourceIdentifier.getResourceHierarchy(), 1, childResType); + } + ResourceIdentifier childResourceIdentifier = new ResourceIdentifier(resourceIdentifier, childExp, childResourceHierarchy); + Map.Entry> childInfo = createResourceState(childResourceIdentifier, curResVar, resCurStateVal, listValue.get(i)); + state.addChildState(childInfo.getKey()); + createInfo.getValue().addAll(childInfo.getValue()); + createInfo = new AbstractMap.SimpleEntry<>(state, createInfo.getValue()); + } + return createInfo; + } else if (resStateValue instanceof Term) { + Term listValue = (Term) resStateValue; + if (listValue.getSymbol().equals(DataConstraintModel.append)) { + List createdResources = new ArrayList<>(); + ListResourceState state = new ListResourceState(); + if (listValue.getChild(0) instanceof Term) { + if (listValue.getChild(0) instanceof Constant && ((Constant) listValue.getChild(0)).getSymbol().equals(DataConstraintModel.nil)) { + // If the child list is nil. + } else { + Expression childList = ((Term) listValue.getChild(0)); + childList = ((Term) childList).substitute(curResVar, resStateValue); + childList = ((Term) childList).reduce(); + if (childList instanceof ListTerm) { + // If the child list is a list literal. + Map.Entry> childInfo = createResourceState(resourceIdentifier, curResVar, resCurStateVal, childList); // one more recursion. + state = (ListResourceState) childInfo.getKey(); + if (childInfo != null) createdResources.addAll(childInfo.getValue()); + } else if (childList instanceof Term) { + // If the child list is a list term. + Map.Entry> childInfo = createResourceState(resourceIdentifier, curResVar, resCurStateVal, listValue.getChild(0)); + state = (ListResourceState) childInfo.getKey(); + if (childInfo != null) createdResources.addAll(childInfo.getValue()); + } + } + } + Expression childExp = new Constant(Integer.toString(((ListResourceState) state).getChildStates().size()), DataConstraintModel.typeInt); + ResourceHierarchy childResourceHierarchy = null; + if (resourceIdentifier.getResourceHierarchy().getChildren() != null && resourceIdentifier.getResourceHierarchy().getChildren().size() > 0) { + childResourceHierarchy = resourceIdentifier.getResourceHierarchy().getChildren().iterator().next(); + } else { + Type childResType = null; + if (listValue.getChild(1) instanceof Variable) { + childResType = ((Variable) listValue.getChild(1)).getType(); + } else if (listValue.getChild(1) instanceof Term) { + childResType = ((Term) listValue.getChild(1)).getType(); + } + childResourceHierarchy = new ResourceHierarchy(resourceIdentifier.getResourceHierarchy(), 1, childResType); + } + ResourceIdentifier childResourceIdentifier = new ResourceIdentifier(resourceIdentifier, childExp, childResourceHierarchy); + Map.Entry> childInfo = createResourceState(childResourceIdentifier, curResVar, resCurStateVal, listValue.getChild(1)); + state.addChildState(childInfo.getKey()); + createdResources.addAll(childInfo.getValue()); + return new AbstractMap.SimpleEntry<>(state, createdResources); + } else if (listValue.getSymbol().equals(DataConstraintModel.set)) { + Expression childIdxExp = listValue.getChild(1); + if (childIdxExp instanceof Term) { + childIdxExp = ((Term) childIdxExp).substitute(curResVar, resCurStateVal); + childIdxExp = ((Term) childIdxExp).reduce(); + } + if (childIdxExp instanceof Constant) { + List createdResources = new ArrayList<>(); + ListResourceState state = new ListResourceState(); + if (listValue.getChild(0) instanceof Term) { + if (listValue.getChild(0) instanceof Constant && ((Constant) listValue.getChild(0)).getSymbol().equals(DataConstraintModel.nil)) { + // If the child list is nil. + } else { + Expression childList = ((Term) listValue.getChild(0)); + childList = ((Term) childList).substitute(curResVar, resStateValue); + childList = ((Term) childList).reduce(); + if (childList instanceof ListTerm) { + // If the child list is a list literal. + Map.Entry> childInfo = createResourceState(resourceIdentifier, curResVar, resCurStateVal, childList); // one more recursion. + state = (ListResourceState) childInfo.getKey(); + if (childInfo != null) createdResources.addAll(childInfo.getValue()); + } else if (childList instanceof Term) { + // If the child list is a list term. + Map.Entry> childInfo = createResourceState(resourceIdentifier, curResVar, resCurStateVal, listValue.getChild(0)); + state = (ListResourceState) childInfo.getKey(); + if (childInfo != null) createdResources.addAll(childInfo.getValue()); + } + } + } + ResourceHierarchy childResourceHierarchy = null; + if (resourceIdentifier.getResourceHierarchy().getChildren() != null && resourceIdentifier.getResourceHierarchy().getChildren().size() > 0) { + childResourceHierarchy = resourceIdentifier.getResourceHierarchy().getChildren().iterator().next(); + } else { + Type childResType = null; + if (listValue.getChild(2) instanceof Variable) { + childResType = ((Variable) listValue.getChild(2)).getType(); + } else if (listValue.getChild(2) instanceof Term) { + childResType = ((Term) listValue.getChild(2)).getType(); + } + childResourceHierarchy = new ResourceHierarchy(resourceIdentifier.getResourceHierarchy(), 1, childResType); + } + ResourceIdentifier childResourceIdentifier = new ResourceIdentifier(resourceIdentifier, childIdxExp, childResourceHierarchy); + Map.Entry> childInfo = createResourceState(childResourceIdentifier, curResVar, resCurStateVal, listValue.getChild(2)); + state.replaceChildState(Integer.parseInt(childIdxExp.toString()), childInfo.getKey()); + createdResources.addAll(childInfo.getValue()); + return new AbstractMap.SimpleEntry<>(state, createdResources); + } + } else if (listValue.getSymbol().equals(DataConstraintModel.remove)) { + Expression childIdxExp = listValue.getChild(1); + if (childIdxExp instanceof Term) { + childIdxExp = ((Term) childIdxExp).substitute(curResVar, resCurStateVal); + childIdxExp = ((Term) childIdxExp).reduce(); + } + if (childIdxExp instanceof Constant) { + List createdResources = new ArrayList<>(); + ListResourceState state = new ListResourceState(); + if (listValue.getChild(0) instanceof Term) { + if (listValue.getChild(0) instanceof Constant && ((Constant) listValue.getChild(0)).getSymbol().equals(DataConstraintModel.nil)) { + // If the child list is nil. + } else { + Expression childList = ((Term) listValue.getChild(0)); + childList = ((Term) childList).substitute(curResVar, resStateValue); + childList = ((Term) childList).reduce(); + if (childList instanceof ListTerm) { + // If the child list is a list literal. + Map.Entry> childInfo = createResourceState(resourceIdentifier, curResVar, resCurStateVal, childList); // one more recursion. + state = (ListResourceState) childInfo.getKey(); + if (childInfo != null) createdResources.addAll(childInfo.getValue()); + } else if (childList instanceof Term) { + // If the child list is a list term. + Map.Entry> childInfo = createResourceState(resourceIdentifier, curResVar, resCurStateVal, listValue.getChild(0)); + state = (ListResourceState) childInfo.getKey(); + if (childInfo != null) createdResources.addAll(childInfo.getValue()); + } + } + } + ((ListResourceState) state).removeChildState(Integer.parseInt(childIdxExp.toString())); + return new AbstractMap.SimpleEntry<>(state, createdResources); + } + } else { + listValue = listValue.substitute(curResVar, resCurStateVal); + Expression listExp = listValue.reduce(); + if (listExp instanceof ListTerm) { + ListTerm listVal = (ListTerm) listExp; + ListResourceState state = new ListResourceState(); + Map.Entry> createInfo = new AbstractMap.SimpleEntry<>(state, new ArrayList<>()); + for (int i = 0; i < listVal.size(); i++) { + Expression childExp = new Constant(Integer.toString(i), DataConstraintModel.typeInt); + ResourceHierarchy childResourceHierarchy = null; + if (resourceIdentifier.getResourceHierarchy().getChildren() != null && resourceIdentifier.getResourceHierarchy().getChildren().size() > 0) { + childResourceHierarchy = resourceIdentifier.getResourceHierarchy().getChildren().iterator().next(); + } else { + Type childResType = null; + if (listVal.get(i) instanceof Variable) { + childResType = ((Variable) listVal.get(i)).getType(); + } else if (listVal.get(i) instanceof Term) { + childResType = ((Term) listVal.get(i)).getType(); + } + childResourceHierarchy = new ResourceHierarchy(resourceIdentifier.getResourceHierarchy(), 1, childResType); + } + ResourceIdentifier childResourceIdentifier = new ResourceIdentifier(resourceIdentifier, childExp, childResourceHierarchy); + Map.Entry> childInfo = createResourceState(childResourceIdentifier, curResVar, resCurStateVal, listVal.get(i)); + state.addChildState(childInfo.getKey()); + createInfo.getValue().addAll(childInfo.getValue()); + createInfo = new AbstractMap.SimpleEntry<>(state, createInfo.getValue()); + } + return createInfo; + } + } + } + } else if (DataConstraintModel.typeMap.isAncestorOf(resType)) { + if (resStateValue instanceof Constant) { + } else if (resStateValue instanceof JsonTerm) { + JsonTerm mapValue = (JsonTerm) resStateValue; + MapResourceState state = new MapResourceState(); + Map.Entry> createInfo = new AbstractMap.SimpleEntry<>(state, new ArrayList<>()); + for (String key : mapValue.keySet()) { + Expression childExp = new Constant(key, DataConstraintModel.typeString); + ResourceHierarchy childResourceHierarchy = null; + if (resourceIdentifier.getResourceHierarchy().getChildren() != null && resourceIdentifier.getResourceHierarchy().getChildren().size() > 0) { + childResourceHierarchy = resourceIdentifier.getResourceHierarchy().getChildren().iterator().next(); + } else { + Type childResType = null; + if (mapValue.get(key) instanceof Variable) { + childResType = ((Variable) mapValue.get(key)).getType(); + } else if (mapValue.get(key) instanceof Term) { + childResType = ((Term) mapValue.get(key)).getType(); + } + childResourceHierarchy = new ResourceHierarchy(resourceIdentifier.getResourceHierarchy(), 1, childResType); + } + ResourceIdentifier childResourceIdentifier = new ResourceIdentifier(resourceIdentifier, childExp, childResourceHierarchy); + Map.Entry> childInfo = createResourceState(childResourceIdentifier, curResVar, resCurStateVal, mapValue.get(key)); + state.addChildState(key, childInfo.getKey()); + createInfo.getValue().addAll(childInfo.getValue()); + createInfo = new AbstractMap.SimpleEntry<>(state, createInfo.getValue()); + } + return createInfo; + } else if (resStateValue instanceof MapTerm) { + MapTerm mapValue = (MapTerm) resStateValue; + MapResourceState state = new MapResourceState(); + Map.Entry> createInfo = new AbstractMap.SimpleEntry<>(state, new ArrayList<>()); + for (String key : mapValue.keySet()) { + Expression childExp = new Constant(key, DataConstraintModel.typeString); + ResourceHierarchy childResourceHierarchy = null; + if (resourceIdentifier.getResourceHierarchy().getChildren() != null && resourceIdentifier.getResourceHierarchy().getChildren().size() > 0) { + childResourceHierarchy = resourceIdentifier.getResourceHierarchy().getChildren().iterator().next(); + } else { + Type childResType = null; + if (mapValue.get(key) instanceof Variable) { + childResType = ((Variable) mapValue.get(key)).getType(); + } else if (mapValue.get(key) instanceof Term) { + childResType = ((Term) mapValue.get(key)).getType(); + } + childResourceHierarchy = new ResourceHierarchy(resourceIdentifier.getResourceHierarchy(), 1, childResType); + } + ResourceIdentifier childResourceIdentifier = new ResourceIdentifier(resourceIdentifier, childExp, childResourceHierarchy); + Map.Entry> childInfo = createResourceState(childResourceIdentifier, curResVar, resCurStateVal, mapValue.get(key)); + state.addChildState(key, childInfo.getKey()); + createInfo.getValue().addAll(childInfo.getValue()); + createInfo = new AbstractMap.SimpleEntry<>(state, createInfo.getValue()); + } + return createInfo; + } else if (resStateValue instanceof Term) { + Term mapValue = (Term) resStateValue; + if (mapValue.getSymbol().equals(DataConstraintModel.insert)) { + Expression childKeyExp = mapValue.getChild(1); + if (childKeyExp instanceof Term) { + childKeyExp = ((Term) childKeyExp).substitute(curResVar, resCurStateVal); + childKeyExp = ((Term) childKeyExp).reduce(); + } + if (childKeyExp instanceof Constant) { + List createdResources = new ArrayList<>(); + MapResourceState state = new MapResourceState(); + if (mapValue.getChild(0) instanceof Term) { + if (mapValue.getChild(0) instanceof Constant && ((Constant) mapValue.getChild(0)).getSymbol().equals(DataConstraintModel.nil)) { + // If the child map is nil. + } else { + Expression childMap = ((Term) mapValue.getChild(0)); + childMap = ((Term) childMap).substitute(curResVar, resStateValue); + childMap = ((Term) childMap).reduce(); + if (childMap instanceof MapTerm) { + // If the child map is a map literal. + Map.Entry> childInfo = createResourceState(resourceIdentifier, curResVar, resCurStateVal, childMap); // one more recursion. + state = (MapResourceState) childInfo.getKey(); + if (childInfo != null) createdResources.addAll(childInfo.getValue()); + } else if (childMap instanceof Term) { + // If the child map is a map term. + Map.Entry> childInfo = createResourceState(resourceIdentifier, curResVar, resCurStateVal, mapValue.getChild(0)); + state = (MapResourceState) childInfo.getKey(); + if (childInfo != null) createdResources.addAll(childInfo.getValue()); + } + } + } + ResourceHierarchy childResourceHierarchy = null; + if (resourceIdentifier.getResourceHierarchy().getChildren() != null && resourceIdentifier.getResourceHierarchy().getChildren().size() > 0) { + childResourceHierarchy = resourceIdentifier.getResourceHierarchy().getChildren().iterator().next(); + } else { + Type childResType = null; + if (mapValue.getChild(2) instanceof Variable) { + childResType = ((Variable) mapValue.getChild(2)).getType(); + } else if (mapValue.getChild(2) instanceof Term) { + childResType = ((Term) mapValue.getChild(2)).getType(); + } + childResourceHierarchy = new ResourceHierarchy(resourceIdentifier.getResourceHierarchy(), 1, childResType); + } + ResourceIdentifier childResourceIdentifier = new ResourceIdentifier(resourceIdentifier, childKeyExp, childResourceHierarchy); + String childId = (String) ((Constant) childKeyExp).getValue(); + Map.Entry> childInfo = createResourceState(childResourceIdentifier, curResVar, resCurStateVal, mapValue.getChild(2)); + state.addChildState(childId, childInfo.getKey()); + createdResources.addAll(childInfo.getValue()); + return new AbstractMap.SimpleEntry<>(state, createdResources); + } + } else if (mapValue.getSymbol().equals(DataConstraintModel.delete)) { + Expression childKeyExp = mapValue.getChild(1); + if (childKeyExp instanceof Term) { + childKeyExp = ((Term) childKeyExp).substitute(curResVar, resCurStateVal); + childKeyExp = ((Term) childKeyExp).reduce(); + } + if (childKeyExp instanceof Constant) { + List createdResources = new ArrayList<>(); + MapResourceState state = new MapResourceState(); + if (mapValue.getChild(0) instanceof Term) { + if (mapValue.getChild(0) instanceof Constant && ((Constant) mapValue.getChild(0)).getSymbol().equals(DataConstraintModel.nil)) { + // If the child map is nil. + } else { + Expression childMap = ((Term) mapValue.getChild(0)); + childMap = ((Term) childMap).substitute(curResVar, resStateValue); + childMap = ((Term) childMap).reduce(); + if (childMap instanceof MapTerm) { + // If the child map is a map literal. + Map.Entry> childInfo = createResourceState(resourceIdentifier, curResVar, resCurStateVal, childMap); // one more recursion. + state = (MapResourceState) childInfo.getKey(); + if (childInfo != null) createdResources.addAll(childInfo.getValue()); + } else if (childMap instanceof Term) { + // If the child map is a map term. + Map.Entry> childInfo = createResourceState(resourceIdentifier, curResVar, resCurStateVal, mapValue.getChild(0)); + state = (MapResourceState) childInfo.getKey(); + if (childInfo != null) createdResources.addAll(childInfo.getValue()); + } + } + } + String childId = (String) ((Constant) childKeyExp).getValue(); + ((MapResourceState) state).removeChildState(childId); + return new AbstractMap.SimpleEntry<>(state, createdResources); + } + } else { + mapValue = mapValue.substitute(curResVar, resCurStateVal); + Expression mapExp = mapValue.reduce(); + if (mapExp instanceof MapTerm) { + MapTerm mapVal = (MapTerm) mapExp; + MapResourceState state = new MapResourceState(); + Map.Entry> createInfo = new AbstractMap.SimpleEntry<>(null, new ArrayList<>()); + for (String key : mapVal.keySet()) { + Expression childExp = new Constant(key, DataConstraintModel.typeString); + ResourceHierarchy childResourceHierarchy = null; + if (resourceIdentifier.getResourceHierarchy().getChildren() != null && resourceIdentifier.getResourceHierarchy().getChildren().size() > 0) { + childResourceHierarchy = resourceIdentifier.getResourceHierarchy().getChildren().iterator().next(); + } else { + Type childResType = null; + if (mapVal.get(key) instanceof Variable) { + childResType = ((Variable) mapVal.get(key)).getType(); + } else if (mapVal.get(key) instanceof Term) { + childResType = ((Term) mapVal.get(key)).getType(); + } + childResourceHierarchy = new ResourceHierarchy(resourceIdentifier.getResourceHierarchy(), 1, childResType); + } + ResourceIdentifier childResourceIdentifier = new ResourceIdentifier(resourceIdentifier, childExp, childResourceHierarchy); + Map.Entry> childInfo = createResourceState(childResourceIdentifier, curResVar, resCurStateVal, mapVal.get(key)); + state.addChildState(key, childInfo.getKey()); + createInfo.getValue().addAll(childInfo.getValue()); + createInfo = new AbstractMap.SimpleEntry<>(state, createInfo.getValue()); + } + return createInfo; + } + } + } + } else if (DataConstraintModel.typeJson.isAncestorOf(resType)) { + if (resStateValue instanceof Constant) { + } else if (resStateValue instanceof JsonTerm) { + JsonTerm jsonValue = (JsonTerm) resStateValue; + JsonResourceState state = new JsonResourceState(); + Map.Entry> createInfo = new AbstractMap.SimpleEntry<>(null, new ArrayList<>()); + for (String key : jsonValue.keySet()) { + ResourceHierarchy childResourceHierarchy = null; + for (ResourceHierarchy childRes : resourceIdentifier.getResourceHierarchy().getChildren()) { + if (childRes.getResourceName().equals(key)) { + childResourceHierarchy = childRes; + break; + } + } + if (childResourceHierarchy == null) { + Type childResType = null; + if (jsonValue.get(key) instanceof Variable) { + childResType = ((Variable) jsonValue.get(key)).getType(); + } else if (jsonValue.get(key) instanceof Term) { + childResType = ((Term) jsonValue.get(key)).getType(); + } + childResourceHierarchy = new ResourceHierarchy(resourceIdentifier.getResourceHierarchy(), key, childResType); + } + ResourceIdentifier childResourceIdentifier = new ResourceIdentifier(resourceIdentifier, key, childResourceHierarchy); + Map.Entry> childInfo = createResourceState(childResourceIdentifier, curResVar, resCurStateVal, jsonValue.get(key)); + ResourceState childState = childInfo.getKey(); + Resource res = getResource(resourceIdentifier); + if (res != null && res.getChildrenMap() != null && res.getChildrenMap().get(key) != null) { + res.getChildrenMap().get(key).changeState(childState); + } + state.addChildState(key, childState); + createInfo.getValue().addAll(childInfo.getValue()); + createInfo = new AbstractMap.SimpleEntry<>(state, createInfo.getValue()); + } + return createInfo; + } else if (resStateValue instanceof Term) { + Term jsonValue = (Term) resStateValue; + JsonResourceState state = new JsonResourceState(); + Map.Entry> createInfo = new AbstractMap.SimpleEntry<>(null, new ArrayList<>()); + if (jsonValue.getSymbol().equals(DataConstraintModel.addMember)) { + while (jsonValue.getSymbol().equals(DataConstraintModel.addMember)) { + Expression childExp = jsonValue.getChild(1); + if (childExp instanceof Constant) { + String memberName = (String) ((Constant) childExp).getValue(); + ResourceHierarchy childResourceHierarchy = null; + for (ResourceHierarchy childRes : resourceIdentifier.getResourceHierarchy().getChildren()) { + if (childRes.getResourceName().equals(memberName)) { + childResourceHierarchy = childRes; + break; + } + } + if (childResourceHierarchy == null) { + Type childResType = null; + if (jsonValue.getChild(2) instanceof Variable) { + childResType = ((Variable) jsonValue.getChild(2)).getType(); + } else if (jsonValue.getChild(2) instanceof Term) { + childResType = ((Term) jsonValue.getChild(2)).getType(); + } + childResourceHierarchy = new ResourceHierarchy(resourceIdentifier.getResourceHierarchy(), memberName, childResType); + } + ResourceIdentifier childResourceIdentifier = new ResourceIdentifier(resourceIdentifier, memberName, childResourceHierarchy); + Map.Entry> childInfo = createResourceState(childResourceIdentifier, curResVar, resCurStateVal, jsonValue.getChild(2)); + state.addChildState(memberName, childInfo.getKey()); + createInfo.getValue().addAll(childInfo.getValue()); + createInfo = new AbstractMap.SimpleEntry<>(state, createInfo.getValue()); + } + if (!(jsonValue.getChild(0) instanceof Term)) break; + jsonValue = (Term) jsonValue.getChild(0); + } + } else { + jsonValue = jsonValue.substitute(curResVar, resCurStateVal); + Expression jsonExp = jsonValue.reduce(); + if (jsonExp instanceof JsonTerm) { + JsonTerm jsonVal = (JsonTerm) jsonExp; + for (String key : jsonVal.keySet()) { + ResourceHierarchy childResourceHierarchy = null; + for (ResourceHierarchy childRes : resourceIdentifier.getResourceHierarchy().getChildren()) { + if (childRes.getResourceName().equals(key)) { + childResourceHierarchy = childRes; + break; + } + } + if (childResourceHierarchy == null) { + Type childResType = null; + if (jsonVal.get(key) instanceof Variable) { + childResType = ((Variable) jsonVal.get(key)).getType(); + } else if (jsonVal.get(key) instanceof Term) { + childResType = ((Term) jsonVal.get(key)).getType(); + } + childResourceHierarchy = new ResourceHierarchy(resourceIdentifier.getResourceHierarchy(), key, childResType); + } + ResourceIdentifier childResourceIdentifier = new ResourceIdentifier(resourceIdentifier, key, childResourceHierarchy); + Map.Entry> childInfo = createResourceState(childResourceIdentifier, curResVar, resCurStateVal, jsonVal.get(key)); + ResourceState childState = childInfo.getKey(); + Resource res = getResource(resourceIdentifier); + if (res != null && res.getChildrenMap() != null && res.getChildrenMap().get(key) != null) { + res.getChildrenMap().get(key).changeState(childState); + } + state.addChildState(key, childState); + createInfo.getValue().addAll(childInfo.getValue()); + createInfo = new AbstractMap.SimpleEntry<>(state, createInfo.getValue()); + } + } + } + return createInfo; + } + } + } + if (curResVar != null && resCurStateVal != null) { + if (resStateValue instanceof Term) { + resStateValue = ((Term) resStateValue).substitute(curResVar, resCurStateVal); + resStateValue = ((Term) resStateValue).reduce(); + } + } + 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); + } + List updatedResources = new ArrayList<>(); + updatedResources.add(resourceIdentifier); + return new AbstractMap.SimpleEntry<>(state, updatedResources); + } + 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); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/INativeInitializer.java b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/INativeInitializer.java new file mode 100644 index 0000000..fc73bdc --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/INativeInitializer.java @@ -0,0 +1,7 @@ +package simulator.interfaces; + +import simulator.SystemState; + +public interface INativeInitializer { + public void onInitFromModel(SystemState initialSystemState); +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/INativeReceiver.java b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/INativeReceiver.java new file mode 100644 index 0000000..296506a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/INativeReceiver.java @@ -0,0 +1,8 @@ +package simulator.interfaces; + +import simulator.Event; +import simulator.SystemState; + +public interface INativeReceiver { + public void onReceiveFromModel(Event event, SystemState nextSystemState); +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/NativeSender.java b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/NativeSender.java new file mode 100644 index 0000000..f2ab162 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/NativeSender.java @@ -0,0 +1,33 @@ +package simulator.interfaces; + +import models.algebra.*; +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/ComponentHeightReceiver.java b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentHeightReceiver.java new file mode 100644 index 0000000..a13f5cc --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentHeightReceiver.java @@ -0,0 +1,31 @@ +package simulator.interfaces.swing; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Term; +import simulator.Event; +import simulator.SystemState; +import simulator.interfaces.INativeReceiver; + +import java.awt.*; + +public class ComponentHeightReceiver implements INativeReceiver { + protected Component component; + + public ComponentHeightReceiver(Component component) { + this.component = component; + } + + @Override + public void onReceiveFromModel(Event event, SystemState nextSystemState) { + Expression message = event.getMessage(); + if (message instanceof Term) { + Expression height = ((Term) message).getChild(0); + if (height instanceof Constant) { + int curWidth = component.getSize().width; + component.setSize(curWidth, Integer.parseInt(height.toString())); + } + } + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentMouseSender.java b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentMouseSender.java new file mode 100644 index 0000000..bc484b1 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentMouseSender.java @@ -0,0 +1,46 @@ +package simulator.interfaces.swing; + +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; + +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; + +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..3fd084a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentTextReceiver.java @@ -0,0 +1,38 @@ +package simulator.interfaces.swing; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Term; +import simulator.Event; +import simulator.SystemState; +import simulator.interfaces.INativeReceiver; + +import javax.swing.*; +import javax.swing.text.JTextComponent; +import java.awt.*; + +public class ComponentTextReceiver implements INativeReceiver { + protected Component component; + + public ComponentTextReceiver(Component component) { + this.component = component; + } + + @Override + public void onReceiveFromModel(Event event, SystemState nextSystemState) { + 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((String) ((Constant) text).getValue()); + } else if (component instanceof JLabel) { + ((JLabel) component).setText((String) ((Constant) text).getValue()); + } else if (component instanceof JButton) { + ((JButton) component).setText((String) ((Constant) text).getValue()); + } + } + } + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentTextSender.java b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentTextSender.java new file mode 100644 index 0000000..b27d21f --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentTextSender.java @@ -0,0 +1,66 @@ +package simulator.interfaces.swing; + +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; + +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; + +public class ComponentTextSender extends NativeSender implements DocumentListener { + + public ComponentTextSender(Simulator simulator, DataTransferChannel channel, ResourcePath resourcePath, Resource resource) { + super(simulator, channel, resourcePath, resource); + } + + @Override + public void insertUpdate(DocumentEvent e) { + Document d = e.getDocument(); + try { + Constant text = new Constant(d.getText(0, d.getLength()), DataConstraintModel.typeString); + Expression message = channel.getOutputChannelMembers().iterator().next().getStateTransition().getMessageExpression(); + message = (Term) message.clone(); + ((Term) message).setChild(0, text); + sendToModel(message); + } catch (BadLocationException e1) { + e1.printStackTrace(); + } + } + + @Override + public void removeUpdate(DocumentEvent e) { + Document d = e.getDocument(); + try { + Constant text = new Constant(d.getText(0, d.getLength()), DataConstraintModel.typeString); + Expression message = channel.getOutputChannelMembers().iterator().next().getStateTransition().getMessageExpression(); + message = (Term) message.clone(); + ((Term) message).setChild(0, text); + sendToModel(message); + } catch (BadLocationException e1) { + e1.printStackTrace(); + } + } + + @Override + public void changedUpdate(DocumentEvent e) { + Document d = e.getDocument(); + try { + Constant text = new Constant(d.getText(0, d.getLength()), DataConstraintModel.typeString); + Expression message = channel.getOutputChannelMembers().iterator().next().getStateTransition().getMessageExpression(); + message = (Term) message.clone(); + ((Term) message).setChild(0, text); + sendToModel(message); + } catch (BadLocationException e1) { + e1.printStackTrace(); + } + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentVisibilityReceiver.java b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentVisibilityReceiver.java new file mode 100644 index 0000000..e07e5ba --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentVisibilityReceiver.java @@ -0,0 +1,35 @@ +package simulator.interfaces.swing; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Term; +import models.dataConstraintModel.DataConstraintModel; +import simulator.Event; +import simulator.SystemState; +import simulator.interfaces.INativeReceiver; + +import java.awt.*; + +public class ComponentVisibilityReceiver implements INativeReceiver { + protected Component component; + + public ComponentVisibilityReceiver(Component component) { + this.component = component; + } + + @Override + public void onReceiveFromModel(Event event, SystemState nextSystemState) { + 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/ComponentWidthReceiver.java b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentWidthReceiver.java new file mode 100644 index 0000000..465c4f5 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentWidthReceiver.java @@ -0,0 +1,31 @@ +package simulator.interfaces.swing; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Term; +import simulator.Event; +import simulator.SystemState; +import simulator.interfaces.INativeReceiver; + +import java.awt.*; + +public class ComponentWidthReceiver implements INativeReceiver { + protected Component component; + + public ComponentWidthReceiver(Component component) { + this.component = component; + } + + @Override + public void onReceiveFromModel(Event event, SystemState nextSystemState) { + Expression message = event.getMessage(); + if (message instanceof Term) { + Expression width = ((Term) message).getChild(0); + if (width instanceof Constant) { + int curHeight = component.getSize().height; + component.setSize(Integer.parseInt(width.toString()), curHeight); + } + } + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentXReceiver.java b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentXReceiver.java new file mode 100644 index 0000000..21e5a11 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentXReceiver.java @@ -0,0 +1,31 @@ +package simulator.interfaces.swing; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Term; +import simulator.Event; +import simulator.SystemState; +import simulator.interfaces.INativeReceiver; + +import java.awt.*; + +public class ComponentXReceiver implements INativeReceiver { + protected Component component; + + public ComponentXReceiver(Component component) { + this.component = component; + } + + @Override + public void onReceiveFromModel(Event event, SystemState nextSystemState) { + Expression message = event.getMessage(); + if (message instanceof Term) { + Expression x = ((Term) message).getChild(0); + if (x instanceof Constant) { + int curY = component.getLocation().y; + component.setLocation(Integer.parseInt(x.toString()), curY); + } + } + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentYReceiver.java b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentYReceiver.java new file mode 100644 index 0000000..542fd24 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentYReceiver.java @@ -0,0 +1,31 @@ +package simulator.interfaces.swing; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Term; +import simulator.Event; +import simulator.SystemState; +import simulator.interfaces.INativeReceiver; + +import java.awt.*; + +public class ComponentYReceiver implements INativeReceiver { + protected Component component; + + public ComponentYReceiver(Component component) { + this.component = component; + } + + @Override + public void onReceiveFromModel(Event event, SystemState nextSystemState) { + Expression message = event.getMessage(); + if (message instanceof Term) { + Expression y = ((Term) message).getChild(0); + if (y instanceof Constant) { + int curX = component.getLocation().x; + component.setLocation(curX, Integer.parseInt(y.toString())); + } + } + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/SwingPresenter.java b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/SwingPresenter.java new file mode 100644 index 0000000..1650854 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/SwingPresenter.java @@ -0,0 +1,543 @@ +package simulator.interfaces.swing; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Term; +import models.dataConstraintModel.JsonTerm; +import models.dataConstraintModel.ListTerm; +import models.dataConstraintModel.MapTerm; +import models.dataConstraintModel.ResourcePath; +import models.dataFlowModel.DataTransferChannel; +import simulator.Event; +import simulator.Resource; +import simulator.Simulator; +import simulator.SystemState; +import simulator.interfaces.INativeReceiver; + +import javax.swing.*; +import javax.swing.table.DefaultTableModel; +import java.awt.*; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class SwingPresenter implements INativeReceiver { + // Native channel names. + public final String screenUpdateChannelName = "ScreenUpdate"; + public final String setLayoutChannelName = "SetLayout"; + public final String setVisibleChannelName = "SetVisible"; + public final String setTextChannelName = "SetText"; + public final String setXChannelName = "SetX"; + public final String setYChannelName = "SetY"; + public final String setWidthChannelName = "SetWidth"; + public final String setHeightChannelName = "SetHeight"; + public final String mouseEventChannelName = "MouseEvent"; + public final String textEventChannelName = "TextEvent"; + public final String OnTableChangedChannelName = "OnTableChanged"; + + protected JPanel mainPanel; + protected Simulator simulator; + protected Map components; + + // Native channels. + protected DataTransferChannel screenUpdateChannel; + protected DataTransferChannel setLayoutChannel; + protected DataTransferChannel setVisibleChannel; + protected DataTransferChannel setTextChannel; + protected DataTransferChannel setXChannel; + protected DataTransferChannel setYChannel; + protected DataTransferChannel setWidthChannel; + protected DataTransferChannel setHeightChannel; + protected DataTransferChannel mouseEventChannel; + protected DataTransferChannel textEventChannel; + protected DataTransferChannel onTableChangedChannel; + + protected Map> channelAndResourcesForReceiving = new HashMap<>(); + + public SwingPresenter(JPanel mainPanel, Simulator simulator) { + this.mainPanel = mainPanel; + this.simulator = simulator; + components = new HashMap<>(); + screenUpdateChannel = (DataTransferChannel) simulator.getModel().getChannel(screenUpdateChannelName); + setLayoutChannel = (DataTransferChannel) simulator.getModel().getChannel(setLayoutChannelName); + setVisibleChannel = (DataTransferChannel) simulator.getModel().getChannel(setVisibleChannelName); + setTextChannel = (DataTransferChannel) simulator.getModel().getChannel(setTextChannelName); + setXChannel = (DataTransferChannel) simulator.getModel().getChannel(setXChannelName); + setYChannel = (DataTransferChannel) simulator.getModel().getChannel(setYChannelName); + setWidthChannel = (DataTransferChannel) simulator.getModel().getChannel(setWidthChannelName); + setHeightChannel = (DataTransferChannel) simulator.getModel().getChannel(setHeightChannelName); + mouseEventChannel = (DataTransferChannel) simulator.getModel().getInputChannel(mouseEventChannelName); + textEventChannel = (DataTransferChannel) simulator.getModel().getInputChannel(textEventChannelName); + onTableChangedChannel = (DataTransferChannel) simulator.getModel().getChannel(OnTableChangedChannelName); + simulator.addNativeReceiver(this, screenUpdateChannel); + simulator.addNativeReceiver(new SwingLayout(), setLayoutChannel); + } + + @Override + public void onReceiveFromModel(Event event, SystemState nextSystemState) { + Expression message = event.getMessage(); + if (message instanceof Term && ((Term) message).getChildren().size() >= 2) { + Expression curScExp = ((Term) message).getChild(0); + Expression nextScExp = ((Term) message).getChild(1); + if (curScExp instanceof JsonTerm && nextScExp instanceof JsonTerm) { + JsonTerm curSc = (JsonTerm) curScExp; + JsonTerm nextSc = (JsonTerm) nextScExp; + Expression newLayout = nextSc.get("layout"); + if (newLayout != null) { + if (newLayout.toString().equals("true")) { + mainPanel.setLayout(new FlowLayout()); + } else if (newLayout.toString().equals("false")) { + mainPanel.setLayout(null); + } + } + Expression oldWidgets = curSc.get("widgets"); + Expression newWidgets = nextSc.get("widgets"); + if (oldWidgets instanceof MapTerm && newWidgets instanceof MapTerm) { + Set oldWidSet = new HashSet<>(((MapTerm) oldWidgets).keySet()); + Set newWidSet = new HashSet<>(((MapTerm) newWidgets).keySet()); + oldWidSet.removeAll(((MapTerm) newWidgets).keySet()); + newWidSet.removeAll(((MapTerm) oldWidgets).keySet()); + if (!oldWidSet.isEmpty() || !newWidSet.isEmpty()) { + // If the set of screen components is changed. + + // Remove old components and their native receivers. + for (String oldWid : oldWidSet) { + mainPanel.remove(components.get(oldWid)); + } + + for (DataTransferChannel channel : channelAndResourcesForReceiving.keySet()) { + Map widToResource = channelAndResourcesForReceiving.get(channel); + for (String oldWid : oldWidSet) { + Resource resource = widToResource.remove(oldWid); + if (resource != null) { + simulator.removeNativeReceiver(channel, resource); + } + } + } + + // Add new swing components. + Resource screenResource = nextSystemState.getResource(event.getInputResource().getResourceIdentifier()); + Resource widgetsResource = screenResource.getChildrenMap().get("widgets"); + for (String newWid : newWidSet) { + Expression value = ((MapTerm) newWidgets).get(newWid); + if (value instanceof JsonTerm) { + JsonTerm widget = (JsonTerm) value; + Resource widgetResource = widgetsResource.getChildrenMap().get(newWid); + Expression type = widget.get("type"); + if (type instanceof Constant && ((String) ((Constant) type).getValue()).equals("button")) { + // Add a button component. + Expression text = widget.get("text"); + Expression x = widget.get("x"); + Expression y = widget.get("y"); + Expression width = widget.get("width"); + Expression height = widget.get("height"); + JButton button = new JButton(((String) ((Constant) text).getValue())); + if (x != null && y != null) { + button.setLocation(Integer.parseInt(x.toString()), Integer.parseInt(y.toString())); + } + if (width != null && height != null) { + button.setSize(Integer.parseInt(width.toString()), Integer.parseInt(height.toString())); + } + mainPanel.add(button); + components.put(newWid, button); + // Connect swing component and model. + ResourcePath resPath = mouseEventChannel.getOutputResources().iterator().next(); + button.addMouseListener(new ComponentMouseSender(simulator, mouseEventChannel, resPath, widgetResource)); // button => widgetResource + + ComponentVisibilityReceiver nativeVisibilityReceiver = new ComponentVisibilityReceiver(button); // widgetResource => button + simulator.addNativeReceiver(nativeVisibilityReceiver, setVisibleChannel, widgetResource.getChildrenMap().get("visible")); + Map resources = channelAndResourcesForReceiving.get(setVisibleChannel); + if (resources == null) { + resources = new HashMap<>(); + channelAndResourcesForReceiving.put(setVisibleChannel, resources); + } + resources.put(newWid, widgetResource); + + ComponentTextReceiver nativeTextReceiver = new ComponentTextReceiver(button); // widgetResource => button + simulator.addNativeReceiver(nativeTextReceiver, setTextChannel, widgetResource.getChildrenMap().get("text")); + resources = channelAndResourcesForReceiving.get(setTextChannel); + if (resources == null) { + resources = new HashMap<>(); + channelAndResourcesForReceiving.put(setTextChannel, resources); + } + resources.put(newWid, widgetResource); + + Resource widgetXResource = widgetResource.getChildrenMap().get("x"); + if (widgetXResource != null) { + ComponentXReceiver nativeXReceiver = new ComponentXReceiver(button); // widgetResource => button + simulator.addNativeReceiver(nativeXReceiver, setXChannel, widgetXResource); + resources = channelAndResourcesForReceiving.get(setXChannel); + if (resources == null) { + resources = new HashMap<>(); + channelAndResourcesForReceiving.put(setXChannel, resources); + } + resources.put(newWid, widgetXResource); + } + + Resource widgetYResource = widgetResource.getChildrenMap().get("y"); + if (widgetYResource != null) { + ComponentYReceiver nativeYReceiver = new ComponentYReceiver(button); // widgetResource => button + simulator.addNativeReceiver(nativeYReceiver, setYChannel, widgetYResource); + resources = channelAndResourcesForReceiving.get(setYChannel); + if (resources == null) { + resources = new HashMap<>(); + channelAndResourcesForReceiving.put(setYChannel, resources); + } + resources.put(newWid, widgetYResource); + } + + Resource widgetWidthResource = widgetResource.getChildrenMap().get("width"); + if (widgetWidthResource != null) { + ComponentWidthReceiver nativeWidthReceiver = new ComponentWidthReceiver(button); // widgetResource => button + simulator.addNativeReceiver(nativeWidthReceiver, setWidthChannel, widgetWidthResource); + resources = channelAndResourcesForReceiving.get(setWidthChannel); + if (resources == null) { + resources = new HashMap<>(); + channelAndResourcesForReceiving.put(setWidthChannel, resources); + } + resources.put(newWid, widgetWidthResource); + } + + Resource widgetHeightResource = widgetResource.getChildrenMap().get("height"); + if (widgetHeightResource != null) { + ComponentHeightReceiver nativeHeightReceiver = new ComponentHeightReceiver(button); // widgetResource => button + simulator.addNativeReceiver(nativeHeightReceiver, setHeightChannel, widgetHeightResource); + resources = channelAndResourcesForReceiving.get(setHeightChannel); + if (resources == null) { + resources = new HashMap<>(); + channelAndResourcesForReceiving.put(setHeightChannel, resources); + } + resources.put(newWid, widgetHeightResource); + } + } else if (type instanceof Constant && ((String) ((Constant) type).getValue()).equals("label")) { + // Add a label component. + Expression text = widget.get("text"); + Expression x = widget.get("x"); + Expression y = widget.get("y"); + Expression width = widget.get("width"); + Expression height = widget.get("height"); + JLabel label = new JLabel(((String) ((Constant) text).getValue())); + if (x != null && y != null) { + label.setLocation(Integer.parseInt(x.toString()), Integer.parseInt(y.toString())); + } + if (width != null && height != null) { + label.setSize(Integer.parseInt(width.toString()), Integer.parseInt(height.toString())); + } + mainPanel.add(label); + components.put(newWid, label); + + // Connect swing component and model. + ComponentVisibilityReceiver nativeVisibilityReceiver = new ComponentVisibilityReceiver(label); // widgetResource => label + simulator.addNativeReceiver(nativeVisibilityReceiver, setVisibleChannel, widgetResource.getChildrenMap().get("visible")); + Map resources = channelAndResourcesForReceiving.get(setVisibleChannel); + if (resources == null) { + resources = new HashMap<>(); + channelAndResourcesForReceiving.put(setVisibleChannel, resources); + } + resources.put(newWid, widgetResource); + + ComponentTextReceiver nativeTextReceiver = new ComponentTextReceiver(label); // widgetResource => label + simulator.addNativeReceiver(nativeTextReceiver, setTextChannel, widgetResource.getChildrenMap().get("text")); + resources = channelAndResourcesForReceiving.get(setTextChannel); + if (resources == null) { + resources = new HashMap<>(); + channelAndResourcesForReceiving.put(setTextChannel, resources); + } + resources.put(newWid, widgetResource); + + Resource widgetXResource = widgetResource.getChildrenMap().get("x"); + if (widgetXResource != null) { + ComponentXReceiver nativeXReceiver = new ComponentXReceiver(label); // widgetResource => label + simulator.addNativeReceiver(nativeXReceiver, setXChannel, widgetXResource); + resources = channelAndResourcesForReceiving.get(setXChannel); + if (resources == null) { + resources = new HashMap<>(); + channelAndResourcesForReceiving.put(setXChannel, resources); + } + resources.put(newWid, widgetXResource); + } + + Resource widgetYResource = widgetResource.getChildrenMap().get("y"); + if (widgetYResource != null) { + ComponentYReceiver nativeYReceiver = new ComponentYReceiver(label); // widgetResource => label + simulator.addNativeReceiver(nativeYReceiver, setYChannel, widgetYResource); + resources = channelAndResourcesForReceiving.get(setYChannel); + if (resources == null) { + resources = new HashMap<>(); + channelAndResourcesForReceiving.put(setYChannel, resources); + } + resources.put(newWid, widgetYResource); + } + + Resource widgetWidthResource = widgetResource.getChildrenMap().get("width"); + if (widgetWidthResource != null) { + ComponentWidthReceiver nativeWidthReceiver = new ComponentWidthReceiver(label); // widgetResource => label + simulator.addNativeReceiver(nativeWidthReceiver, setWidthChannel, widgetWidthResource); + resources = channelAndResourcesForReceiving.get(setWidthChannel); + if (resources == null) { + resources = new HashMap<>(); + channelAndResourcesForReceiving.put(setWidthChannel, resources); + } + resources.put(newWid, widgetWidthResource); + } + + Resource widgetHeightResource = widgetResource.getChildrenMap().get("height"); + if (widgetHeightResource != null) { + ComponentHeightReceiver nativeHeightReceiver = new ComponentHeightReceiver(label); // widgetResource => label + simulator.addNativeReceiver(nativeHeightReceiver, setHeightChannel, widgetHeightResource); + resources = channelAndResourcesForReceiving.get(setHeightChannel); + if (resources == null) { + resources = new HashMap<>(); + channelAndResourcesForReceiving.put(setHeightChannel, resources); + } + resources.put(newWid, widgetHeightResource); + } + } else if (type instanceof Constant && ((String) ((Constant) type).getValue()).equals("table")) { + // Add a label component. + Expression text = widget.get("text"); + Expression x = widget.get("x"); + Expression y = widget.get("y"); + Expression width = widget.get("width"); + Expression height = widget.get("height"); + MapTerm data = (MapTerm) widget.get("data"); + ListTerm columnsList = (ListTerm) widget.get("columns"); + Constant rowNumExp = (Constant) widget.get("rowNum"); + Constant rowHeightExp = (Constant) widget.get("rowHeight"); + Constant primaryKeyNameExp = (Constant) widget.get("primaryKeyName"); + boolean primaryKeyVisible = !primaryKeyNameExp.getValue().equals(""); + +// JLabel label = new JLabel(((String) ((Constant) text).getValue())); + int colNum = columnsList.size() + (primaryKeyVisible ? 1 : 0); + String[] columns = new String[colNum]; + String[][] tableDatas = new String[data.keySet().size()][colNum]; + if (primaryKeyVisible) { + columns[0] = (String) primaryKeyNameExp.getValue(); + for (int i = 1; i < colNum; i++) { + columns[i] = (String) ((Constant) columnsList.get(i - 1)).getValue(); + } + } else { + for (int i = 0; i < colNum; i++) { + columns[i] = (String) ((Constant) columnsList.get(i)).getValue(); + } + } + int dataCount = 0; + for (String dataKey : data.keySet()) { + JsonTerm rowData = (JsonTerm) data.get(dataKey); + if (primaryKeyVisible) { + tableDatas[dataCount][0] = dataKey; + for (int j = 1; j < columns.length; j++) { + Constant cellValue = (Constant) rowData.get(columns[j]); + if (cellValue == null) { + tableDatas[dataCount][j] = "error"; + } else { + tableDatas[dataCount][j] = (String) ((Constant) rowData.get(columns[j])).getValue(); + } + } + } else { + for (int j = 0; j < columns.length; j++) { + Constant cellValue = (Constant) rowData.get(columns[j]); + if (cellValue == null) { + tableDatas[dataCount][j] = "error"; + } else { + tableDatas[dataCount][j] = (String) ((Constant) rowData.get(columns[j])).getValue(); + } + } + } + dataCount++; + } + DefaultTableModel tableModel = new DefaultTableModel(tableDatas, columns); + JTable table = new JTable(tableModel) { + @Override + public boolean isCellEditable(int row, int col) { + return false; + } + }; + JScrollPane scroll = new JScrollPane(table); + + if (x != null && y != null) { + scroll.setLocation(Integer.parseInt(x.toString()), Integer.parseInt(y.toString())); + } + if (width != null && height != null) { + scroll.setSize(Integer.parseInt(width.toString()), Integer.parseInt(height.toString())); + } + mainPanel.add(scroll); + components.put(newWid, scroll); + + // Connect swing component and model. + ComponentVisibilityReceiver nativeVisibilityReceiver = new ComponentVisibilityReceiver(scroll); // widgetResource => label + simulator.addNativeReceiver(nativeVisibilityReceiver, setVisibleChannel, widgetResource.getChildrenMap().get("visible")); + Map resources = channelAndResourcesForReceiving.get(setVisibleChannel); + if (resources == null) { + resources = new HashMap<>(); + channelAndResourcesForReceiving.put(setVisibleChannel, resources); + } + resources.put(newWid, widgetResource); + + Resource widgetXResource = widgetResource.getChildrenMap().get("x"); + if (widgetXResource != null) { + ComponentXReceiver nativeXReceiver = new ComponentXReceiver(scroll); // widgetResource => label + simulator.addNativeReceiver(nativeXReceiver, setXChannel, widgetXResource); + resources = channelAndResourcesForReceiving.get(setXChannel); + if (resources == null) { + resources = new HashMap<>(); + channelAndResourcesForReceiving.put(setXChannel, resources); + } + resources.put(newWid, widgetXResource); + } + + Resource widgetYResource = widgetResource.getChildrenMap().get("y"); + if (widgetYResource != null) { + ComponentYReceiver nativeYReceiver = new ComponentYReceiver(scroll); // widgetResource => label + simulator.addNativeReceiver(nativeYReceiver, setYChannel, widgetYResource); + resources = channelAndResourcesForReceiving.get(setYChannel); + if (resources == null) { + resources = new HashMap<>(); + channelAndResourcesForReceiving.put(setYChannel, resources); + } + resources.put(newWid, widgetYResource); + } + + Resource widgetWidthResource = widgetResource.getChildrenMap().get("width"); + if (widgetWidthResource != null) { + ComponentWidthReceiver nativeWidthReceiver = new ComponentWidthReceiver(scroll); // widgetResource => label + simulator.addNativeReceiver(nativeWidthReceiver, setWidthChannel, widgetWidthResource); + resources = channelAndResourcesForReceiving.get(setWidthChannel); + if (resources == null) { + resources = new HashMap<>(); + channelAndResourcesForReceiving.put(setWidthChannel, resources); + } + resources.put(newWid, widgetWidthResource); + } + + Resource widgetHeightResource = widgetResource.getChildrenMap().get("height"); + if (widgetHeightResource != null) { + ComponentHeightReceiver nativeHeightReceiver = new ComponentHeightReceiver(scroll); // widgetResource => label + simulator.addNativeReceiver(nativeHeightReceiver, setHeightChannel, widgetHeightResource); + resources = channelAndResourcesForReceiving.get(setHeightChannel); + if (resources == null) { + resources = new HashMap<>(); + channelAndResourcesForReceiving.put(setHeightChannel, resources); + } + resources.put(newWid, widgetHeightResource); + } + + Resource widgetDataResource = widgetResource.getChildrenMap().get("data"); + if (widgetDataResource != null) { + TableDataReceiver tableDataReceiver = new TableDataReceiver(tableModel, columns, (String) ((Constant) primaryKeyNameExp).getValue()); + simulator.addNativeReceiver(tableDataReceiver, onTableChangedChannel, widgetDataResource); + resources = channelAndResourcesForReceiving.get(onTableChangedChannel); + if (resources == null) { + resources = new HashMap<>(); + channelAndResourcesForReceiving.put(onTableChangedChannel, resources); + } + resources.put(newWid, widgetDataResource); + } + + } else if (type instanceof Constant && ((String) ((Constant) type).getValue()).equals("textInput")) { + // Add a text input component. + Expression x = widget.get("x"); + Expression y = widget.get("y"); + Expression width = widget.get("width"); + Expression height = widget.get("height"); + JTextField textField = new JTextField(10); + if (x != null && y != null) { + textField.setLocation(Integer.parseInt(x.toString()), Integer.parseInt(y.toString())); + } + if (width != null && height != null) { + textField.setSize(Integer.parseInt(width.toString()), Integer.parseInt(height.toString())); + } + mainPanel.add(textField); + components.put(newWid, textField); + // Connect swing component and model. + ResourcePath resPath = textEventChannel.getOutputResources().iterator().next(); + textField.getDocument().addDocumentListener(new ComponentTextSender(simulator, textEventChannel, resPath, widgetResource)); // textField => widgetResource + + ComponentVisibilityReceiver nativeReceiver = new ComponentVisibilityReceiver(textField); // widgetResource => textField + simulator.addNativeReceiver(nativeReceiver, setVisibleChannel, widgetResource.getChildrenMap().get("visible")); + Map resources = channelAndResourcesForReceiving.get(setVisibleChannel); + if (resources == null) { + resources = new HashMap<>(); + channelAndResourcesForReceiving.put(setVisibleChannel, resources); + } + resources.put(newWid, widgetResource); + + Resource widgetXResource = widgetResource.getChildrenMap().get("x"); + if (widgetXResource != null) { + ComponentXReceiver nativeXReceiver = new ComponentXReceiver(textField); // widgetResource => textField + simulator.addNativeReceiver(nativeXReceiver, setXChannel, widgetXResource); + resources = channelAndResourcesForReceiving.get(setXChannel); + if (resources == null) { + resources = new HashMap<>(); + channelAndResourcesForReceiving.put(setXChannel, resources); + } + resources.put(newWid, widgetXResource); + } + + Resource widgetYResource = widgetResource.getChildrenMap().get("y"); + if (widgetYResource != null) { + ComponentYReceiver nativeYReceiver = new ComponentYReceiver(textField); // widgetResource => textField + simulator.addNativeReceiver(nativeYReceiver, setYChannel, widgetYResource); + resources = channelAndResourcesForReceiving.get(setYChannel); + if (resources == null) { + resources = new HashMap<>(); + channelAndResourcesForReceiving.put(setYChannel, resources); + } + resources.put(newWid, widgetYResource); + } + + Resource widgetWidthResource = widgetResource.getChildrenMap().get("width"); + if (widgetWidthResource != null) { + ComponentWidthReceiver nativeWidthReceiver = new ComponentWidthReceiver(textField); // widgetResource => textField + simulator.addNativeReceiver(nativeWidthReceiver, setWidthChannel, widgetWidthResource); + resources = channelAndResourcesForReceiving.get(setWidthChannel); + if (resources == null) { + resources = new HashMap<>(); + channelAndResourcesForReceiving.put(setWidthChannel, resources); + } + resources.put(newWid, widgetWidthResource); + } + + Resource widgetHeightResource = widgetResource.getChildrenMap().get("height"); + if (widgetHeightResource != null) { + ComponentHeightReceiver nativeHeightReceiver = new ComponentHeightReceiver(textField); // widgetResource => textField + simulator.addNativeReceiver(nativeHeightReceiver, setHeightChannel, widgetHeightResource); + resources = channelAndResourcesForReceiving.get(setHeightChannel); + if (resources == null) { + resources = new HashMap<>(); + channelAndResourcesForReceiving.put(setHeightChannel, resources); + } + resources.put(newWid, widgetHeightResource); + } + } + } + } + mainPanel.invalidate(); + mainPanel.validate(); + mainPanel.repaint(); + } + } + } + } + } + + public class SwingLayout implements INativeReceiver { + + @Override + public void onReceiveFromModel(Event event, SystemState nextSystemState) { + Expression message = event.getMessage(); + if (message instanceof Term && ((Term) message).getChildren().size() >= 1) { + Expression newLayout = ((Term) message).getChild(0); + if (newLayout instanceof Constant) { + if (newLayout.toString().equals("true")) { + mainPanel.setLayout(new FlowLayout()); + } else if (newLayout.toString().equals("false")) { + mainPanel.setLayout(null); + } + } + } + } + + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/TableDataReceiver.java b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/TableDataReceiver.java new file mode 100644 index 0000000..697276a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/TableDataReceiver.java @@ -0,0 +1,55 @@ +package simulator.interfaces.swing; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Term; +import models.dataConstraintModel.JsonTerm; +import models.dataConstraintModel.MapTerm; +import simulator.Event; +import simulator.SystemState; +import simulator.interfaces.INativeReceiver; + +import javax.swing.table.DefaultTableModel; + +public class TableDataReceiver implements INativeReceiver { + + protected DefaultTableModel tableModel; + protected String[] columns; + protected String primaryKeyName; + protected boolean primaryKeyVisible; + + public TableDataReceiver(DefaultTableModel table, String[] columns, String primaryKeyName) { + this.tableModel = table; + this.columns = columns; + this.primaryKeyName = primaryKeyName; + this.primaryKeyVisible = !primaryKeyName.equals(""); + } + + @Override + public void onReceiveFromModel(Event event, SystemState nextSystemState) { + Expression message = event.getMessage(); + System.out.println(message); + if (message instanceof Term) { + MapTerm data = (MapTerm) ((Term) message).getChild(0); + int colNum = columns.length + (primaryKeyVisible ? 1 : 0); + String[][] tableDatas = new String[data.keySet().size()][colNum]; + int dataCount = 0; + for (String dataKey : data.keySet()) { + JsonTerm rowData = (JsonTerm) data.get(dataKey); + if (primaryKeyVisible) { + tableDatas[dataCount][0] = dataKey; + for (int j = 1; j < columns.length; j++) { + tableDatas[dataCount][j] = (String) ((Constant) rowData.get(columns[j])).getValue(); + } + } else { + for (int j = 0; j < columns.length; j++) { + tableDatas[dataCount][j] = (String) ((Constant) rowData.get(columns[j])).getValue(); + } + } + dataCount++; + } + tableModel.setDataVector(tableDatas, columns); + } + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/timers/TimerEventSender.java b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/timers/TimerEventSender.java new file mode 100644 index 0000000..f3c625c --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/timers/TimerEventSender.java @@ -0,0 +1,23 @@ +package simulator.interfaces.timers; + +import models.algebra.Expression; +import models.dataConstraintModel.ResourcePath; +import models.dataFlowModel.DataTransferChannel; +import simulator.Resource; +import simulator.Simulator; +import simulator.interfaces.NativeSender; + +public class TimerEventSender extends NativeSender implements Runnable { + + public TimerEventSender(Simulator simulator, DataTransferChannel channel, ResourcePath resourcePath, Resource resource) { + super(simulator, channel, resourcePath, resource); + } + + @Override + public void run() { + Expression message = channel.getOutputChannelMembers().iterator().next().getStateTransition().getMessageExpression(); + message = (Expression) message.clone(); + sendToModel(message); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/timers/TimerService.java b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/timers/TimerService.java new file mode 100644 index 0000000..8337058 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/timers/TimerService.java @@ -0,0 +1,139 @@ +package simulator.interfaces.timers; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Term; +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.SystemState; +import simulator.interfaces.INativeReceiver; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +public class TimerService implements INativeReceiver { + public final String timersUpdatedChannelName = "TimersUpdated"; + public final String timerEventChannelName = "TimerEvent"; + + protected final Simulator simulator; + protected final Map timers; + protected DataTransferChannel timersUpdatedChannel; + protected DataTransferChannel timerEventChannel; + + public TimerService(Simulator simulator) { + this.simulator = simulator; + timers = new HashMap<>(); + timersUpdatedChannel = (DataTransferChannel) simulator.getModel().getChannel(timersUpdatedChannelName); + timerEventChannel = (DataTransferChannel) simulator.getModel().getInputChannel(timerEventChannelName); + simulator.addNativeReceiver(this, timersUpdatedChannel); + } + + @Override + public void onReceiveFromModel(Event event, SystemState nextSystemState) { + Expression message = event.getMessage(); + if (message instanceof Term && ((Term) message).getChildren().size() >= 2) { + Expression curTimersExp = ((Term) message).getChild(0); + Expression nextTimersExp = ((Term) message).getChild(1); + if (curTimersExp instanceof MapTerm && nextTimersExp instanceof MapTerm) { + MapTerm curTimers = (MapTerm) curTimersExp; + MapTerm nextTimers = (MapTerm) nextTimersExp; + Set oldTidSet = new HashSet<>(curTimers.keySet()); + Set newTidSet = new HashSet<>(nextTimers.keySet()); + oldTidSet.removeAll(nextTimers.keySet()); + newTidSet.removeAll(curTimers.keySet()); + if (!oldTidSet.isEmpty() || !newTidSet.isEmpty()) { + // If the set of timers is changed. + + // Remove old timers and their native receivers. + for (String tid : oldTidSet) { + ScheduledThreadPoolExecutor timer = timers.get(tid); + timer.shutdown(); + timers.remove(tid); + } + + // Add new timers. + Resource timersResource = nextSystemState.getResource(event.getInputResource().getResourceIdentifier()); + for (String tid : newTidSet) { + Expression value = nextTimers.get(tid); + if (value instanceof JsonTerm) { + JsonTerm timerValue = (JsonTerm) value; + Resource timerResource = timersResource.getChildrenMap().get(tid); + // Add a timer. + Expression intervalExp = timerValue.get("interval"); + long interval = Long.parseLong(((Constant) intervalExp).toString()); + ScheduledThreadPoolExecutor timer = new ScheduledThreadPoolExecutor(0); + timers.put(tid, timer); + + // Connect Java timer and model. + ResourcePath resPath = timerEventChannel.getOutputResources().iterator().next(); + TimerEventSender sender = new TimerEventSender(simulator, timerEventChannel, resPath, timerResource); // timer => timerResource + timer.scheduleAtFixedRate(sender, 0, interval, TimeUnit.MILLISECONDS); + } + } + } + } + } + } + + public class TimerStart implements INativeReceiver { + public final String timerStartChannelName = "TimerStart"; + protected DataTransferChannel timerStartChannel; + protected DataTransferChannel timerEventChannel; + + public TimerStart() { + timerStartChannel = (DataTransferChannel) simulator.getModel().getChannel(timerStartChannelName); + timerEventChannel = (DataTransferChannel) simulator.getModel().getInputChannel(timerEventChannelName); + simulator.addNativeReceiver(this, timerStartChannel); + } + + @Override + public void onReceiveFromModel(Event event, SystemState nextSystemState) { + Expression message = event.getMessage(); + if (message instanceof Term && ((Term) message).getChildren().size() >= 1) { + Expression tidExp = event.getInputResource().getResourceIdentifier().getLastParam(); + Expression intervalExp = ((Term) message).getChild(0); + if (tidExp instanceof Constant && intervalExp instanceof Constant) { + String tid = ((Constant) tidExp).toString(); + long interval = Long.parseLong(((Constant) intervalExp).toString()); + ScheduledThreadPoolExecutor timer = timers.get(tid); + TimerEventSender sender = new TimerEventSender(simulator, timerEventChannel, event.getInputResourcePath(), event.getInputResource()); + timer.scheduleAtFixedRate(sender, 0, interval, TimeUnit.MILLISECONDS); + } + } + } + } + + public class TimerClear implements INativeReceiver { + public final String timerClearChannelName = "TimerClear"; + protected DataTransferChannel timerClearChannel; + + public TimerClear() { + timerClearChannel = (DataTransferChannel) simulator.getModel().getChannel(timerClearChannelName); + simulator.addNativeReceiver(this, timerClearChannel); + } + + @Override + public void onReceiveFromModel(Event event, SystemState nextSystemState) { + Expression message = event.getMessage(); + if (message instanceof Term && ((Term) message).getChildren().size() >= 1) { + Expression tidExp = event.getInputResource().getResourceIdentifier().getLastParam(); + if (tidExp instanceof Constant) { + String tid = ((Constant) tidExp).toString(); + ScheduledThreadPoolExecutor timer = timers.get(tid); + timer.shutdown(); + timers.remove(tid); + } + } + } + + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/states/CompositeResourceState.java b/AlgebraicDataflowArchitectureModel/src/simulator/states/CompositeResourceState.java new file mode 100644 index 0000000..7809cf0 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/states/CompositeResourceState.java @@ -0,0 +1,16 @@ +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 clearChildStates(); + + 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..7b46931 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/states/JsonResourceState.java @@ -0,0 +1,56 @@ +package simulator.states; + +import models.algebra.Expression; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.JsonTerm; + +import java.util.HashMap; +import java.util.Map; + +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); + } + + public void removeChildState(String param) { + children.remove(param); + } + + public void clearChildStates() { + children.clear(); + } + + @Override + public void replaceChildState(ResourceState state, ResourceState newState) { + for (Map.Entry childEnt : children.entrySet()) { + if (childEnt.getValue().equals(state)) { + children.put(childEnt.getKey(), newState); + } + } + } + + public Object clone() { + JsonResourceState newJsonResourceState = new JsonResourceState(); + for (String key : children.keySet()) { + newJsonResourceState.children.put(key, (ResourceState) children.get(key).clone()); + } + return newJsonResourceState; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/states/ListResourceState.java b/AlgebraicDataflowArchitectureModel/src/simulator/states/ListResourceState.java new file mode 100644 index 0000000..ad10cec --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/states/ListResourceState.java @@ -0,0 +1,64 @@ +package simulator.states; + +import models.algebra.Expression; +import models.dataConstraintModel.ListTerm; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +public class ListResourceState extends CompositeResourceState { + private List children = new ArrayList<>(); + + @Override + public Expression getValue() { + ListTerm value = new ListTerm(); + for (ResourceState child : children) { + value.append(child.getValue()); + } + 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); + } + + public void removeChildState(int idx) { + children.remove(idx); + } + + public void clearChildStates() { + children.clear(); + } + + @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); + } + } + } + + public void replaceChildState(int idx, ResourceState newState) { + children.set(idx, newState); + } + + public Object clone() { + ListResourceState newListResourceState = new ListResourceState(); + for (ResourceState state : children) { + newListResourceState.children.add((ResourceState) state.clone()); + } + return newListResourceState; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/states/MapResourceState.java b/AlgebraicDataflowArchitectureModel/src/simulator/states/MapResourceState.java new file mode 100644 index 0000000..f341703 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/states/MapResourceState.java @@ -0,0 +1,56 @@ +package simulator.states; + +import models.algebra.Expression; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.MapTerm; + +import java.util.HashMap; +import java.util.Map; + +public class MapResourceState extends CompositeResourceState { + private Map children = new HashMap<>(); + + @Override + public Expression getValue() { + MapTerm value = new MapTerm(); + value.setType(DataConstraintModel.typeMap); + for (Map.Entry childEnt : children.entrySet()) { + value.insert(childEnt.getKey(), childEnt.getValue().getValue()); + } + return value; + } + + @Override + public Map getChildStates() { + return children; + } + + public void addChildState(String param, ResourceState childState) { + children.put(param, childState); + } + + public void removeChildState(String param) { + children.remove(param); + } + + public void clearChildStates() { + children.clear(); + } + + @Override + public void replaceChildState(ResourceState state, ResourceState newState) { + for (Map.Entry childEnt : children.entrySet()) { + if (childEnt.getValue().equals(state)) { + children.put(childEnt.getKey(), newState); + } + } + } + + public Object clone() { + MapResourceState newMapResourceState = new MapResourceState(); + for (String key : children.keySet()) { + newMapResourceState.children.put(key, (ResourceState) children.get(key).clone()); + } + return newMapResourceState; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/states/PrimitiveResourceState.java b/AlgebraicDataflowArchitectureModel/src/simulator/states/PrimitiveResourceState.java new file mode 100644 index 0000000..eaa02da --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/states/PrimitiveResourceState.java @@ -0,0 +1,32 @@ +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; + } + + public Object clone() { + if (value == null) return new PrimitiveResourceState(null); + return new PrimitiveResourceState((Constant) value.clone()); + } +} 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..eaf83cc --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/states/State.java @@ -0,0 +1,15 @@ +package simulator.states; + +import models.algebra.Expression; + +/** + * A state of a resource + * + * @author Nitta + * + */ +abstract public class State implements Cloneable { + abstract public Expression getValue(); + + abstract public Object clone(); +} diff --git a/AlgebraicDataflowArchitectureModel/src/tests/CodeGeneratorTest.java b/AlgebraicDataflowArchitectureModel/src/tests/CodeGeneratorTest.java deleted file mode 100644 index a89ea3c..0000000 --- a/AlgebraicDataflowArchitectureModel/src/tests/CodeGeneratorTest.java +++ /dev/null @@ -1,51 +0,0 @@ -package tests; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.util.ArrayList; - -import algorithms.*; -import code.ast.CompilationUnit; -import code.ast.TypeDeclaration; -import generators.DataTransferMethodAnalyzer; -import generators.JavaCodeGenerator; -import generators.JavaMethodBodyGenerator; -import models.dataFlowModel.*; -import parser.*; -import parser.exceptions.ExpectedAssignment; -import parser.exceptions.ExpectedChannel; -import parser.exceptions.ExpectedChannelName; -import parser.exceptions.ExpectedEquals; -import parser.exceptions.ExpectedInOrOutOrRefKeyword; -import parser.exceptions.ExpectedLeftCurlyBracket; -import parser.exceptions.ExpectedRHSExpression; -import parser.exceptions.ExpectedRightBracket; -import parser.exceptions.ExpectedStateTransition; -import parser.exceptions.WrongLHSExpression; -import parser.exceptions.WrongRHSExpression; - -public class CodeGeneratorTest { - public static void main(String[] args) { - File file = new File("models/POS.model"); - try { - Parser parser = new Parser(new BufferedReader(new FileReader(file))); - DataTransferModel model; - try { - model = parser.doParse(); - DataFlowGraph graph = DataTransferModelAnalyzer.createDataFlowGraphWithStateStoringAttribute(model); - DataTransferModelAnalyzer.annotateWithSelectableDataTransferAttiribute(graph); - DataTransferMethodAnalyzer.decideToStoreResourceStates(graph); - ArrayList codetree = JavaMethodBodyGenerator.doGenerate(graph, model, JavaCodeGenerator.doGenerate(graph, model)); - System.out.println(codetree); - } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefKeyword - | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression - | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment e) { - e.printStackTrace(); - } - } catch (FileNotFoundException e) { - e.printStackTrace(); - } - } -} diff --git a/AlgebraicDataflowArchitectureModel/src/tests/DataConstraintModelTest.java b/AlgebraicDataflowArchitectureModel/src/tests/DataConstraintModelTest.java index afe405a..fee2aba 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/DataConstraintModelTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/DataConstraintModelTest.java @@ -1,59 +1,62 @@ package tests; -import static org.junit.Assert.*; - +import models.algebra.Variable; +import models.dataConstraintModel.Channel; +import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.ResourcePath; import org.junit.Test; -import models.dataConstraintModel.*; +import static org.junit.Assert.assertEquals; public class DataConstraintModelTest { - + @Test 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..225639b 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/DataStorageDecisionTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/DataStorageDecisionTest.java @@ -1,30 +1,35 @@ package tests; +import algorithms.DataTransferModelAnalyzer; +import generators.DataTransferMethodAnalyzer; +import models.Node; +import models.dataFlowModel.DataFlowGraph; +import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.ResourceNode; +import models.dataFlowModel.StoreAttribute; +import org.junit.Test; +import parser.Parser; +import parser.exceptions.*; + import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; +import java.util.HashMap; -import algorithms.*; -import generators.DataTransferMethodAnalyzer; -import models.Node; -import models.dataFlowModel.*; -import parser.*; -import parser.exceptions.ExpectedAssignment; -import parser.exceptions.ExpectedChannel; -import parser.exceptions.ExpectedChannelName; -import parser.exceptions.ExpectedEquals; -import parser.exceptions.ExpectedInOrOutOrRefKeyword; -import parser.exceptions.ExpectedLeftCurlyBracket; -import parser.exceptions.ExpectedRHSExpression; -import parser.exceptions.ExpectedRightBracket; -import parser.exceptions.ExpectedStateTransition; -import parser.exceptions.WrongLHSExpression; -import parser.exceptions.WrongRHSExpression; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; public class DataStorageDecisionTest { - public static void main(String[] args) { + + @Test + public void test() { File file = new File("models/POS2.model"); + HashMap exprectedDecision = new HashMap<>(); + exprectedDecision.put("history", true); + exprectedDecision.put("total", true); + exprectedDecision.put("points", false); + exprectedDecision.put("payment", true); try { Parser parser = new Parser(new BufferedReader(new FileReader(file))); DataTransferModel model = null; @@ -34,12 +39,17 @@ 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()) { + String resName = ((ResourceNode) resNode).getResourceName(); + boolean decision = ((StoreAttribute) ((ResourceNode) resNode).getAttribute()).isStored(); + System.out.println(resName + ":" + decision); + assertNotNull(exprectedDecision.get(resName)); + assertEquals(decision, exprectedDecision.get(resName)); } - } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefKeyword - | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression - | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment e) { + } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket | + WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { e.printStackTrace(); } } catch (FileNotFoundException e) { diff --git a/AlgebraicDataflowArchitectureModel/src/tests/DataStorageNecessityTest.java b/AlgebraicDataflowArchitectureModel/src/tests/DataStorageNecessityTest.java index 9a7c300..5bb65ef 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/DataStorageNecessityTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/DataStorageNecessityTest.java @@ -1,29 +1,34 @@ package tests; +import algorithms.DataTransferModelAnalyzer; +import models.Node; +import models.dataFlowModel.DataFlowGraph; +import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.ResourceNode; +import models.dataFlowModel.StoreAttribute; +import org.junit.Test; +import parser.Parser; +import parser.exceptions.*; + import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; +import java.util.HashMap; -import algorithms.DataTransferModelAnalyzer; -import models.Node; -import models.dataFlowModel.*; -import parser.Parser; -import parser.exceptions.ExpectedAssignment; -import parser.exceptions.ExpectedChannel; -import parser.exceptions.ExpectedChannelName; -import parser.exceptions.ExpectedEquals; -import parser.exceptions.ExpectedInOrOutOrRefKeyword; -import parser.exceptions.ExpectedLeftCurlyBracket; -import parser.exceptions.ExpectedRHSExpression; -import parser.exceptions.ExpectedRightBracket; -import parser.exceptions.ExpectedStateTransition; -import parser.exceptions.WrongLHSExpression; -import parser.exceptions.WrongRHSExpression; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; public class DataStorageNecessityTest { - public static void main(String[] args) { + + @Test + public void test() { File file = new File("models/POS2.model"); + HashMap exprectedNecessity = new HashMap<>(); + exprectedNecessity.put("history", true); + exprectedNecessity.put("total", true); + exprectedNecessity.put("points", false); + exprectedNecessity.put("payment", false); try { Parser parser = new Parser(new BufferedReader(new FileReader(file))); DataTransferModel model; @@ -31,15 +36,22 @@ model = parser.doParse(); System.out.println(model); DataFlowGraph graph = DataTransferModelAnalyzer.createDataFlowGraphWithStateStoringAttribute(model); - for (Node n:graph.getNodes()) { - ResourceNode resource = (ResourceNode)n; - if((StoreAttribute)resource.getAttribute() != null) { - System.out.println(resource.toString() + ":" + ((StoreAttribute)resource.getAttribute()).isNeeded()); + for (Node n : graph.getNodes()) { + if (n instanceof ResourceNode) { + ResourceNode resource = (ResourceNode) n; + if ((StoreAttribute) resource.getAttribute() != null) { + String resName = resource.getResourceName(); + boolean necessity = ((StoreAttribute) resource.getAttribute()).isNeeded(); + System.out.println(resName + ":" + necessity); + assertNotNull(exprectedNecessity.get(resName)); + assertEquals(necessity, exprectedNecessity.get(resName)); + } } } - } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefKeyword - | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression - | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment e) { + } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket | + WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation 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..5c9bc49 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/tests/DataTransferModelTest.java @@ -0,0 +1,90 @@ +package tests; + +import models.Edge; +import models.algebra.Variable; +import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.ResourcePath; +import models.dataFlowModel.DataFlowGraph; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.DataTransferModel; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +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/DirectedGraphTest.java b/AlgebraicDataflowArchitectureModel/src/tests/DirectedGraphTest.java index 4f6db8e..5eb5032 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/DirectedGraphTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/DirectedGraphTest.java @@ -1,13 +1,14 @@ package tests; -import static org.junit.Assert.*; - +import models.DirectedGraph; +import models.Edge; +import models.Node; import org.junit.Test; -import models.*; +import static org.junit.Assert.assertEquals; public class DirectedGraphTest { - + @Test public void test() { // Build a directed graph. @@ -41,7 +42,7 @@ assertEquals(1, n4.getOutdegree()); // Remove an edge. - g.removeEdge(e5); + g.removeEdge(e5); // Re-check the number of nodes. assertEquals(4, g.getNodes().size()); // Re-check the number of edges. @@ -56,5 +57,5 @@ assertEquals(1, n4.getIndegree()); assertEquals(1, n4.getOutdegree()); } - + } diff --git a/AlgebraicDataflowArchitectureModel/src/tests/EdgeTransitionSelectableTest.java b/AlgebraicDataflowArchitectureModel/src/tests/EdgeTransitionSelectableTest.java index 847fb6f..e6b6683 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/EdgeTransitionSelectableTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/EdgeTransitionSelectableTest.java @@ -1,26 +1,19 @@ package tests; +import algorithms.DataTransferModelAnalyzer; +import models.Edge; +import models.dataFlowModel.DataFlowEdge; +import models.dataFlowModel.DataFlowGraph; +import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.PushPullAttribute; +import parser.Parser; +import parser.exceptions.*; + import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; -import algorithms.*; -import models.Edge; -import models.dataFlowModel.*; -import parser.*; -import parser.exceptions.ExpectedAssignment; -import parser.exceptions.ExpectedChannel; -import parser.exceptions.ExpectedChannelName; -import parser.exceptions.ExpectedEquals; -import parser.exceptions.ExpectedInOrOutOrRefKeyword; -import parser.exceptions.ExpectedLeftCurlyBracket; -import parser.exceptions.ExpectedRHSExpression; -import parser.exceptions.ExpectedRightBracket; -import parser.exceptions.ExpectedStateTransition; -import parser.exceptions.WrongLHSExpression; -import parser.exceptions.WrongRHSExpression; - public class EdgeTransitionSelectableTest { public static void main(String[] args) { File file = new File("models/POS2.model"); @@ -32,13 +25,14 @@ System.out.println(model); DataFlowGraph graph = DataTransferModelAnalyzer.createDataFlowGraphWithStateStoringAttribute(model); DataTransferModelAnalyzer.annotateWithSelectableDataTransferAttiribute(graph); - for(Edge e:graph.getEdges()) { + for (Edge e : graph.getEdges()) { DataFlowEdge re = (DataFlowEdge) e; - System.out.println(re.getSource() + "-" + re.getDestination() + ":" + ((PushPullAttribute)(re.getAttribute())).getOptions()); + System.out.println(re.getSource() + "-" + re.getDestination() + ":" + ((PushPullAttribute) (re.getAttribute())).getOptions()); } - } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefKeyword - | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression - | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment e) { + } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket | + WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { e.printStackTrace(); } } catch (FileNotFoundException e) { diff --git a/AlgebraicDataflowArchitectureModel/src/tests/FormulaChannelTest.java b/AlgebraicDataflowArchitectureModel/src/tests/FormulaChannelTest.java index a8f3503..e7a9c87 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/FormulaChannelTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/FormulaChannelTest.java @@ -1,34 +1,32 @@ package tests; -import org.junit.Test; - -import models.algebra.Symbol; import models.dataConstraintModel.ChannelMember; import models.dataConstraintModel.DataConstraintModel; import models.dataConstraintModel.ResourcePath; import models.visualModel.FormulaChannel; +import org.junit.Test; public class FormulaChannelTest { - + @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,19 +35,19 @@ 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/GraphicalViewer.java b/AlgebraicDataflowArchitectureModel/src/tests/GraphicalViewer.java index 95c7e05..cece7ad 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/GraphicalViewer.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/GraphicalViewer.java @@ -1,61 +1,40 @@ package tests; -import java.awt.Color; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Rectangle; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.util.EventObject; -import java.util.Map; - -import javax.swing.BorderFactory; -import javax.swing.CellRendererPane; -import javax.swing.JComboBox; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JScrollPane; -import javax.swing.border.BevelBorder; - -import com.mxgraph.canvas.mxICanvas; -import com.mxgraph.canvas.mxImageCanvas; import com.mxgraph.model.mxCell; import com.mxgraph.model.mxGeometry; import com.mxgraph.model.mxIGraphModel; -import com.mxgraph.shape.mxITextShape; import com.mxgraph.swing.mxGraphComponent; -import com.mxgraph.swing.handler.mxRubberband; -import com.mxgraph.swing.view.mxCellEditor; import com.mxgraph.swing.view.mxICellEditor; -import com.mxgraph.swing.view.mxInteractiveCanvas; import com.mxgraph.util.mxConstants; import com.mxgraph.util.mxPoint; import com.mxgraph.util.mxUtils; import com.mxgraph.view.mxCellState; import com.mxgraph.view.mxGraph; -public class GraphicalViewer extends JFrame -{ +import javax.swing.*; +import java.awt.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.EventObject; +public class GraphicalViewer extends JFrame { + /** - * + * */ private static final long serialVersionUID = -844106998814982739L; final int PORT_DIAMETER = 8; final int PORT_RADIUS = PORT_DIAMETER / 2; - - public GraphicalViewer() - { + + public GraphicalViewer() { super("Graphical Viewer"); - + // Demonstrates the use of a Swing component for rendering vertices. // Note: Use the heavyweight feature to allow for event handling in // the Swing component that is used for rendering the vertex. - + mxGraph graph = new mxGraph() { public boolean isPort(Object cell) { mxGeometry geo = getCellGeometry(cell); @@ -67,9 +46,9 @@ return false; } }; - + Object parent = graph.getDefaultParent(); - + graph.getModel().beginUpdate(); try { mxGeometry geo1 = new mxGeometry(0, 0.5, PORT_DIAMETER, PORT_DIAMETER); @@ -82,7 +61,7 @@ Object c1 = graph.insertVertex(parent, null, "c1", 150, 20, 30, 30); mxCell c1_in = new mxCell(null, geo1, "shape=ellipse;perimter=ellipsePerimeter"); - c1_in.setVertex(true); + c1_in.setVertex(true); graph.addCell(c1_in, c1); mxCell c1_out = new mxCell(null, geo2, "shape=ellipse;perimter=ellipsePerimeter"); c1_out.setVertex(true); @@ -90,7 +69,7 @@ Object c2 = graph.insertVertex(parent, null, "c2", 150, 100, 30, 30); mxCell c2_in = new mxCell(null, geo1, "shape=ellipse;perimter=ellipsePerimeter"); - c2_in.setVertex(true); + c2_in.setVertex(true); graph.addCell(c2_in, c2); mxCell c2_out = new mxCell(null, geo2, "shape=ellipse;perimter=ellipsePerimeter"); c2_out.setVertex(true); @@ -108,13 +87,13 @@ } finally { graph.getModel().endUpdate(); } - - mxGraphComponent graphComponent = new mxGraphComponent(graph) { + + mxGraphComponent graphComponent = new mxGraphComponent(graph) { protected mxICellEditor createCellEditor() { final mxGraphComponent graphComponent = this; return new mxICellEditor() { /** - * + * */ public int DEFAULT_MIN_WIDTH = 70; public int DEFAULT_MIN_HEIGHT = 30; @@ -132,13 +111,13 @@ public Object getEditingCell() { return editingCell; } - + @Override public void startEditing(Object cell, EventObject evt) { if (editingCell != null) { stopEditing(true); } - + mxCellState state = graphComponent.getGraph().getView().getState(cell); if (state != null && state.getLabel() != null && !state.getLabel().equals("")) { editingCell = cell; @@ -156,7 +135,7 @@ comboBox.updateUI(); } } - + @Override public void stopEditing(boolean cancel) { if (editingCell != null) { @@ -171,12 +150,12 @@ mxCellState state = graphComponent.getGraph().getView().getState(cell); graphComponent.redraw(state); } - + if (comboBox.getParent() != null) { comboBox.setVisible(false); comboBox.getParent().remove(comboBox); } - + graphComponent.requestFocusInWindow(); } } @@ -184,44 +163,44 @@ 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; } }; @@ -238,23 +217,23 @@ }; graphComponent.getGraphControl().addMouseListener(new MouseAdapter() { - + public void mouseReleased(MouseEvent e) { Object cell = graphComponent.getCellAt(e.getX(), e.getY()); if (cell != null) { - System.out.println("cell="+graph.getLabel(cell)); + System.out.println("cell=" + graph.getLabel(cell)); } } }); getContentPane().add(graphComponent); } - + public static void main(String[] args) { GraphicalViewer frame = new GraphicalViewer(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(400, 320); frame.setVisible(true); } - + } diff --git a/AlgebraicDataflowArchitectureModel/src/tests/InverseTest.java b/AlgebraicDataflowArchitectureModel/src/tests/InverseTest.java index bfa8957..f1eafff 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/InverseTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/InverseTest.java @@ -1,53 +1,111 @@ package tests; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.util.HashMap; - -import org.junit.Test; - 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 org.junit.Test; import parser.Parser; import parser.Parser.TokenStream; +import parser.exceptions.ExpectedColon; +import parser.exceptions.ExpectedDoubleQuotation; import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.WrongJsonExpression; + +import java.util.HashMap; + +import static org.junit.Assert.*; 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(); Parser parser = new Parser(stream); - stream.addLine(rhs); - + 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()); - + // Solve {y = (a * x + b) + c} for a, b, c, x Variable y = new Variable(lhs); - for (Position vPos: rhsVars.keySet()) { + for (Position vPos : rhsVars.keySet()) { Variable v = rhsVars.get(vPos); - Expression inv = rhsExp.getInverseMap(y, vPos); // inverse map to get v back from the output value y + Expression inv = rhsExp.getInverseMap(y, vPos); // inverse map to get v back from the output value y assertTrue(inv.contains(y)); 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 | ExpectedDoubleQuotation e) { e.printStackTrace(); } } diff --git a/AlgebraicDataflowArchitectureModel/src/tests/JAXRSCodeGeneratorTest.java b/AlgebraicDataflowArchitectureModel/src/tests/JAXRSCodeGeneratorTest.java new file mode 100644 index 0000000..9d10492 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/tests/JAXRSCodeGeneratorTest.java @@ -0,0 +1,1539 @@ +package tests; + +import algorithms.DataTransferModelAnalyzer; +import algorithms.TypeInference; +import code.ast.*; +import generators.DataTransferMethodAnalyzer; +import generators.JerseyCodeGenerator; +import generators.JerseyMethodBodyGenerator; +import models.Edge; +import models.dataFlowModel.*; +import org.junit.Test; +import parser.Parser; +import parser.exceptions.*; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.util.*; +import java.util.Map.Entry; + +import static org.junit.Assert.*; + +public class JAXRSCodeGeneratorTest { + + @Test + public void test() { + testAccounts(); + testClock(); + testCustomerManagement(); + testGroupChat(); + testInventoryManagement(); + testOnlineBattleGame(); + testOnlineBattleGame2(); + testPOS(); + testSimpleTwitter(); + testVotingSystem(); + testWeatherObservationSystem(); + } + + private void testAccounts() { + try { + ArrayList generatedCode = generateCode("models/Accounts.model", null); + Map, // class annotations + Entry, // field name to type + Map, // class annotations + Entry, // arg types + Integer>>>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Accounts", Map.entry(Set.of("@Path(\"/accounts\")", "@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "List")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("List", + Map.entry(List.of(), + 1)))), + Map.entry("getAccount", Map.entry(Set.of(), + Map.entry("Account", + Map.entry(List.of("int"), + 1)))), + Map.entry("getNameValue", Map.entry(Set.of("@Path(\"/{uid}/name\")", "@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("String", + Map.entry(List.of("int"), + 1)))), + Map.entry("getAccountValue", Map.entry(Set.of("@Path(\"/{uid}\")", "@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("Map", + Map.entry(List.of("int"), + 1)))), + Map.entry("changeName", Map.entry(Set.of("@Path(\"/{uid}/name\")", "@PUT"), + Map.entry("void", + Map.entry(List.of("int", "String"), + 1)))), + Map.entry("signup", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("String"), + 1)))))))); + exprectedStructure.put("Account", Map.entry(Set.of(), + Map.entry(Map.ofEntries(Map.entry("name", "String")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of(), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getName", Map.entry(Set.of(), + Map.entry("String", + Map.entry(List.of(), + 1)))), + Map.entry("changeName", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("int", "String"), + 1)))), + Map.entry("Account", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String"), + 1)))))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private void testClock() { + try { + // check PULL-first + ArrayList generatedCode = generateCode("models/Clock.model", PushPullValue.PULL); + Map, // class annotations + Entry, // field name to type + Map, // class annotations + Entry, // arg types + Integer>>>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Hour", Map.entry(Set.of("@Path(\"/hour\")", "@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "int")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("int", + Map.entry(List.of(), + 1)))), + Map.entry("updateFromMin", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("int"), + 1)))))))); + exprectedStructure.put("Min", Map.entry(Set.of("@Path(\"/min\")", "@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "int"), + Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("int", + Map.entry(List.of(), + 1)))), + Map.entry("tick", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of(), + 3)))))))); + exprectedStructure.put("Min_ang", Map.entry(Set.of("@Path(\"/min_ang\")", "@Component"), + Map.entry(Map.ofEntries(Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("double", + Map.entry(List.of(), + 2)))))))); + exprectedStructure.put("Hour_ang", Map.entry(Set.of("@Path(\"/hour_ang\")", "@Component"), + Map.entry(Map.ofEntries(Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("double", + Map.entry(List.of(), + 2)))))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + + // check PUSH-first + generatedCode = generateCode("models/Clock.model", PushPullValue.PUSH); + exprectedStructure.clear(); + exprectedStructure.put("Hour_ang", Map.entry(Set.of("@Path(\"/hour_ang\")", "@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "double")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("double", + Map.entry(List.of(), + 1)))), + Map.entry("updateFromHour", Map.entry(Set.of("@PUT"), + Map.entry("void", + Map.entry(List.of("int"), + 1)))))))); + exprectedStructure.put("Hour", Map.entry(Set.of("@Path(\"/hour\")", "@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "int"), + Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("int", + Map.entry(List.of(), + 1)))), + Map.entry("updateFromMin", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("int"), + 3)))))))); + exprectedStructure.put("Min", Map.entry(Set.of("@Path(\"/min\")", "@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "int"), + Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("int", + Map.entry(List.of(), + 1)))), + Map.entry("tick", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of(), + 5)))))))); + exprectedStructure.put("Min_ang", Map.entry(Set.of("@Path(\"/min_ang\")", "@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "double")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("double", + Map.entry(List.of(), + 1)))), + Map.entry("updateFromMin", Map.entry(Set.of("@PUT"), + Map.entry("void", + Map.entry(List.of("int"), + 1)))))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private void testCustomerManagement() { + try { + ArrayList generatedCode = generateCode("models/CustomerManagement.model", null); + Map, // class annotations + Entry, // field name to type + Map, // class annotations + Entry, // arg types + Integer>>>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Companies", Map.entry(Set.of("@Path(\"/companies\")", "@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "Map")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getCompany", Map.entry(Set.of(), + Map.entry("Company", + Map.entry(List.of("String"), + 1)))), + Map.entry("getAddressValue", Map.entry(Set.of("@Path(\"/{cid}/address\")", "@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("String", + Map.entry(List.of("String"), + 1)))), + Map.entry("getCompanyValue", Map.entry(Set.of("@Path(\"/{cid}\")", "@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("Map", + Map.entry(List.of("String"), + 1)))), + Map.entry("setAddress", Map.entry(Set.of("@Path(\"/{cid}/address\")", "@PUT"), + Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))), + Map.entry("addCampany", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))))))); + exprectedStructure.put("Customers", Map.entry(Set.of("@Path(\"/customers\")", "@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "Map")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getCustomer", Map.entry(Set.of(), + Map.entry("Customer", + Map.entry(List.of("String"), + 1)))), + Map.entry("getAddressValue", Map.entry(Set.of("@Path(\"/{uid}/address\")", "@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("String", + Map.entry(List.of("String"), + 1)))), + Map.entry("getCustomerValue", Map.entry(Set.of("@Path(\"/{uid}\")", "@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("Map", + Map.entry(List.of("String"), + 1)))), + Map.entry("getOrganizationValue", Map.entry(Set.of("@Path(\"/{uid}/organization\")", "@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("String", + Map.entry(List.of("String"), + 1)))), + Map.entry("addCustomer", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))), + Map.entry("setOrganization", Map.entry(Set.of("@Path(\"/{uid}/organization\")", "@PUT"), + Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))))))); + exprectedStructure.put("Customer", Map.entry(Set.of(), + Map.entry(Map.ofEntries(Map.entry("organization", "String"), + Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of(), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getOrganization", Map.entry(Set.of(), + Map.entry("String", + Map.entry(List.of(), + 1)))), + Map.entry("getAddress", Map.entry(Set.of(), + Map.entry("String", + Map.entry(List.of(), + 2)))), + Map.entry("setOrganization", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))), + Map.entry("Customer", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String"), + 1)))))))); + exprectedStructure.put("Company", Map.entry(Set.of(), + Map.entry(Map.ofEntries(Map.entry("address", "String")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of(), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getAddress", Map.entry(Set.of(), + Map.entry("String", + Map.entry(List.of(), + 1)))), + Map.entry("setAddress", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))), + Map.entry("Company", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String"), + 1)))))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private void testGroupChat() { + try { + ArrayList generatedCode = generateCode("models/GroupChat.model", null); + Map, // class annotations + Entry, // field name to type + Map, // class annotations + Entry, // arg types + Integer>>>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Groups", Map.entry(Set.of("@Path(\"/groups\")", "@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "Map")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getGroup", Map.entry(Set.of(), + Map.entry("Group", + Map.entry(List.of("String"), + 1)))), + Map.entry("getMembersValue", Map.entry(Set.of("@Path(\"/{gid}/members\")", "@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("List", + Map.entry(List.of("String"), + 1)))), + Map.entry("getMessagesValue", Map.entry(Set.of("@Path(\"/{gid}/messages\")", "@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("List", + Map.entry(List.of("String"), + 1)))), + Map.entry("getGroupValue", Map.entry(Set.of("@Path(\"/{gid}\")", "@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("Map", + Map.entry(List.of("String"), + 1)))), + Map.entry("addGroupMember", Map.entry(Set.of("@Path(\"/{gid}/members\")", "@POST"), + Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))), + Map.entry("postMessage", Map.entry(Set.of("@Path(\"/{gid}/messages\")", "@POST"), + Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))), + Map.entry("createGroup", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("String"), + 1)))))))); + exprectedStructure.put("Account", Map.entry(Set.of(), + Map.entry(Map.ofEntries(Map.entry("notifications", "Map")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of(), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getNotifications", Map.entry(Set.of(), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("updateNotificationsFromMessages", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String", "String", "int", "List", "String"), + 1)))), + Map.entry("hasRead", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))), + Map.entry("Account", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("Map"), + 1)))))))); + exprectedStructure.put("Group", Map.entry(Set.of(), + Map.entry(Map.ofEntries(Map.entry("messages", "List"), + Map.entry("client", "Client"), + Map.entry("members", "List")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of(), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getMessages", Map.entry(Set.of(), + Map.entry("List", + Map.entry(List.of(), + 1)))), + Map.entry("getMembers", Map.entry(Set.of(), + Map.entry("List", + Map.entry(List.of(), + 1)))), + Map.entry("postMessage", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String", "String"), + 6)))), + Map.entry("addGroupMember", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))), + Map.entry("Group", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("List", "List"), + 2)))))))); + exprectedStructure.put("Accounts", Map.entry(Set.of("@Path(\"/accounts\")", "@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "Map")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getAccount", Map.entry(Set.of(), + Map.entry("Account", + Map.entry(List.of("String"), + 1)))), + Map.entry("updateNotificationsFromMessages", Map.entry(Set.of("@Path(\"accounts/{v1}/notifications\")", "@POST"), + Map.entry("void", + Map.entry(List.of("String", "String", "int", "List", "String"), + 1)))), + Map.entry("getNotificationsValue", Map.entry(Set.of("@Path(\"/{v1}/notifications\")", "@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("Map", + Map.entry(List.of("String"), + 1)))), + Map.entry("getAccountValue", Map.entry(Set.of("@Path(\"/{v1}\")", "@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("Map", + Map.entry(List.of("String"), + 1)))), + Map.entry("hasRead", Map.entry(Set.of("@Path(\"/{aid}/notifications\")", "@DELETE"), + Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))), + Map.entry("signUp", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("String"), + 1)))))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private void testInventoryManagement() { + try { + ArrayList generatedCode = generateCode("models/InventoryManagement.model", null); + Map, // class annotations + Entry, // field name to type + Map, // class annotations + Entry, // arg types + Integer>>>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("InventoryElement", Map.entry(Set.of(), + Map.entry(Map.ofEntries(Map.entry("count", "int")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of(), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getCount", Map.entry(Set.of(), + Map.entry("int", + Map.entry(List.of(), + 1)))), + Map.entry("receiveOrShip", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String", "int"), + 1)))), + Map.entry("InventoryElement", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("int"), + 1)))))))); + exprectedStructure.put("Inventory", Map.entry(Set.of("@Path(\"/inventory\")", "@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "Map")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getInventoryElement", Map.entry(Set.of(), + Map.entry("InventoryElement", + Map.entry(List.of("String"), + 1)))), + Map.entry("getInventoryElementValue", Map.entry(Set.of("@Path(\"/{itemId}\")", "@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("Map", + Map.entry(List.of("String"), + 1)))), + Map.entry("getCountValue", Map.entry(Set.of("@Path(\"/{itemId}/count\")", "@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("int", + Map.entry(List.of("String"), + 1)))), + Map.entry("registerItem", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("String", "int", "String"), + 1)))), + Map.entry("receiveOrShip", Map.entry(Set.of("@Path(\"/{itemId}/count\")", "@POST"), + Map.entry("void", + Map.entry(List.of("String", "int"), + 1)))))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private void testOnlineBattleGame() { + try { + // check PULL-first + ArrayList generatedCode = generateCode("models/OnlineBattleGame.model", null); + Map, // class annotations + Entry, // field name to type + Map, // class annotations + Entry, // arg types + Integer>>>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Rooms", Map.entry(Set.of("@Path(\"/rooms\")", "@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "Map")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getRoom", Map.entry(Set.of(), + Map.entry("Room", + Map.entry(List.of("String"), + 1)))), + Map.entry("getBlue_idValue", Map.entry(Set.of("@Path(\"/{rid}/blue_id\")", "@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("String", + Map.entry(List.of("String"), + 1)))), + Map.entry("getRed_nameValue", Map.entry(Set.of("@Path(\"/{rid}/red_name\")", "@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("String", + Map.entry(List.of("String"), + 1)))), + Map.entry("getBlue_nameValue", Map.entry(Set.of("@Path(\"/{rid}/blue_name\")", "@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("String", + Map.entry(List.of("String"), + 1)))), + Map.entry("getRed_idValue", Map.entry(Set.of("@Path(\"/{rid}/red_id\")", "@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("String", + Map.entry(List.of("String"), + 1)))), + Map.entry("getRoomValue", Map.entry(Set.of("@Path(\"/{rid}\")", "@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("Map", + Map.entry(List.of("String"), + 1)))), + Map.entry("changeBlueId", Map.entry(Set.of("@Path(\"/{rid}/blue_id\")", "@PUT"), + Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))), + Map.entry("createRoom", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("String", "String", "String"), + 1)))), + Map.entry("changeRedId", Map.entry(Set.of("@Path(\"/{rid}/red_id\")", "@PUT"), + Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))))))); + exprectedStructure.put("Room", Map.entry(Set.of(), + Map.entry(Map.ofEntries(Map.entry("blue_id", "String"), + Map.entry("red_id", "String"), + Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of(), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getBlue_id", Map.entry(Set.of(), + Map.entry("String", + Map.entry(List.of(), + 1)))), + Map.entry("getRed_id", Map.entry(Set.of(), + Map.entry("String", + Map.entry(List.of(), + 1)))), + Map.entry("getRed_name", Map.entry(Set.of(), + Map.entry("String", + Map.entry(List.of(), + 2)))), + Map.entry("getBlue_name", Map.entry(Set.of(), + Map.entry("String", + Map.entry(List.of(), + 2)))), + Map.entry("changeRedId", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))), + Map.entry("changeBlueId", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))), + Map.entry("Room", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String", "String"), + 2)))))))); + exprectedStructure.put("Account", Map.entry(Set.of(), + Map.entry(Map.ofEntries(Map.entry("name", "String")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of(), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getName", Map.entry(Set.of(), + Map.entry("String", + Map.entry(List.of(), + 1)))), + Map.entry("changeName", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))), + Map.entry("Account", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String"), + 1)))))))); + exprectedStructure.put("Accounts", Map.entry(Set.of("@Path(\"/accounts\")", "@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "Map")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getAccount", Map.entry(Set.of(), + Map.entry("Account", + Map.entry(List.of("String"), + 1)))), + Map.entry("getAccountValue", Map.entry(Set.of("@Path(\"/{aid}\")", "@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("Map", + Map.entry(List.of("String"), + 1)))), + Map.entry("getNameValue", Map.entry(Set.of("@Path(\"/{aid}/name\")", "@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("String", + Map.entry(List.of("String"), + 1)))), + Map.entry("signUp", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))), + Map.entry("changeName", Map.entry(Set.of("@Path(\"/{aid}/name\")", "@PUT"), + Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + + private void testOnlineBattleGame2() { + try { + // check PULL-first + ArrayList generatedCode = generateCode("models/OnlineBattleGame2.model", null); + Map, // class annotations + Entry, // field name to type + Map, // class annotations + Entry, // arg types + Integer>>>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Rooms", Map.entry(Set.of("@Path(\"/rooms\")", "@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "Map")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getRoom", Map.entry(Set.of(), + Map.entry("Room", + Map.entry(List.of("String"), + 1)))), + Map.entry("getIdValue", Map.entry(Set.of("@Path(\"/{rid}/members/{mno}/id\")", "@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("String", + Map.entry(List.of("String", "int"), + 1)))), + Map.entry("getBattleValue", Map.entry(Set.of("@Path(\"/{rid}/battle\")", "@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("boolean", + Map.entry(List.of("String"), + 1)))), + Map.entry("getRoomValue", Map.entry(Set.of("@Path(\"/{rid}\")", "@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("Map", + Map.entry(List.of("String"), + 1)))), + Map.entry("getMemberValue", Map.entry(Set.of("@Path(\"/{rid}/members/{mno}\")", "@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("Map", + Map.entry(List.of("String", "int"), + 1)))), + Map.entry("getMembersValue", Map.entry(Set.of("@Path(\"/{rid}/members\")", "@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("List", + Map.entry(List.of("String"), + 1)))), + Map.entry("getNameValue", Map.entry(Set.of("@Path(\"/{rid}/members/{mno}/name\")", "@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("String", + Map.entry(List.of("String", "int"), + 1)))), + Map.entry("battle", Map.entry(Set.of("@Path(\"/{rid}/battle\")", "@PUT"), + Map.entry("void", + Map.entry(List.of("String", "boolean"), + 1)))), + Map.entry("addRoomMember", Map.entry(Set.of("@Path(\"/{rid}/members\")", "@POST"), + Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))), + Map.entry("createRoom", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("String"), + 1)))))))); + exprectedStructure.put("Member", Map.entry(Set.of(), + Map.entry(Map.ofEntries(Map.entry("id", "String"), + Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of(), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getId", Map.entry(Set.of(), + Map.entry("String", + Map.entry(List.of(), + 1)))), + Map.entry("getName", Map.entry(Set.of(), + Map.entry("String", + Map.entry(List.of(), + 2)))), + Map.entry("Member", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String"), + 1)))))))); + exprectedStructure.put("Account", Map.entry(Set.of(), + Map.entry(Map.ofEntries(Map.entry("name", "String"), + Map.entry("point", "int")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of(), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getName", Map.entry(Set.of(), + Map.entry("String", + Map.entry(List.of(), + 1)))), + Map.entry("getPoint", Map.entry(Set.of(), + Map.entry("int", + Map.entry(List.of(), + 1)))), + Map.entry("updatePointFromBattle", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String", "String", "int", "boolean", "String"), + 1)))), + Map.entry("changeName", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))), + Map.entry("Account", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String", "int"), + 2)))))))); + exprectedStructure.put("Room", Map.entry(Set.of(), + Map.entry(Map.ofEntries(Map.entry("members", "Members"), + Map.entry("battle", "boolean"), + Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of(), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getMembers", Map.entry(Set.of(), + Map.entry("Members", + Map.entry(List.of(), + 1)))), + Map.entry("getBattle", Map.entry(Set.of(), + Map.entry("boolean", + Map.entry(List.of(), + 1)))), + Map.entry("battle", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String", "boolean"), + 6)))), + Map.entry("Room", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("boolean"), + 1)))))))); + exprectedStructure.put("Accounts", Map.entry(Set.of("@Path(\"/accounts\")", "@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "Map")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getAccount", Map.entry(Set.of(), + Map.entry("Account", + Map.entry(List.of("String"), + 1)))), + Map.entry("updatePointFromBattle", Map.entry(Set.of("@Path(\"accounts/{mid}/point\")", "@POST"), + Map.entry("void", + Map.entry(List.of("String", "String", "int", "boolean", "String"), + 1)))), + Map.entry("getAccountValue", Map.entry(Set.of("@Path(\"/{mid}\")", "@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("Map", + Map.entry(List.of("String"), + 1)))), + Map.entry("getNameValue", Map.entry(Set.of("@Path(\"/{mid}/name\")", "@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("String", + Map.entry(List.of("String"), + 1)))), + Map.entry("getPointValue", Map.entry(Set.of("@Path(\"/{mid}/point\")", "@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("int", + Map.entry(List.of("String"), + 1)))), + Map.entry("signUp", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))), + Map.entry("changeName", Map.entry(Set.of("@Path(\"/{aid}/name\")", "@PUT"), + Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))))))); + exprectedStructure.put("Members", Map.entry(Set.of(), + Map.entry(Map.ofEntries(Map.entry("value", "List")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of(), + Map.entry("List", + Map.entry(List.of(), + 1)))), + Map.entry("getMember", Map.entry(Set.of(), + Map.entry("Member", + Map.entry(List.of("int"), + 1)))), + Map.entry("addRoomMember", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private void testPOS() { + try { + // check PULL-first + ArrayList generatedCode = generateCode("models/POS.model", PushPullValue.PULL); + Map, // class annotations + Entry, // field name to type + Map, // class annotations + Entry, // arg types + Integer>>>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Points", Map.entry(Set.of("@Path(\"/points\")", "@Component"), + Map.entry(Map.ofEntries(Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("int", + Map.entry(List.of(), + 2)))))))); + exprectedStructure.put("Total", Map.entry(Set.of("@Path(\"/total\")", "@Component"), + Map.entry(Map.ofEntries(Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("int", + Map.entry(List.of(), + 2)))))))); + exprectedStructure.put("Payment", Map.entry(Set.of("@Path(\"/payment\")", "@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "int"), + Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("int", + Map.entry(List.of(), + 1)))), + Map.entry("purchase", Map.entry(Set.of("@PUT"), + Map.entry("void", + Map.entry(List.of("int"), + 3)))))))); + exprectedStructure.put("History", Map.entry(Set.of("@Path(\"/history\")", "@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "List")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("List", + Map.entry(List.of(), + 1)))), + Map.entry("updateFromPayment", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("int"), + 1)))))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + + // check PUSH-first + generatedCode = generateCode("models/POS.model", PushPullValue.PUSH); + exprectedStructure.clear(); + exprectedStructure.put("Payment", Map.entry(Set.of("@Path(\"/payment\")", "@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "int"), + Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("int", + Map.entry(List.of(), + 1)))), + Map.entry("purchase", Map.entry(Set.of("@PUT"), + Map.entry("void", + Map.entry(List.of("int"), + 5)))))))); + exprectedStructure.put("Total", Map.entry(Set.of("@Path(\"/total\")", "@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "int")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("int", + Map.entry(List.of(), + 1)))), + Map.entry("updateFromHistory", Map.entry(Set.of("@PUT"), + Map.entry("void", + Map.entry(List.of("List"), + 1)))))))); + exprectedStructure.put("History", Map.entry(Set.of("@Path(\"/history\")", "@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "List"), + Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("List", + Map.entry(List.of(), + 1)))), + Map.entry("updateFromPayment", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("int"), + 3)))))))); + exprectedStructure.put("Points", Map.entry(Set.of("@Path(\"/points\")", "@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "int")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("int", + Map.entry(List.of(), + 1)))), + Map.entry("updateFromPayment", Map.entry(Set.of("@PUT"), + Map.entry("void", + Map.entry(List.of("int"), + 1)))))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private void testSimpleTwitter() { + try { + ArrayList generatedCode = generateCode("models/SimpleTwitter.model", null); + Map, // class annotations + Entry, // field name to type + Map, // class annotations + Entry, // arg types + Integer>>>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Accounts", Map.entry(Set.of("@Path(\"/accounts\")", "@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "Map")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getAccount", Map.entry(Set.of(), + Map.entry("Account", + Map.entry(List.of("String"), + 1)))), + Map.entry("getAccountValue", Map.entry(Set.of("@Path(\"/{accountId}\")", "@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("Map", + Map.entry(List.of("String"), + 1)))), + Map.entry("getTweetsValue", Map.entry(Set.of("@Path(\"/{accountId}/tweets\")", "@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("List", + Map.entry(List.of("String"), + 1)))), + Map.entry("tweet", Map.entry(Set.of("@Path(\"/{accountId}/tweets\")", "@POST"), + Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))), + Map.entry("signUp", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))))))); + exprectedStructure.put("Account", Map.entry(Set.of(), + Map.entry(Map.ofEntries(Map.entry("tweets", "List")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of(), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getTweets", Map.entry(Set.of(), + Map.entry("List", + Map.entry(List.of(), + 1)))), + Map.entry("tweet", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))), + Map.entry("Account", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("List"), + 1)))))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private void testVotingSystem() { + try { + ArrayList generatedCode = generateCode("models/VotingSystem.model", null); + Map, // class annotations + Entry, // field name to type + Map, // class annotations + Entry, // arg types + Integer>>>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Counts", Map.entry(Set.of("@Path(\"/counts\")", "@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "Map"), + Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("Map", + Map.entry(List.of(), + 8)))))))); + exprectedStructure.put("Account", Map.entry(Set.of(), + Map.entry(Map.ofEntries(Map.entry("vote", "String")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of(), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getVote", Map.entry(Set.of(), + Map.entry("String", + Map.entry(List.of(), + 1)))), + Map.entry("cast", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))), + Map.entry("Account", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String"), + 1)))))))); + exprectedStructure.put("Accounts", Map.entry(Set.of("@Path(\"/accounts\")", "@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "Map")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getAccount", Map.entry(Set.of(), + Map.entry("Account", + Map.entry(List.of("String"), + 1)))), + Map.entry("getVoteValue", Map.entry(Set.of("@Path(\"/{aid}/vote\")", "@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("String", + Map.entry(List.of("String"), + 1)))), + Map.entry("getAccountValue", Map.entry(Set.of("@Path(\"/{aid}\")", "@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("Map", + Map.entry(List.of("String"), + 1)))), + Map.entry("cast", Map.entry(Set.of("@Path(\"/{aid}/vote\")", "@PUT"), + Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))), + Map.entry("signUp", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private void testWeatherObservationSystem() { + try { + // check PULL-first + ArrayList generatedCode = generateCode("models/WeatherObservationSystem.model", PushPullValue.PULL); + Map, // class annotations + Entry, // field name to type + Map, // class annotations + Entry, // arg types + Integer>>>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Temp_c", Map.entry(Set.of("@Path(\"/temp_c\")", "@Component"), + Map.entry(Map.ofEntries(Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("double", + Map.entry(List.of(), + 2)))))))); + exprectedStructure.put("Highest", Map.entry(Set.of("@Path(\"/highest\")", "@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "double")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("double", + Map.entry(List.of(), + 1)))), + Map.entry("updateFromTemp_f", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("double"), + 1)))), + Map.entry("reset", Map.entry(Set.of("@PUT"), + Map.entry("void", + Map.entry(List.of("double"), + 1)))))))); + exprectedStructure.put("Temp_f", Map.entry(Set.of("@Path(\"/temp_f\")", "@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "double"), + Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("double", + Map.entry(List.of(), + 1)))), + Map.entry("observe", Map.entry(Set.of("@PUT"), + Map.entry("void", + Map.entry(List.of("double"), + 3)))))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + + // check PUSH-first + generatedCode = generateCode("models/WeatherObservationSystem.model", PushPullValue.PUSH); + exprectedStructure.clear(); + exprectedStructure.put("Highest", Map.entry(Set.of("@Path(\"/highest\")", "@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "double")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("double", + Map.entry(List.of(), + 1)))), + Map.entry("updateFromTemp_f", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("double"), + 1)))), + Map.entry("reset", Map.entry(Set.of("@PUT"), + Map.entry("void", + Map.entry(List.of("double"), + 1)))))))); + exprectedStructure.put("Temp_f", Map.entry(Set.of("@Path(\"/temp_f\")", "@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "double"), + Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("double", + Map.entry(List.of(), + 1)))), + Map.entry("observe", Map.entry(Set.of("@PUT"), + Map.entry("void", + Map.entry(List.of("double"), + 5)))))))); + exprectedStructure.put("Temp_c", Map.entry(Set.of("@Path(\"/temp_c\")", "@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "double")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)", "@GET"), + Map.entry("double", + Map.entry(List.of(), + 1)))), + Map.entry("updateFromTemp_f", Map.entry(Set.of("@PUT"), + Map.entry("void", + Map.entry(List.of("double"), + 1)))))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private ArrayList generateCode(String fileName, PushPullValue pushPullValue) throws FileNotFoundException, + ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedRightCurlyBracket, ExpectedInOrOutOrRefOrSubKeyword, + ExpectedStateTransition, ExpectedEquals, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, + WrongPathExpression, WrongJsonExpression, ExpectedColon, ExpectedDoubleQuotation { + File file = new File(fileName); + Parser parser = new Parser(new BufferedReader(new FileReader(file))); + DataTransferModel model; + model = parser.doParse(); + DataFlowGraph graph = DataTransferModelAnalyzer.createDataFlowGraphWithStateStoringAttribute(model); + DataTransferModelAnalyzer.annotateWithSelectableDataTransferAttiribute(graph); + if (pushPullValue != null) { + // Select a specified push/pull transfer method if possible. + for (Edge e : graph.getEdges()) { + if (!((DataFlowEdge) e).isChannelToResource() && ((DataFlowEdge) e).getAttribute() instanceof PushPullAttribute) { + PushPullAttribute ppat = (PushPullAttribute) ((DataFlowEdge) e).getAttribute(); + ppat.selectOption(pushPullValue); + } + } + } + TypeInference.infer(model); + DataTransferMethodAnalyzer.decideToStoreResourceStates(graph); + ArrayList codetree = JerseyMethodBodyGenerator.doGenerate(graph, model, JerseyCodeGenerator.doGenerate(graph, model)); +// ArrayList codetree = new CodeGeneratorFromDataFlowGraph().generateCode(model, graph, new JerseySpecific(), new JavaSpecific()); + return codetree; + } + + private void checkStructure(ArrayList generatedCode, + Map, Entry, Map, Entry, Integer>>>>>>> exprectedStructure) { + for (var classEnt : exprectedStructure.entrySet()) { + String expectedClassName = classEnt.getKey(); + Entry, Entry, Map, Entry, Integer>>>>>> expectedClassInfo = classEnt.getValue(); + TypeDeclaration generatedClass = null; + for (CompilationUnit cu : generatedCode) { + for (TypeDeclaration type : cu.types()) { + if (type.getTypeName().equals(expectedClassName)) { + generatedClass = type; + break; + } + } + } + assertNotNull(generatedClass); + + Set expectedClassAnnotations = expectedClassInfo.getKey(); + for (String expectedAnnotation : expectedClassAnnotations) { + boolean existsAnnotation = false; + for (Annotation generatedAnnotation : generatedClass.getAnnotations()) { + if (expectedAnnotation.equals(generatedAnnotation.toString())) { + existsAnnotation = true; + } + } + assertTrue(existsAnnotation); + } + Entry, Map, Entry, Integer>>>>> expectedClassStructure = expectedClassInfo.getValue(); + Map exprectedFields = expectedClassStructure.getKey(); + Map, Entry, Integer>>>> exprectedMethods = expectedClassStructure.getValue(); + + for (String expectedFieldName : exprectedFields.keySet()) { + FieldDeclaration generatedField = null; + for (FieldDeclaration field : generatedClass.getFields()) { + if (field.getName().equals(expectedFieldName)) { + generatedField = field; + break; + } + } + assertNotNull(generatedField); + + String expectedFieldType = exprectedFields.get(expectedFieldName); + if (expectedFieldType.equals("void")) { + assertNull(generatedField.getType()); + } else { + assertEquals(expectedFieldType, generatedField.getType().getInterfaceTypeName()); + } + } + + for (String expectedMethodName : exprectedMethods.keySet()) { + MethodDeclaration generatedMethod = null; + for (MethodDeclaration method : generatedClass.getMethods()) { + if (method.getName().equals(expectedMethodName)) { + generatedMethod = method; + break; + } + } + assertNotNull(generatedMethod); + + Entry, Entry, Integer>>> expectedMethodInfo = exprectedMethods.get(expectedMethodName); + Set expectedMethodAnnotations = expectedMethodInfo.getKey(); + for (String expectedAnnotation : expectedMethodAnnotations) { + boolean existsAnnotation = false; + for (Annotation generatedAnnotation : generatedMethod.getAnnotations()) { + if (expectedAnnotation.replaceAll("\\{.*\\}", "\\{\\}").equals(generatedAnnotation.toString().replaceAll("\\{.*\\}", "\\{\\}"))) { + existsAnnotation = true; + } + } + assertTrue(existsAnnotation); + } + Entry, Integer>> expectedMethodSignature = expectedMethodInfo.getValue(); + String expectedReturnType = expectedMethodSignature.getKey(); + if (expectedReturnType.equals("void")) { + if (generatedMethod.getReturnType() != null) { + assertEquals(expectedReturnType, generatedMethod.getReturnType().getInterfaceTypeName()); + } else { + assertNull(generatedMethod.getReturnType()); + } + } else { + assertEquals(expectedReturnType, generatedMethod.getReturnType().getInterfaceTypeName()); + } + Entry, Integer> expectedMethodInfo2 = expectedMethodSignature.getValue(); + List expectedArgTypes = expectedMethodInfo2.getKey(); + for (String expectedArgType : expectedArgTypes) { + boolean existsArg = false; + for (VariableDeclaration var : generatedMethod.getParameters()) { + if (expectedArgType.equals(var.getType().getInterfaceTypeName())) { + existsArg = true; + } + } + assertTrue(existsArg); + } + int expectedLinesOfCode = expectedMethodInfo2.getValue(); + assertEquals(expectedLinesOfCode, generatedMethod.getBody().getStatements().size()); + } + } + } + + private void generateCheckCode(ArrayList generatedCode) { +// exprectedStructure.put("Main", Map.entry(Set.of(), +// Map.entry(Map.ofEntries(Map.entry("history", "History"), +// Map.entry("total", "Total"), +// Map.entry("payment", "Payment"), +// Map.entry("points", "Points")), +// Map.ofEntries(Map.entry("Main", Map.entry(Set.of(), +// Map.entry("void", +// Map.entry(List.of(), +// 4)))), +// Map.entry("getHistory", Map.entry(Set.of(), +// Map.entry("List", +// Map.entry(List.of(), +// 1)))), +// Map.entry("getTotal", Map.entry(Set.of(), +// Map.entry("int", +// Map.entry(List.of(), +// 1)))), +// Map.entry("getPayment", Map.entry(Set.of(), +// Map.entry("int", +// Map.entry(List.of(), +// 1)))), +// Map.entry("purchase", Map.entry(Set.of(), +// Map.entry("void", +// Map.entry(List.of("int"), +// 1)))), +// Map.entry("getPoints", Map.entry(Set.of(), +// Map.entry("int", +// Map.entry(List.of(), +// 1)))))))); + for (CompilationUnit cu : generatedCode) { + for (TypeDeclaration type : cu.types()) { + Collection annotations = type.getAnnotations(); + List fields = type.getFields(); + List methods = type.getMethods(); + // class annotations + if (annotations.size() == 0) { + System.out.println("\t\t\texprectedStructure.put(\"" + type.getTypeName() + "\", Map.entry(Set.of(),"); + } else { + System.out.print("\t\t\texprectedStructure.put(\"" + type.getTypeName() + "\", Map.entry(Set.of("); + String delim = ""; + for (Annotation annotation : annotations) { + System.out.print(delim + "\"" + annotation.toString().replace("\"", "\\\"") + "\""); + delim = ","; + } + System.out.println("),"); + } + // fields + System.out.print("\t\t\t\t\t\t\t\t\t\t\t\t"); + for (int j = 0; j < (type.getTypeName().length() + 1) / 4; j++) { + System.out.print("\t"); + } + for (int j = 0; j < (type.getTypeName().length() + 1) % 4; j++) { + System.out.print(" "); + } + if (fields.size() == 0) { + System.out.println("Map.entry(Map.ofEntries(),"); + } else if (fields.size() == 1) { + FieldDeclaration field = fields.get(0); + System.out.println("Map.entry(Map.ofEntries(Map.entry(\"" + field.getName() + "\", \"" + field.getType().getInterfaceTypeName() + "\")),"); + } else { + int i = 0; + for (FieldDeclaration field : fields) { + if (i == 0) { + System.out.println("Map.entry(Map.ofEntries(Map.entry(\"" + field.getName() + "\", \"" + field.getType().getInterfaceTypeName() + "\"),"); + } else { + System.out.print("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"); + for (int j = 0; j < (type.getTypeName().length() + 1) / 4; j++) { + System.out.print("\t"); + } + for (int j = 0; j < (type.getTypeName().length() + 1) % 4; j++) { + System.out.print(" "); + } + if (i < fields.size() - 1) { + System.out.println("Map.entry(\"" + field.getName() + "\", \"" + field.getType().getInterfaceTypeName() + "\"),"); + } else { + System.out.println("Map.entry(\"" + field.getName() + "\", \"" + field.getType().getInterfaceTypeName() + "\")),"); + } + } + i++; + } + } + // methods + if (methods.size() == 0) { + System.out.print("\t\t\t\t\t\t\t\t\t\t\t\t\t\t"); + for (int j = 0; j < (type.getTypeName().length() + 3) / 4; j++) { + System.out.print("\t"); + } + for (int j = 0; j < (type.getTypeName().length() + 3) % 4; j++) { + System.out.print(" "); + } + System.out.println("Map.ofEntries())));"); + } else { + int i = 0; + for (MethodDeclaration method : methods) { + // method name and method annotations + if (i == 0) { + System.out.print("\t\t\t\t\t\t\t\t\t\t\t\t\t\t"); + for (int j = 0; j < (type.getTypeName().length() + 3) / 4; j++) { + System.out.print("\t"); + } + for (int j = 0; j < (type.getTypeName().length() + 3) % 4; j++) { + System.out.print(" "); + } + Collection methodAnnotations = method.getAnnotations(); + if (methodAnnotations.size() == 0) { + System.out.println("Map.ofEntries(Map.entry(\"" + method.getName() + "\", Map.entry(Set.of(),"); + } else { + System.out.print("Map.ofEntries(Map.entry(\"" + method.getName() + "\", Map.entry(Set.of("); + String delim = ""; + for (Annotation annotation : methodAnnotations) { + System.out.print(delim + "\"" + annotation.toString().replace("\"", "\\\"") + "\""); + delim = ","; + } + System.out.println("),"); + } + } else { + System.out.print("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"); + for (int j = 0; j < (type.getTypeName().length() + 1) / 4; j++) { + System.out.print("\t"); + } + for (int j = 0; j < (type.getTypeName().length() + 1) % 4; j++) { + System.out.print(" "); + } + Collection methodAnnotations = method.getAnnotations(); + if (methodAnnotations.size() == 0) { + System.out.println("Map.entry(\"" + method.getName() + "\", Map.entry(Set.of(),"); + } else { + System.out.print("Map.entry(\"" + method.getName() + "\", Map.entry(Set.of("); + String delim = ""; + for (Annotation annotation : methodAnnotations) { + System.out.print(delim + "\"" + annotation.toString().replace("\"", "\\\"") + "\""); + delim = ","; + } + System.out.println("),"); + } + } + // return type + System.out.print("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"); + for (int j = 0; j < (type.getTypeName().length() + method.getName().length() + 3) / 4; j++) { + System.out.print("\t"); + } + for (int j = 0; j < (type.getTypeName().length() + method.getName().length() + 3) % 4; j++) { + System.out.print(" "); + } + if (method.getReturnType() == null) { + System.out.println("Map.entry(\"void\", "); + } else { + System.out.println("Map.entry(\"" + method.getReturnType().getInterfaceTypeName() + "\", "); + } + // method parameters + System.out.print("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"); + for (int j = 0; j < (type.getTypeName().length() + method.getName().length() + 3) / 4; j++) { + System.out.print("\t"); + } + for (int j = 0; j < (type.getTypeName().length() + method.getName().length() + 3) % 4; j++) { + System.out.print(" "); + } + if (method.getParameters() == null || method.getParameters().size() == 0) { + System.out.println("Map.entry(List.of(),"); + } else { + System.out.print("Map.entry(List.of("); + String delim = ""; + for (VariableDeclaration arg : method.getParameters()) { + System.out.print(delim + "\"" + arg.getType().getInterfaceTypeName() + "\""); + delim = ","; + } + System.out.println("),"); + } + // method lines of code + System.out.print("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"); + for (int j = 0; j < (type.getTypeName().length() + method.getName().length() + 3) / 4; j++) { + System.out.print("\t"); + } + for (int j = 0; j < (type.getTypeName().length() + method.getName().length() + 3) % 4; j++) { + System.out.print(" "); + } + if (i < methods.size() - 1) { + System.out.println("" + method.getBody().getStatements().size() + ")))),"); + } else { + System.out.println("" + method.getBody().getStatements().size() + "))))))));"); + } + i++; + } + } + } + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/tests/JavaCodeGeneratorTest.java b/AlgebraicDataflowArchitectureModel/src/tests/JavaCodeGeneratorTest.java new file mode 100644 index 0000000..3d3804d --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/tests/JavaCodeGeneratorTest.java @@ -0,0 +1,1633 @@ +package tests; + +import algorithms.DataTransferModelAnalyzer; +import algorithms.TypeInference; +import code.ast.*; +import generators.DataTransferMethodAnalyzer; +import generators.JavaCodeGenerator; +import generators.JavaMethodBodyGenerator; +import models.Edge; +import models.dataFlowModel.*; +import org.junit.Test; +import parser.Parser; +import parser.exceptions.*; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import static org.junit.Assert.*; + +public class JavaCodeGeneratorTest { + + @Test + public void test() { + testAccounts(); + testClock(); + testCustomerManagement(); // Two methods with the same signature are generated. + testGroupChat(); + testInventoryManagement(); +// testOnlineBattleGame(); // A feature has not been implemented for Java prototype generation. + testOnlineBattleGame2(); // Two methods with the same signature are generated. PUSH-first implementation still does not work. + testPOS(); + testSimpleTwitter(); + testVotingSystem(); + testWeatherObservationSystem(); + } + + private void testAccounts() { + try { + ArrayList generatedCode = generateCode("models/Accounts.model", null); + Map, // field name to type + Map, // arg types + Integer>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Main", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("Main", Map.entry("void", + Map.entry(List.of(), + 1))), + Map.entry("getAccount", Map.entry("Map", + Map.entry(List.of("int"), + 1))), + Map.entry("getName", Map.entry("String", + Map.entry(List.of("int"), + 1))), + Map.entry("changeName", Map.entry("void", + Map.entry(List.of("int", "String"), + 1))), + Map.entry("getAccounts", Map.entry("List", + Map.entry(List.of(), + 1))), + Map.entry("signup", Map.entry("void", + Map.entry(List.of("String"), + 1)))))); + exprectedStructure.put("Account", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getName", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("changeName", Map.entry("void", + Map.entry(List.of("int", "String"), + 1))), + Map.entry("Account", Map.entry("void", + Map.entry(List.of("String"), + 1)))))); + exprectedStructure.put("Accounts", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("List", + Map.entry(List.of(), + 1))), + Map.entry("getAccount", Map.entry("Account", + Map.entry(List.of("int"), + 1))), + Map.entry("signup", Map.entry("void", + Map.entry(List.of("String"), + 1)))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private void testClock() { + try { + // check PULL-first + ArrayList generatedCode = generateCode("models/Clock.model", PushPullValue.PULL); + Map, // field name to type + Map, // arg types + Integer>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Main", Map.entry(Map.ofEntries(Map.entry("hour", "Hour"), + Map.entry("hour_ang", "Hour_ang"), + Map.entry("min", "Min"), + Map.entry("min_ang", "Min_ang")), + Map.ofEntries(Map.entry("Main", Map.entry("void", + Map.entry(List.of(), + 4))), + Map.entry("getHour", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("getHour_ang", Map.entry("double", + Map.entry(List.of(), + 1))), + Map.entry("getMin", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("tick", Map.entry("void", + Map.entry(List.of(), + 1))), + Map.entry("getMin_ang", Map.entry("double", + Map.entry(List.of(), + 1)))))); + exprectedStructure.put("Hour", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("updateFromMin", Map.entry("void", + Map.entry(List.of("int"), + 1)))))); + exprectedStructure.put("Hour_ang", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("double", + Map.entry(List.of(), + 1))), + Map.entry("Hour_ang", Map.entry("void", + Map.entry(List.of("Hour"), + 1)))))); + exprectedStructure.put("Min", Map.entry(Map.ofEntries(Map.entry("value", "int"), + Map.entry("hour", "Hour")), + Map.ofEntries(Map.entry("getValue", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("tick", Map.entry("void", + Map.entry(List.of(), + 2))), + Map.entry("Min", Map.entry("void", + Map.entry(List.of("Hour"), + 1)))))); + exprectedStructure.put("Min_ang", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("double", + Map.entry(List.of(), + 1))), + Map.entry("Min_ang", Map.entry("void", + Map.entry(List.of("Min"), + 1)))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + + // check PUSH-first + generatedCode = generateCode("models/Clock.model", PushPullValue.PUSH); + exprectedStructure.clear(); + exprectedStructure.put("Main", Map.entry(Map.ofEntries(Map.entry("hour_ang", "Hour_ang"), + Map.entry("min_ang", "Min_ang"), + Map.entry("hour", "Hour"), + Map.entry("min", "Min")), + Map.ofEntries(Map.entry("Main", Map.entry("void", + Map.entry(List.of(), + 4))), + Map.entry("getHour_ang", Map.entry("double", + Map.entry(List.of(), + 1))), + Map.entry("getMin_ang", Map.entry("double", + Map.entry(List.of(), + 1))), + Map.entry("getHour", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("getMin", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("tick", Map.entry("void", + Map.entry(List.of(), + 1)))))); + exprectedStructure.put("Hour_ang", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("double", + Map.entry(List.of(), + 1))), + Map.entry("updateFromHour", Map.entry("void", + Map.entry(List.of("int"), + 1)))))); + exprectedStructure.put("Min_ang", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("double", + Map.entry(List.of(), + 1))), + Map.entry("updateFromMin", Map.entry("void", + Map.entry(List.of("int"), + 1)))))); + exprectedStructure.put("Hour", Map.entry(Map.ofEntries(Map.entry("value", "int"), + Map.entry("hour_ang", "Hour_ang")), + Map.ofEntries(Map.entry("getValue", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("updateFromMin", Map.entry("void", + Map.entry(List.of("int"), + 2))), + Map.entry("Hour", Map.entry("void", + Map.entry(List.of("Hour_ang"), + 1)))))); + exprectedStructure.put("Min", Map.entry(Map.ofEntries(Map.entry("value", "int"), + Map.entry("min_ang", "Min_ang"), + Map.entry("hour", "Hour")), + Map.ofEntries(Map.entry("getValue", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("tick", Map.entry("void", + Map.entry(List.of(), + 3))), + Map.entry("Min", Map.entry("void", + Map.entry(List.of("Min_ang", "Hour"), + 2)))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private void testCustomerManagement() { + try { + ArrayList generatedCode = generateCode("models/CustomerManagement.model", null); + Map, // field name to type + Map, // arg types + Integer>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Main", Map.entry(Map.ofEntries(Map.entry("companies", "Companies"), + Map.entry("customers", "Customers")), + Map.ofEntries(Map.entry("Main", Map.entry("void", + Map.entry(List.of(), + 2))), + Map.entry("getAddress", Map.entry("String", + Map.entry(List.of("String"), + 1))), + Map.entry("setAddress", Map.entry("void", + Map.entry(List.of("String", "String"), + 1))), + Map.entry("getCustomer", Map.entry("Map", + Map.entry(List.of("String"), + 1))), + Map.entry("getCompanies", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("addCampany", Map.entry("void", + Map.entry(List.of("String", "String"), + 1))), + Map.entry("getOrganization", Map.entry("String", + Map.entry(List.of("String"), + 1))), + Map.entry("setOrganization", Map.entry("void", + Map.entry(List.of("String", "String"), + 1))), +// Map.entry("getAddress", Map.entry("String", +// Map.entry(List.of("String"), +// 1))), + Map.entry("getCompany", Map.entry("Map", + Map.entry(List.of("String"), + 1))), + Map.entry("getCustomers", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("addCustomer", Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))))); + exprectedStructure.put("Customer", Map.entry(Map.ofEntries(Map.entry("organization", "String"), + Map.entry("company", "Company"), + Map.entry("companies", "Companies")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getOrganization", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("getAddress", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("setOrganization", Map.entry("void", + Map.entry(List.of("String", "String"), + 2))), + Map.entry("Customer", Map.entry("void", + Map.entry(List.of("String", "Companies"), + 3)))))); + exprectedStructure.put("Companies", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getCompany", Map.entry("Company", + Map.entry(List.of("String"), + 1))), + Map.entry("addCampany", Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))))); + exprectedStructure.put("Company", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getAddress", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("setAddress", Map.entry("void", + Map.entry(List.of("String", "String"), + 1))), + Map.entry("Company", Map.entry("void", + Map.entry(List.of("String"), + 1)))))); + exprectedStructure.put("Customers", Map.entry(Map.ofEntries(Map.entry("value", "Map"), + Map.entry("companies", "Companies")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getCustomer", Map.entry("Customer", + Map.entry(List.of("String"), + 1))), + Map.entry("addCustomer", Map.entry("void", + Map.entry(List.of("String", "String"), + 1))), + Map.entry("Customers", Map.entry("void", + Map.entry(List.of("Companies"), + 1)))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private void testGroupChat() { + try { + ArrayList generatedCode = generateCode("models/GroupChat.model", null); + Map, // field name to type + Map, // arg types + Integer>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Main", Map.entry(Map.ofEntries(Map.entry("accounts", "Accounts"), + Map.entry("groups", "Groups")), + Map.ofEntries(Map.entry("Main", Map.entry("void", + Map.entry(List.of(), + 2))), + Map.entry("getAccounts", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("signUp", Map.entry("void", + Map.entry(List.of("String"), + 1))), + Map.entry("getGroup", Map.entry("Map", + Map.entry(List.of("String"), + 1))), + Map.entry("getMessages", Map.entry("List", + Map.entry(List.of("String"), + 1))), + Map.entry("postMessage", Map.entry("void", + Map.entry(List.of("String", "String"), + 1))), + Map.entry("getAccount", Map.entry("Map", + Map.entry(List.of("String"), + 1))), + Map.entry("getMember", Map.entry("String", + Map.entry(List.of("String", "int"), + 1))), + Map.entry("getNotifications", Map.entry("Map", + Map.entry(List.of("String"), + 1))), + Map.entry("hasRead", Map.entry("void", + Map.entry(List.of("String", "String"), + 1))), + Map.entry("getMembers", Map.entry("List", + Map.entry(List.of("String"), + 1))), + Map.entry("addGroupMember", Map.entry("void", + Map.entry(List.of("String", "String"), + 1))), + Map.entry("getGroups", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("createGroup", Map.entry("void", + Map.entry(List.of("String"), + 1)))))); + exprectedStructure.put("Accounts", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getAccount", Map.entry("Account", + Map.entry(List.of("String"), + 1))), + Map.entry("signUp", Map.entry("void", + Map.entry(List.of("String"), + 1)))))); + exprectedStructure.put("Group", Map.entry(Map.ofEntries(Map.entry("messages", "List"), + Map.entry("account", "Account"), + Map.entry("accounts", "Accounts"), + Map.entry("members", "List")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getMessages", Map.entry("List", + Map.entry(List.of(), + 1))), + Map.entry("getMember", Map.entry("String", + Map.entry(List.of("int"), + 1))), + Map.entry("getMembers", Map.entry("List", + Map.entry(List.of(), + 1))), + Map.entry("postMessage", Map.entry("void", + Map.entry(List.of("String", "String"), + 6))), + Map.entry("addGroupMember", Map.entry("void", + Map.entry(List.of("String", "String"), + 1))), + Map.entry("Group", Map.entry("void", + Map.entry(List.of("List", "Accounts", "List"), + 3)))))); + exprectedStructure.put("Account", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getNotifications", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("updateNotificationsFromMessages", Map.entry("void", + Map.entry(List.of("String", "String", "int", "List", "String"), + 1))), + Map.entry("hasRead", Map.entry("void", + Map.entry(List.of("String", "String"), + 1))), + Map.entry("Account", Map.entry("void", + Map.entry(List.of("Map"), + 1)))))); + exprectedStructure.put("Groups", Map.entry(Map.ofEntries(Map.entry("value", "Map"), + Map.entry("accounts", "Accounts")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getGroup", Map.entry("Group", + Map.entry(List.of("String"), + 1))), + Map.entry("createGroup", Map.entry("void", + Map.entry(List.of("String"), + 1))), + Map.entry("Groups", Map.entry("void", + Map.entry(List.of("Accounts"), + 1)))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private void testInventoryManagement() { + try { + ArrayList generatedCode = generateCode("models/InventoryManagement.model", null); + Map, // field name to type + Map, // arg types + Integer>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Main", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("Main", Map.entry("void", + Map.entry(List.of(), + 1))), + Map.entry("getInventoryElement", Map.entry("Map", + Map.entry(List.of("String"), + 1))), + Map.entry("getInventory", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("registerItem", Map.entry("void", + Map.entry(List.of("String", "int", "String"), + 1))), + Map.entry("getCount", Map.entry("int", + Map.entry(List.of("String"), + 1))), + Map.entry("receiveOrShip", Map.entry("void", + Map.entry(List.of("String", "int"), + 1)))))); + exprectedStructure.put("InventoryElement", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getCount", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("receiveOrShip", Map.entry("void", + Map.entry(List.of("String", "int"), + 1))), + Map.entry("InventoryElement", Map.entry("void", + Map.entry(List.of("int"), + 1)))))); + exprectedStructure.put("Inventory", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getInventoryElement", Map.entry("InventoryElement", + Map.entry(List.of("String"), + 1))), + Map.entry("registerItem", Map.entry("void", + Map.entry(List.of("String", "int", "String"), + 1)))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private void testOnlineBattleGame() { + try { + // check PULL-first + ArrayList generatedCode = generateCode("models/OnlineBattleGame.model", PushPullValue.PULL); + Map, // field name to type + Map, // arg types + Integer>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Main", Map.entry(Map.ofEntries(Map.entry("accounts", "Accounts"), + Map.entry("rooms", "Rooms")), + Map.ofEntries(Map.entry("Main", Map.entry("void", + Map.entry(List.of(), + 2))), + Map.entry("getRoom", Map.entry("Map", + Map.entry(List.of("String"), + 1))), + Map.entry("getName", Map.entry("String", + Map.entry(List.of("String"), + 1))), + Map.entry("changeName", Map.entry("void", + Map.entry(List.of("String", "String"), + 1))), + Map.entry("getAccount", Map.entry("Map", + Map.entry(List.of("String"), + 1))), + Map.entry("getBlue_id", Map.entry("String", + Map.entry(List.of("String"), + 1))), + Map.entry("changeBlueId", Map.entry("void", + Map.entry(List.of("String", "String"), + 1))), + Map.entry("getRed_id", Map.entry("String", + Map.entry(List.of("String"), + 1))), + Map.entry("changeRedId", Map.entry("void", + Map.entry(List.of("String", "String"), + 1))), + Map.entry("getAccounts", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("signUp", Map.entry("void", + Map.entry(List.of("String", "String"), + 1))), + Map.entry("getRooms", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("createRoom", Map.entry("void", + Map.entry(List.of("String", "String", "String"), + 1))), + Map.entry("getRed_name", Map.entry("String", + Map.entry(List.of("String"), + 1))), + Map.entry("getBlue_name", Map.entry("String", + Map.entry(List.of("String"), + 1)))))); + exprectedStructure.put("Room", Map.entry(Map.ofEntries(Map.entry("blue_id", "String"), + Map.entry("red_id", "String"), + Map.entry("account", "Account"), + Map.entry("accounts", "Accounts")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getBlue_id", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("getRed_id", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("getRed_name", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("getBlue_name", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("changeRedId", Map.entry("void", + Map.entry(List.of("String", "String"), + 2))), + Map.entry("changeBlueId", Map.entry("void", + Map.entry(List.of("String", "String"), + 2))), + Map.entry("Room", Map.entry("void", + Map.entry(List.of("String", "String", "Accounts"), + 5)))))); + exprectedStructure.put("Account", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getName", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("changeName", Map.entry("void", + Map.entry(List.of("String", "String"), + 1))), + Map.entry("Account", Map.entry("void", + Map.entry(List.of("String"), + 1)))))); + exprectedStructure.put("Accounts", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getAccount", Map.entry("Account", + Map.entry(List.of("String"), + 1))), + Map.entry("signUp", Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))))); + exprectedStructure.put("Rooms", Map.entry(Map.ofEntries(Map.entry("value", "Map"), + Map.entry("accounts", "Accounts")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getRoom", Map.entry("Room", + Map.entry(List.of("String"), + 1))), + Map.entry("createRoom", Map.entry("void", + Map.entry(List.of("String", "String", "String"), + 1))), + Map.entry("Rooms", Map.entry("void", + Map.entry(List.of("Accounts"), + 1)))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + + // check PUSH-first + generatedCode = generateCode("models/OnlineBattleGame.model", PushPullValue.PUSH); + exprectedStructure.clear(); + exprectedStructure.put("Main", Map.entry(Map.ofEntries(Map.entry("accounts", "Accounts"), + Map.entry("rooms", "Rooms")), + Map.ofEntries(Map.entry("Main", Map.entry("void", + Map.entry(List.of(), + 2))), + Map.entry("getAccount", Map.entry("Map", + Map.entry(List.of("String"), + 1))), + Map.entry("getBlue_name", Map.entry("String", + Map.entry(List.of("String"), + 1))), + Map.entry("getBlue_id", Map.entry("String", + Map.entry(List.of("String"), + 1))), + Map.entry("changeBlueId", Map.entry("void", + Map.entry(List.of("String", "String"), + 1))), + Map.entry("getRed_name", Map.entry("String", + Map.entry(List.of("String"), + 1))), + Map.entry("getRed_id", Map.entry("String", + Map.entry(List.of("String"), + 1))), + Map.entry("changeRedId", Map.entry("void", + Map.entry(List.of("String", "String"), + 1))), + Map.entry("getName", Map.entry("String", + Map.entry(List.of("String"), + 1))), + Map.entry("changeName", Map.entry("void", + Map.entry(List.of("String", "String"), + 1))), + Map.entry("getAccounts", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("signUp", Map.entry("void", + Map.entry(List.of("String", "String"), + 1))), + Map.entry("getRooms", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("createRoom", Map.entry("void", + Map.entry(List.of("String", "String", "String"), + 1))), + Map.entry("getRoom", Map.entry("Map", + Map.entry(List.of("String"), + 1)))))); + exprectedStructure.put("Account", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getName", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("changeName", Map.entry("void", + Map.entry(List.of("String", "String"), + 1))), + Map.entry("Account", Map.entry("void", + Map.entry(List.of("String"), + 1)))))); + exprectedStructure.put("Accounts", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getAccount", Map.entry("Account", + Map.entry(List.of("String"), + 1))), + Map.entry("signUp", Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))))); + exprectedStructure.put("Rooms", Map.entry(Map.ofEntries(Map.entry("value", "Map"), + Map.entry("accounts", "Accounts")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getRoom", Map.entry("Room", + Map.entry(List.of("String"), + 1))), + Map.entry("createRoom", Map.entry("void", + Map.entry(List.of("String", "String", "String"), + 1))), + Map.entry("Rooms", Map.entry("void", + Map.entry(List.of("Accounts"), + 1)))))); + exprectedStructure.put("Room", Map.entry(Map.ofEntries(Map.entry("account", "Account"), + Map.entry("accounts", "Accounts"), + Map.entry("blue_id", "String"), + Map.entry("red_id", "String")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getBlue_name", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("getBlue_id", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("getRed_name", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("getRed_id", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("updateBlue_nameFromBlue_id", Map.entry("void", + Map.entry(List.of("String", "String", "String"), + 2))), + Map.entry("updateRed_nameFromRed_id", Map.entry("void", + Map.entry(List.of("String", "String", "String"), + 2))), + Map.entry("changeRedId", Map.entry("void", + Map.entry(List.of("String", "String"), + 3))), + Map.entry("changeBlueId", Map.entry("void", + Map.entry(List.of("String", "String"), + 3))), + Map.entry("Room", Map.entry("void", + Map.entry(List.of("Accounts", "String", "String"), + 5)))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + + private void testOnlineBattleGame2() { + try { + ArrayList generatedCode = generateCode("models/OnlineBattleGame2.model", null); + Map, // field name to type + Map, // arg types + Integer>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Main", Map.entry(Map.ofEntries(Map.entry("accounts", "Accounts"), + Map.entry("rooms", "Rooms")), + Map.ofEntries(Map.entry("Main", Map.entry("void", + Map.entry(List.of(), + 2))), + Map.entry("getAccount", Map.entry("Map", + Map.entry(List.of("String"), + 1))), + Map.entry("getMember", Map.entry("Map", + Map.entry(List.of("String", "int"), + 1))), + Map.entry("getMembers", Map.entry("List", + Map.entry(List.of("String"), + 1))), + Map.entry("addRoomMember", Map.entry("void", + Map.entry(List.of("String", "String"), + 1))), + Map.entry("getAccounts", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("signUp", Map.entry("void", + Map.entry(List.of("String", "String"), + 1))), + Map.entry("getRoom", Map.entry("Map", + Map.entry(List.of("String"), + 1))), + Map.entry("getId", Map.entry("String", + Map.entry(List.of("String", "int"), + 1))), + Map.entry("getName", Map.entry("String", + Map.entry(List.of("String"), + 1))), + Map.entry("changeName", Map.entry("void", + Map.entry(List.of("String", "String"), + 1))), + Map.entry("getRooms", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("createRoom", Map.entry("void", + Map.entry(List.of("String"), + 1))), +// Map.entry("getName", Map.entry("String", +// Map.entry(List.of("String","int"), +// 1))), + Map.entry("getBattle", Map.entry("boolean", + Map.entry(List.of("String"), + 1))), + Map.entry("battle", Map.entry("void", + Map.entry(List.of("String", "boolean"), + 1))), + Map.entry("getPoint", Map.entry("int", + Map.entry(List.of("String"), + 1)))))); + exprectedStructure.put("Account", Map.entry(Map.ofEntries(Map.entry("name", "String"), + Map.entry("point", "int")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getName", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("getPoint", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("updatePointFromBattle", Map.entry("void", + Map.entry(List.of("String", "String", "int", "boolean", "String"), + 1))), + Map.entry("changeName", Map.entry("void", + Map.entry(List.of("String", "String"), + 1))), + Map.entry("Account", Map.entry("void", + Map.entry(List.of("String", "int"), + 2)))))); + exprectedStructure.put("Member", Map.entry(Map.ofEntries(Map.entry("id", "String"), + Map.entry("account", "Account"), + Map.entry("accounts", "Accounts")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getId", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("getName", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("Member", Map.entry("void", + Map.entry(List.of("String", "Accounts"), + 2)))))); + exprectedStructure.put("Members", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("List", + Map.entry(List.of(), + 1))), + Map.entry("getMember", Map.entry("Member", + Map.entry(List.of("int"), + 1))), + Map.entry("addRoomMember", Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))))); + exprectedStructure.put("Accounts", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getAccount", Map.entry("Account", + Map.entry(List.of("String"), + 1))), + Map.entry("signUp", Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))))); + exprectedStructure.put("Room", Map.entry(Map.ofEntries(Map.entry("members", "Members"), + Map.entry("battle", "boolean"), + Map.entry("account", "Account"), + Map.entry("accounts", "Accounts")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getMembers", Map.entry("Members", + Map.entry(List.of(), + 1))), + Map.entry("getBattle", Map.entry("boolean", + Map.entry(List.of(), + 1))), + Map.entry("battle", Map.entry("void", + Map.entry(List.of("String", "boolean"), + 6))), + Map.entry("Room", Map.entry("void", + Map.entry(List.of("boolean", "Accounts"), + 2)))))); + exprectedStructure.put("Rooms", Map.entry(Map.ofEntries(Map.entry("value", "Map"), + Map.entry("accounts", "Accounts")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getRoom", Map.entry("Room", + Map.entry(List.of("String"), + 1))), + Map.entry("createRoom", Map.entry("void", + Map.entry(List.of("String"), + 1))), + Map.entry("Rooms", Map.entry("void", + Map.entry(List.of("Accounts"), + 1)))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private void testPOS() { + try { + // check PULL-first + ArrayList generatedCode = generateCode("models/POS.model", PushPullValue.PULL); + Map, // field name to type + Map, // arg types + Integer>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Main", Map.entry(Map.ofEntries(Map.entry("history", "History"), + Map.entry("total", "Total"), + Map.entry("payment", "Payment"), + Map.entry("points", "Points")), + Map.ofEntries(Map.entry("Main", Map.entry("void", + Map.entry(List.of(), + 4))), + Map.entry("getHistory", Map.entry("List", + Map.entry(List.of(), + 1))), + Map.entry("getTotal", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("getPayment", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("purchase", Map.entry("void", + Map.entry(List.of("int"), + 1))), + Map.entry("getPoints", Map.entry("int", + Map.entry(List.of(), + 1)))))); + exprectedStructure.put("History", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("List", + Map.entry(List.of(), + 1))), + Map.entry("updateFromPayment", Map.entry("void", + Map.entry(List.of("int"), + 1)))))); + exprectedStructure.put("Total", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("Total", Map.entry("void", + Map.entry(List.of("History"), + 1)))))); + exprectedStructure.put("Payment", Map.entry(Map.ofEntries(Map.entry("value", "int"), + Map.entry("history", "History")), + Map.ofEntries(Map.entry("getValue", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("purchase", Map.entry("void", + Map.entry(List.of("int"), + 2))), + Map.entry("Payment", Map.entry("void", + Map.entry(List.of("History"), + 1)))))); + exprectedStructure.put("Points", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("Points", Map.entry("void", + Map.entry(List.of("Payment"), + 1)))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + + // check PUSH-first + generatedCode = generateCode("models/POS.model", PushPullValue.PUSH); + exprectedStructure.clear(); + exprectedStructure.put("Main", Map.entry(Map.ofEntries(Map.entry("points", "Points"), + Map.entry("total", "Total"), + Map.entry("history", "History"), + Map.entry("payment", "Payment")), + Map.ofEntries(Map.entry("Main", Map.entry("void", + Map.entry(List.of(), + 4))), + Map.entry("getPoints", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("getTotal", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("getHistory", Map.entry("List", + Map.entry(List.of(), + 1))), + Map.entry("getPayment", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("purchase", Map.entry("void", + Map.entry(List.of("int"), + 1)))))); + exprectedStructure.put("Points", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("updateFromPayment", Map.entry("void", + Map.entry(List.of("int"), + 1)))))); + exprectedStructure.put("Total", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("updateFromHistory", Map.entry("void", + Map.entry(List.of("List"), + 1)))))); + exprectedStructure.put("History", Map.entry(Map.ofEntries(Map.entry("value", "List"), + Map.entry("total", "Total")), + Map.ofEntries(Map.entry("getValue", Map.entry("List", + Map.entry(List.of(), + 1))), + Map.entry("updateFromPayment", Map.entry("void", + Map.entry(List.of("int"), + 2))), + Map.entry("History", Map.entry("void", + Map.entry(List.of("Total"), + 1)))))); + exprectedStructure.put("Payment", Map.entry(Map.ofEntries(Map.entry("value", "int"), + Map.entry("points", "Points"), + Map.entry("history", "History")), + Map.ofEntries(Map.entry("getValue", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("purchase", Map.entry("void", + Map.entry(List.of("int"), + 3))), + Map.entry("Payment", Map.entry("void", + Map.entry(List.of("Points", "History"), + 2)))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private void testSimpleTwitter() { + try { + ArrayList generatedCode = generateCode("models/SimpleTwitter.model", null); + Map, // field name to type + Map, // arg types + Integer>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Main", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("Main", Map.entry("void", + Map.entry(List.of(), + 1))), + Map.entry("getAccount", Map.entry("Map", + Map.entry(List.of("String"), + 1))), + Map.entry("getAccounts", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("signUp", Map.entry("void", + Map.entry(List.of("String", "String"), + 1))), + Map.entry("getTweets", Map.entry("List", + Map.entry(List.of("String"), + 1))), + Map.entry("tweet", Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))))); + exprectedStructure.put("Account", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getTweets", Map.entry("List", + Map.entry(List.of(), + 1))), + Map.entry("tweet", Map.entry("void", + Map.entry(List.of("String", "String"), + 1))), + Map.entry("Account", Map.entry("void", + Map.entry(List.of("List"), + 1)))))); + exprectedStructure.put("Accounts", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getAccount", Map.entry("Account", + Map.entry(List.of("String"), + 1))), + Map.entry("signUp", Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private void testVotingSystem() { + try { + ArrayList generatedCode = generateCode("models/VotingSystem.model", null); + Map, // field name to type + Map, // arg types + Integer>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Main", Map.entry(Map.ofEntries(Map.entry("accounts", "Accounts"), + Map.entry("counts", "Counts")), + Map.ofEntries(Map.entry("Main", Map.entry("void", + Map.entry(List.of(), + 2))), + Map.entry("getAccounts", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("signUp", Map.entry("void", + Map.entry(List.of("String", "String"), + 1))), + Map.entry("getVote", Map.entry("String", + Map.entry(List.of("String"), + 1))), + Map.entry("cast", Map.entry("void", + Map.entry(List.of("String", "String"), + 1))), + Map.entry("getAccount", Map.entry("Map", + Map.entry(List.of("String"), + 1))), + Map.entry("getCounts", Map.entry("Map", + Map.entry(List.of(), + 1)))))); + exprectedStructure.put("Accounts", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getAccount", Map.entry("Account", + Map.entry(List.of("String"), + 1))), + Map.entry("signUp", Map.entry("void", + Map.entry(List.of("String", "String"), + 1)))))); + exprectedStructure.put("Account", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getVote", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("cast", Map.entry("void", + Map.entry(List.of("String", "String"), + 1))), + Map.entry("Account", Map.entry("void", + Map.entry(List.of("String"), + 1)))))); + exprectedStructure.put("Counts", Map.entry(Map.ofEntries(Map.entry("value", "Map"), + Map.entry("account", "Account"), + Map.entry("accounts", "Accounts")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 6))), + Map.entry("Counts", Map.entry("void", + Map.entry(List.of("Accounts"), + 1)))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private void testWeatherObservationSystem() { + try { + // check PULL-first + ArrayList generatedCode = generateCode("models/WeatherObservationSystem.model", PushPullValue.PULL); + Map, // field name to type + Map, // arg types + Integer>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Main", Map.entry(Map.ofEntries(Map.entry("highest", "Highest"), + Map.entry("temp_f", "Temp_f"), + Map.entry("temp_c", "Temp_c")), + Map.ofEntries(Map.entry("Main", Map.entry("void", + Map.entry(List.of(), + 3))), + Map.entry("getHighest", Map.entry("double", + Map.entry(List.of(), + 1))), + Map.entry("reset", Map.entry("void", + Map.entry(List.of("double"), + 1))), + Map.entry("getTemp_f", Map.entry("double", + Map.entry(List.of(), + 1))), + Map.entry("observe", Map.entry("void", + Map.entry(List.of("double"), + 1))), + Map.entry("getTemp_c", Map.entry("double", + Map.entry(List.of(), + 1)))))); + exprectedStructure.put("Highest", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("double", + Map.entry(List.of(), + 1))), + Map.entry("updateFromTemp_f", Map.entry("void", + Map.entry(List.of("double"), + 1))), + Map.entry("reset", Map.entry("void", + Map.entry(List.of("double"), + 1)))))); + exprectedStructure.put("Temp_f", Map.entry(Map.ofEntries(Map.entry("value", "double"), + Map.entry("highest", "Highest")), + Map.ofEntries(Map.entry("getValue", Map.entry("double", + Map.entry(List.of(), + 1))), + Map.entry("observe", Map.entry("void", + Map.entry(List.of("double"), + 2))), + Map.entry("Temp_f", Map.entry("void", + Map.entry(List.of("Highest"), + 1)))))); + exprectedStructure.put("Temp_c", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("double", + Map.entry(List.of(), + 1))), + Map.entry("Temp_c", Map.entry("void", + Map.entry(List.of("Temp_f"), + 1)))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + + // check PUSH-first + generatedCode = generateCode("models/WeatherObservationSystem.model", PushPullValue.PUSH); + exprectedStructure.clear(); + exprectedStructure.put("Main", Map.entry(Map.ofEntries(Map.entry("temp_c", "Temp_c"), + Map.entry("highest", "Highest"), + Map.entry("temp_f", "Temp_f")), + Map.ofEntries(Map.entry("Main", Map.entry("void", + Map.entry(List.of(), + 3))), + Map.entry("getTemp_c", Map.entry("double", + Map.entry(List.of(), + 1))), + Map.entry("getHighest", Map.entry("double", + Map.entry(List.of(), + 1))), + Map.entry("reset", Map.entry("void", + Map.entry(List.of("double"), + 1))), + Map.entry("getTemp_f", Map.entry("double", + Map.entry(List.of(), + 1))), + Map.entry("observe", Map.entry("void", + Map.entry(List.of("double"), + 1)))))); + exprectedStructure.put("Temp_c", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("double", + Map.entry(List.of(), + 1))), + Map.entry("updateFromTemp_f", Map.entry("void", + Map.entry(List.of("double"), + 1)))))); + exprectedStructure.put("Highest", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("double", + Map.entry(List.of(), + 1))), + Map.entry("updateFromTemp_f", Map.entry("void", + Map.entry(List.of("double"), + 1))), + Map.entry("reset", Map.entry("void", + Map.entry(List.of("double"), + 1)))))); + exprectedStructure.put("Temp_f", Map.entry(Map.ofEntries(Map.entry("value", "double"), + Map.entry("highest", "Highest"), + Map.entry("temp_c", "Temp_c")), + Map.ofEntries(Map.entry("getValue", Map.entry("double", + Map.entry(List.of(), + 1))), + Map.entry("observe", Map.entry("void", + Map.entry(List.of("double"), + 3))), + Map.entry("Temp_f", Map.entry("void", + Map.entry(List.of("Highest", "Temp_c"), + 2)))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private ArrayList generateCode(String fileName, PushPullValue pushPullValue) throws FileNotFoundException, + ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedRightCurlyBracket, ExpectedInOrOutOrRefOrSubKeyword, + ExpectedStateTransition, ExpectedEquals, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, + WrongPathExpression, WrongJsonExpression, ExpectedColon, ExpectedDoubleQuotation { + File file = new File(fileName); + Parser parser = new Parser(new BufferedReader(new FileReader(file))); + DataTransferModel model; + model = parser.doParse(); + DataFlowGraph graph = DataTransferModelAnalyzer.createDataFlowGraphWithStateStoringAttribute(model); + DataTransferModelAnalyzer.annotateWithSelectableDataTransferAttiribute(graph); + if (pushPullValue != null) { + // Select a specified push/pull transfer method if possible. + for (Edge e : graph.getEdges()) { + if (!((DataFlowEdge) e).isChannelToResource() && ((DataFlowEdge) e).getAttribute() instanceof PushPullAttribute) { + PushPullAttribute ppat = (PushPullAttribute) ((DataFlowEdge) e).getAttribute(); + ppat.selectOption(pushPullValue); + } + } + } + TypeInference.infer(model); + DataTransferMethodAnalyzer.decideToStoreResourceStates(graph); + ArrayList codetree = JavaMethodBodyGenerator.doGenerate(graph, model, JavaCodeGenerator.doGenerate(graph, model)); +// ArrayList codetree = new CodeGeneratorFromDataFlowGraph().generateCode(model, graph, new StandaloneSpecific(), new JavaSpecific()); + return codetree; + } + + private void checkStructure(ArrayList generatedCode, + Map, Map, Integer>>>>> exprectedStructure) { + for (var classEnt : exprectedStructure.entrySet()) { + String expectedClassName = classEnt.getKey(); + Entry, Map, Integer>>>> expectedClassStructure = classEnt.getValue(); + TypeDeclaration generatedClass = null; + for (CompilationUnit cu : generatedCode) { + for (TypeDeclaration type : cu.types()) { + if (type.getTypeName().equals(expectedClassName)) { + generatedClass = type; + break; + } + } + } + assertNotNull(generatedClass); + + Map exprectedFields = expectedClassStructure.getKey(); + Map, Integer>>> exprectedMethods = expectedClassStructure.getValue(); + + for (String expectedFieldName : exprectedFields.keySet()) { + FieldDeclaration generatedField = null; + for (FieldDeclaration field : generatedClass.getFields()) { + if (field.getName().equals(expectedFieldName)) { + generatedField = field; + break; + } + } + assertNotNull(generatedField); + + String expectedFieldType = exprectedFields.get(expectedFieldName); + if (expectedFieldType.equals("void")) { + assertNull(generatedField.getType()); + } else { + assertEquals(expectedFieldType, generatedField.getType().getInterfaceTypeName()); + } + } + + for (String expectedMethodName : exprectedMethods.keySet()) { + MethodDeclaration generatedMethod = null; + for (MethodDeclaration method : generatedClass.getMethods()) { + if (method.getName().equals(expectedMethodName)) { + generatedMethod = method; + break; + } + } + assertNotNull(generatedMethod); + + Entry, Integer>> expectedMethodInfo = exprectedMethods.get(expectedMethodName); + String expectedReturnType = expectedMethodInfo.getKey(); + if (expectedReturnType.equals("void")) { + if (generatedMethod.getReturnType() != null) { + assertEquals(expectedReturnType, generatedMethod.getReturnType().getInterfaceTypeName()); + } else { + assertNull(generatedMethod.getReturnType()); + } + } else { + assertEquals(expectedReturnType, generatedMethod.getReturnType().getInterfaceTypeName()); + } + Entry, Integer> expectedMethodInfo2 = expectedMethodInfo.getValue(); + List expectedArgTypes = expectedMethodInfo2.getKey(); + for (String expectedArgType : expectedArgTypes) { + boolean existsArg = false; + for (VariableDeclaration var : generatedMethod.getParameters()) { + if (expectedArgType.equals(var.getType().getInterfaceTypeName())) { + existsArg = true; + } + } + assertTrue(existsArg); + } + int expectedLinesOfCode = expectedMethodInfo2.getValue(); + assertEquals(expectedLinesOfCode, generatedMethod.getBody().getStatements().size()); + } + } + } + + private void generateCheckCode(ArrayList generatedCode) { +// exprectedStructure.put("Main", Map.entry(Map.ofEntries(Map.entry("history", "History"), +// Map.entry("total", "Total"), +// Map.entry("payment", "Payment"), +// Map.entry("points", "Points")), +// Map.ofEntries(Map.entry("Main", Map.entry("void", +// Map.entry(List.of(), +// 4))), +// Map.entry("getHistory", Map.entry("List", +// Map.entry(List.of(), +// 1))), +// Map.entry("getTotal", Map.entry("int", +// Map.entry(List.of(), +// 1))), +// Map.entry("getPayment", Map.entry("int", +// Map.entry(List.of(), +// 1))), +// Map.entry("purchase", Map.entry("void", +// Map.entry(List.of("int"), +// 1))), +// Map.entry("getPoints", Map.entry("int", +// Map.entry(List.of(), +// 1)))))); + for (CompilationUnit cu : generatedCode) { + for (TypeDeclaration type : cu.types()) { + List fields = type.getFields(); + List methods = type.getMethods(); + // fields + if (fields.size() == 0) { + System.out.println("\t\t\texprectedStructure.put(\"" + type.getTypeName() + "\", Map.entry(Map.ofEntries(),"); + } + if (fields.size() == 1) { + System.out.println("\t\t\texprectedStructure.put(\"" + type.getTypeName() + "\", Map.entry(Map.ofEntries(),"); + } else { + int i = 0; + for (FieldDeclaration field : fields) { + if (i == 0) { + System.out.println("\t\t\texprectedStructure.put(\"" + type.getTypeName() + "\", Map.entry(Map.ofEntries(Map.entry(\"" + field.getName() + "\", \"" + field.getType().getInterfaceTypeName() + "\"),"); + } else { + System.out.print("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"); + for (int j = 0; j < (type.getTypeName().length() + 3) / 4; j++) { + System.out.print("\t"); + } + for (int j = 0; j < (type.getTypeName().length() + 3) % 4; j++) { + System.out.print(" "); + } + if (i < fields.size() - 1) { + System.out.println("Map.entry(\"" + field.getName() + "\", \"" + field.getType().getInterfaceTypeName() + "\"),"); + } else { + System.out.println("Map.entry(\"" + field.getName() + "\", \"" + field.getType().getInterfaceTypeName() + "\")),"); + } + } + i++; + } + } + // methods + if (methods.size() == 0) { + System.out.print("\t\t\t\t\t\t\t\t\t\t\t\t"); + for (int j = 0; j < (type.getTypeName().length() + 1) / 4; j++) { + System.out.print("\t"); + } + for (int j = 0; j < (type.getTypeName().length() + 1) % 4; j++) { + System.out.print(" "); + } + System.out.println("Map.ofEntries())));"); + } else { + int i = 0; + for (MethodDeclaration method : methods) { + // method name and return type + if (i == 0) { + System.out.print("\t\t\t\t\t\t\t\t\t\t\t\t"); + for (int j = 0; j < (type.getTypeName().length() + 1) / 4; j++) { + System.out.print("\t"); + } + for (int j = 0; j < (type.getTypeName().length() + 1) % 4; j++) { + System.out.print(" "); + } + if (method.getReturnType() == null) { + System.out.println("Map.ofEntries(Map.entry(\"" + method.getName() + "\", Map.entry(\"void\", "); + } else { + System.out.println("Map.ofEntries(Map.entry(\"" + method.getName() + "\", Map.entry(\"" + method.getReturnType().getInterfaceTypeName() + "\", "); + } + } else { + System.out.print("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"); + for (int j = 0; j < (type.getTypeName().length() + 3) / 4; j++) { + System.out.print("\t"); + } + for (int j = 0; j < (type.getTypeName().length() + 3) % 4; j++) { + System.out.print(" "); + } + if (method.getReturnType() == null) { + System.out.println("Map.entry(\"" + method.getName() + "\", Map.entry(\"void\", "); + } else { + System.out.println("Map.entry(\"" + method.getName() + "\", Map.entry(\"" + method.getReturnType().getInterfaceTypeName() + "\", "); + } + } + // method parameters + System.out.print("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"); + for (int j = 0; j < (type.getTypeName().length() + method.getName().length() + 3) / 4; j++) { + System.out.print("\t"); + } + for (int j = 0; j < (type.getTypeName().length() + method.getName().length() + 3) % 4; j++) { + System.out.print(" "); + } + if (method.getParameters() == null || method.getParameters().size() == 0) { + System.out.println("Map.entry(List.of(),"); + } else { + System.out.print("Map.entry(List.of("); + String delim = ""; + for (VariableDeclaration arg : method.getParameters()) { + System.out.print(delim + "\"" + arg.getType().getInterfaceTypeName() + "\""); + delim = ","; + } + System.out.println("),"); + } + // method lines of code + System.out.print("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"); + for (int j = 0; j < (type.getTypeName().length() + method.getName().length() + 3) / 4; j++) { + System.out.print("\t"); + } + for (int j = 0; j < (type.getTypeName().length() + method.getName().length() + 3) % 4; j++) { + System.out.print(" "); + } + if (i < methods.size() - 1) { + System.out.println("" + method.getBody().getStatements().size() + "))),"); + } else { + System.out.println("" + method.getBody().getStatements().size() + "))))));"); + } + i++; + } + } + } + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/tests/NativeAccessTest.java b/AlgebraicDataflowArchitectureModel/src/tests/NativeAccessTest.java new file mode 100644 index 0000000..75c7e6a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/tests/NativeAccessTest.java @@ -0,0 +1,96 @@ +package tests; + +import algorithms.TypeInference; +import models.algebra.Expression; +import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.ResourcePath; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.DataTransferModel; +import org.junit.Test; +import parser.Parser; +import parser.Parser.TokenStream; +import parser.exceptions.ExpectedColon; +import parser.exceptions.ExpectedDoubleQuotation; +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, SystemState nextSystemState) { // 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 | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/tests/SimplifiedDataFlowModelTest.java b/AlgebraicDataflowArchitectureModel/src/tests/SimplifiedDataFlowModelTest.java index acb5333..7a2d84b 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/SimplifiedDataFlowModelTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/SimplifiedDataFlowModelTest.java @@ -1,23 +1,29 @@ package tests; -import static org.junit.Assert.*; - +import models.Edge; +import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.ResourcePath; +import models.dataFlowModel.DataFlowGraph; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.DataTransferModel; import org.junit.Test; -import models.*; -import models.dataConstraintModel.*; -import models.dataFlowModel.*; +import static org.junit.Assert.assertEquals; public class SimplifiedDataFlowModelTest { - + @Test 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 +37,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,25 +77,26 @@ 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()); - for (Edge e: resourceDependencyGraph.getEdges()) { + 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..917f636 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/tests/SimulatorTest.java @@ -0,0 +1,218 @@ +package tests; + +import models.algebra.*; +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 org.junit.Test; +import parser.Parser; +import parser.Parser.TokenStream; +import parser.exceptions.ExpectedColon; +import parser.exceptions.ExpectedDoubleQuotation; +import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.WrongJsonExpression; +import simulator.*; +import simulator.states.MapResourceState; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +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_addCustomer); + model.addInputChannel(cio_addCompany); + 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 | + ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/tests/TermTest.java b/AlgebraicDataflowArchitectureModel/src/tests/TermTest.java index 9ae2ec0..8acffe8 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/TermTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/TermTest.java @@ -1,16 +1,16 @@ package tests; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.assertFalse; -import org.junit.Test; - import models.algebra.Constant; import models.algebra.Symbol; import models.algebra.Term; import models.algebra.Variable; +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; public class TermTest { - + @Test public void test() { Symbol add = new Symbol("add", 2); @@ -20,13 +20,13 @@ Variable x = new Variable("x"); Variable y = new Variable("y"); Variable z = new Variable("z"); - Term t1 = new Term(add); // add(1, x) + Term t1 = new Term(add); // add(1, x) t1.addChild(one); t1.addChild(x); - Term t2 = new Term(mul); // mul(add(1, x), y) + Term t2 = new Term(mul); // mul(add(1, x), y) t2.addChild(t1); t2.addChild(y); - Term t3 = new Term(add); // add(1, x) + Term t3 = new Term(add); // add(1, x) t3.addChild(one); t3.addChild(x); @@ -48,5 +48,5 @@ System.out.println(t1); System.out.println(t2); } - + } diff --git a/AlgebraicDataflowArchitectureModel/src/tests/UpdateCodeGenerationTest.java b/AlgebraicDataflowArchitectureModel/src/tests/UpdateCodeGenerationTest.java index 69a5b1f..eaced86 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/UpdateCodeGenerationTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/UpdateCodeGenerationTest.java @@ -1,20 +1,13 @@ package tests; -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.Symbol; -import models.algebra.Term; -import models.algebra.UnificationFailed; -import models.algebra.ValueUndefined; -import models.algebra.Variable; -import models.dataConstraintModel.*; -import models.dataFlowModel.DataTransferModel; +import models.algebra.*; +import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.ResourcePath; +import models.dataConstraintModel.StateTransition; import models.dataFlowModel.DataTransferChannel; import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor; +import models.dataFlowModel.DataTransferModel; import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork; public class UpdateCodeGenerationTest { @@ -25,92 +18,114 @@ 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); @@ -119,13 +134,13 @@ Variable l = new Variable("l"); Constant c_0_05 = new Constant("0.05"); Symbol update1 = new Symbol("update1", 1); - Term c1_message = new Term(update1); // update1(y) + 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,38 +148,38 @@ 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); try { - String[] sideEffects = new String[] {""}; - System.out.println("-----"); + String[] sideEffects = new String[]{""}; + System.out.println("-----"); 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)) { + for (Parameter p : loyaltyPushUpdate.getSubTerms(Parameter.class).values()) { + 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) { + | UnificationFailed | ValueUndefined e) { e.printStackTrace(); } @@ -172,8 +187,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); @@ -184,9 +199,9 @@ Variable z = new Variable("z"); Variable h = new Variable("h"); Symbol update2 = new Symbol("update2", 1); - Term c2_message = new Term(update2); // update2(z) + Term c2_message = new Term(update2); // update2(z) c2_message.addChild(z); - Term nextHistory = new Term(DataTransferModel.cons); // cons(z, h) + Term nextHistory = new Term(DataTransferModel.cons); // cons(z, h) nextHistory.addChild(z); nextHistory.addChild(h); @@ -202,18 +217,18 @@ c2_history_transition.setNextStateExpression(nextHistory); c2_history.setStateTransition(c2_history_transition); - System.out.println(c2); + System.out.println(c2); try { - String[] sideEffects = new String[] {""}; + String[] sideEffects = new String[]{""}; System.out.println("-----"); 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)) { + for (Parameter p : historyPushUpdate.getSubTerms(Parameter.class).values()) { + if (p.equals(pPayment) || p.equals(pPoints) || p.equals(pHistory) || p.equals(pTotal)) { param = p; break; } @@ -224,10 +239,10 @@ 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) { + | UnificationFailed | ValueUndefined e) { e.printStackTrace(); } @@ -247,7 +262,7 @@ Variable u = new Variable("u"); Variable t = new Variable("t"); Symbol update3 = new Symbol("update3", 1); - Term c3_message = new Term(update3); // update3(u) + Term c3_message = new Term(update3); // update3(u) c3_message.addChild(u); Expression nextHistory2 = u; Term nextTotal = new Term(sum); @@ -258,7 +273,7 @@ c3_history_transition.setMessageExpression(c3_message); c3_history_transition.setNextStateExpression(nextHistory2); c3_history.setStateTransition(c3_history_transition); - + StateTransition c3_total_transition = new StateTransition(); c3_total_transition.setCurStateExpression(t); c3_total_transition.setMessageExpression(c3_message); @@ -266,17 +281,17 @@ c3_total.setStateTransition(c3_total_transition); System.out.println(c3); - + try { - String[] sideEffects = new String[] {""}; + String[] sideEffects = new String[]{""}; System.out.println("-----"); 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)) { + for (Parameter p : totalPushUpdate.getSubTerms(Parameter.class).values()) { + if (p.equals(pPayment) || p.equals(pPoints) || p.equals(pHistory) || p.equals(pTotal)) { param = p; break; } @@ -287,14 +302,14 @@ 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) { + | UnificationFailed | ValueUndefined e) { e.printStackTrace(); } System.out.println("=========="); } - + } diff --git a/AlgebraicDataflowArchitectureModel/src/tests/UpdateConflictCheckTest.java b/AlgebraicDataflowArchitectureModel/src/tests/UpdateConflictCheckTest.java index 5f51af7..3f860c1 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/UpdateConflictCheckTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/UpdateConflictCheckTest.java @@ -1,65 +1,34 @@ package tests; +import algorithms.Validation; +import models.dataFlowModel.DataTransferModel; +import org.junit.Test; +import parser.Parser; +import parser.exceptions.*; + import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; -import algorithms.*; -import models.dataFlowModel.DataTransferModel; -import parser.Parser; -import parser.exceptions.ExpectedAssignment; -import parser.exceptions.ExpectedChannel; -import parser.exceptions.ExpectedChannelName; -import parser.exceptions.ExpectedEquals; -import parser.exceptions.ExpectedInOrOutOrRefKeyword; -import parser.exceptions.ExpectedLeftCurlyBracket; -import parser.exceptions.ExpectedRHSExpression; -import parser.exceptions.ExpectedRightBracket; -import parser.exceptions.ExpectedStateTransition; -import parser.exceptions.WrongLHSExpression; -import parser.exceptions.WrongRHSExpression; +import static org.junit.Assert.assertTrue; public class UpdateConflictCheckTest { - public static void main(String[] args) { + + @Test + public void test() { File file = new File("models/POS2.model"); try { Parser parser = new Parser(new BufferedReader(new FileReader(file))); + DataTransferModel model; try { - DataTransferModel 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 + model = parser.doParse(); + assertTrue(Validation.checkUpdateConflict(model)); + } catch (ExpectedRightBracket | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket + | ExpectedRightCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword | ExpectedStateTransition | + ExpectedEquals + | ExpectedRHSExpression | WrongLHSExpression | WrongRHSExpression | ExpectedAssignment + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { e.printStackTrace(); } } catch (FileNotFoundException e) { diff --git a/AlgebraicDataflowArchitectureModel/src/tests/parser/ParseTest.java b/AlgebraicDataflowArchitectureModel/src/tests/parser/ParseTest.java index a1ea0a3..2de472e 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/parser/ParseTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/parser/ParseTest.java @@ -1,10 +1,5 @@ package tests.parser; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; - import models.Edge; import models.algebra.InvalidMessage; import models.algebra.ParameterizedIdentifierIsFutureWork; @@ -12,22 +7,20 @@ import models.algebra.ValueUndefined; import models.dataConstraintModel.Channel; import models.dataConstraintModel.ChannelMember; -import models.dataFlowModel.*; +import models.dataFlowModel.DataFlowGraph; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork; import parser.Parser; -import parser.exceptions.ExpectedAssignment; -import parser.exceptions.ExpectedChannel; -import parser.exceptions.ExpectedChannelName; -import parser.exceptions.ExpectedEquals; -import parser.exceptions.ExpectedInOrOutOrRefKeyword; -import parser.exceptions.ExpectedLeftCurlyBracket; -import parser.exceptions.ExpectedRHSExpression; -import parser.exceptions.ExpectedRightBracket; -import parser.exceptions.ExpectedStateTransition; -import parser.exceptions.WrongLHSExpression; -import parser.exceptions.WrongRHSExpression; +import parser.exceptions.*; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; public class ParseTest { - + public static void main(String[] args) { File file = new File("models/POS.model"); try { @@ -36,30 +29,31 @@ try { model = parser.doParse(); System.out.println(model); - - 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)); + + for (Channel c : model.getChannels()) { + for (ChannelMember out : ((DataTransferChannel) c).getOutputChannelMembers()) { + String[] sideEffects = new String[]{""}; + System.out.println("next" + out.getResource().getLeafResourceName() + " = " + ((DataTransferChannel) c).deriveUpdateExpressionOf(out).toImplementation(sideEffects)); } } - + System.out.println(); - + DataFlowGraph resourceDependencyGraph = model.getDataFlowGraph(); - for (Edge e: resourceDependencyGraph.getEdges()) { + for (Edge e : resourceDependencyGraph.getEdges()) { System.out.println(e.getSource() + "-(" + e + ")->" + e.getDestination()); } - } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefKeyword - | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression - | WrongRHSExpression | ExpectedRightBracket | ParameterizedIdentifierIsFutureWork - | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage - | UnificationFailed | ValueUndefined | ExpectedAssignment e) { + } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ParameterizedIdentifierIsFutureWork + | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage + | UnificationFailed | ValueUndefined | ExpectedAssignment | ExpectedRightCurlyBracket | + WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { e.printStackTrace(); } } catch (FileNotFoundException e) { e.printStackTrace(); } } - + }