diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..fa782b8 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..bc9dc65 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..712ab9d --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..7816306 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..748a0cc --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/.classpath b/AlgebraicDataflowArchitectureModel/.classpath index 3e99697..9d5db7b 100644 --- a/AlgebraicDataflowArchitectureModel/.classpath +++ b/AlgebraicDataflowArchitectureModel/.classpath @@ -1,12 +1,13 @@ + - + 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/AlgebraicDataflowArchitectureModel.iml b/AlgebraicDataflowArchitectureModel/AlgebraicDataflowArchitectureModel.iml new file mode 100644 index 0000000..0d73431 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/AlgebraicDataflowArchitectureModel.iml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file 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..bcea2b2 --- /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)))) +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/models/Clock.dtram b/AlgebraicDataflowArchitectureModel/models/Clock.dtram new file mode 100644 index 0000000..bbeb6dc --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/Clock.dtram @@ -0,0 +1,27 @@ +model { +channel CIO1 { + out min(m: Int, tick) = (m + 1) % 60 +} +channel HourUpdate { + in hour(h: Int, update(h2)) = h2 + out hour_ang(h_ang: Double, update(h2)) = h2 / 6 * PI +} +channel MinUpdate { + in min(m, update(m2)) = m2 + out min_ang(m_ang: Double, update(m2)) = m2 / 30 * PI +} +channel Clock { + in min(m, update(m2)) = m2 + out hour(h, update(m2)) = if(m2 == 0, (h + 1) % 24, h) +} +} +geometry { + node c HourUpdate:520,340,30,30 + node c MinUpdate:520,100,30,30 + node c Clock:280,220,30,30 + node r min_ang:670,100,80,30 + node r min:250,100,80,30 + node r hour:270,340,80,30 + node r hour_ang:680,340,80,30 + node ioc CIO1:100,100,30,30 +} diff --git a/AlgebraicDataflowArchitectureModel/models/Clock.model b/AlgebraicDataflowArchitectureModel/models/Clock.model new file mode 100644 index 0000000..0ecb11b --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/Clock.model @@ -0,0 +1,15 @@ +channel CIO1 { + out min(m: Int, tick) = (m + 1) % 60 +} +channel HourUpdate { + in hour(h: Int, update(h2)) = h2 + out hour_ang(h_ang: Double, update(h2)) = h2 / 6 * PI +} +channel MinUpdate { + in min(m, update(m2)) = m2 + out min_ang(m_ang: Double, update(m2)) = m2 / 30 * PI +} +channel Clock { + in min(m, update(m2)) = m2 + out hour(h, update(m2)) = if(m2 == 0, (h + 1) % 24, h) +} diff --git a/AlgebraicDataflowArchitectureModel/models/CustomerOffice.model b/AlgebraicDataflowArchitectureModel/models/CustomerOffice.model index f140bff..5714436 100644 --- a/AlgebraicDataflowArchitectureModel/models/CustomerOffice.model +++ b/AlgebraicDataflowArchitectureModel/models/CustomerOffice.model @@ -1,33 +1,21 @@ -channel C_CustomerAOff_In { - out customerA_off(c:String, setOff(x)) == x - out customerA_off(c, e) == c +channel AddCustomer { + out customers(csDB:Map, addCustomer(uid:Str, office:Str)) = insert(csDB, uid, {"office": office}) } -channel C_CustomerBOff_In { - out customerB_off(c:String, setOff(x)) == x - out customerB_off(c, e) == c +channel AddCampany { + out companies(cmDB:Map, addCampany(cid:Str, address:Str)) = insert(cmDB, cid, {"address": address}) } -channel C_CompanyC1Add_In { - out companyC1_add(a:String, setAdd(y)) == y - out companyC1_add(a, e) == a +channel SetCustomerOffice(uid:Str) { + out customers.{uid}.office(prevCid:Str, setOffice(cid)) = cid } -channel C_CompanyC2Add_In { - out companyC2_add(a:String, setAdd(y)) == y - out companyC2_add(a, e) == a +channel SetCompanyAddress(cid:Str) { + out companies.{cid}.address(prevAdd:Str, setAddress(add)) = add } -channel CA { - in customerA_off(c, sync(z, u, v)) == z - in companyC1_add(a1, sync(z, u, v)) == u - in companyC2_add(a2, sync(z, u, v)) == v - out customerA_add(a3:String, sync(z, u, v)) == if(eq(z, C1), u, if(eq(z, C2), v, null)) -} - -channel CB { - in customerB_off(c, sync(z, u, v)) == z - in companyC1_add(a1, sync(z, u, v)) == u - in companyC2_add(a2, sync(z, u, v)) == v - out customerB_add(a3:String, sync(z, u, v)) == if(eq(z, C1), u, if(eq(z, C2), v, null)) +channel UpdateCustomerAddress(uid:Str) { + in customers.{uid}.office(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/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/GameEngine.model b/AlgebraicDataflowArchitectureModel/models/GameEngine.model new file mode 100644 index 0000000..8c6f525 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/GameEngine.model @@ -0,0 +1,104 @@ +init { + enemy := { + "position": {"x": 0.0, "y": 0.0, "z": 0.0}, + "rotation": {"x": 0.0, "y": 0.0, "z": 0.0}, + "id": "001" + } +} + +native channel SceneUpdateEvent { + out scene.time(time: Long, updateEvent(dt: Long)) = time + dt +} + +native channel SceneUpdate { + in scene(curSc: Json, update(curSc, nextSc)) = nextSc +} + +native channel CameraPositionUpdate { + in scene.camera.transform.position(curPos: Json, updatePosition(nextPos.x, nextPos.y, nextPos.z)) = nextPos +} + +native channel CameraRotationUpdate { + in scene.camera.transform.rotation(curRot: Json, updateRotation(nextRot.x, nextRot.y, nextRot.z)) = nextRot +} + +native channel CameraScaleUpdate { + in scene.camera.transform.scale(curScale: Json, updateScale(nextScale.x, nextScale.y, nextScale.z)) = nextScale +} + +native channel CameraProjectionUpdate { + in scene.camera.projection(curProj: Json, updateProjection(curProj, nextProj)) = nextProj +} + +native channel EntityPositionUpdate(eid: Str) { + in scene.entities.{eid}.transform.position(curPos: Json, updatePosition(nextPos.x, nextPos.y, nextPos.z)) = nextPos +} + +native channel EntityRotationUpdate(eid: Str) { + in scene.entities.{eid}.transform.rotation(curRot: Json, updateRotation(nextRot.x, nextRot.y, nextRot.z)) = nextRot +} + +native channel EntityScaleUpdate(eid: Str) { + in scene.entities.{eid}.transform.scale(curScale: Json, updateScale(nextScale.x, nextScale.y, nextScale.z)) = nextScale +} + +native channel EntitySpriteUpdate(eid: Str) { + in scene.entities.{eid}.mesh(curMesh: Json, updateSprite(spritePath: Str)) = {"type": "sprite", "sprite": spritePath} +} + +native channel KeyEvent(kno: Int) { + out scene.keys.{kno}.state(curState: Int, keyEvent(nextState)) = nextState +} + +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) +} + +channel AddSprite { + out scene.entities(entityDB: Map, addSprite(eid: Str, spritePath: Str)) = insert(entityDB, eid, { + "transform": { + "position": {"x": 0.0, "y": 0.0, "z": 0.0}, + "rotation": {"x": 0.0, "y": 0.0, "z": 0.0}, + "scale": {"x": 1.0, "y": 1.0, "z": 1.0} + }, + "mesh": {"type": "sprite", "sprite": spritePath} + }) +} + +channel MoveEntity(eid: Str) { + out scene.entities.{eid}.transform.position(curPos: Json, move(x: Double, y: Double, z: Double)) = {"x": x, "y": y, "z": z} +} + +channel RotateEntity(eid: Str) { + out scene.entities.{eid}.transform.rotation(curRot: Json, rotate(x: Double, y: Double, z: Double)) = {"x": x, "y": y, "z": z} +} + +channel UpdateEnemy(tid: Str) { + in timers.{tid}.count(curCount: Long, updateEnemy(nextCount)) = nextCount + out enemy.position(curPos: Json, updateEnemy(nextCount)) = {"x": curPos.x, "y": curPos.y + 5, "z": curPos.z} + out enemy.rotation(curRot: Json, updateEnemy(nextCount)) = {"x": curRot.x, "y": curRot.y, "z": curRot.z + 5} +} + +channel MoveEnemy { + in enemy.position(curPos: Json, moveEnemy(nextPos, nextRot, nextId)) = nextPos + in enemy.id(curId: Str, moveEnemy(nextPos, nextRot, nextId)) = nextId + out scene.entities.{nextId}.transform.position(curPos: Json, moveEnemy(nextPos, nextRot, nextId)) = nextPos +} + +channel RotateEnemy { + in enemy.rotation(curRot: Json, rotateEnemy(nextPos, nextRot, nextId)) = nextRot + in enemy.id(curId: Str, rotateEnemy(nextPos, nextRot, nextId)) = nextId + out scene.entities.{nextId}.transform.rotation(curRot: Json, rotateEnemy(nextPos, nextRot, nextId)) = nextRot +} diff --git a/AlgebraicDataflowArchitectureModel/models/GroupChat.model b/AlgebraicDataflowArchitectureModel/models/GroupChat.model new file mode 100644 index 0000000..634ddf0 --- /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}}.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/POS.dtram b/AlgebraicDataflowArchitectureModel/models/POS.dtram index dc3d2d1..d1cb11c 100644 --- a/AlgebraicDataflowArchitectureModel/models/POS.dtram +++ b/AlgebraicDataflowArchitectureModel/models/POS.dtram @@ -1,18 +1,18 @@ model { channel CIO { - out payment(p:Int, purchase(x:Int)) == x + out payment(p:Int, purchase(x:Int)) = x } channel C3 { - in history(h, update3(u)) == u - out total(t:Int, update3(u)) == sum(u) + in history(h, update3(u)) = u + out total(t:Int, update3(u)) = sum(u) } channel C1 { - in payment(p, update1(y)) == y - out points(l:Int, update1(y)) == floor(y * 0.05) + in payment(p, update1(y)) = y + out points(l:Int, update1(y)) = floor(y * 0.05) } channel C2 { - in payment(p, update2(z)) == z - out history(h:List, update2(z)) == cons(z, h) + in payment(p, update2(z)) = z + out history(h:List, update2(z)) = cons(z, h) } } geometry { diff --git a/AlgebraicDataflowArchitectureModel/models/POS.model b/AlgebraicDataflowArchitectureModel/models/POS.model index 150c106..c15c931 100644 --- a/AlgebraicDataflowArchitectureModel/models/POS.model +++ b/AlgebraicDataflowArchitectureModel/models/POS.model @@ -1,15 +1,15 @@ channel CIO { - out payment(p:Int, purchase(x:Int)) == x + out payment(p:Int, purchase(x:Int)) = x } channel C3 { - in history(h, update3(u)) == u - out total(t:Int, update3(u)) == sum(u) + in history(h, update3(u)) = u + out total(t:Int, update3(u)) = sum(u) } channel C1 { - in payment(p, update1(y)) == y - out points(l:Int, update1(y)) == floor(y * 0.05) + in payment(p, update1(y)) = y + out points(l:Int, update1(y)) = floor(y * 0.05) } channel C2 { - in payment(p, update2(z)) == z - out history(h:List, update2(z)) == cons(z, h) + in payment(p, update2(z)) = z + out history(h:List, update2(z)) = cons(z, h) } diff --git a/AlgebraicDataflowArchitectureModel/models/POS2.model b/AlgebraicDataflowArchitectureModel/models/POS2.model index 6ae676d..0ee31d2 100644 --- a/AlgebraicDataflowArchitectureModel/models/POS2.model +++ b/AlgebraicDataflowArchitectureModel/models/POS2.model @@ -1,14 +1,14 @@ channel CIO { - out payment(p:Int, purchase(x:Int)) == x + out payment(p:Int, purchase(x:Int)) = x } channel C1 { - in payment(p1, update1(y)) == y - out points(l:Int, update1(y)) == floor(y * 0.05) + in payment(p1, update1(y)) = y + out points(l:Int, update1(y)) = floor(y * 0.05) } channel C2 { - in payment(p1, update2(z)) == z - out history(h:List, update2(z)) == cons(z, h) - out total(t:Int, update2(z)) == z + t + in payment(p1, update2(z)) = z + out history(h:List, update2(z)) = cons(z, h) + out total(t:Int, update2(z)) = z + t } \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/models/SimpleAddressBook.model b/AlgebraicDataflowArchitectureModel/models/SimpleAddressBook.model new file mode 100644 index 0000000..9a9f38a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/SimpleAddressBook.model @@ -0,0 +1,11 @@ +channel Init { + out book.owner(pre_name: Str, init(name: Str)) = name +} + +channel Add { + out book.addr(pre_addr: Map, add(name: Str, addr: Str)) = insert(pre_addr, name, addr) +} + +channel Del { + out book.addr(pre_addr: Map, del(name: Str)) = delete(pre_addr, name) +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/models/SimpleTwitter.model b/AlgebraicDataflowArchitectureModel/models/SimpleTwitter.model new file mode 100644 index 0000000..486fafb --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/SimpleTwitter.model @@ -0,0 +1,7 @@ +channel Signup { + out accounts(accountDB:Map, signUp(accountId:Str, name:Str)) = insert(accountDB, accountId, {"name": name, "tweets": nil}) +} + +channel Tweet(accountId:Str) { + out accounts.{accountId}.tweets(tweetList:List, tweet(contents:Str)) = append(tweetList, contents) +} diff --git a/AlgebraicDataflowArchitectureModel/models/SimpleUI.model b/AlgebraicDataflowArchitectureModel/models/SimpleUI.model new file mode 100644 index 0000000..e003d9a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/SimpleUI.model @@ -0,0 +1,122 @@ +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 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/Test.model b/AlgebraicDataflowArchitectureModel/models/Test.model deleted file mode 100644 index a8a1348..0000000 --- a/AlgebraicDataflowArchitectureModel/models/Test.model +++ /dev/null @@ -1,9 +0,0 @@ -channel CIO { - out a(x:Int, set(n:Int)) == n -} - -channel C1 { - in a(x, update(y)) == y - out b(z, update(y)) == y + 1 - out c(v, update(y)) == v + y -} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/models/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/WOS.dtram b/AlgebraicDataflowArchitectureModel/models/WOS.dtram index 45f640c..81b0779 100644 --- a/AlgebraicDataflowArchitectureModel/models/WOS.dtram +++ b/AlgebraicDataflowArchitectureModel/models/WOS.dtram @@ -1,17 +1,17 @@ model { channel CIO2 { - out highest(h:Double, reset(v)) == v + out highest(h:Double, reset(v)) = v } channel CIO1 { - out temp_f(p:Double, observe(x)) == x + out temp_f(p:Double, observe(x)) = x } channel C1{ - in temp_f(q:Double, conversion(y)) == y - out temp_c(r:Double, conversion(z)) == (z-32) / 1.8 + in temp_f(q:Double, conversion(y)) = y + out temp_c(r:Double, conversion(z)) = (z-32) / 1.8 } channel C2{ - in temp_f(q:Double, update(y)) == y - out highest(h:Double, update(z)) == if(gt(z, h), z, h) + in temp_f(q:Double, update(y)) = y + out highest(h:Double, update(z)) = if(z >= h, z, h) } } geometry { diff --git a/AlgebraicDataflowArchitectureModel/models/WeatherObservationSystem.model b/AlgebraicDataflowArchitectureModel/models/WeatherObservationSystem.model index 2cd84d7..0096407 100644 --- a/AlgebraicDataflowArchitectureModel/models/WeatherObservationSystem.model +++ b/AlgebraicDataflowArchitectureModel/models/WeatherObservationSystem.model @@ -1,14 +1,14 @@ channel CIO2 { - out highest(h:Double, reset(v)) == v + out highest(h:Double, reset(v)) = v } channel CIO1 { - out temp_f(p:Double, observe(x)) == x + out temp_f(p:Double, observe(x)) = x } channel C1{ - in temp_f(q:Double, conversion(y)) == y - out temp_c(r:Double, conversion(z)) == (z-32) / 1.8 + in temp_f(q:Double, conversion(y)) = y + out temp_c(r:Double, conversion(z)) = (z-32) / 1.8 } channel C2{ - in temp_f(q:Double, update(y)) == y - out highest(h:Double, update(z)) == if(gt(z, h), z, h) + in temp_f(q:Double, update(y)) = y + out highest(h:Double, update(z)) = if(z >= h, z, h) } diff --git a/AlgebraicDataflowArchitectureModel/src/algorithms/DataTransferModelAnalyzer.java b/AlgebraicDataflowArchitectureModel/src/algorithms/DataTransferModelAnalyzer.java index 836227a..f54aea4 100644 --- a/AlgebraicDataflowArchitectureModel/src/algorithms/DataTransferModelAnalyzer.java +++ b/AlgebraicDataflowArchitectureModel/src/algorithms/DataTransferModelAnalyzer.java @@ -24,41 +24,47 @@ */ static public DataFlowGraph createDataFlowGraphWithStateStoringAttribute(DataTransferModel model) { DataFlowGraph graph = model.getDataFlowGraph(); - Collection channels = new HashSet<>(model.getIOChannels()); + Collection channels = new HashSet<>(model.getInputChannels()); channels.addAll(model.getChannels()); for (Channel channel: channels) { for (ChannelMember member: ((DataTransferChannel) channel).getOutputChannelMembers()) { - boolean flag = !member.getStateTransition().isRightUnary(); // The state does not need to be stored if the state transition function is right unary. - for (Node node : graph.getNodes()) { - if (((ResourceNode) node).getResource().equals(member.getResource())) { - setStoreAttribute(flag, (ResourceNode) node); + boolean toBeStored = !member.getStateTransition().isRightUnary(); // The state does not need to be stored if the state transition function is right unary. + for (Node node : graph.getResourceNodes()) { + if (((ResourceNode) node).getInSideResources().contains(member.getResource())) { + setStoreAttribute((ResourceNode) node, toBeStored); } } } } - for (Node node : graph.getNodes()) { + for (Node node: graph.getResourceNodes()) { HashSet inChannels = new HashSet<>(); - for(Edge pre : ((ResourceNode) node).getInEdges()) { - inChannels.add(((DataFlowEdge) pre).getChannel()); + for(Edge inEdge: ((ResourceNode) node).getInEdges()) { + if (inEdge instanceof DataFlowEdge) { + DataFlowEdge dfEdge = (DataFlowEdge) inEdge; + if (dfEdge.isChannelToResource()) { + inChannels.add(((ChannelNode) dfEdge.getSource()).getChannel()); + } + } } if ((inChannels.size() > 1)) { - setStoreAttribute(true, (ResourceNode) node); + // If the resource has multiple input channels, then the state of the resource needs to be stored. + setStoreAttribute((ResourceNode) node, true); } else if (((ResourceNode) node).getAttribute() == null) { - setStoreAttribute(false, (ResourceNode) node); + setStoreAttribute((ResourceNode) node, false); } } return graph; } - static private void setStoreAttribute(boolean flag, ResourceNode node) { + static private void setStoreAttribute(ResourceNode node, boolean toBeStored) { NodeAttribute attr = node.getAttribute(); StoreAttribute store; if (attr != null && attr instanceof NodeAttribute) { store = (StoreAttribute) attr; - store.setNeeded(store.isNeeded() || flag); + store.setNeeded(store.isNeeded() || toBeStored); } else { store = new StoreAttribute(); - store.setNeeded(flag); + store.setNeeded(toBeStored); node.setAttribute(store); } } @@ -69,17 +75,17 @@ * @return annotated data flow graph */ static public DataFlowGraph annotateWithSelectableDataTransferAttiribute(DataFlowGraph graph) { - List nodes = new ArrayList<>(graph.getNodes()); + List resNodes = new ArrayList<>(graph.getResourceNodes()); // set push only attributes - for (Node n: graph.getNodes()) { - if (nodes.contains(n) && ((StoreAttribute) ((ResourceNode) n).getAttribute()).isNeeded()) { - nodes.remove(n); - trackEdges(n, nodes); + for (Node resNode: graph.getResourceNodes()) { + if (resNodes.contains(resNode) && ((StoreAttribute) ((ResourceNode) resNode).getAttribute()).isNeeded()) { + resNodes.remove(resNode); + trackEdges(resNode, resNodes); } } // set push/pull attributes to the remaining edges for (Edge e : graph.getEdges()) { - if (((DataFlowEdge) e).getAttribute() == null) { + if (!((DataFlowEdge) e).isChannelToResource() && ((DataFlowEdge) e).getAttribute() == null) { PushPullAttribute ppat = new PushPullAttribute(); ppat.addOption(PushPullValue.PUSHorPULL); ppat.addOption(PushPullValue.PUSH); @@ -90,16 +96,19 @@ return graph; } - static private void trackEdges(Node n, List nodes) { + static private void trackEdges(Node resNode, List resNodes) { // recursively set push only attributes to input side edges - for (Edge e : ((ResourceNode) n).getInEdges()) { - PushPullAttribute ppat = new PushPullAttribute(); - ppat.addOption(PushPullValue.PUSH); - ((DataFlowEdge) e).setAttribute(ppat); - Node n2 = e.getSource(); - if (nodes.contains(n2)) { - nodes.remove(n2); - trackEdges(n2, nodes); + for (Edge chToRes : ((ResourceNode) resNode).getInEdges()) { + Node chNode = chToRes.getSource(); + for (Edge resToCh : ((ChannelNode) chNode).getInEdges()) { + PushPullAttribute ppat = new PushPullAttribute(); + ppat.addOption(PushPullValue.PUSH); + ((DataFlowEdge) resToCh).setAttribute(ppat); + Node resNode2 = resToCh.getSource(); + if (resNodes.contains(resNode2)) { + resNodes.remove(resNode2); + trackEdges(resNode2, resNodes); + } } } } diff --git a/AlgebraicDataflowArchitectureModel/src/algorithms/TypeInference.java b/AlgebraicDataflowArchitectureModel/src/algorithms/TypeInference.java index d29070d..ca92529 100644 --- a/AlgebraicDataflowArchitectureModel/src/algorithms/TypeInference.java +++ b/AlgebraicDataflowArchitectureModel/src/algorithms/TypeInference.java @@ -13,6 +13,7 @@ import javax.xml.crypto.Data; import models.Node; +import models.algebra.Constant; import models.algebra.Expression; import models.algebra.Position; import models.algebra.Symbol; @@ -22,7 +23,10 @@ import models.dataConstraintModel.Channel; import models.dataConstraintModel.ChannelMember; import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.JsonType; +import models.dataConstraintModel.ResourceHierarchy; import models.dataConstraintModel.ResourcePath; +import models.dataConstraintModel.Selector; import models.dataConstraintModel.StateTransition; import models.dataFlowModel.DataTransferModel; import models.dataFlowModel.ResourceNode; @@ -42,6 +46,8 @@ static private Map pairComponentTypes = new HashMap<>(); static private Map, Type> mapTypes = new HashMap<>(); static private Map> mapComponentTypes = new HashMap<>(); + static private Map, Type> jsonTypes = new HashMap<>(); + static private Map> jsonMemberTypes = new HashMap<>(); public static Type getListType(Type compType) { return listTypes.get(compType); @@ -83,29 +89,46 @@ return mapComponentTypes.get(mapType); } + public static Type getJsonType(Map memberTypes) { + return jsonTypes.get(memberTypes); + } + + public static Map getJsonMemberTypes(Type jsonType) { + return jsonMemberTypes.get(jsonType); + } + static public void infer(DataTransferModel model) { - Map> resources = new HashMap<>(); + Map> resources = new HashMap<>(); + Map> resourcePathParams = new HashMap<>(); Map variables = new HashMap<>(); Map, Type>>> messages = new HashMap<>(); Map consOrSet = new HashMap<>(); Map tuple = new HashMap<>(); Map pair = new HashMap<>(); Map map = new HashMap<>(); + Map json = new HashMap<>(); + + // Maps from the objectId of each expression to its belonging group that has the same type as the expression Map> expToResource = new HashMap<>(); + Map> expToPathParams = new HashMap<>(); Map> expToVariable = new HashMap<>(); Map> expToMessage = new HashMap<>(); Map>> expToConsOrSet = new HashMap<>(); Map>> expToTuple = new HashMap<>(); Map>> expToPair = new HashMap<>(); Map>> expToMap = new HashMap<>(); + Map>> expToJson = new HashMap<>(); + // Maps from the objectId of each group to the set of updated expressions. Map> updateFromResource = new HashMap<>(); + Set updateFromResourceOwnership = new HashSet<>(); Map> updateFromVariable = new HashMap<>(); Map> updateFromMessage = new HashMap<>(); Map> updateFromConsOrSet = new HashMap<>(); Map> updateFromTuple = new HashMap<>(); Map> updateFromPair = new HashMap<>(); Map> updateFromMap = new HashMap<>(); + Map> updateFromJson = new HashMap<>(); listComponentTypes.put(DataConstraintModel.typeList, null); listComponentTypes.put(DataConstraintModel.typeListInt, DataConstraintModel.typeInt); @@ -123,813 +146,1121 @@ 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); } } + }; + groupExpressionsByResource.groupForChannel(ch); - // 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); + // 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 +1274,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 +1285,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 +1303,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 +1320,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 +1337,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 +1354,14 @@ updateFromPair.remove(pairKey); for (int i : pairValue.keySet()) { Expression pairExp = pairValue.get(i); - updateResourceTypes(pairExp, resources, expToResource, updateFromResource); + updateResourceTypes(pairExp, resources, expToResource, updateFromResource, updateFromResourceOwnership); updateVaribleTypes(pairExp, variables, expToVariable, updateFromVariable); updateMessageTypes(pairExp, messages, expToMessage, updateFromMessage); updateConsOrSetTypes(pairExp, consOrSet, expToConsOrSet, updateFromConsOrSet); updateTupleTypes(pairExp, tuple, expToTuple, updateFromTuple); updatePairTypes(pairExp, pair, expToPair, updateFromPair); updateMapTypes(pairExp, map, expToMap, updateFromMap); + updateJsonTypes(pairExp, json, expToJson, updateFromJson); } } if (updateFromMap.size() > 0) { @@ -1031,15 +1371,38 @@ updateFromMap.remove(mapKey); for (int i : mapValue.keySet()) { Expression mapExp = mapValue.get(i); - updateResourceTypes(mapExp, resources, expToResource, updateFromResource); + updateResourceTypes(mapExp, resources, expToResource, updateFromResource, updateFromResourceOwnership); updateVaribleTypes(mapExp, variables, expToVariable, updateFromVariable); updateMessageTypes(mapExp, messages, expToMessage, updateFromMessage); updateConsOrSetTypes(mapExp, consOrSet, expToConsOrSet, updateFromConsOrSet); updateTupleTypes(mapExp, tuple, expToTuple, updateFromTuple); updatePairTypes(mapExp, pair, expToPair, updateFromPair); updateMapTypes(mapExp, map, expToMap, updateFromMap); + updateJsonTypes(mapExp, json, expToJson, updateFromJson); } } + if (updateFromJson.size() > 0) { + Set jsonKeys = updateFromJson.keySet(); + Integer jsonKey = jsonKeys.iterator().next(); + Map jsonValue = updateFromJson.get(jsonKey); + updateFromJson.remove(jsonKey); + for (int i : jsonValue.keySet()) { + Expression jsonExp = jsonValue.get(i); + updateResourceTypes(jsonExp, resources, expToResource, updateFromResource, updateFromResourceOwnership); + updateVaribleTypes(jsonExp, variables, expToVariable, updateFromVariable); + updateMessageTypes(jsonExp, messages, expToMessage, updateFromMessage); + updateConsOrSetTypes(jsonExp, consOrSet, expToConsOrSet, updateFromConsOrSet); + updateTupleTypes(jsonExp, tuple, expToTuple, updateFromTuple); + updatePairTypes(jsonExp, pair, expToPair, updateFromPair); + updateMapTypes(jsonExp, map, expToMap, updateFromMap); + updateJsonTypes(jsonExp, json, expToJson, updateFromJson); + } + } + if (updateFromResourceOwnership.size() > 0) { + ResourceHierarchy res = updateFromResourceOwnership.iterator().next(); + updateFromResourceOwnership.remove(res); + updateResourceOwnershipTypes(res, resources, expToResource, updateFromResource, updateFromResourceOwnership); + } } } @@ -1056,18 +1419,20 @@ } } - private static void updateResourceTypes(Expression exp, Map> resources, - Map> expToResource, Map> updateFromResource) { - List sameResource = expToResource.get(System.identityHashCode(exp)); - if (sameResource == null) return; - for (ResourcePath id : resources.keySet()) { - if (resources.get(id) == sameResource) { - Type resType = id.getResourceStateType(); + private static void updateResourceTypes(Expression exp, Map> resources, Map> expToResource, + Map> updateFromResource, Set updateFromResourceOwnership) { + List identicalResources = expToResource.get(System.identityHashCode(exp)); + if (identicalResources == null) return; + for (ResourceHierarchy res: resources.keySet()) { + if (resources.get(res) == identicalResources) { + Type resType = res.getResourceStateType(); Type newResType = getExpTypeIfUpdatable(resType, exp); if (newResType != null) { - id.setResourceStateType(newResType); - Map updateExps = getUpdateSet(updateFromResource, sameResource); - for (Expression resExp : sameResource) { + res.setResourceStateType(newResType); + updateFromResourceOwnership.add(res); // To update parent and children resources + // Update identical resources + Map updateExps = getUpdateSet(updateFromResource, identicalResources); + for (Expression resExp : identicalResources) { if (resExp != exp) { if (resExp instanceof Variable) { ((Variable) resExp).setType(newResType); @@ -1082,6 +1447,145 @@ } } } + + private static void updateResourcePathParamsTypes(Expression exp, Map> resourcePathParams, Set updateFromResourceOwnership) { + for (ResourceHierarchy parent: resourcePathParams.keySet()) { + List pathParams = resourcePathParams.get(parent); + if (pathParams.contains(exp)) { + Type parentType = parent.getResourceStateType(); + Type paramType = null; + if (exp instanceof Variable) { + paramType = ((Variable) exp).getType(); + } else if (exp instanceof Term) { + paramType = ((Term) exp).getType(); + } + if (paramType != null && parentType == null) { + if (paramType.equals(DataConstraintModel.typeString)) { + parentType = DataConstraintModel.typeMap; + } else if (paramType.equals(DataConstraintModel.typeInt)) { + parentType = DataConstraintModel.typeList; + } + if (parentType != null) { + parent.setResourceStateType(parentType); + updateFromResourceOwnership.add(parent); + } + } + } + } + } + + private static void updateResourceOwnershipTypes(ResourceHierarchy res, Map> resources, + Map> expToResource, Map> updateFromResource, Set updateFromResourceOwnership) { + for (ResourceHierarchy parent: resources.keySet()) { + Type resType = res.getResourceStateType(); + Set children = parent.getChildren(); + if (res.equals(parent)) { + // Propagate an update of a parent resource type to its children' types. + if (DataConstraintModel.typeList.isAncestorOf(resType)) { + Type newElementType = listComponentTypes.get(resType); + if (newElementType != null && children != null && children.size() == 1) { + ResourceHierarchy element = children.iterator().next(); + Type elementType = element.getResourceStateType(); + if (compareTypes(elementType, newElementType)) { + element.setResourceStateType(newElementType); + updateFromResourceOwnership.add(element); + } + } + } else if (DataConstraintModel.typeMap.isAncestorOf(resType)) { + List newComponentTypes = mapComponentTypes.get(resType); + if (newComponentTypes != null && newComponentTypes.size() == 2 && children != null && children.size() == 1) { + ResourceHierarchy value = children.iterator().next(); + Type valueType = value.getResourceStateType(); + if (compareTypes(valueType, newComponentTypes.get(1))) { + value.setResourceStateType(newComponentTypes.get(1)); + updateFromResourceOwnership.add(value); + } + } + } else if (DataConstraintModel.typeJson.isAncestorOf(resType)) { + Map newMemberTypes = jsonMemberTypes.get(resType); + if (newMemberTypes != null && newMemberTypes.size() > 0 && children != null && children.size() > 0) { + for (ResourceHierarchy chlid: children) { + String key = chlid.getResourceName(); + Type memberType = chlid.getResourceStateType(); + if (compareTypes(memberType, newMemberTypes.get(key))) { + chlid.setResourceStateType(newMemberTypes.get(key)); + updateFromResourceOwnership.add(chlid); + } + } + } + } + } + if (children.contains(res)) { + // Propagate an update of a child resource type to its parent's type. + Type parentType = parent.getResourceStateType(); + if (parentType != null && DataConstraintModel.typeList.isAncestorOf(parentType)) { + Type oldElementType = listComponentTypes.get(parentType); + if (compareTypes(oldElementType, resType)) { + Type newListType = listTypes.get(resType); + if (newListType == null) { + newListType = createNewListType(resType, parentType); + } + if (newListType != null) { + parent.setResourceStateType(newListType); + updateFromResourceOwnership.add(parent); + } + } + } else if (parentType != null && DataConstraintModel.typeMap.isAncestorOf(parentType)) { + List oldComponentTypes = mapComponentTypes.get(parentType); + if (compareTypes(oldComponentTypes.get(1), resType)) { + List newComponentTypes = Arrays.asList(new Type[] {DataConstraintModel.typeString, resType}); + Type newMapType = mapTypes.get(newComponentTypes); + if (newMapType == null) { + newMapType = createNewMapType(newComponentTypes, parentType); + } + if (newMapType != null) { + parent.setResourceStateType(newMapType); + updateFromResourceOwnership.add(parent); + } + } + } else if (parentType != null && DataConstraintModel.typeJson.isAncestorOf(parentType)) { + Map oldMemberTypes = jsonMemberTypes.get(parentType); + String key= res.getResourceName(); + if (oldMemberTypes == null || compareTypes(oldMemberTypes.get(key), resType)) { + Map newMemberTypes = new HashMap<>(); + if (oldMemberTypes != null) newMemberTypes.putAll(oldMemberTypes); + newMemberTypes.put(key, resType); + Type newJsonType = jsonTypes.get(newMemberTypes); + if (newJsonType == null) { + newJsonType = createNewJsonType(newMemberTypes, parentType); + } + if (newJsonType != null) { + parent.setResourceStateType(newJsonType); + updateFromResourceOwnership.add(parent); + } + } + } + } + } + // Propagate an update of the resource to its expressions. + List identicalResources = resources.get(res); + if (identicalResources != null) { + Type newResType = res.getResourceStateType(); + for (Expression resExp: identicalResources) { + Type resType = null; + if (resExp instanceof Variable) { + resType = ((Variable) resExp).getType(); + } else if (resExp instanceof Term) { + resType = ((Term) resExp).getType(); + } + if (resType == null || compareTypes(resType, newResType)) { + Map updateExps = getUpdateSet(updateFromResource, identicalResources); + if (resExp instanceof Variable) { + ((Variable) resExp).setType(newResType); + updateExps.put(System.identityHashCode(resExp), resExp); + } else if (resExp instanceof Term) { + ((Term) resExp).setType(newResType); + updateExps.put(System.identityHashCode(resExp), resExp); + } + } + } + } + } private static void updateVaribleTypes(Expression exp, Map variables, Map> expToVariable, Map> updateFromVariable) { @@ -1167,6 +1671,7 @@ int idx = consOrSetComponentGroup.indexOf(exp); switch (idx) { case 0: + // exp is a list itself. if (!(exp instanceof Term)) break; Type listType = consOrSet.get(System.identityHashCode(consOrSetComponentGroup)); Type expType = getExpTypeIfUpdatable(listType, exp); @@ -1191,6 +1696,7 @@ } break; case 1: + // exp is a list's component. listType = consOrSet.get(System.identityHashCode(consOrSetComponentGroup)); Type compType = listComponentTypes.get(listType); Type newCompType = getExpTypeIfUpdatable(compType, exp); @@ -1216,6 +1722,7 @@ } break; case 2: + // exp is a list itself. listType = consOrSet.get(System.identityHashCode(consOrSetComponentGroup)); expType = getExpTypeIfUpdatable(listType, exp); if (expType != null) { @@ -1245,6 +1752,7 @@ for (List tupleComponentGroup: tupleComponentGroups) { int idx = tupleComponentGroup.indexOf(exp); if (idx == 0) { + // exp is a tuple itself. Type tupleType = tuple.get(System.identityHashCode(tupleComponentGroup)); Type newTupleType = getExpTypeIfUpdatable(tupleType, exp); if (newTupleType != null) { @@ -1312,6 +1820,7 @@ } } } else { + // exp is a tuple's component. Type tupleType = tuple.get(System.identityHashCode(tupleComponentGroup)); List componentTypes = tupleComponentTypes.get(tupleType); boolean updated = false; @@ -1382,6 +1891,7 @@ for (List pairComponentGroup: pairComponentGroups) { int idx = pairComponentGroup.indexOf(exp); if (idx == 0) { + // exp is a pair itself. Type pairType = pair.get(System.identityHashCode(pairComponentGroup)); Type newPairType = getExpTypeIfUpdatable(pairType, exp); if (newPairType != null) { @@ -1405,6 +1915,7 @@ } } } else { + // exp is a pair's component. Type pairType = pair.get(System.identityHashCode(pairComponentGroup)); Type compType = pairComponentTypes.get(pairType); Type newCompType = getExpTypeIfUpdatable(compType, exp); @@ -1435,6 +1946,7 @@ for (List mapComponentGroup: mapComponentGroups) { int idx = mapComponentGroup.indexOf(exp); if (idx == 0 || idx == 3) { + // exp is a map itself. Type mapType = map.get(System.identityHashCode(mapComponentGroup)); Type newMapType = getExpTypeIfUpdatable(mapType, exp); if (newMapType != null) { @@ -1478,6 +1990,7 @@ } } } else { + // exp is a map's key or value. Type mapType = map.get(System.identityHashCode(mapComponentGroup)); List componentTypes = mapComponentTypes.get(mapType); Type compType = componentTypes.get(idx - 1); @@ -1516,6 +2029,118 @@ } } + + private static void updateJsonTypes(Expression exp, Map json, + Map>> expToJson, Map> updateFromJson) { + Set> jsonMemberGroups = expToJson.get(System.identityHashCode(exp)); + if (jsonMemberGroups == null) return; + for (List jsonMemberGroup: jsonMemberGroups) { + int idx = jsonMemberGroup.indexOf(exp); + if (idx == 3) { + // exp is a json argument (0:t = addMember(3:json, 1:key, 2:value)). + Type jsonType = json.get(System.identityHashCode(jsonMemberGroup)); + if (jsonType != null && jsonMemberTypes.get(jsonType) != null) { + Map memberTypes = new HashMap<>(jsonMemberTypes.get(jsonType)); + Map argMemberTypes = new HashMap<>(memberTypes); + String keyName = null; + Type valueType = null; + if (jsonMemberGroup.get(1) instanceof Constant) { + keyName = ((Constant) jsonMemberGroup.get(1)).getSymbol().getName(); + valueType = ((Constant) jsonMemberGroup.get(1)).getType(); + argMemberTypes.remove(keyName); + } + Type jsonArgType = jsonTypes.get(argMemberTypes); + Type newJsonArgType = getExpTypeIfUpdatable(jsonArgType, exp); + if (newJsonArgType != null && keyName != null) { + // Propagate an update of a json arg's type to its container's (json's) type. + argMemberTypes = ((JsonType) newJsonArgType).getMemberTypes(); + argMemberTypes.put(keyName, valueType); + memberTypes.putAll(argMemberTypes); + Type newJsonType = jsonTypes.get(memberTypes); + if (newJsonType == null) { + // Create new json type. + newJsonType = createNewJsonType(memberTypes, jsonType); + } + // Update the type of the json term and record the updated expression. + Map updateExps = getUpdateSet(updateFromJson, jsonMemberGroup); + Expression jsonExp = jsonMemberGroup.get(0); + if (jsonExp instanceof Variable) { + ((Variable) jsonExp).setType(newJsonType); + updateExps.put(System.identityHashCode(jsonExp), jsonExp); + } else if (jsonExp instanceof Term) { + ((Term) jsonExp).setType(newJsonType); + updateExps.put(System.identityHashCode(jsonExp), jsonExp); + } + json.put(System.identityHashCode(jsonMemberGroup), newJsonType); + } + } + } else if (idx == 2) { + // exp is a value (0:t = addMember(3:json, 1:key, 2:value) or 2:value = dot(0:list/map, 1:key)). + Type jsonType = json.get(System.identityHashCode(jsonMemberGroup)); + Type newJsonType = null; + if (exp instanceof Term && ((Term) exp).getSymbol().equals(DataConstraintModel.dotParam)) { + if (DataConstraintModel.typeList.isAncestorOf(jsonType)) { + Type elementType = listComponentTypes.get(jsonType); + Type newElementType = getExpTypeIfUpdatable(elementType, exp); + if (newElementType != null) { + // Propagate an update of a member's type to its container's (json's) type. + newJsonType = listTypes.get(newElementType); + if (newJsonType == null) { + // Create new json type. + newJsonType = createNewListType(newElementType, jsonType); + } + } + } else if (DataConstraintModel.typeMap.isAncestorOf(jsonType)) { + List keyValueTypes = mapComponentTypes.get(jsonType); + Type newValueType = getExpTypeIfUpdatable(keyValueTypes.get(1), exp); + if (newValueType != null) { + // Propagate an update of a member's type to its container's (json's) type. + List newKeyValueTypes = Arrays.asList(new Type[] {DataConstraintModel.typeString, newValueType}); + newJsonType = mapTypes.get(newKeyValueTypes); + if (newJsonType == null) { + // Create new json type. + newJsonType = createNewMapType(newKeyValueTypes, jsonType); + } + } + } + } else { + Map memberTypes = jsonMemberTypes.get(jsonType); + String keyName = null; + if (jsonMemberGroup.get(1) instanceof Constant) { + keyName = ((Constant) jsonMemberGroup.get(1)).getSymbol().getName(); + } + if (memberTypes != null) { + Type memberType = memberTypes.get(keyName); + Type newMemberType = getExpTypeIfUpdatable(memberType, exp); + if (newMemberType != null && keyName != null) { + // Propagate an update of a member's type to its container's (json's) type. + Map newMemberTypes = new HashMap<>(memberTypes); + newMemberTypes.put(keyName, newMemberType); + newJsonType = jsonTypes.get(newMemberTypes); + if (newJsonType == null) { + // Create new json type. + newJsonType = createNewJsonType(newMemberTypes, jsonType); + } + } + } + } + if (newJsonType != null) { + // Update the type of the json term and record the updated expression. + Map updateExps = getUpdateSet(updateFromJson, jsonMemberGroup); + Expression jsonExp = jsonMemberGroup.get(0); + if (jsonExp instanceof Variable) { + ((Variable) jsonExp).setType(newJsonType); + updateExps.put(System.identityHashCode(jsonExp), jsonExp); + } else if (jsonExp instanceof Term) { + ((Term) jsonExp).setType(newJsonType); + updateExps.put(System.identityHashCode(jsonExp), jsonExp); + } + json.put(System.identityHashCode(jsonMemberGroup), newJsonType); + } + } + } + } + private static Type createNewListType(Type compType, Type parentType) { String compTypeName = getInterfaceTypeName(compType); List childrenTypes = getChildrenTypes(parentType, listComponentTypes.keySet()); @@ -1598,9 +2223,39 @@ return newMapType; } - private static List getChildrenTypes(Type parentType, Set componentTypes) { + private static JsonType createNewJsonType(Map memberTypes, Type parentJsonType) { + String implTypeName = "HashMap<>"; + String interfaceTypeName = "Map"; + List childrenTypes = getChildrenTypes(parentJsonType, jsonMemberTypes.keySet()); + JsonType newJsonType = new JsonType("Json", implTypeName, interfaceTypeName, parentJsonType); + for (String key: memberTypes.keySet()) { + newJsonType.addMemberType(key, memberTypes.get(key)); + } + jsonTypes.put(memberTypes, newJsonType); + jsonMemberTypes.put(newJsonType, memberTypes); + for (Type childType : childrenTypes) { + if (compareTypes(childType, newJsonType)) { + if (newJsonType.getParentTypes().contains(parentJsonType)) { + newJsonType.replaceParentType(parentJsonType, childType); + } else { + newJsonType.addParentType(childType); + } + } else if (compareTypes(newJsonType, childType)) { + childType.replaceParentType(parentJsonType, newJsonType); + } + } + return newJsonType; + } + + /** + * Get children types of a given type from given set of types. + * @param parentType a type + * @param allTypes set of types + * @return list of the children types + */ + private static List getChildrenTypes(Type parentType, Set allTypes) { List childrenTypes = new ArrayList<>(); - for (Type childType : componentTypes) { + for (Type childType : allTypes) { if (childType.getParentTypes().contains(parentType)) { childrenTypes.add(childType); } @@ -1626,8 +2281,7 @@ return type.getInterfaceTypeName(); } - private static > Map getUpdateSet( - Map> updateSets, U keySet) { + private static > Map getUpdateSet(Map> updateSets, U keySet) { Map updatedExps = updateSets.get(System.identityHashCode(keySet)); if (updatedExps == null) { updatedExps = new HashMap<>(); @@ -1683,6 +2337,7 @@ for (int i = 0; i < originalCompTypes.size(); i++) { if (originalCompTypes.get(i) != null) { if (DataConstraintModel.typeTuple.isAncestorOf(originalCompTypes.get(i))) { + // Unfold the nested tuple's types. Type tupleType = originalCompTypes.remove(i); for (Type t: tupleComponentTypes.get(tupleType)) { originalCompTypes.add(t); @@ -1690,6 +2345,7 @@ } if (newCompTypes.size() - 1 < i) return false; if (newCompTypes.get(i) != null && DataConstraintModel.typeTuple.isAncestorOf(newCompTypes.get(i))) { + // Unfold the nested tuple's types. Type tupleType = newCompTypes.remove(i); for (Type t: tupleComponentTypes.get(tupleType)) { newCompTypes.add(t); @@ -1707,7 +2363,40 @@ 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/application/ApplicationMenuBar.java b/AlgebraicDataflowArchitectureModel/src/application/ApplicationMenuBar.java index 38e2370..1835abf 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/ApplicationMenuBar.java +++ b/AlgebraicDataflowArchitectureModel/src/application/ApplicationMenuBar.java @@ -17,6 +17,7 @@ import application.actions.OpenAction; import application.actions.SaveAction; import application.actions.SaveAsAction; +import application.actions.SimulateAction; import application.actions.TreeLayoutAction; import application.actions.ZoomInAction; import application.actions.ZoomOutAction; @@ -37,6 +38,8 @@ private DAGLayoutAction dagLayoutAction = null; private TreeLayoutAction treeLayoutAction = null; private CircleLayoutAction circleLayoutAction = null; + //private SimulateAction simulateAction = null; + public ApplicationMenuBar(ApplicationWindow applicationWindow) { this.applicationWindow = applicationWindow; @@ -72,6 +75,9 @@ menu.add(new ZoomInAction(applicationWindow.getGraphComponent())); menu.add(new ZoomOutAction(applicationWindow.getGraphComponent())); + menu = add(new JMenu("Simulate")); + menu.add(new SimulateAction(applicationWindow)); + menu = add(new JMenu("Generate")); menu.add(javaPrototypeGenerateAction = new JavaPrototypeGenerateAction(applicationWindow.getEditor())); menu.add(jerseyPrototypeGenerateAction = new JerseyPrototypeGenerateAction(applicationWindow.getEditor())); diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/JavaPrototypeGenerateAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/JavaPrototypeGenerateAction.java index 83acc69..4083dce 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/actions/JavaPrototypeGenerateAction.java +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/JavaPrototypeGenerateAction.java @@ -13,9 +13,12 @@ import algorithms.*; import application.editor.Editor; import code.ast.*; +import generators.CodeGeneratorFromDataFlowGraph; import generators.DataTransferMethodAnalyzer; import generators.JavaCodeGenerator; import generators.JavaMethodBodyGenerator; +import generators.JavaSpecific; +import models.dataConstraintModel.ResourceHierarchy; import models.dataConstraintModel.ResourcePath; import models.dataFlowModel.DataTransferModel; import models.dataFlowModel.ModelExtension; @@ -45,8 +48,8 @@ if (fileName == null) fileName = "Main"; String mainTypeName = fileName.split("\\.")[0]; boolean exist = false; - for (ResourcePath id: model.getResourcePaths()) { - String resourceName = id.getResourceName().substring(0, 1).toUpperCase() + id.getResourceName().substring(1); + for (ResourceHierarchy res: model.getResourceHierarchies()) { + String resourceName = res.getResourceName().substring(0, 1).toUpperCase() + res.getResourceName().substring(1); if (mainTypeName.equals(resourceName)) { exist = true; } @@ -57,6 +60,7 @@ JavaCodeGenerator.resetMainTypeName(); // use the default main type's name. } editor.setCodes(JavaMethodBodyGenerator.doGenerate(graph, model, JavaCodeGenerator.doGenerate(graph, model))); +// editor.setCodes(new CodeGeneratorFromDataFlowGraph().generateCode(model, graph, new JavaSpecific())); ModelExtension.recoverModel(model); for (CompilationUnit file : editor.getCodes()) { System.out.println(file); diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/JerseyPrototypeGenerateAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/JerseyPrototypeGenerateAction.java index 766c384..dbf75fd 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/actions/JerseyPrototypeGenerateAction.java +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/JerseyPrototypeGenerateAction.java @@ -17,6 +17,7 @@ import generators.JerseyCodeGenerator; import generators.JerseyMethodBodyGenerator; import models.algebra.Type; +import models.dataConstraintModel.ResourceHierarchy; import models.dataConstraintModel.ResourcePath; import models.dataFlowModel.DataTransferModel; import models.dataFlowModel.ModelExtension; @@ -46,8 +47,8 @@ if (fileName == null) fileName = "Main"; String mainTypeName = fileName.split("\\.")[0]; boolean exist = false; - for (ResourcePath id: model.getResourcePaths()) { - String resourceName = id.getResourceName().substring(0, 1).toUpperCase() + id.getResourceName().substring(1); + for (ResourceHierarchy res: model.getResourceHierarchies()) { + String resourceName = res.getResourceName().substring(0, 1).toUpperCase() + res.getResourceName().substring(1); if (mainTypeName.equals(resourceName)) { exist = true; } diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/NewResourceAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/NewResourceAction.java index 50b1512..b01fc46 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/actions/NewResourceAction.java +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/NewResourceAction.java @@ -22,7 +22,7 @@ public void actionPerformed(ActionEvent e) { String resName = JOptionPane.showInputDialog("Resourece Name:"); if (resName == null) return; - editor.addResourcePath(new ResourcePath(resName, 0)); + editor.addResourcePath(null, resName); } } diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/SaveAsAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/SaveAsAction.java index 1a38b08..9542cdb 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/actions/SaveAsAction.java +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/SaveAsAction.java @@ -65,9 +65,7 @@ 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(); diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/SimulateAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/SimulateAction.java new file mode 100644 index 0000000..26a1779 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/SimulateAction.java @@ -0,0 +1,38 @@ +package application.actions; + +import java.awt.event.ActionEvent; +import java.io.File; + +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JOptionPane; +import javax.swing.filechooser.FileFilter; +import javax.swing.filechooser.FileNameExtensionFilter; + +import application.ApplicationWindow; +import application.editor.Editor; +import application.simulator.SimulatorWindow; +import models.dataFlowModel.DataTransferChannel; + +public class SimulateAction extends AbstractSystemAction { + + private SimulatorWindow simulatorWindow = null; + + /** + * + */ + private static final long serialVersionUID = -995609232489316795L; + + public SimulateAction(ApplicationWindow frame) { + super("Simulate", frame); + //this.simulatorWindow = new SimulatorWindow(frame.getEditor()); + } + + + @Override + public void actionPerformed(ActionEvent e) { + this.simulatorWindow = new SimulatorWindow(frame.getEditor()); + } + +} + diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/DataTransferModelingCellEditor.java b/AlgebraicDataflowArchitectureModel/src/application/editor/DataTransferModelingCellEditor.java index b4d81a6..f560501 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/editor/DataTransferModelingCellEditor.java +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/DataTransferModelingCellEditor.java @@ -30,7 +30,10 @@ 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; public class DataTransferModelingCellEditor implements mxICellEditor { public int DEFAULT_MIN_WIDTH = 70; @@ -67,7 +70,7 @@ DataTransferModel model = editor.getModel(); DataTransferChannel ch = (DataTransferChannel) model.getChannel((String) ((mxCell) cell).getValue()); if (ch == null) { - ch = (DataTransferChannel) model.getIOChannel((String) ((mxCell) cell).getValue()); + ch = (DataTransferChannel) model.getInputChannel((String) ((mxCell) cell).getValue()); if(ch == null) { //resource return; @@ -123,7 +126,7 @@ Expression exp = parser.parseTerm(stream, editor.getModel()); ((FormulaChannel) ch).setFormula(formula); ((FormulaChannel) ch).setFormulaTerm(exp); - } catch (ExpectedRightBracket e) { + } catch (ExpectedRightBracket | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { e.printStackTrace(); } } diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/Editor.java b/AlgebraicDataflowArchitectureModel/src/application/editor/Editor.java index b285ce0..c9bfbd5 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/editor/Editor.java +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/Editor.java @@ -6,12 +6,16 @@ import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; +import java.util.AbstractMap; +import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import com.mxgraph.layout.mxCircleLayout; @@ -19,6 +23,7 @@ import com.mxgraph.model.mxCell; import com.mxgraph.model.mxGeometry; import com.mxgraph.model.mxGraphModel; +import com.mxgraph.model.mxICell; import com.mxgraph.model.mxIGraphModel; import com.mxgraph.swing.mxGraphComponent; import com.mxgraph.util.mxConstants; @@ -32,15 +37,26 @@ import algorithms.Validation; import application.layouts.*; import code.ast.CompilationUnit; +import generators.JavaCodeGenerator; import models.Edge; import models.EdgeAttribute; import models.Node; +import models.algebra.Expression; +import models.algebra.InvalidMessage; +import models.algebra.ParameterizedIdentifierIsFutureWork; +import models.algebra.UnificationFailed; +import models.algebra.ValueUndefined; import models.dataConstraintModel.Channel; import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.ResourceHierarchy; import models.dataConstraintModel.ResourcePath; +import models.dataConstraintModel.Selector; import models.dataFlowModel.DataTransferModel; import models.dataFlowModel.DataTransferChannel; import models.dataFlowModel.PushPullAttribute; +import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork; +import models.dataFlowModel.ChannelNode; import models.dataFlowModel.DataFlowEdge; import models.dataFlowModel.DataFlowGraph; import models.dataFlowModel.ResourceNode; @@ -50,10 +66,12 @@ import parser.exceptions.ExpectedAssignment; import parser.exceptions.ExpectedChannel; import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; +import parser.exceptions.ExpectedDoubleQuotation; import parser.exceptions.ExpectedEquals; import parser.exceptions.ExpectedFormulaChannel; import parser.exceptions.ExpectedGeometry; -import parser.exceptions.ExpectedInOrOutOrRefKeyword; +import parser.exceptions.ExpectedInOrOutOrRefOrSubKeyword; import parser.exceptions.ExpectedIoChannel; import parser.exceptions.ExpectedLeftCurlyBracket; import parser.exceptions.ExpectedModel; @@ -62,8 +80,11 @@ import parser.exceptions.ExpectedResource; import parser.exceptions.ExpectedRightBracket; import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; import parser.exceptions.WrongLHSExpression; import parser.exceptions.WrongRHSExpression; +import parser.exceptions.ExpectedRightCurlyBracket; +import parser.exceptions.WrongPathExpression; import parser.ParserDTRAM; public class Editor { @@ -79,6 +100,8 @@ protected String curFileName = null; protected String curFilePath = null; protected ArrayList codes = null; + + private boolean bReflectingArchitectureModel = false; public Editor(mxGraphComponent graphComponent) { this.graphComponent = graphComponent; @@ -105,6 +128,7 @@ public DataFlowGraph getDataFlowGraph() { if (dataFlowGraph == null) { analyzeDataTransferModel(getModel()); + updateEdgeAttiributes(dataFlowGraph); } return dataFlowGraph; } @@ -112,7 +136,6 @@ public DataFlowGraph analyzeDataTransferModel(DataTransferModel model) { DataFlowGraph flowGraph = DataTransferModelAnalyzer.createDataFlowGraphWithStateStoringAttribute(model); dataFlowGraph = DataTransferModelAnalyzer.annotateWithSelectableDataTransferAttiribute(flowGraph); - updateEdgeAttiributes(dataFlowGraph); return dataFlowGraph; } @@ -175,17 +198,26 @@ } else { ParserDTRAM parserDTRAM = new ParserDTRAM(new BufferedReader(new FileReader(file))); try { + // Parse the .dtram file. model = parserDTRAM.doParseModel(); - graph = constructGraph(model); + + // Analyze the model. + if (!Validation.checkUpdateConflict(model)) return null; + DataFlowGraph dataFlowGraph = analyzeDataTransferModel(model); + + // Visualize the model. + graph = constructGraph(model, dataFlowGraph); parserDTRAM.doParseGeometry(graph); + updateEdgeAttiributes(dataFlowGraph); + curFilePath = file.getAbsolutePath(); curFileName = file.getName(); - if (!Validation.checkUpdateConflict(model)) return null; - analyzeDataTransferModel(model); return model; - } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefKeyword + } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression - | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedModel | ExpectedGeometry | ExpectedNode | ExpectedResource | ExpectedFormulaChannel | ExpectedIoChannel e) { + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedModel + | ExpectedGeometry | ExpectedNode | ExpectedResource | ExpectedFormulaChannel | ExpectedIoChannel + | ExpectedRightCurlyBracket | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { e.printStackTrace(); } } @@ -205,20 +237,23 @@ // Parse the .model file. model = parser.doParse(); - curFilePath = file.getAbsolutePath(); - curFileName = file.getName(); - // Analyze the model. if (!Validation.checkUpdateConflict(model)) return null; - graph = constructGraph(model); - analyzeDataTransferModel(model); + DataFlowGraph dataFlowGraph = analyzeDataTransferModel(model); + + // Visualize the model. + graph = constructGraph(model, dataFlowGraph); + updateEdgeAttiributes(dataFlowGraph); // Set DAG layout. - setDAGLayout(); +// setDAGLayout(); + + curFilePath = file.getAbsolutePath(); + curFileName = file.getName(); return model; - } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefKeyword + } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression - | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment e) { + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { e.printStackTrace(); } } catch (FileNotFoundException e) { @@ -303,11 +338,11 @@ } for (ResourcePath res: model.getResourcePaths()){ - if(res instanceof ResourcePath && state.getLabel().equals(res.getResourceName())) + if(res instanceof ResourcePath && state.getLabel().equals(res.getLeafResourceName())) fileString += "\tnode r " + state.getLabel() + ":" + x + "," + y + "," + w + "," + h + "\n"; } - for (Channel ioC: model.getIOChannels()) { + for (Channel ioC: model.getInputChannels()) { if(ioC instanceof Channel && state.getLabel().equals(ioC.getChannelName())) { fileString += "\tnode ioc " + state.getLabel() + ":" + x + "," + y + "," + w + "," + h + "\n"; } @@ -322,10 +357,11 @@ /** * Construct a mxGraph from DataFlowModel and DataFlowModel * @param model - * @param dataFlowGraph + * @param dataFlowGraph * @return constructed mxGraph */ - public mxGraph constructGraph(DataTransferModel model) { + public mxGraph constructGraph(DataTransferModel model, DataFlowGraph dataFlowGraph) { + bReflectingArchitectureModel = true; ((mxGraphModel) graph.getModel()).clear(); Object parent = graph.getDefaultParent(); graph.getModel().beginUpdate(); @@ -340,69 +376,139 @@ Map channelsIn = new HashMap<>(); Map channelsOut = new HashMap<>(); - Map resources = new HashMap<>(); - - // create channel vertices - for (Channel c: model.getChannels()) { - DataTransferChannel channelGen = (DataTransferChannel) c; - if (channelsIn.get(channelGen) == null || channelsOut.get(channelGen) == null) { - Object channel = graph.insertVertex(parent, null, channelGen.getChannelName(), 150, 20, 30, 30); // insert a channel as a vertex - mxCell port_in = new mxCell(null, geo1, "shape=ellipse;perimter=ellipsePerimeter"); - port_in.setVertex(true); - graph.addCell(port_in, channel); // insert the input port of a channel - mxCell port_out = new mxCell(null, geo2, "shape=ellipse;perimter=ellipsePerimeter"); - port_out.setVertex(true); - graph.addCell(port_out, channel); // insert the output port of a channel - channelsIn.put(channelGen, port_in); - channelsOut.put(channelGen, port_out); - } - } + Map resources = new HashMap<>(); // create resource vertices - for (ResourcePath res: model.getResourcePaths()) { + for (ResourceNode resNode: dataFlowGraph.getRootResourceNodes()) { + int w = 80; + int h = 30; + ResourcePath res = resNode.getPrimaryResourcePath(); Object resource = graph.insertVertex(parent, null, - res.getResourceName(), 20, 20, 80, 30, + res.getLeafResourceName(), 20, 20, w, h, "shape=ellipse;perimeter=ellipsePerimeter"); // insert a resource as a vertex - resources.put(res, resource); + resources.put(resNode, resource); + createChildResourceVerticies(resource, resNode, resources, w, h); } - - // add input, output and reference edges - for (Channel ch: model.getChannels()) { - DataTransferChannel channelGen = (DataTransferChannel) ch; - // input edge - for (ResourcePath srcRes: channelGen.getInputResources()) { - graph.insertEdge(parent, null, new SrcDstAttribute(srcRes, channelGen), resources.get(srcRes), channelsIn.get(channelGen), "movable=false"); - } - // output edge - for (ResourcePath dstRes: channelGen.getOutputResources()) { - graph.insertEdge(parent, null, new SrcDstAttribute(channelGen, dstRes), channelsOut.get(channelGen), resources.get(dstRes), "movable=false"); - } - // reference edges - for (ResourcePath refRes: channelGen.getReferenceResources()) { - graph.insertEdge(parent, null, null, resources.get(refRes), channelsIn.get(channelGen), "dashed=true;movable=false"); - } - } - - for (Channel ioChannelGen: model.getIOChannels()) { - if (channelsOut.get(ioChannelGen) == null) { - Object channel = graph.insertVertex(parent, null, ioChannelGen.getChannelName(), 150, 20, 30, 30); // insert an I/O channel as a vertex - mxCell port_out = new mxCell(null, geo2, "shape=ellipse;perimter=ellipsePerimeter"); - port_out.setVertex(true); - graph.addCell(port_out, channel); // insert the output port of a channel - channelsOut.put((DataTransferChannel) ioChannelGen, port_out); - for (ResourcePath outRes: ((DataTransferChannel) ioChannelGen).getOutputResources()) { - graph.insertEdge(parent, null, null, port_out, resources.get(outRes), "movable=false"); + + // create channel vertices + for (ChannelNode c: dataFlowGraph.getRootChannelNodes()) { + DataTransferChannel channel = (DataTransferChannel) c.getChannel(); + if (channel.getInputResources().size() > 0) { + // Normal channel + if (channelsIn.get(channel) == null || channelsOut.get(channel) == null) { + if (channel.getSelectors().toString() == "[]") { + Object chCell = graph.insertVertex(parent, null, channel.getChannelName(), 150, 20, 30, 30); // insert a channel as a vertex + mxCell port_in = new mxCell(null, geo1, "shape=ellipse;perimter=ellipsePerimeter"); + port_in.setVertex(true); + graph.addCell(port_in, chCell); // insert the input port of a channel + mxCell port_out = new mxCell(null, geo2, "shape=ellipse;perimter=ellipsePerimeter"); + port_out.setVertex(true); + graph.addCell(port_out, chCell); // insert the output port of a channel + channelsIn.put(channel, port_in); + channelsOut.put(channel, port_out); + } else { + for (Selector s: channel.getSelectors()) { + Expression exp = s.getExpression(); + String selName = exp.toString(); + String cName = channel.getChannelName(); + String channelName = cName + " (" + selName + ")"; + Object chCell = graph.insertVertex(parent, null, channelName, 150, 20, 60, 30); // insert a channel as a vertex + mxCell port_in = new mxCell(null, geo1, "shape=ellipse;perimter=ellipsePerimeter"); + port_in.setVertex(true); + graph.addCell(port_in, chCell); // insert the input port of a channel + mxCell port_out = new mxCell(null, geo2, "shape=ellipse;perimter=ellipsePerimeter"); + port_out.setVertex(true); + graph.addCell(port_out, chCell); // insert the output port of a channel + channelsIn.put(channel, port_in); + channelsOut.put(channel, port_out); + } + } } + } else { + // I/O channel + if (channelsOut.get(channel) == null) { + if (channel.getSelectors().toString() == "[]") { + Object chCell = graph.insertVertex(parent, null, channel.getChannelName(), 150, 20, 30, 30); // insert a channel as a vertex + mxCell port_in = new mxCell(null, geo1, "shape=ellipse;perimter=ellipsePerimeter"); + port_in.setVertex(true); + graph.addCell(port_in, chCell); // insert the input port of a channel + mxCell port_out = new mxCell(null, geo2, "shape=ellipse;perimter=ellipsePerimeter"); + port_out.setVertex(true); + graph.addCell(port_out, chCell); // insert the output port of a channel + channelsIn.put(channel, port_in); + channelsOut.put(channel, port_out); + } else { + for (Selector s: channel.getSelectors()) { + Expression exp = s.getExpression(); + String selName = exp.toString(); + String cName = channel.getChannelName(); + String channelName = cName + " (" + selName + ")"; + Object chCell = graph.insertVertex(parent, null, channelName, 150, 20, 60, 30); // insert a channel as a vertex + mxCell port_in = new mxCell(null, geo1, "shape=ellipse;perimter=ellipsePerimeter"); + port_in.setVertex(true); + graph.addCell(port_in, chCell); // insert the input port of a channel + mxCell port_out = new mxCell(null, geo2, "shape=ellipse;perimter=ellipsePerimeter"); + port_out.setVertex(true); + graph.addCell(port_out, chCell); // insert the output port of a channel + channelsIn.put(channel, port_in); + channelsOut.put(channel, port_out); + } + } + } + } + } + + // add input, output and reference edges + for (Edge edge: dataFlowGraph.getEdges()) { + DataFlowEdge dfEdge = (DataFlowEdge) edge; + if (dfEdge.isChannelToResource()) { + // output edge + DataTransferChannel channel = ((ChannelNode) dfEdge.getSource()).getChannel(); + ResourcePath dstRes = ((ResourceNode) dfEdge.getDestination()).getInSideResource(channel); + graph.insertEdge(parent, null, new SrcDstAttribute(channel, dstRes), channelsOut.get(channel), resources.get((ResourceNode) dfEdge.getDestination()), "movable=false"); + } else { + // input edge + DataTransferChannel channel = ((ChannelNode) dfEdge.getDestination()).getChannel(); + ResourcePath srcRes = ((ResourceNode) dfEdge.getSource()).getOutSideResource(channel); + Set> toRes = getResourceDependencyForChannel(channel, model, dataFlowGraph); + for(Map.Entry RtoR: toRes) { + graph.insertEdge(parent, null, null, + resources.get(RtoR.getValue()), + resources.get(RtoR.getKey()), "dashed=true;movable=false"); + } + + graph.insertEdge(parent, null, new SrcDstAttribute(srcRes, channel), resources.get((ResourceNode) dfEdge.getSource()), channelsIn.get(channel), "movable=false"); + } + } + + for (Channel ch: model.getChannels()) { + // reference edges + DataTransferChannel channel = (DataTransferChannel) ch; + for (ResourcePath refRes: channel.getReferenceResources()) { + graph.insertEdge(parent, null, null, resources.get(dataFlowGraph.getResourceNode(refRes)), channelsIn.get(channel), "dashed=true;movable=false"); } } } finally { graph.getModel().endUpdate(); } - setTreeLayout(); - + setDAGLayout(); + + bReflectingArchitectureModel = false; return graph; } + public void createChildResourceVerticies(Object resource, ResourceNode resNode, Map resources, int w, int h) { + + for (ResourceNode childNode: resNode.getChildren()) { + ResourcePath childRes = childNode.getPrimaryResourcePath(); + Object childResource = graph.insertVertex(resource, null, + childRes.getName(), 0, 0, w, h, + "shape=ellipse;perimeter=ellipsePerimeter"); // insert a resource as a vertex + resources.put(childNode, childResource); + createChildResourceVerticies(childResource, childNode, resources, w, h); + } + } + public void setDAGLayout() { Object parent = graph.getDefaultParent(); graph.getModel().beginUpdate(); @@ -422,16 +528,18 @@ for (Edge e : dataFlowGraph.getEdges()) { if (e instanceof DataFlowEdge) { DataFlowEdge dataFlow = (DataFlowEdge) e; - DataTransferChannel channelGen = dataFlow.getChannel(); - ResourceNode srcRes = (ResourceNode) dataFlow.getSource(); - // input edge - for (Object edge: graph.getChildEdges(parent)) { - mxCell edgeCell = (mxCell) edge; - if (edgeCell.getValue() instanceof SrcDstAttribute) { - SrcDstAttribute edgeAttr = (SrcDstAttribute) edgeCell.getValue(); - if (edgeAttr.getSrouce() == srcRes.getResource() && edgeAttr.getDestination() == channelGen) { - edgeCell.setValue(dataFlow.getAttribute()); - break; + if (!dataFlow.isChannelToResource()) { + ResourceNode srcRes = (ResourceNode) dataFlow.getSource(); + DataTransferChannel channel = ((ChannelNode) dataFlow.getDestination()).getChannel(); + // input edge + for (Object edge: graph.getChildEdges(parent)) { + mxCell edgeCell = (mxCell) edge; + if (edgeCell.getValue() instanceof SrcDstAttribute) { + SrcDstAttribute edgeAttr = (SrcDstAttribute) edgeCell.getValue(); + if (srcRes.getPrimaryResourcePath().equals(edgeAttr.getSource()) && channel.equals(edgeAttr.getDestination())) { + edgeCell.setValue(dataFlow.getAttribute()); + break; + } } } } @@ -468,17 +576,39 @@ } } - public void addResourcePath(ResourcePath res) { - getModel().addResourcePath(res); + public ResourcePath addResourcePath(ResourcePath parentPath, String resName) { + ResourcePath resourcePath = null; + if (parentPath == null) { + resourcePath = new ResourcePath(resName); + getModel().addResourcePath(resourcePath); + } else { + if (resName.startsWith(Parser.LEFT_CURLY_BRACKET) && resName.endsWith(Parser.RIGHT_CURLY_BRACKET)) { + TokenStream stream = new Parser.TokenStream(); + Parser parser = new Parser(stream); + stream.addLine(resName.substring(1, resName.length() - 1)); + try { + Expression exp = parser.parseTerm(stream, getModel()); + resourcePath = new ResourcePath(parentPath, exp); + getModel().addResourcePath(resourcePath); + } catch (ExpectedRightBracket | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + return null; + } + } else { + resourcePath = new ResourcePath(parentPath, resName); + getModel().addResourcePath(resourcePath); + } + } resetDataFlowGraph(); graph.getModel().beginUpdate(); Object parent = graph.getDefaultParent(); try { - graph.insertVertex(parent, null, res.getResourceName(), 20, 20, 80, 30, + graph.insertVertex(parent, null, resName, 20, 20, 80, 30, "shape=ellipse;perimeter=ellipsePerimeter"); // insert a resource as a vertex } finally { graph.getModel().endUpdate(); } + return resourcePath; } public void addChannel(DataTransferChannel channelGen) { @@ -508,7 +638,7 @@ } public void addIOChannel(DataTransferChannel ioChannelGen) { - getModel().addIOChannel(ioChannelGen); + getModel().addInputChannel(ioChannelGen); resetDataFlowGraph(); graph.getModel().beginUpdate(); Object parent = graph.getDefaultParent(); @@ -553,10 +683,11 @@ } public boolean connectEdge(mxCell edge, mxCell src, mxCell dst) { + if (bReflectingArchitectureModel) return false; DataTransferModel model = getModel(); Channel srcCh = model.getChannel((String) src.getValue()); if (srcCh == null) { - srcCh = model.getIOChannel((String) src.getValue()); + srcCh = model.getInputChannel((String) src.getValue()); if (srcCh == null) { ResourcePath srcRes = model.getResourcePath((String) src.getValue()); Channel dstCh = model.getChannel((String) dst.getValue()); @@ -593,7 +724,7 @@ // channel to resource edge Channel ch = model.getChannel(srcName); if (ch == null) { - ch = model.getIOChannel(srcName); + ch = model.getInputChannel(srcName); } ch.removeChannelMember(model.getResourcePath(dstName)); } @@ -601,8 +732,8 @@ String name = (String) cell.getValue(); if (model.getChannel(name) != null) { model.removeChannel(name); - } else if (model.getIOChannel(name) != null) { - model.removeIOChannel(name); + } else if (model.getInputChannel(name) != null) { + model.removeInputChannel(name); } else if (model.getResourcePath(name) != null) { model.removeResourcePath(name); } @@ -624,7 +755,7 @@ DataTransferChannel ch2 = parser.parseChannel(getModel()); for (ChannelMember chm2: ch2.getInputChannelMembers()) { for (ChannelMember chm: ch.getInputChannelMembers()) { - if (chm2.getResource() == chm.getResource()) { + if (chm2.getResource().getResourceHierarchy() == chm.getResource().getResourceHierarchy()) { chm.setStateTransition(chm2.getStateTransition()); break; } @@ -632,7 +763,7 @@ } for (ChannelMember chm2: ch2.getOutputChannelMembers()) { for (ChannelMember chm: ch.getOutputChannelMembers()) { - if (chm2.getResource() == chm.getResource()) { + if (chm2.getResource().getResourceHierarchy() == chm.getResource().getResourceHierarchy()) { chm.setStateTransition(chm2.getStateTransition()); break; } @@ -640,7 +771,7 @@ } for (ChannelMember chm2: ch2.getReferenceChannelMembers()) { for (ChannelMember chm: ch.getReferenceChannelMembers()) { - if (chm2.getResource() == chm.getResource()) { + if (chm2.getResource().getResourceHierarchy() == chm.getResource().getResourceHierarchy()) { chm.setStateTransition(chm2.getStateTransition()); break; } @@ -648,11 +779,36 @@ } resetDataFlowGraph(); } catch (ExpectedRightBracket | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket - | ExpectedInOrOutOrRefKeyword | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression - | WrongLHSExpression | WrongRHSExpression | ExpectedAssignment e) { + | ExpectedInOrOutOrRefOrSubKeyword | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression + | WrongLHSExpression | WrongRHSExpression | ExpectedAssignment | ExpectedRightCurlyBracket | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { e.printStackTrace(); } } + + public Set> getResourceDependencyForChannel(DataTransferChannel ch, DataTransferModel model, DataFlowGraph dataFlowGraph) { + Set> resourceDpendency = new HashSet<>(); + if (ch.getOutputChannelMembers().size() > 0) { + try { + Map>> dependency = ch.fillOutsideResourcePaths(ch.getOutputChannelMembers().iterator().next(), JavaCodeGenerator.pullAccessor); + for (ChannelMember srcMem: dependency.keySet()) { + ResourceNode srcNode = dataFlowGraph.getResourceNode(srcMem.getResource()); + if (srcNode != null) { + for (ChannelMember dstMem: dependency.get(srcMem).getValue()) { + ResourceNode dstNode = dataFlowGraph.getResourceNode(dstMem.getResource()); + while (srcNode.getResourceHierarchy().getNumParameters() == 0 && srcNode.getParent() != null) { + srcNode = srcNode.getParent(); + } + resourceDpendency.add(new AbstractMap.SimpleEntry<>(srcNode, dstNode)); + } + } + } + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage + | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + } + return resourceDpendency; + } private class SrcDstAttribute extends EdgeAttribute { private Object src; @@ -663,7 +819,7 @@ this.dst = dst; } - public Object getSrouce() { + public Object getSource() { return src; } diff --git a/AlgebraicDataflowArchitectureModel/src/application/layouts/DAGLayout.java b/AlgebraicDataflowArchitectureModel/src/application/layouts/DAGLayout.java index d493d10..8d9b8b8 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/layouts/DAGLayout.java +++ b/AlgebraicDataflowArchitectureModel/src/application/layouts/DAGLayout.java @@ -1,9 +1,14 @@ package application.layouts; - + import java.util.ArrayList; -import java.util.Collections; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; - +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + import com.mxgraph.layout.mxGraphLayout; import com.mxgraph.model.mxCell; import com.mxgraph.model.mxGeometry; @@ -11,133 +16,825 @@ import com.mxgraph.view.mxCellState; import com.mxgraph.view.mxGraph; import com.mxgraph.view.mxGraphView; - + 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..476c942 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/simulator/InputEventCellEditor.java @@ -0,0 +1,350 @@ +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 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.Term; +import models.algebra.UnificationFailed; +import models.algebra.ValueUndefined; +import models.algebra.Variable; +import models.dataConstraintModel.Channel; +import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.ResourceHierarchy; +import models.dataConstraintModel.ResourcePath; +import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.DataFlowGraph; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.PushPullAttribute; +import models.dataFlowModel.PushPullValue; +import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork; +import models.visualModel.FormulaChannel; +import parser.Parser; +import parser.Parser.TokenStream; +import parser.exceptions.ExpectedColon; +import parser.exceptions.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.graphComponent = graphComponent; + this.window = window; + 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();//resource + ArrayList eventChs = new ArrayList<>();//iOchannelList + ArrayList messages = new ArrayList<>();//ADLmessage + ArrayList eventMessages = new ArrayList<>();//messageList + ResourcePath eventResPath = null; + + for (Channel ch: simulator.getModel().getInputChannels()) {//all channel + eventResPath = getSelectableMessages(ch, resId, eventChs, messages, eventMessages, eventResPath); + } + + if(messages.isEmpty()) { + return; + } + String[] eventList = messages.toArray(new String[messages.size()]); + JComboBox event = new JComboBox(eventList); + + JPanel eventChoice = new JPanel(); + eventChoice.add(event);//FirstEventChoice + + int ret = JOptionPane.showConfirmDialog(window, eventChoice, "Event Choice", JOptionPane.OK_CANCEL_OPTION); + if (ret == JOptionPane.OK_OPTION) { + + JPanel inputEvent = new JPanel(); + int i , eventNum; + i = eventNum = 0; + + for(String eventString : eventList) { + if(eventString.equals(event.getSelectedItem().toString())) { + eventNum = i; + } + i++; + } + + JTextArea textArea = new JTextArea(eventMessages.get(eventNum).toString(), 10, 30);//EventInput + inputEvent.add(textArea); + + int approve = JOptionPane.showConfirmDialog(window, inputEvent, "Event Code", JOptionPane.OK_CANCEL_OPTION); + if (approve == JOptionPane.OK_OPTION) { + try { + + TokenStream stream = new Parser.TokenStream(); + Parser parser = new Parser(stream); + stream.addLine(textArea.getText()); + Expression eventMessage = parser.parseTerm(stream,simulator.getModel()); + + Event newEvent = new Event(eventChs.get(eventNum), eventMessage, eventResPath, simulator.getCurState().getResource(resId)); + simulator.transition(newEvent); + +// SimulationLayout layout = new SimulationLayout(simulator.getCurState()); +// layout.constructSimulateGraph(graph, simulator); + + graphComponent.setCellEditor(new InputEventCellEditor(window, graphComponent, simulator, this.editor)); + + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork + | InvalidMessage | UnificationFailed | ValueUndefined | ExpectedRightBracket | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + } + } + + 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 messages, ArrayList eventMessages, + ResourcePath eventResPath) { + if (((DataTransferChannel)ch).getInputResources().size() == 0) {//ioch Or normalch + for (ChannelMember out: ((DataTransferChannel)ch).getOutputChannelMembers()) { + ResourcePath resPath = out.getResource(); + if (!out.isOutside() && resId.isInstanceOf(resPath)) {//account.uid == acounts.123 + eventResPath = resPath; + eventChs.add(((DataTransferChannel)ch)); + String message = null; + Expression mesExp = out.getStateTransition().getMessageExpression();//sync(a,b) + eventMessages.add(mesExp); + if (mesExp instanceof Term) { + message = ((Term) mesExp).getSymbol().toString();//sync + } else if(mesExp instanceof Variable) { + message = ((Variable) mesExp).getName();//x,y,z.. + } + messages.add(message);//pulldown + } + } + for (Channel childCh: ch.getChildren()) { + eventResPath = getSelectableMessages(childCh, resId, eventChs, messages, eventMessages, eventResPath); + } + } + return eventResPath; + } + +// private mxGraph constructNextSimulateGraph(Set simulateRes, DataTransferModel model,DataFlowGraph dataFlowGraph, mxGraph nextGraph) { +// bReflectingArchitectureModel = true; +// ((mxGraphModel) nextGraph.getModel()).clear(); +// Object parent = nextGraph.getDefaultParent(); +// nextGraph.getModel().beginUpdate(); +// +// try { +// Map resources = new HashMap<>(); +// +// // create resource vertices +// for (Resource resNode: simulateRes) { +// int w = 400; +// int h = 150; +// ResourcePath res = resNode.getResourceIdentifier(); +// Object resource = nextGraph.insertVertex(parent, null, +// res.toString(), x, y, w, h, +// "shape=ellipse;perimeter=ellipsePerimeter;verticalAlign=top;"); // insert a resource as a vertex +// resources.put(resNode, resource); +// +// createNextChildSimulateResourceVerticies(resource, resNode, resources, w, h); +// x+=400; +// } +// +// } finally { +// nextGraph.getModel().endUpdate(); +// } +// +// bReflectingArchitectureModel = false; +// return nextGraph; +// } +// +// private void createNextChildSimulateResourceVerticies(Object resource, Resource resNode, Map resources, int w, int h) { //sample +// +// if(resNode.getChildren() != null) { +// for (Resource childNode: resNode.getChildren()) { +// int i = 1; +// ResourcePath childRes = childNode.getResourceIdentifier(); +// Object childResource = graph.insertVertex(resource, null, +// childRes.toString(), x/i, 0, w/(i+1), h/(i+2), +// "shape=ellipse;perimeter=ellipsePerimeter;verticalAlign=top;"); // insert a resource as a vertex +// resources.put(childNode, childResource); +// i++; +// createNextChildSimulateResourceVerticies(childResource, childNode, resources, w, h); +// } +// } +// if (resNode.getChildren() == null || resNode.getChildren().size() == 0) { +// Object state = graph.insertVertex(resource, null, +// resNode.getState().getValue().toString(), 0.5, 0.5, 0.25, 0.25, "opacity=0;verticalAlign=down;", true); // insert a state label as a vertex +// } +// } + + @Override + public void stopEditing(boolean cancel) { + if (editingCell != null) { + comboBox.transferFocusUpCycle(); + Object cell = editingCell; + editingCell = null; + if (!cancel) { + EventObject trig = trigger; + trigger = null; + Object value = graphComponent.getGraph().getModel().getValue(cell); + if (value != null && value instanceof PushPullAttribute) { + PushPullAttribute attr = (PushPullAttribute) value; + List options = attr.getOptions(); + PushPullValue selected = null; + for (PushPullValue option: options) { + if (option.toString().equals(getCurrentValue())) { + selected = option; + break; + } + } + if (selected != null) { + options.remove(selected); + options.add(0, selected); + } + graphComponent.labelChanged(cell, attr, trig); + } + } else { + mxCellState state = graphComponent.getGraph().getView().getState(cell); + graphComponent.redraw(state); + } + + if (comboBox.getParent() != null) { + comboBox.setVisible(false); + comboBox.getParent().remove(comboBox); + } + + graphComponent.requestFocusInWindow(); + } + } + + public String getCurrentValue() { + return (String) comboBox.getSelectedItem(); + } + + /** + * Returns the bounds to be used for the editor. + */ + public Rectangle getEditorBounds(mxCellState state, double scale) { + mxIGraphModel model = state.getView().getGraph().getModel(); + Rectangle bounds = null; + + bounds = state.getLabelBounds().getRectangle(); + bounds.height += 10; + + // Applies the horizontal and vertical label positions + if (model.isVertex(state.getCell())) { + String horizontal = mxUtils.getString(state.getStyle(), mxConstants.STYLE_LABEL_POSITION, mxConstants.ALIGN_CENTER); + + if (horizontal.equals(mxConstants.ALIGN_LEFT)) { + bounds.x -= state.getWidth(); + } else if (horizontal.equals(mxConstants.ALIGN_RIGHT)) { + bounds.x += state.getWidth(); + } + + String vertical = mxUtils.getString(state.getStyle(), + mxConstants.STYLE_VERTICAL_LABEL_POSITION, + mxConstants.ALIGN_MIDDLE); + + if (vertical.equals(mxConstants.ALIGN_TOP)) { + bounds.y -= state.getHeight(); + } else if (vertical.equals(mxConstants.ALIGN_BOTTOM)) { + bounds.y += state.getHeight(); + } + } + + bounds.setSize( + (int) Math.max(bounds.getWidth(), + Math.round(minimumWidth * scale)), + (int) Math.max(bounds.getHeight(), + Math.round(minimumHeight * scale))); + + return bounds; + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/simulator/SimulationLayout.java b/AlgebraicDataflowArchitectureModel/src/application/simulator/SimulationLayout.java new file mode 100644 index 0000000..eced48d --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/simulator/SimulationLayout.java @@ -0,0 +1,157 @@ +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.List; +import java.util.Map; +import java.util.Set; + +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..29463f4 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/simulator/SimulatorMenuBar.java @@ -0,0 +1,27 @@ +package application.simulator; + +import javax.swing.JMenu; +import javax.swing.JMenuBar; + +import application.actions.ZoomInAction; +import application.actions.ZoomOutAction; +import application.simulator.actions.ShowUISimulatorAction; + +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..adfd10f --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/simulator/SimulatorWindow.java @@ -0,0 +1,213 @@ +package application.simulator; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.swing.JFrame; + +import com.mxgraph.model.mxCell; +import com.mxgraph.model.mxGeometry; +import com.mxgraph.model.mxGraphModel; +import com.mxgraph.swing.mxGraphComponent; +import com.mxgraph.swing.handler.mxRubberband; +import com.mxgraph.util.mxEvent; +import com.mxgraph.util.mxEventObject; +import com.mxgraph.util.mxPoint; +import com.mxgraph.util.mxEventSource.mxIEventListener; +import com.mxgraph.view.mxGraph; + +import algorithms.TypeInference; +import application.editor.DataTransferModelingCellEditor; +import application.editor.Editor; +import application.layouts.DAGLayout; +import generators.JavaCodeGenerator; +import models.Edge; +import models.EdgeAttribute; +import models.algebra.Expression; +import models.algebra.InvalidMessage; +import models.algebra.ParameterizedIdentifierIsFutureWork; +import models.algebra.UnificationFailed; +import models.algebra.ValueUndefined; +import models.dataConstraintModel.Channel; +import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.ResourcePath; +import models.dataConstraintModel.Selector; +import models.dataFlowModel.ChannelNode; +import models.dataFlowModel.DataFlowEdge; +import models.dataFlowModel.DataFlowGraph; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork; +import models.dataFlowModel.ResourceNode; +import parser.Parser; +import parser.ParserDTRAM; +import parser.exceptions.ExpectedColon; +import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.WrongJsonExpression; +import parser.Parser.TokenStream; +import simulator.Event; +import simulator.Resource; +import simulator.ResourceIdentifier; +import simulator.Simulator; +import simulator.SystemState; +import simulator.interfaces.INativeReceiver; + +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..1c246b9 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/simulator/UISimulatorWindow.java @@ -0,0 +1,29 @@ +package application.simulator; + +import javax.swing.JFrame; +import javax.swing.JPanel; + +import simulator.Simulator; +import simulator.interfaces.swing.SwingPresenter; +import simulator.interfaces.timers.TimerService; + +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..758d2d9 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/simulator/actions/ShowUISimulatorAction.java @@ -0,0 +1,23 @@ +package application.simulator.actions; + +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; + +import application.simulator.UISimulatorWindow; +import simulator.Simulator; + +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/code/ast/MethodDeclaration.java b/AlgebraicDataflowArchitectureModel/src/code/ast/MethodDeclaration.java index fad7629..5f5aafc 100644 --- a/AlgebraicDataflowArchitectureModel/src/code/ast/MethodDeclaration.java +++ b/AlgebraicDataflowArchitectureModel/src/code/ast/MethodDeclaration.java @@ -109,6 +109,10 @@ } thrws.addException(exception); } + + public Throws getThrows() { + return thrws; + } @Override public Annotation getAnnotation(String name) { diff --git a/AlgebraicDataflowArchitectureModel/src/generators/CodeGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/CodeGenerator.java new file mode 100644 index 0000000..2a7db3b --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/generators/CodeGenerator.java @@ -0,0 +1,746 @@ +package generators; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Stack; + +import code.ast.Block; +import code.ast.CompilationUnit; +import code.ast.FieldDeclaration; +import code.ast.MethodDeclaration; +import code.ast.TypeDeclaration; +import code.ast.VariableDeclaration; +import models.Edge; +import models.Node; +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Field; +import models.algebra.Parameter; +import models.algebra.Symbol; +import models.algebra.Term; +import models.algebra.Type; +import models.algebra.Variable; +import models.dataConstraintModel.Channel; +import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.ResourceHierarchy; +import models.dataConstraintModel.ResourcePath; +import models.dataConstraintModel.Selector; +import models.dataFlowModel.ChannelNode; +import models.dataFlowModel.DataFlowEdge; +import models.dataFlowModel.DataFlowGraph; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.PushPullAttribute; +import models.dataFlowModel.PushPullValue; +import models.dataFlowModel.ResourceNode; +import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor; + +/** + * Common generator for prototypes + * + * @author Nitta + * + */ +public abstract class CodeGenerator { + public static final String fieldOfResourceState = "value"; + public static final String getterPrefix = "get"; + public static final String getterOfResourceState = "getValue"; + public static final String updateMethodPrefix = "update"; + public static final String from = "From"; + public static final String _for = "For"; + private static String mainTypeName = null; + private static ILanguageSpecific langSpec = null; + + public static String getMainTypeName() { + return mainTypeName; + } + + public static void setMainTypeName(String mainTypeName) { + CodeGenerator.mainTypeName = mainTypeName; + } + + public static void resetMainTypeName() { + CodeGenerator.mainTypeName = null; + } + + public static String getComponentName(ResourceHierarchy res, ILanguageSpecific langSpec) { + String name = res.getResourceName(); + if (res.getNumParameters() > 0) { + if (name.length() > 3 && name.endsWith("ies")) { + name = name.substring(0, name.length() - 3) + "y"; + } else if (name.length() > 1 && name.endsWith("s")) { + name = name.substring(0, name.length() - 1); + } else { + name += "Element"; + } + } + return langSpec.toComponentName(name); + } + + public static Type getImplStateType(ResourceHierarchy res, ILanguageSpecific langSpec) { + Set children = res.getChildren(); + if (children == null || children.size() == 0) { + // leaf resource. + return res.getResourceStateType(); + } else { + ResourceHierarchy child = children.iterator().next(); + if (children.size() == 1 && child.getNumParameters() > 0) { + // map or list. + if (DataConstraintModel.typeList.isAncestorOf(res.getResourceStateType())) { + // list. + if (generatesComponent(child)) { + return langSpec.newListType(getComponentName(child, langSpec)); + } else { + return langSpec.newListType(getImplStateType(child, langSpec).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; + 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, DataFlowGraph flowGraph, ILanguageSpecific langSpec) { + CodeGenerator.langSpec = langSpec; + ArrayList codes = new ArrayList<>(); + + // Get the dependency among root nodes. + Map> dependedRootComponentGraph = getDependedRootComponentGraph(model); + + // Sort the all components. + ArrayList components = determineComponentOrder(flowGraph, dependedRootComponentGraph); + + // Add the main component. + if (mainTypeName == null) { + mainTypeName = langSpec.getMainComponentName(); + } + TypeDeclaration mainComponent = langSpec.newTypeDeclaration(mainTypeName); + MethodDeclaration mainConstructor = mainComponent.createConstructor(); + CompilationUnit mainCU = langSpec.newCompilationUnit(mainComponent); + codes.add(mainCU); + + // Generate the other components. + generateCodeFromFlowGraph(model, flowGraph, components, dependedRootComponentGraph, mainComponent, mainConstructor, codes, langSpec); + + return codes; + } + + public abstract void generateCodeFromFlowGraph(DataTransferModel model, DataFlowGraph flowGraph, ArrayList components, Map> dependedRootComponentGraph, + TypeDeclaration mainComponent, MethodDeclaration mainConstructor, ArrayList codes, ILanguageSpecific langSpec); + + private static Map> getDependedRootComponentGraph(DataTransferModel model) { + Map> dependedComponentGraph = new HashMap<>(); + for (Channel ch: model.getChannels()) { + DataTransferChannel dtCh = (DataTransferChannel) ch; + Set inRes = new HashSet<>(); + Set outRes = new HashSet<>(); + for (ChannelMember cm: dtCh.getChannelMembers()) { + if (cm.isOutside()) { + outRes.add(cm.getResource().getResourceHierarchy()); + } else { + inRes.add(cm.getResource().getResourceHierarchy()); + } + } + if (outRes.size() > 0 && inRes.size() > 0) { + for (ResourceHierarchy out: outRes) { + for (ResourceHierarchy in: inRes) { + Set dependings = dependedComponentGraph.get(out.getRoot()); + if (dependings == null) { + dependings = new HashSet<>(); + dependedComponentGraph.put(out.getRoot(), dependings); + } + dependings.add(in.getRoot()); + } + } + } + } + return dependedComponentGraph; + } + + private static ArrayList determineComponentOrder(DataFlowGraph graph, Map> dependedRootComponentGraph) { + ArrayList objects = new ArrayList<>(); + Set visited = new HashSet<>(); + Collection allNodes = graph.getResourceNodes(); + for (ResourceNode resNode: allNodes) { + topologicalSort(resNode, allNodes, dependedRootComponentGraph, visited, objects); + } + return objects; + } + + private static void topologicalSort(ResourceNode curResNode, Collection allNodes, Map> dependedRootComponentGraph, Set visited, List orderedList) { + if (visited.contains(curResNode)) return; + visited.add(curResNode); + // A caller is before the callee + + // For each incoming PUSH transfer. + for (Edge chToRes: curResNode.getInEdges()) { + for (Edge resToCh: chToRes.getSource().getInEdges()) { + if (!(resToCh instanceof DataFlowEdge) || ((PushPullAttribute)((DataFlowEdge) resToCh).getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { + topologicalSort((ResourceNode) resToCh.getSource(), allNodes, dependedRootComponentGraph, visited, orderedList); + } + } + } + // For each outgoing PULL transfer. + if (curResNode instanceof ResourceNode) { + for (Edge resToCh: curResNode.getOutEdges()) { + DataFlowEdge de = (DataFlowEdge) resToCh; + if (((PushPullAttribute) de.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { + for (Edge chToRes : resToCh.getDestination().getOutEdges()) { + topologicalSort((ResourceNode) chToRes.getDestination(), allNodes, dependedRootComponentGraph, visited, orderedList); + } + } + } + } + // For each depending root node. + if (curResNode instanceof ResourceNode && dependedRootComponentGraph.get(curResNode.getResourceHierarchy()) != null) { + for (ResourceHierarchy dependingRes: dependedRootComponentGraph.get(curResNode.getResourceHierarchy())) { + for (ResourceNode rootNode: allNodes) { + ResourceHierarchy rootRes = rootNode.getResourceHierarchy(); + if (rootRes.getParent() == null && rootRes.equals(dependingRes)) { + topologicalSort(rootNode, allNodes, dependedRootComponentGraph, visited, orderedList); + } + } + } + } + // For each reference resource. + ResourceNode cn = null; + if (curResNode instanceof ResourceNode) { + cn = (ResourceNode) curResNode; + } + if (cn != null) { + for (Node n: allNodes) { + ResourceNode resNode = null; + if (n instanceof ResourceNode) { + resNode = (ResourceNode) n; + } + if (resNode != null) { + for (Edge resToCh: resNode.getOutEdges()) { + ChannelNode chNode = (ChannelNode) resToCh.getDestination(); + for (ChannelMember m: chNode.getChannel().getReferenceChannelMembers()) { + if (curResNode.getOutSideResources().contains(m.getResource())) { + topologicalSort(resNode, allNodes, dependedRootComponentGraph, visited, orderedList); + } + } + } + } + } + } + orderedList.add(0, curResNode); + } + + protected void updateMainComponent(TypeDeclaration mainType, MethodDeclaration mainConstructor, Node componentNode, + MethodDeclaration constructor, final List depends, ILanguageSpecific langSpec) { + // Declare the field to refer to each object in the main type. + ResourceNode resNode = null; + String nodeName = null; + if (componentNode instanceof ResourceNode) { + resNode = (ResourceNode) componentNode; + nodeName = resNode.getResourceName(); + } + String componentName = langSpec.toComponentName(nodeName); + // Declare a field to refer each object. + if (langSpec.declareField()) { + FieldDeclaration refField = langSpec.newFieldDeclaration(new Type(componentName, componentName), nodeName); + mainType.addField(refField); + } + // Add a statement to instantiate each object to the main constructor. + List parameters = new ArrayList<>(); + for (ResourceHierarchy res: depends) { + parameters.add(res.getResourceName()); + } + if (constructor.getParameters() != null) { + for (VariableDeclaration var: constructor.getParameters()) { + parameters.add(var.getName()); + } + } + + Block mainConstructorBody = mainConstructor.getBody(); + if (mainConstructorBody == null) { + mainConstructorBody = new Block(); + mainConstructor.setBody(mainConstructorBody); + } + mainConstructorBody.addStatement(langSpec.getFieldAccessor(nodeName) + langSpec.getAssignment() + langSpec.getConstructorInvocation(componentName, parameters) + langSpec.getStatementDelimiter()); + } + + protected ResourceHierarchy addReference(TypeDeclaration component, MethodDeclaration constructor, ResourceHierarchy dstRes, ILanguageSpecific langSpec) { + if (!generatesComponent(dstRes)) { + dstRes = dstRes.getParent(); + } + String dstComponentName = getComponentName(dstRes, langSpec); + if (dstComponentName != null) { + String dstNodeName = langSpec.toVariableName(dstComponentName); + if (langSpec.declareField()) { + // Declare a field to refer to another component. + component.addField(langSpec.newFieldDeclaration(new Type(dstComponentName, dstComponentName), dstNodeName)); + } + // Initialize the field to refer to another component. + constructor.addParameter(langSpec.newVariableDeclaration(new Type(dstComponentName, dstComponentName), dstNodeName)); + constructor.getBody().addStatement(langSpec.getFieldAccessor(dstNodeName) + langSpec.getAssignment() + dstNodeName + langSpec.getStatementDelimiter()); + } + return dstRes; + } + + protected void fillStateGetterMethod(MethodDeclaration stateGetter, ResourceHierarchy resourceHierarchy, Type resStateType, ILanguageSpecific langSpec) { + // returns the state field when all incoming data-flow edges are PUSH-style. + if (langSpec.isValueType(resStateType)) { + stateGetter.addStatement(langSpec.getReturnStatement(langSpec.getFieldAccessor(fieldOfResourceState)) + langSpec.getStatementDelimiter()); // return value; + } else { + if (resourceHierarchy.getChildren() != null && resourceHierarchy.getChildren().size() == 1 && resourceHierarchy.getChildren().iterator().next().getNumParameters() > 0) { + // list or map + String implTypeName = resStateType.getImplementationTypeName(); + // copy the current state to be returned as a 'value' + List parameters = new ArrayList<>(); + parameters.add(langSpec.getFieldAccessor(fieldOfResourceState)); + stateGetter.addStatement(langSpec.getReturnStatement(langSpec.getConstructorInvocation(implTypeName, parameters)) + langSpec.getStatementDelimiter()); // return new Resource(value); + } else { + if (resourceHierarchy.getChildren() == null || resourceHierarchy.getChildren().size() == 0) { + // a leaf resource + String implTypeName = resStateType.getImplementationTypeName(); + // copy the current state to be returned as a 'value' + List parameters = new ArrayList<>(); + parameters.add(langSpec.getFieldAccessor(fieldOfResourceState)); + stateGetter.addStatement(langSpec.getReturnStatement(langSpec.getConstructorInvocation(implTypeName, parameters)) + langSpec.getStatementDelimiter()); // return new Resource(value); + } else { + Term composer = null; + Term composerSub = new Constant(DataConstraintModel.nil); + composerSub.setType(DataConstraintModel.typeMap); + for (ResourceHierarchy child: resourceHierarchy.getChildren()) { + String childTypeName = getComponentName(child, langSpec); + String fieldName = langSpec.toVariableName(childTypeName); + Term childGetter = null; + if ((child.getChildren() == null || child.getChildren().size() == 0) && child.getNumParameters() == 0) { + // the child is not a class + childGetter = new Term(new Symbol("get" + childTypeName, 1, Symbol.Type.METHOD)); + childGetter.addChild(new Constant("this")); + } else { + // the child is a class + childGetter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); + childGetter.addChild(new Field(fieldName, getImplStateType(child, langSpec))); + } + composer = new Term(DataConstraintModel.insert); + composer.addChild(composerSub); + composer.addChild(new Constant(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 ancestor, TypeDeclaration ancestorComponent, ILanguageSpecific langSpec) { + 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(descendant, 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, ILanguageSpecific langSpec) { + List mainGetterParams = new ArrayList<>(); + int v = 1; + for (Expression param: accessRes.getPrimaryResourcePath().getPathParams()) { + if (param instanceof Variable) { + Variable var = (Variable) param; + mainGetterParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + } else if (param instanceof Term) { + Term var = (Term) param; + mainGetterParams.add(new VariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + MethodDeclaration accessor = null; + if (mainGetterParams.size() == 0) { + accessor = langSpec.newMethodDeclaration(getterPrefix + getComponentName(accessRes.getResourceHierarchy(), langSpec), getImplStateType(accessRes.getResourceHierarchy(), langSpec)); + } else { + accessor = langSpec.newMethodDeclaration(getterPrefix + getComponentName(accessRes.getResourceHierarchy(), langSpec), false, getImplStateType(accessRes.getResourceHierarchy(), langSpec), mainGetterParams); + } + Block block = new Block(); + Expression getState = getPullAccessor().getDirectStateAccessorFor(accessRes.getPrimaryResourcePath(), null); + block.addStatement(langSpec.getReturnStatement(getState.toImplementation(new String[] {null})) + langSpec.getStatementDelimiter()); +// if (stateGetter.getParameters() == null || stateGetter.getParameters().size() == 0) { +// block.addStatement(langSpec.getReturnStatement(langSpec.getMethodInvocation(accessRes.getResourceName(), stateGetter.getName())) + langSpec.getStatementDelimiter()); +// } else { +// List resParams = new ArrayList<>(); +// for (VariableDeclaration var: stateGetter.getParameters()) { +// resParams.add(var.getName()); +// } +// block.addStatement(langSpec.getReturnStatement(langSpec.getMethodInvocation(accessRes.getResourceName(), stateGetter.getName(), resParams)) + langSpec.getStatementDelimiter()); +// } + accessor.setBody(block); + mainComponent.addMethod(accessor); + } + + protected void declareFieldsToReferenceResources(DataTransferModel model, ResourceNode resourceNode, TypeDeclaration component, MethodDeclaration constructor, + final List depends, ILanguageSpecific langSpec) { + Set refs = new HashSet<>(); + for (Channel ch : model.getChannels()) { + DataTransferChannel c = (DataTransferChannel) ch; + if (resourceNode.getOutSideResource(c) != null) { + for (ResourcePath res: c.getReferenceResources()) { + if (!refs.contains(res) && !depends.contains(res.getResourceHierarchy())) { + refs.add(res); + String refResName = langSpec.toComponentName(res.getLeafResourceName()); + component.addField(langSpec.newFieldDeclaration(new Type(refResName, refResName), res.getLeafResourceName())); + constructor.addParameter(langSpec.newVariableDeclaration(new Type(refResName, refResName), res.getLeafResourceName())); + constructor.getBody().addStatement(langSpec.getFieldAccessor(res.getLeafResourceName()) + langSpec.getAssignment() + res.getLeafResourceName() + langSpec.getStatementDelimiter()); + } + } + } + } + } + + protected MethodDeclaration getConstructor(TypeDeclaration component) { + for (MethodDeclaration m: component.getMethods()) { + if (m.isConstructor()) return m; + } + return null; + } + + protected 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) { + for (MethodDeclaration m: component.getMethods()) { + if (m.getName().equals(methodName)) return m; + } + return null; + } + + protected IResourceStateAccessor getPushAccessor() { + return new IResourceStateAccessor() { + @Override + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes= target.getResource(); + ResourcePath fromRes= from.getResource(); + if (targetRes.equals(fromRes)) { + return new Field(fieldOfResourceState, + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + // use the cached value as the current state + return new Field(targetRes.getLeafResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targerRes= target.getResource(); + return new Parameter(targerRes.getLeafResourceName(), + targerRes.getResourceStateType() != null ? targerRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { + if (fromRes != null && targetRes.equals(fromRes)) { + return new Field(fieldOfResourceState, + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + return new Parameter(targetRes.getLeafResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + }; + } + + protected IResourceStateAccessor getPullAccessor() { + return new IResourceStateAccessor() { + @Override + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes= target.getResource(); + if (from != null) { + ResourcePath fromRes= from.getResource(); + if (!target.isOutside()) { + return getDirectStateAccessorFor(targetRes, fromRes); + } + Term getter = null; + String targetComponentName = getComponentName(targetRes.getResourceHierarchy(), langSpec); + if (generatesComponent(targetRes.getResourceHierarchy())) { + getter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); + getter.addChild(new Field(langSpec.toVariableName(targetComponentName), targetRes.getResourceStateType())); + } else { + String parentName = langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy().getParent(), langSpec)); + Type parentType = targetRes.getResourceHierarchy().getParent().getResourceStateType(); + getter = new Term(new Symbol(getterPrefix + targetComponentName, 1, Symbol.Type.METHOD)); + getter.addChild(new Field(parentName, parentType)); + } + return getter; + } else { + return getDirectStateAccessorFor(targetRes, null); + } + } + + @Override + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes= target.getResource(); + if (from != null) { + ResourcePath fromRes= from.getResource(); + if (!target.isOutside()) { + return getDirectStateAccessorFor(targetRes, fromRes); + } + Term getter = null; + String targetComponentName = getComponentName(targetRes.getResourceHierarchy(), langSpec); + if (generatesComponent(targetRes.getResourceHierarchy())) { + getter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); + getter.addChild(new Field(langSpec.toVariableName(targetComponentName), targetRes.getResourceStateType())); + } else { + String parentName = langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy().getParent(), langSpec)); + Type parentType = targetRes.getResourceHierarchy().getParent().getResourceStateType(); + getter = new Term(new Symbol(getterPrefix + targetComponentName, 1, Symbol.Type.METHOD)); + getter.addChild(new Field(parentName, parentType)); + } + return getter; + } else { + return getDirectStateAccessorFor(targetRes, null); + } + } + + @Override + public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { + if (fromRes != null) { + if (targetRes.equals(fromRes)) { + return new Field(fieldOfResourceState, + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + Term getter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); + getter.addChild(new Field(targetRes.getLeafResourceName(), targetRes.getResourceStateType())); + return getter; + } else { + // access from the outside of the hierarchy + Stack pathStack = new Stack<>(); + ResourcePath curPath = targetRes; + do { + pathStack.push(curPath); + curPath = curPath.getParent(); + } while (curPath != null); + // iterate from the root resource + Term getter = null; + int v = 1; + int arity = 2; + while (!pathStack.empty()) { + curPath = pathStack.pop(); + String typeName = getComponentName(curPath.getResourceHierarchy(), langSpec); + if (getter == null) { + // root resource + String fieldName = langSpec.toVariableName(typeName); + getter = new Field(fieldName, new Type(typeName, typeName)); + } else { + 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) { + Variable var = null; + Expression param = curPath.getLastParam(); + if (param instanceof Variable) { + var = (Variable) param; + } else if (param instanceof Term) { + var = new Variable("v" + v, ((Term) param).getType()); + } + if (var != null) { + newGetter.addChild(var); + newGetter.getSymbol().setArity(2); + } + v++; + } + getter = newGetter; + } else { + if (curPath.getResourceHierarchy().getNumParameters() > 0) { + Variable var = null; + Expression param = curPath.getLastParam(); + if (param instanceof Variable) { + var = (Variable) param; + } else if (param instanceof Term) { + var = new Variable("v" + v, ((Term) param).getType()); + } + if (var != null) { + getter.getSymbol().setArity(arity); + getter.addChild(var); + } + v++; + } + } + arity = 2; + } else { + // to get a descendant resource directly. + if (arity == 2) { + Term newGetter = new Term(new Symbol(getterPrefix + typeName, -1, Symbol.Type.METHOD)); + newGetter.addChild(getter); + getter = newGetter; + } + if (curPath.getResourceHierarchy().getNumParameters() > 0) { + Variable var = null; + Expression param = curPath.getLastParam(); + if (param instanceof Variable) { + var = (Variable) param; + } else if (param instanceof Term) { + var = new Variable("v" + v, ((Term) param).getType()); + } + if (var != null) { + getter.getSymbol().setArity(arity); + getter.addChild(var); + arity++; + } + v++; + } + } + } + } + + if (generatesComponent(targetRes.getResourceHierarchy())) { + Term newGetter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); + newGetter.addChild(getter); + getter = newGetter; + } + return getter; + } + } + }; + } + + protected IResourceStateAccessor getRefAccessor() { + return new IResourceStateAccessor() { + @Override + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes= target.getResource(); + ResourcePath fromRes= from.getResource(); + if (targetRes.equals(fromRes)) { + return new Field(fieldOfResourceState, + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + // use the cached value as the current state + return new Parameter(targetRes.getLeafResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targerRes= target.getResource(); + return new Parameter(targerRes.getLeafResourceName(), + targerRes.getResourceStateType() != null ? targerRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { + if (fromRes != null && targetRes.equals(fromRes)) { + return new Field(fieldOfResourceState, + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + return new Parameter(targetRes.getLeafResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + }; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java b/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java new file mode 100644 index 0000000..f8ecbc6 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java @@ -0,0 +1,1770 @@ +package generators; + +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.Stack; +import java.util.AbstractMap.SimpleEntry; + +import code.ast.Block; +import code.ast.CompilationUnit; +import code.ast.FieldDeclaration; +import code.ast.MethodDeclaration; +import code.ast.TypeDeclaration; +import code.ast.VariableDeclaration; +import models.Edge; +import models.Node; +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Field; +import models.algebra.InvalidMessage; +import models.algebra.Parameter; +import models.algebra.ParameterizedIdentifierIsFutureWork; +import models.algebra.Position; +import models.algebra.Term; +import models.algebra.Type; +import models.algebra.UnificationFailed; +import models.algebra.ValueUndefined; +import models.algebra.Variable; +import models.dataConstraintModel.Channel; +import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.JsonAccessor; +import models.dataConstraintModel.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.PushPullAttribute; +import models.dataFlowModel.PushPullValue; +import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork; +import models.dataFlowModel.ResourceNode; +import models.dataFlowModel.ChannelNode; +import models.dataFlowModel.StoreAttribute; +import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor; + +public class CodeGeneratorFromDataFlowGraph extends CodeGenerator { + + public void generateCodeFromFlowGraph(DataTransferModel model, DataFlowGraph flowGraph, ArrayList components, Map> dependedRootComponentGraph, + TypeDeclaration mainComponent, MethodDeclaration mainConstructor, ArrayList codes, ILanguageSpecific langSpec) { + Map resourceComponents = new HashMap<>(); + Map resourceConstructors = new HashMap<>(); + List> constructorParams = new ArrayList<>(); + List> constructorStatements = new ArrayList<>(); + Map>> updateStatements = new HashMap<>(); + Map> descendantGetters = new HashMap<>(); + + // For each components (1st pass). + for (Node componentNode: components) { + ResourceNode resourceNode = (ResourceNode) componentNode; + TypeDeclaration component = null; + if (generatesComponent(resourceNode.getResourceHierarchy())) { + // A component will be generated for this resource. + String resourceName = getComponentName(resourceNode.getResourceHierarchy(), langSpec); + Type resStateType = getImplStateType(resourceNode.getResourceHierarchy(), langSpec); + component = resourceComponents.get(resourceNode.getResourceHierarchy()); + List depends = new ArrayList<>(); + if (component == null) { + // Add compilation unit for this component. + component = langSpec.newTypeDeclaration(resourceName); + resourceComponents.put(resourceNode.getResourceHierarchy(), component); + CompilationUnit cu = langSpec.newCompilationUnit(component); + codes.add(cu); + + // Declare the constructor. + MethodDeclaration constructor = declareConstructor(resourceNode, component, dependedRootComponentGraph, depends, langSpec); + + if (resourceNode.getResourceHierarchy().getParent() == null) { + // For each root resource + // Update the main component for this component. + updateMainComponent(mainComponent, mainConstructor, componentNode, constructor, depends, langSpec); + } + + // Declare the fields to refer to reference resources. + declareFieldsToReferenceResources(model, resourceNode, component, constructor, depends, langSpec); + + if (constructor.getParameters() == null || constructor.getParameters().size() == 0) { + component.removeMethod(constructor); + } else { + resourceConstructors.put(resourceNode.getResourceHierarchy(), constructor); + } + + // Declare the field to store the state in this resource. + if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { + declareStateField(resourceNode, resourceName, component, resStateType, constructorParams, langSpec); + } + + // Declare the getter method in this resource to obtain the state. + MethodDeclaration stateGetter = declareStateGetterMethod(resourceNode, component, resStateType, langSpec); + + // Declare the accessor method in the main component to call the getter method. + declareAccessorInMainComponent(mainComponent, resourceNode, stateGetter, langSpec); + } + if (component != null) { + // Declare the getter methods in this resource to obtain the descendant resources. + declareDescendantGetterMethods(resourceNode, component, descendantGetters, langSpec); + } + } + } + + // For each components (2nd pass). + for (Node componentNode: components) { + // Declare this resource. + ResourceNode resourceNode = (ResourceNode) componentNode; + Type resStateType = getImplStateType(resourceNode.getResourceHierarchy(), langSpec); + TypeDeclaration component = null; + TypeDeclaration parentComponent = null; + if (generatesComponent(resourceNode.getResourceHierarchy())) { + component = resourceComponents.get(resourceNode.getResourceHierarchy()); + } + if (resourceNode.getResourceHierarchy().getParent() != null) { + parentComponent = resourceComponents.get(resourceNode.getResourceHierarchy().getParent()); + } + + // Declare cache fields and update methods in this resource. + Map.Entry, Map>>> initStatementsAndUpdateUpdates = declareCacheFieldsAndUpdateMethods(resourceNode, component, parentComponent, langSpec); + if (component == null) { + // Constructor statements were not added to any component because no component had been generated. + for (String statement: initStatementsAndUpdateUpdates.getKey()) { + constructorStatements.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy().getParent(), statement)); + } + } + for (Map.Entry>> entry: initStatementsAndUpdateUpdates.getValue().entrySet()) { + updateStatements.put(entry.getKey(), entry.getValue()); + } + + // Declare the fields to refer to other resources in the parent/this component, and the state field in the parent component. + declareFieldsToReferToOtherResourcesAndStateFieldInParentComponent(resourceNode, component, parentComponent, constructorParams, langSpec); + + // Declare the getter method to obtain the resource state in an ancestor resource. + if (component == null) { + MethodDeclaration stateGetter = declareStateGetterMethodInAncestor(resourceNode, resourceComponents, resStateType, langSpec); + + if (stateGetter != null) { + // Declare the accessor method in the main component to call the getter method. + declareAccessorInMainComponent(mainComponent, resourceNode, stateGetter, langSpec); + } + } + + // Declare input methods in this component and the main component. + Map.Entry, Map>>> initStatementsAndInputUpdates = declareInputMethodsInThisAndMainComponents(resourceNode, component, parentComponent, mainComponent, model, langSpec); + if (component == null) { + // Constructor statements were not added to any component because no component had been generated. + for (String statement: initStatementsAndInputUpdates.getKey()) { + constructorStatements.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy().getParent(), statement)); + } + } + for (Map.Entry>> entry: initStatementsAndInputUpdates.getValue().entrySet()) { + updateStatements.put(entry.getKey(), entry.getValue()); + } + } + + // Add constructor parameters to the ancestor components. + for (ResourceNode root: flowGraph.getRootResourceNodes()) { + addConstructorParameters(root.getResourceHierarchy(), resourceComponents, resourceConstructors, constructorParams, langSpec); + } + + // Add constructor statements. + for (Map.Entry entry: constructorStatements) { + resourceConstructors.get(entry.getKey()).addStatement(entry.getValue()); + } + + // Add update statements. + for (MethodDeclaration method: updateStatements.keySet()) { + Expression updateExp = updateStatements.get(method).getKey(); + ResourceHierarchy 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, List> constructorParams, ILanguageSpecific langSpec) { + List params = new ArrayList<>(); + for (ResourceHierarchy child: resource.getChildren()) { + params.addAll(addConstructorParameters(child, resourceComponents, resourceConstructors, constructorParams, langSpec)); + } + for (Entry paramEnt: constructorParams) { + if (paramEnt.getKey().equals(resource)) { + params.add(paramEnt.getValue()); + } + } + if (params.size() > 0) { + MethodDeclaration constructor = resourceConstructors.get(resource); + if (constructor == null) { + if (resourceComponents.get(resource) != null) { + String resourceName = getComponentName(resource, langSpec); + constructor = langSpec.newMethodDeclaration(resourceName, true, null, null); + Block body = new Block(); + constructor.setBody(body); + resourceComponents.get(resource).addMethod(constructor); + resourceConstructors.put(resource, constructor); + } + } + if (constructor != null) { + for (VariableDeclaration param: params) { + boolean existsParam = false; + if (constructor.getParameters() != null) { + for (VariableDeclaration constParam: constructor.getParameters()) { + if (constParam.getName().equals(param.getName())) { + existsParam = true; + break; + } + } + } + if (!existsParam) { + constructor.addParameter(param); + constructor.getBody().addStatement(langSpec.getFieldAccessor(langSpec.toVariableName(param.getName())) + langSpec.getAssignment() + langSpec.toVariableName(param.getName()) + langSpec.getStatementDelimiter()); + } + } + } + } + if (resource.getNumParameters() > 0) params.clear(); + return params; + } + + private void addUpdateStatementWithConstructorInvocationToMethod(MethodDeclaration method, Expression exp, ResourceHierarchy resource, ResourceHierarchy descendantRes, TypeDeclaration descendantComponent, ILanguageSpecific langSpec) { + 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<>(); + for (VariableDeclaration var: childConstructor.getParameters()) { + JsonAccessor jsonMember = new JsonAccessor(DataConstraintModel.dot); + jsonMember.addChild(jsonTerm); + jsonMember.addChild(new Constant(var.getName(), DataConstraintModel.typeString)); + Expression param = jsonMember.reduce(); + if (param != null) { + if (param instanceof Term) { + if (((Term) param).getType() == null) { + ((Term) param).setType(var.getType()); + } + } else if (param instanceof Variable) { + if (((Variable) param).getType() == null) { + ((Variable) param).setType(var.getType()); + } + } + params.add(param.toImplementation(new String[] {""})); + } else { + params.add(var.getName()); + } + } + ((Term) exp).replaceSubTerm(termEnt.getKey(), new Constant(langSpec.getConstructorInvocation(replacingClassName, params))); + subTerms = ((Term) exp).getSubTerms(Term.class); + termEntItr = subTerms.entrySet().iterator(); + } 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]; + } else { + updateStatement = sideEffects[0] + langSpec.getFieldAccessor(fieldOfResourceState) + langSpec.getAssignment() + newState + langSpec.getStatementDelimiter(); + } + method.addFirstStatement(updateStatement); + } + + private MethodDeclaration declareConstructor(ResourceNode resourceNode, TypeDeclaration component, Map> dependedRootComponentGraph, + List depends, ILanguageSpecific langSpec) { + // Declare a constructor in each component. + String resourceName = getComponentName(resourceNode.getResourceHierarchy(), langSpec); + MethodDeclaration constructor = langSpec.newMethodDeclaration(resourceName, true, null, null); + Block block = new Block(); + constructor.setBody(block); + component.addMethod(constructor); + + for (Edge resToCh: resourceNode.getOutEdges()) { + DataTransferChannel ch = ((ChannelNode) resToCh.getDestination()).getChannel(); + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (resourceNode.getOutSideResources().contains(cm.getResource()) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + for (Edge chToRes: resToCh.getDestination().getOutEdges()) { + if (chToRes.getDestination() instanceof ResourceNode) { + ResourceHierarchy dstRes = ((ResourceNode) chToRes.getDestination()).getResourceHierarchy(); + // Check if the output resource is outside of the channel scope. + boolean outsideOutputResource = false; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (((ResourceNode) chToRes.getDestination()).getInSideResources().contains(cm.getResource()) && cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + if ((((PushPullAttribute) ((DataFlowEdge) resToCh).getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) { + // for PUSH transfer +// ResourceHierarchy dstRes = addReference(component, constructor, ((ResourceNode) chToRes.getDestination()).getOutSideResource().getResourceHierarchy(), langSpec); +// if (outsideOutputResource) { +// if (dstRes != null && dstRes.getParent() != null) { +// // Reference to root resource. +// addReference(component, constructor, dstRes.getRoot(), langSpec); +// } +// } + if (!generatesComponent(dstRes)) { + dstRes = dstRes.getParent(); + } + if (!depends.contains(dstRes)) depends.add(dstRes); + } + } + } + } + for (Edge chToRes: resourceNode.getInEdges()) { + for (Edge resToCh: chToRes.getSource().getInEdges()) { + ResourceHierarchy srcRes = ((ResourceNode) resToCh.getSource()).getResourceHierarchy(); + DataTransferChannel ch = ((ChannelNode) resToCh.getDestination()).getChannel(); + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (((ResourceNode) resToCh.getSource()).getOutSideResources().contains(cm.getResource()) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + // Check if the output resource is outside of the channel scope. + boolean outsideOutputResource = false; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (resourceNode.getInSideResources().contains(cm.getResource()) && cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + if ((((PushPullAttribute) ((DataFlowEdge) resToCh).getAttribute()).getOptions().get(0) != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { + // for PULL transfer +// srcRes = addReference(component, constructor, ((ResourceNode) resToCh.getSource()).getOutSideResource().getResourceHierarchy(), langSpec); +// if (outsideInputResource) { +// if (srcRes != null & srcRes.getParent() != null) { +// // Reference to root resource. +// addReference(component, constructor, srcRes.getRoot(), langSpec); +// } +// } + if (!generatesComponent(srcRes)) { + srcRes = srcRes.getParent(); + } + if (!depends.contains(srcRes)) depends.add(srcRes); + } + } + } + // Declare a field to refer to outside resources. + if (resourceNode.getParent() == null) { + for (ResourceHierarchy dependedRes: dependedRootComponentGraph.keySet()) { + for (ResourceHierarchy dependingRes: dependedRootComponentGraph.get(dependedRes)) { + if (resourceNode.getResourceHierarchy().equals(dependingRes)) { + // Declare a field to refer to outside resources. + depends.add(dependedRes); + addReference(component, constructor, dependedRes, langSpec); + } + } + } + } + return constructor; + } + + private void declareStateField(ResourceNode resourceNode, String resourceName, TypeDeclaration component, Type resStateType, List> constructorParams, ILanguageSpecific langSpec) { + Set children = resourceNode.getResourceHierarchy().getChildren(); + if (children == null || children.size() == 0) { + // leaf resource. + FieldDeclaration stateField = langSpec.newFieldDeclaration(resStateType, fieldOfResourceState, langSpec.getFieldInitializer(resStateType, resourceNode.getResourceHierarchy().getInitialValue())); + component.addField(stateField); + } else { + ResourceHierarchy child = children.iterator().next(); + if (children.size() == 1 && child.getNumParameters() > 0) { + // map or list. + FieldDeclaration stateField = langSpec.newFieldDeclaration(resStateType, fieldOfResourceState, langSpec.getFieldInitializer(resStateType, resourceNode.getResourceHierarchy().getInitialValue())); + component.addField(stateField); + } else { + // class + for (ResourceHierarchy c: children) { + String childTypeName = getComponentName(c, langSpec); + Type childType = null; + if (generatesComponent(c)) { + // The child has a component. + childType = new Type(childTypeName, childTypeName); + String fieldName = langSpec.toVariableName(childTypeName); + FieldDeclaration stateField = langSpec.newFieldDeclaration(childType, fieldName, langSpec.getConstructorInvocation(childTypeName, new ArrayList<>())); + component.addField(stateField); + } + } + } + } + } + + private void declareFieldsToReferToOtherResourcesAndStateFieldInParentComponent(ResourceNode resourceNode, TypeDeclaration component, TypeDeclaration parentComponent, List> constructorParams, ILanguageSpecific langSpec) { + // Declare reference fields for push data transfer. + boolean noPullTransfer = true; + for (Edge resToCh : resourceNode.getOutEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + // Check if the source resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (resourceNode.getOutSideResources().contains(cm.getResource()) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + for (Edge chToRes: re.getDestination().getOutEdges()) { + if (chToRes.getDestination() instanceof ResourceNode) { + ResourceHierarchy dstRes = ((ResourceNode) chToRes.getDestination()).getResourceHierarchy(); + // Check if the destination resource is outside of the channel scope. + boolean outsideOutputResource = false; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (((ResourceNode) chToRes.getDestination()).getInSideResources().contains(cm.getResource()) && cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + if ((((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) { + // Declare a field in the parent component to refer to the destination resource of push transfer. + if (!generatesComponent(dstRes)) { + dstRes = dstRes.getParent(); + } + String dstResName = getComponentName(dstRes, langSpec); + FieldDeclaration refFieldForPush = langSpec.newFieldDeclaration(new Type(dstResName, dstResName), langSpec.toVariableName(dstResName)); + VariableDeclaration refVarForPush = langSpec.newVariableDeclaration(new Type(dstResName, dstResName), langSpec.toVariableName(dstResName)); + if (component != null) { + // A component is created for this resource. + if (resourceNode.getResourceHierarchy() != dstRes) { + component.addField(refFieldForPush); + if (!outsideOutputResource) { + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy(), refVarForPush)); + } + } + } else { + // No component is created for this resource. + if (resourceNode.getParent().getResourceHierarchy() != dstRes) { + parentComponent.addField(refFieldForPush); + if (!outsideOutputResource) { + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), refVarForPush)); + } + } + } + if (outsideOutputResource) { + // When the reference to the destination resource can vary. + if (dstRes.getParent() != null) { + // Reference to its root resource. + String dstRootResName = getComponentName(dstRes.getRoot(), langSpec); + Type dstRootResType = new Type(dstRootResName, dstRootResName); + dstRootResName = langSpec.toVariableName(dstRootResName); + FieldDeclaration refRootFieldForPush = langSpec.newFieldDeclaration(dstRootResType, dstRootResName); + VariableDeclaration refRootVarForPush = langSpec.newVariableDeclaration(dstRootResType, dstRootResName); + if (component != null) { + // A component is created for this resource. + boolean existsField = false; + for (FieldDeclaration field: component.getFields()) { + if (dstRootResName.equals(field.getName())) { + existsField = true; + break; + } + } + if (!existsField) { + component.addField(refRootFieldForPush); + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy(), refRootVarForPush)); + } + } else { + // No component is created for this resource. + boolean existsField = false; + for (FieldDeclaration field: parentComponent.getFields()) { + if (dstRootResName.equals(field.getName())) { + existsField = true; + break; + } + } + if (!existsField) { + parentComponent.addField(refRootFieldForPush); + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), refRootVarForPush)); + } + } + } + } + } + } + } + } + // Declare reference fields for pull data transfer. + for (Edge chToRes : resourceNode.getInEdges()) { + for (Edge resToCh: chToRes.getSource().getInEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(ch); + // Check if the source resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().equals(srcRes) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + // Check if the output resource is outside of the channel scope. + boolean outsideOutputResource = false; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (resourceNode.getInSideResources().contains(cm.getResource()) && cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + if ((((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { + noPullTransfer = false; + // Declare a field in the parent/this component to refer to the source resource of pull transfer. + if (!generatesComponent(srcRes.getResourceHierarchy())) { + srcRes = srcRes.getParent(); + } + String srcResName = getComponentName(srcRes.getResourceHierarchy(), langSpec); + FieldDeclaration refFieldForPull = langSpec.newFieldDeclaration(new Type(srcResName, srcResName), langSpec.toVariableName(srcResName)); + VariableDeclaration refVarForPull = langSpec.newVariableDeclaration(new Type(srcResName, srcResName), langSpec.toVariableName(srcResName)); + if (component != null) { + // A component is created for this resource. + if (resourceNode.getResourceHierarchy() != srcRes.getResourceHierarchy()) { + component.addField(refFieldForPull); + if (!outsideInputResource) { + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy(), refVarForPull)); + } + } + } else { + // No component is created for this resource. + if (resourceNode.getParent().getResourceHierarchy() != srcRes.getResourceHierarchy()) { + parentComponent.addField(refFieldForPull); + if (!outsideInputResource) { + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), refVarForPull)); + } + } + } + if (outsideInputResource) { + // When the reference to the source resource can vary. + if (srcRes.getParent() != null) { + // Reference to its root resource. + String srcRootResName = getComponentName(srcRes.getRoot().getResourceHierarchy(), langSpec); + Type srcRootResType = new Type(srcRootResName, srcRootResName); + srcRootResName = langSpec.toVariableName(srcRootResName); + FieldDeclaration refRootFieldForPull = langSpec.newFieldDeclaration(srcRootResType, srcRootResName); + VariableDeclaration refRootVarForPull = langSpec.newVariableDeclaration(srcRootResType, srcRootResName); + if (component != null) { + // A component is created for this resource. + boolean existsField = false; + for (FieldDeclaration field: component.getFields()) { + if (srcRootResName.equals(field.getName())) { + existsField = true; + break; + } + } + if (!existsField) { + component.addField(refRootFieldForPull); + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy(), refRootVarForPull)); + } + } else { + // No component is created for this resource. + boolean existsField = false; + for (FieldDeclaration field: parentComponent.getFields()) { + if (srcRootResName.equals(field.getName())) { + existsField = true; + break; + } + } + if (!existsField) { + parentComponent.addField(refRootFieldForPull); + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), refRootVarForPull)); + } + } + } + } + } + } + } + // Declare the state field in the parent component. + if (component == null) { + ResourceHierarchy res = resourceNode.getResourceHierarchy(); + if (((StoreAttribute) resourceNode.getAttribute()).isStored() && noPullTransfer && res.getNumParameters() == 0) { + String resName = langSpec.toVariableName(getComponentName(res, langSpec)); + boolean existsField = false; + for (FieldDeclaration field: parentComponent.getFields()) { + if (resName.equals(field.getName())) { + existsField = true; + break; + } + } + if (!existsField) { + FieldDeclaration stateField = langSpec.newFieldDeclaration(res.getResourceStateType(), resName); + parentComponent.addField(stateField); + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), langSpec.newVariableDeclaration(res.getResourceStateType(), resName))); + } + } + } + } + + + private MethodDeclaration declareStateGetterMethod(ResourceNode resourceNode, TypeDeclaration component, Type resStateType, ILanguageSpecific langSpec) { + // Declare the getter method of the resource state. + MethodDeclaration stateGetter = langSpec.newMethodDeclaration(getterOfResourceState, resStateType); + component.addMethod(stateGetter); + + if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { + fillStateGetterMethod(stateGetter, resourceNode.getResourceHierarchy(), resStateType, langSpec); + } else { + // invocations to other getter methods when at least one incoming data-flow edges is PULL-style. + addOtherGetterInvocationsToStateGatter(stateGetter, resourceNode, langSpec); + } + + return stateGetter; + } + + private MethodDeclaration declareStateGetterMethodInAncestor(ResourceNode resourceNode, Map resourceComponents, Type resStateType, ILanguageSpecific langSpec) { + // Search an ancestor in which the getter method is declared. + ResourceNode ancestorNode = resourceNode; + Stack ancestors = new Stack<>(); + do { + ancestors.push(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); + } + + if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { + fillDescendantGetterMethod(stateGetter, resourceNode.getResourceHierarchy(), ancestorNode.getResourceHierarchy(), ancestorComponent, langSpec); + } else { + addOtherGetterInvocationsToStateGatter(stateGetter, resourceNode, langSpec); + } + + return stateGetter; + } + + private void addOtherGetterInvocationsToStateGatter(MethodDeclaration stateGetter, ResourceNode resourceNode, + ILanguageSpecific langSpec) { + boolean isContainedPush = false; + DataTransferChannel ch = null; + HashMap inputResourceToStateAccessor = new HashMap<>(); + for (Edge chToRes: resourceNode.getInEdges()) { + DataTransferChannel ch2 = ((ChannelNode) chToRes.getSource()).getChannel(); + for (Edge resToCh: chToRes.getSource().getInEdges()) { + DataFlowEdge dIn = (DataFlowEdge) resToCh; + ChannelMember in = null; + for (ChannelMember cm: ch2.getInputChannelMembers()) { + if (((ResourceNode) dIn.getSource()).getOutSideResources().contains(cm.getResource())) { + in = cm; + break; + } + } + if (((PushPullAttribute) dIn.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { + // PUSH transfer + isContainedPush = true; + inputResourceToStateAccessor.put(in, getPushAccessor()); + } else { + // PULL transfer + inputResourceToStateAccessor.put(in, getPullAccessor()); + ch = ((ChannelNode) resToCh.getDestination()).getChannel(); // pull containing input side channel is always one. + } + } + } + // for reference channel members. + for (ChannelMember c: ch.getReferenceChannelMembers()) { + inputResourceToStateAccessor.put(c, getPullAccessor()); // by pull data transfer + } + + // generate a return statement. + try { + for (ChannelMember out: ch.getOutputChannelMembers()) { + if (resourceNode.getInSideResources().contains(out.getResource())) { + String[] sideEffects = new String[] {""}; + if (!isContainedPush) { + // All incoming edges are in PULL-style. + String curState = ch.deriveUpdateExpressionOf(out, getPullAccessor()).getKey().toImplementation(sideEffects); + stateGetter.addStatement(sideEffects[0] + langSpec.getReturnStatement(curState) + langSpec.getStatementDelimiter()); + } else { + // At least one incoming edge is in PUSH-style. + String curState = ch.deriveUpdateExpressionOf(out, getPullAccessor(), inputResourceToStateAccessor).getKey().toImplementation(sideEffects); + stateGetter.addStatement(sideEffects[0] + langSpec.getReturnStatement(curState) + langSpec.getStatementDelimiter()); + } + break; + } + } + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork + | InvalidMessage | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + } + + private void 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(), 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, ILanguageSpecific langSpec) { + // Declare cash fields and update methods in the component. + String resComponentName = getComponentName(resourceNode.getResourceHierarchy(), langSpec); + List constructorStatements = new ArrayList<>(); + Map>> updateStatements = new HashMap<>(); + for (Edge chToRes: resourceNode.getInEdges()) { + for (Edge resToCh: chToRes.getSource().getInEdges()) { + DataTransferChannel ch = ((ChannelNode) resToCh.getDestination()).getChannel(); + DataFlowEdge re = (DataFlowEdge) resToCh; + ResourcePath srcResPath = ((ResourceNode) re.getSource()).getOutSideResource(ch); + String srcResComponentName = getComponentName(srcResPath.getResourceHierarchy(), langSpec); + String srcResName = langSpec.toVariableName(srcResComponentName); + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().equals(srcResPath) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + // Check if the output resource is outside of the channel scope. + boolean outsideOutputResource = false; + ResourcePath dstResPath = null; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (resourceNode.getInSideResources().contains(cm.getResource())) { + dstResPath = cm.getResource(); + if (cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + } + if ((((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) { + // for push data transfer + + // Declare an update method in the type of the destination resource. + ArrayList parameters = new ArrayList<>(); + int v = 1; + for (Expression exp: dstResPath.getPathParams()) { + if (exp instanceof Variable) { + Variable pathVar = (Variable) exp; + String varName = "self" + (v > 1 ? v : ""); +// String varName = pathVar.getName(); + VariableDeclaration pathParam = langSpec.newVariableDeclaration(pathVar.getType(), varName); + parameters.add(pathParam); // A path parameter to identify the self resource. + } + v++; + } + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + VariableDeclaration chParam = langSpec.newVariableDeclaration(selVar.getType(), selVar.getName()); + parameters.add(chParam); // A channel parameter to specify the context of the collaboration. + } + } + parameters.add(langSpec.newVariableDeclaration(srcResPath.getResourceStateType(), srcResPath.getLeafResourceName())); // The state of the source resource to carry the data-flow. + // For the refs. + for (ResourcePath ref: ch.getReferenceResources()) { + if (!resourceNode.getInSideResources().contains(ref)) { + parameters.add(langSpec.newVariableDeclaration(ref.getResourceStateType(), ref.getLeafResourceName())); + } + } + MethodDeclaration update = null; + if (component != null) { + for (MethodDeclaration method: component.getMethods()) { + if (method.getName().equals(updateMethodPrefix + from + srcResComponentName)) { + update = method; + break; + } + } + if (update == null) { + update = langSpec.newMethodDeclaration(updateMethodPrefix + from + srcResComponentName, false, null, parameters); + component.addMethod(update); + } + } else if (parentComponent != null) { + for (MethodDeclaration method: parentComponent.getMethods()) { + if (method.getName().equals(updateMethodPrefix + resComponentName + from + srcResComponentName)) { + update = method; + break; + } + } + if (update == null) { + update = langSpec.newMethodDeclaration(updateMethodPrefix + resComponentName + from + srcResComponentName, false, null, parameters); + parentComponent.addMethod(update); + } + } + + // Add a statement to update the state field + if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { + try { + for (ChannelMember out: ch.getOutputChannelMembers()) { + if (resourceNode.getInSideResources().contains(out.getResource())) { + Expression updateExp = null; + if (ch.getReferenceChannelMembers().size() == 0) { + updateExp = ch.deriveUpdateExpressionOf(out, getPushAccessor()).getKey(); + } else { + // if there exists one or more reference channel member. + HashMap inputResourceToStateAccessor = new HashMap<>(); + for (ChannelMember in: ch.getInputChannelMembers()) { + inputResourceToStateAccessor.put(in, getPushAccessor()); + } + for (ChannelMember c: ch.getReferenceChannelMembers()) { + inputResourceToStateAccessor.put(c, getRefAccessor()); + } + updateExp = ch.deriveUpdateExpressionOf(out, getPushAccessor(), inputResourceToStateAccessor).getKey(); + } + // Replace Json constructor with a constructor of the child resource. + ResourceHierarchy outRes = out.getResource().getResourceHierarchy(); + if (outRes.getChildren().size() == 1 && outRes.getChildren().iterator().next().getNumParameters() > 0) { + ResourceHierarchy 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); + } + // Add statements to the update method. + String[] sideEffects = new String[] {""}; + String newState = updateExp.toImplementation(sideEffects); + int numOfOutResourcesWithTheSameHierarchy = 0; + for (ResourcePath outResPath: ch.getOutputResources()) { + if (outResPath.getResourceHierarchy().equals(outRes)) { + numOfOutResourcesWithTheSameHierarchy++; + } + } + String updateStatement = ""; + if (generatesComponent(outRes)) { + if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { + updateStatement = sideEffects[0]; + } else { + updateStatement = sideEffects[0] + langSpec.getFieldAccessor(fieldOfResourceState) + langSpec.getAssignment() + newState + langSpec.getStatementDelimiter(); // this.value = ... + } + } else { + if (sideEffects[0] != null) { + updateStatement = sideEffects[0]; + String resourceName = langSpec.toVariableName(getComponentName(outRes, langSpec)); + updateStatement = updateStatement.replace(langSpec.getFieldAccessor(fieldOfResourceState), langSpec.getFieldAccessor(resourceName)); + } + if (DataConstraintModel.typeList.isAncestorOf(resourceNode.getParent().getResourceStateType())) { + Term selector = new Term(DataConstraintModel.set); + selector.addChild(new Constant(langSpec.getFieldAccessor(fieldOfResourceState))); + selector.addChild(new Variable(update.getParameters().get(update.getParameters().size() - 2).getName())); + selector.addChild(new Constant(newState)); + String[] sideEffects2 = new String[] {""}; + String newList = selector.toImplementation(sideEffects2); + updateStatement += sideEffects2[0]; + } else if (DataConstraintModel.typeMap.isAncestorOf(resourceNode.getParent().getResourceStateType())) { + Term selector = new Term(DataConstraintModel.insert); + selector.addChild(new Constant(langSpec.getFieldAccessor(fieldOfResourceState))); + selector.addChild(new Variable(update.getParameters().get(update.getParameters().size() - 2).getName())); + selector.addChild(new Constant(newState)); + String[] sideEffects2 = new String[] {""}; + String newMap = selector.toImplementation(sideEffects2); + updateStatement += sideEffects2[0]; + } else if (!(updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect())) { + String resourceName = langSpec.toVariableName(getComponentName(outRes, langSpec)); + updateStatement += langSpec.getFieldAccessor(resourceName) + langSpec.getAssignment() + newState + langSpec.getStatementDelimiter(); + } + } + if (numOfOutResourcesWithTheSameHierarchy == 1) { + update.addFirstStatement(updateStatement); + } else { + Term conditions = null; + int i = 1; + Map>> resourcePaths = ch.fillOutsideResourcePaths(out, JerseyCodeGenerator.pushAccessor); + for (Expression pathParam: out.getResource().getPathParams()) { + if (pathParam instanceof Variable) { + String selfParamName = ((Variable) pathParam).getName(); + Expression arg = null; + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + if (selVar.getName().equals(selfParamName)) { + arg = selVar; + break; + } + } + } + if (arg == null) { + ResourcePath filledPath = resourcePaths.get(out).getKey(); + arg = filledPath.getPathParams().get(i - 1); + } + Term condition = new Term(DataConstraintModel.eq, new Expression[] { + new Parameter("self" + (i > 1 ? i : ""), 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)); + } + break; + } + } + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork + | InvalidMessage | UnificationFailed | ValueUndefined e1) { + e1.printStackTrace(); + } + } + + // Declare the field to cache the state of the source resource in the type of the destination resource. + if (resToCh.getDestination().getIndegree() > 1 + || (resToCh.getDestination().getIndegree() == 1 && ch.getInputChannelMembers().iterator().next().getStateTransition().isRightPartial())) { + // If incoming edges are multiple, or the current state of an input member is needed. + if (langSpec.declareField()) { + // Declare the cache field. + FieldDeclaration cacheField = langSpec.newFieldDeclaration( + srcResPath.getResourceStateType(), + srcResPath.getLeafResourceName(), + langSpec.getFieldInitializer(srcResPath.getResourceStateType(), srcResPath.getResourceHierarchy().getInitialValue())); + if (component != null) { + component.addField(cacheField); + } else if (parentComponent != null){ + parentComponent.addField(cacheField); + } + + } + // Update the cache field. + String cacheStatement = langSpec.getFieldAccessor(langSpec.toVariableName(srcResName)) + langSpec.getAssignment() + langSpec.toVariableName(srcResName) + langSpec.getStatementDelimiter(); + if (update.getBody() == null || !update.getBody().getStatements().contains(cacheStatement)) { + update.addStatement(cacheStatement); + } + } + + Set outsideInputMembers = new HashSet<>(); + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.isOutside()) { + outsideInputMembers.add(cm); + } + } + if (outsideInputMembers.size() > 0) { + Map>> resourcePaths = null; + for (ChannelMember out: ch.getOutputChannelMembers()) { + if (resourceNode.getInSideResources().contains(out.getResource())) { + try { + resourcePaths = ch.fillOutsideResourcePaths(out, getPullAccessor()); + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork + | InvalidMessage | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + break; + } + } + if (resourcePaths != null && resourcePaths.size() > 0) { + for (ChannelMember outsideMember: outsideInputMembers) { + for (ChannelMember dependingMember: resourcePaths.get(outsideMember).getValue()) { + if (dependingMember.getResource().equals(srcResPath)) { + // An outside input resource path depends on srcRes. + ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey(); + String outsideResName = langSpec.toVariableName(getComponentName(outsidePath.getResourceHierarchy(), langSpec)); + Expression outsideExp = getPullAccessor().getDirectStateAccessorFor(outsidePath, null); + if (generatesComponent(outsidePath.getResourceHierarchy())) { + outsideExp = ((Term) outsideExp).getChild(0); + } + Expression nextExp = dependingMember.getStateTransition().getNextStateExpression(); + if (nextExp != null && outsideExp instanceof Term) { + if (nextExp instanceof Variable) { + outsideExp = ((Term) outsideExp).substitute((Variable) nextExp, new Field(langSpec.toVariableName(getComponentName(dependingMember.getResource().getResourceHierarchy(), langSpec)))); + } else { + // ToDo. + } + } + String[] sideEffects = new String[] {""}; + String outsideAccessor = outsideExp.toImplementation(sideEffects); + String updateReference = langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter(); + update.addStatement(updateReference); // Update the reference field. + // Update constructor. + if (component != null) { + MethodDeclaration constructor = getConstructor(component); + constructor.addStatement(updateReference); // Initialize the reference field. + } else if (parentComponent != null){ + constructorStatements.add(updateReference); + } + } + } + } + } + } + + // Add an invocation to another update method (for a chain of update method invocations). + for (Edge resToCh2: resourceNode.getOutEdges()) { + DataFlowEdge dOut = (DataFlowEdge) resToCh2; + DataTransferChannel ch2 = ((ChannelNode) resToCh2.getDestination()).getChannel(); + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource2 = false; + ChannelMember in = null; + Set outsideInputMembers2 = new HashSet<>(); + for (ChannelMember cm: ch2.getInputChannelMembers()) { + if (resourceNode.getOutSideResources().contains(cm.getResource())) { + if (cm.isOutside()) { + outsideInputResource2 = true; // Regarded as pull transfer. + } + in = cm; + } + if (cm.isOutside()) { + outsideInputMembers2.add(cm); + } + } + for (Edge chToRes2: resToCh2.getDestination().getOutEdges()) { + ResourceNode dstNode = ((ResourceNode) chToRes2.getDestination()); + // Check if the output resource is outside of the channel scope. + boolean outsideOutputResource2 = false; + ChannelMember out = null; + for (ChannelMember cm: ch2.getOutputChannelMembers()) { + if (dstNode.getInSideResources().contains(cm.getResource())) { + out = cm; + if (cm.isOutside()) { + outsideOutputResource2 = true; + break; + } + } + } + if ((((PushPullAttribute) dOut.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource2) || outsideOutputResource2) { + // PUSH transfer + List params = new ArrayList<>(); + // Values of path parameters. + for (Expression pathParam: out.getResource().getPathParams()) { + if (pathParam instanceof Variable) { + Variable pathVar = (Variable) pathParam; + params.add(pathVar.getName()); + } + } + // Values of channel parameters. + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + params.add(selVar.getName()); + } + } + // Value of the source side (input side) resource. + ResourceHierarchy srcRes2 = resourceNode.getResourceHierarchy(); + if (generatesComponent(srcRes2)) { + params.add(langSpec.getFieldAccessor(fieldOfResourceState)); + } else { + params.add(langSpec.getFieldAccessor(langSpec.toVariableName(srcRes2.getResourceName()))); + srcRes2 = srcRes2.getParent(); + } + Map> referredResources = new HashMap<>(); + Set referredSet = referredResources.get(update); + for (ChannelMember rc: ch2.getReferenceChannelMembers()) { + // to get the value of reference member. + ResourcePath ref = rc.getResource(); + if (referredSet == null) { + referredSet = new HashSet<>(); + referredResources.put(update, referredSet); + } + if (!resourceNode.getInSideResources().contains(ref)) { + String refVarName = ref.getLeafResourceName(); + if (!referredSet.contains(ref)) { + referredSet.add(ref); + Expression refGetter = getPullAccessor().getCurrentStateAccessorFor(rc, in); + String[] sideEffects = new String[] {""}; + String refExp = refGetter.toImplementation(sideEffects); + String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); + update.addStatement(sideEffects[0] + langSpec.getVariableDeclaration(refTypeName, refVarName) + langSpec.getAssignment() + refExp + langSpec.getStatementDelimiter()); + } + params.add(refVarName); + } + } + String updateMethodName = null; + ResourceHierarchy dstRes = dstNode.getResourceHierarchy(); + if (!generatesComponent(dstRes)) { + updateMethodName = updateMethodPrefix + getComponentName(dstRes, langSpec) + from + resComponentName; + dstRes = dstRes.getParent(); + } else { + updateMethodName = updateMethodPrefix + from + resComponentName; + } + String dstCompName = langSpec.toVariableName(getComponentName(dstRes, langSpec)); + if (!outsideOutputResource2) { + // The destination resource is not outside. + if (srcRes2 != dstRes) { + update.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, params) + + langSpec.getStatementDelimiter()); // this.dst.updateDstFromSrc(value, refParams); + } else { + update.addStatement(langSpec.getMethodInvocation(updateMethodName, params) + + langSpec.getStatementDelimiter()); // this.updateDstFromSrc(value, refParams); + } + } else { + // Use the reference field to refer to outside destination resource. + update.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, params) + + langSpec.getStatementDelimiter()); // this.dst.updateDstFromSrc(value, refParams); + } + } + } + if (outsideInputMembers2.size() > 0) { + if (!generatesComponent(resourceNode.getResourceHierarchy())) { + // srcRes2 does not have a component. + ResourcePath srcRes2 = resourceNode.getOutSideResource(ch2); + for (ChannelMember out: ch2.getOutputChannelMembers()) { + if (!generatesComponent(out.getResource().getResourceHierarchy())) { + // Also dstRes2 does not have a component. + ResourcePath dstRes2 = out.getResource(); + if (srcRes2.getParent().equals(dstRes2.getParent())) { + Map>> resourcePaths = null; + try { + resourcePaths = ch2.fillOutsideResourcePaths(out, getPullAccessor()); + if (resourcePaths != null && resourcePaths.size() > 0) { + for (ChannelMember outsideMember: outsideInputMembers2) { + for (ChannelMember dependingMember: resourcePaths.get(outsideMember).getValue()) { + if (dependingMember.getResource().equals(srcRes2)) { + // An outside input resource path depends on srcRes. + ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey(); + if (!generatesComponent(outsidePath.getResourceHierarchy())) { + outsidePath = outsidePath.getParent(); + } + String outsideResName = langSpec.toVariableName(getComponentName(outsidePath.getResourceHierarchy(), langSpec)); + Expression outsideExp = getPullAccessor().getDirectStateAccessorFor(outsidePath, null); + if (generatesComponent(outsidePath.getResourceHierarchy())) { + outsideExp = ((Term) outsideExp).getChild(0); + } + Expression nextExp = dependingMember.getStateTransition().getNextStateExpression(); + if (nextExp != null && outsideExp instanceof Term) { + if (nextExp instanceof Variable) { + outsideExp = ((Term) outsideExp).substitute((Variable) nextExp, new Field(langSpec.toVariableName(getComponentName(dependingMember.getResource().getResourceHierarchy(), langSpec)))); + } else { + // ToDo. + } + } + String[] sideEffects = new String[] {""}; + String outsideAccessor = outsideExp.toImplementation(sideEffects); + String updateReference = langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter(); + update.addStatement(updateReference); // Update the reference field. + // Update constructor. + if (component != null) { + MethodDeclaration constructor = getConstructor(component); + constructor.addStatement(updateReference); // Initialize the reference field. + } else if (parentComponent != null) { + constructorStatements.add(updateReference); + } + } + } + } + } + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork + | InvalidMessage | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + } + } + } + } + } + } + } + } + } + return new AbstractMap.SimpleEntry<>(constructorStatements, updateStatements); + } + + private Map.Entry, Map>>> declareInputMethodsInThisAndMainComponents(ResourceNode resourceNode, TypeDeclaration component, + TypeDeclaration parentComponent, TypeDeclaration mainComponent, DataTransferModel model, ILanguageSpecific langSpec) { + // Declare input methods. + String resName = resourceNode.getResourceName(); + String resComponentName = langSpec.toComponentName(resName); + List constructorStatements = new ArrayList<>(); + Map>> inputStatements = new HashMap<>(); + for (Channel ch: model.getInputChannels()) { + for (ChannelMember out: ((DataTransferChannel) ch).getOutputChannelMembers()) { + if (resourceNode.getInSideResources().contains(out.getResource())) { + Expression message = out.getStateTransition().getMessageExpression(); + MethodDeclaration input = null; + MethodDeclaration inputAccessor = null; + if (message instanceof Term) { + // Declare an input method in this component. + ArrayList resInputParams = new ArrayList<>(); + ArrayList mainInputParams = new ArrayList<>(); + // The path parameters are not to be passed to the input method of each resource (resInputParams) + // because they are always equal to either channel selectors or message parameters. + + // Channel parameters to specify the context of the collaboration. + int v = 1; + for (Selector selector: ch.getSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + resInputParams.add(langSpec.newVariableDeclaration(selVar.getType(), selVar.getName())); + mainInputParams.add(langSpec.newVariableDeclaration(selVar.getType(), selVar.getName())); + } else if (selector.getExpression() instanceof Term) { + Term var = (Term) selector.getExpression(); + resInputParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v)); + mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + if (ch.getParent() != null) { + for (Selector selector: ch.getParent().getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + mainInputParams.add(langSpec.newVariableDeclaration(selVar.getType(), selVar.getName())); + } else if (selector.getExpression() instanceof Term) { + Term var = (Term) selector.getExpression(); + mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + } + // Message parameters to carry the data-flows. + for (Map.Entry varEnt: message.getVariables().entrySet()) { + Variable var = varEnt.getValue(); + String refVarName = null; + for (ChannelMember refCm: ((DataTransferChannel) ch).getReferenceChannelMembers()) { + Expression varExp = refCm.getStateTransition().getMessageExpression().getSubTerm(varEnt.getKey()); + if (varExp != null && varExp instanceof Variable) { + if (refCm.getStateTransition().getCurStateExpression().contains(varExp)) { + refVarName = refCm.getResource().getLeafResourceName(); + break; + } + } + } + if (refVarName != null) { + // var has come from a reference resource. + resInputParams.add(langSpec.newVariableDeclaration(var.getType(), refVarName)); + } else { + // var has not come from a reference resource. + resInputParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + boolean bExists = false; + for (VariableDeclaration mainParam: mainInputParams) { + if (mainParam.getName().equals(var.getName()) ) { + bExists = true; + break; + } + } + if (!bExists) { + mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + } + } + } + String inputMethodName = ((Term) message).getSymbol().getImplName(); + if (((DataTransferChannel) ch).getOutputChannelMembers().size() > 1) { + inputMethodName += _for + getComponentName(out.getResource().getResourceHierarchy(), langSpec); + } + if (component != null) { + // A component is created for this resource. + for (MethodDeclaration method: component.getMethods()) { + if (method.getName().equals(inputMethodName)) { + input = method; + break; + } + } + if (input == null) { + input = langSpec.newMethodDeclaration(inputMethodName, false, null, resInputParams); + component.addMethod(input); + } + } else if (parentComponent != null) { + // No component is created for this resource. + for (MethodDeclaration method: parentComponent.getMethods()) { + if (method.getName().equals(inputMethodName)) { + input = method; + break; + } + } + if (input == null) { + input = langSpec.newMethodDeclaration(inputMethodName, false, null, resInputParams); + parentComponent.addMethod(input); + } + } + + // Declare the accessor in the main component to call the input method. + String messageSymbol = ((Term) message).getSymbol().getImplName(); + inputAccessor = getMethod(mainComponent, messageSymbol); + if (inputAccessor == null) { + inputAccessor = langSpec.newMethodDeclaration(messageSymbol, false, null, mainInputParams); + mainComponent.addMethod(inputAccessor); + } else { + // Add type to a parameter without type. + if (inputAccessor.getParameters() != null) { + for (VariableDeclaration param: inputAccessor.getParameters()) { + if (param.getType() == null) { + for (VariableDeclaration p: mainInputParams) { + if (param.getName().equals(p.getName()) && p.getType() != null) { + param.setType(p.getType()); + } + } + } + } + } + } + } else if (message instanceof Variable) { + // Declare an input method in this component. + ArrayList resInputParams = new ArrayList<>(); + ArrayList mainInputParams = new ArrayList<>(); + int v = 1; + if (out.getResource().getLastParam() != null) { + Expression param = out.getResource().getLastParam(); + if (param instanceof Variable) { + Variable var = (Variable) param; + resInputParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + } else if (param instanceof Term) { + Term var = (Term) param; + resInputParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v)); + mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + if (out.getResource().getParent() != null) { + for (Expression param: out.getResource().getParent().getPathParams()) { + if (param instanceof Variable) { + Variable var = (Variable) param; + mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + } else if (param instanceof Term) { + Term var = (Term) param; + mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + } + String inputMethodName = ((Variable) message).getName(); + if (((DataTransferChannel) ch).getOutputChannelMembers().size() > 1) { + inputMethodName += _for + getComponentName(out.getResource().getResourceHierarchy(), langSpec); + } + if (component != null) { + // A component is created for this resource. + for (MethodDeclaration method: component.getMethods()) { + if (method.getName().equals(inputMethodName)) { + input = method; + break; + } + } + if (input == null) { + if (resInputParams.size() == 0) { + input = langSpec.newMethodDeclaration(inputMethodName, null); + } else { + input = langSpec.newMethodDeclaration(inputMethodName, false, null, resInputParams); + } + component.addMethod(input); + } + } else if (parentComponent != null) { + // No component is created for this resource. + for (MethodDeclaration method: parentComponent.getMethods()) { + if (method.getName().equals(inputMethodName)) { + input = method; + break; + } + } + if (input == null) { + if (resInputParams.size() == 0) { + input = langSpec.newMethodDeclaration(inputMethodName, null); + } else { + input = langSpec.newMethodDeclaration(inputMethodName, false, null, resInputParams); + } + parentComponent.addMethod(input); + } + } + + // Declare the accessor in the main component to call the input method. + String messageSymbol = ((Variable) message).getName(); + inputAccessor = getMethod(mainComponent, messageSymbol); + if (inputAccessor == null) { + if (mainInputParams.size() == 0) { + inputAccessor = langSpec.newMethodDeclaration(messageSymbol, null); + } else { + inputAccessor = langSpec.newMethodDeclaration(messageSymbol, false, null, mainInputParams); + } + mainComponent.addMethod(inputAccessor); + } + } + + // Add an invocation to the accessor method. + if (inputAccessor != null) { + for (ChannelMember rc: ((DataTransferChannel) ch).getReferenceChannelMembers()) { + // For each reference channel member, get the current state of the reference side resource by pull data transfer. + ResourcePath ref = rc.getResource(); + if (!out.getResource().equals(ref)) { + String refVarName = ref.getLeafResourceName(); + Expression refGetter = getPullAccessor().getDirectStateAccessorFor(ref, null); + String[] sideEffects = new String[] {""}; + String refExp = refGetter.toImplementation(sideEffects); + String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); + inputAccessor.addFirstStatement(sideEffects[0] + refTypeName + " " + refVarName + " = " + refExp + ";"); + } + } + Expression resExp = getPullAccessor().getDirectStateAccessorFor(out.getResource(), null); + List args = new ArrayList<>(); + if (resExp instanceof Term) { + // to access the parent + if (((Term) resExp).getChildren().size() > 1 && ((Term) resExp).getChild(1) instanceof Variable) { + args.add(((Variable)((Term) resExp).getChild(1)).getName()); + } + resExp = ((Term) resExp).getChild(0); + } + String resourceAccess = resExp.toImplementation(new String[] {""}); + // Values of channel parameters. + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + if (!args.contains(selVar.getName())) { + args.add(selVar.getName()); + } + } + } + // Values of message parameters. + if (message instanceof Term) { + for (Map.Entry varEnt: message.getVariables().entrySet()) { + String refVarName = null; + for (ChannelMember rc: ((DataTransferChannel) ch).getReferenceChannelMembers()) { + Expression varExp = rc.getStateTransition().getMessageExpression().getSubTerm(varEnt.getKey()); + if (varExp != null && rc.getStateTransition().getCurStateExpression().contains(varExp)) { + refVarName = rc.getResource().getLeafResourceName(); + break; + } + } + if (refVarName != null) { + if (!args.contains(refVarName)) { + args.add(refVarName); + } + } else { + if (!args.contains(varEnt.getValue().getName())) args.add(varEnt.getValue().getName()); + } + } + } + inputAccessor.addStatement(langSpec.getMethodInvocation(resourceAccess, input.getName(), args) + langSpec.getStatementDelimiter()); + } + + if (input != null) { + // Add a statement to update the state field to the input method. + try { + Expression updateExp = ((DataTransferChannel) ch).deriveUpdateExpressionOf(out, getRefAccessor()).getKey(); + // Replace Json constructor with a constructor of 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]; + } else { + updateStatement = sideEffects[0] + langSpec.getFieldAccessor(fieldOfResourceState) + langSpec.getAssignment() + newState + langSpec.getStatementDelimiter(); + } + input.addFirstStatement(updateStatement); + } else { + String updateStatement = ""; + if (sideEffects[0] != null) { + updateStatement = sideEffects[0]; + String resourceName = langSpec.toVariableName(getComponentName(resource, langSpec)); + updateStatement = updateStatement.replace(langSpec.getFieldAccessor(fieldOfResourceState), langSpec.getFieldAccessor(resourceName)); + } + if (DataConstraintModel.typeList.isAncestorOf(resourceNode.getParent().getResourceStateType())) { + Term selector = new Term(DataConstraintModel.set); + selector.addChild(new Constant(langSpec.getFieldAccessor(fieldOfResourceState))); + selector.addChild(new Variable(input.getParameters().get(input.getParameters().size() - 2).getName())); + selector.addChild(new Constant(newState)); + String[] sideEffects2 = new String[] {""}; + String newList = selector.toImplementation(sideEffects2); + updateStatement += sideEffects2[0]; + } else if (DataConstraintModel.typeMap.isAncestorOf(resourceNode.getParent().getResourceStateType())) { + Term selector = new Term(DataConstraintModel.insert); + selector.addChild(new Constant(langSpec.getFieldAccessor(fieldOfResourceState))); + selector.addChild(new Variable(input.getParameters().get(input.getParameters().size() - 2).getName())); + selector.addChild(new Constant(newState)); + String[] sideEffects2 = new String[] {""}; + String newMap = selector.toImplementation(sideEffects2); + updateStatement += sideEffects2[0]; + } else if (!(updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect())) { + String resourceName = langSpec.toVariableName(getComponentName(resource, langSpec)); + updateStatement += langSpec.getFieldAccessor(resourceName) + langSpec.getAssignment() + newState + langSpec.getStatementDelimiter(); + } + if (updateStatement != null) { + input.addFirstStatement(updateStatement); + } + } + } + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork + | InvalidMessage | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + + // Add an invocation to an update method (for a chain of update method invocations). + for (Edge resToCh: resourceNode.getOutEdges()) { + DataFlowEdge dOut = (DataFlowEdge) resToCh; + DataTransferChannel ch2 = ((ChannelNode) resToCh.getDestination()).getChannel(); + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource2 = false; + ChannelMember in = null; + Set outsideInputMembers2 = new HashSet<>(); + for (ChannelMember cm: ch2.getInputChannelMembers()) { + if (resourceNode.getOutSideResources().contains(cm.getResource())) { + if (cm.isOutside()) { + outsideInputResource2 = true; // Regarded as pull transfer. + } + in = cm; + } + if (cm.isOutside()) { + outsideInputMembers2.add(cm); + } + } + for (Edge chToRes: resToCh.getDestination().getOutEdges()) { + ResourceNode dstNode = ((ResourceNode) chToRes.getDestination()); + // Check if the output resource is outside of the channel scope. + boolean outsideOutputResource2 = false; + ChannelMember out2 = null; + for (ChannelMember cm: ch2.getOutputChannelMembers()) { + if (dstNode.getInSideResources().contains(cm.getResource())) { + out2 = cm; + if (cm.isOutside()) { + outsideOutputResource2 = true; + break; + } + } + } + if ((((PushPullAttribute) dOut.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource2) || outsideOutputResource2) { + // PUSH transfer + Map> referredResources = new HashMap<>(); + List params = new ArrayList<>(); + // Values of path parameters. + for (Expression pathParam: out2.getResource().getPathParams()) { + if (pathParam instanceof Variable) { + Variable pathVar = (Variable) pathParam; + params.add(pathVar.getName()); + } + } + // Values of channel parameters. + for (Selector selector: ch2.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + params.add(selVar.getName()); + } + } + // Value of the source side (input side) resource. + ResourceHierarchy srcRes = resourceNode.getResourceHierarchy(); + if (generatesComponent(srcRes)) { + params.add(langSpec.getFieldAccessor(fieldOfResourceState)); + } else { + params.add(langSpec.getFieldAccessor(langSpec.toVariableName(srcRes.getResourceName()))); + srcRes = srcRes.getParent(); + } + Set referredSet = referredResources.get(input); + for (ChannelMember rc: ch2.getReferenceChannelMembers()) { + // to get the value of reference member. + ResourcePath ref = rc.getResource(); + if (referredSet == null) { + referredSet = new HashSet<>(); + referredResources.put(input, referredSet); + } + if (!resourceNode.getOutSideResources().contains(ref)) { + String refVarName = ref.getLeafResourceName(); + if (!referredSet.contains(ref)) { + referredSet.add(ref); + Expression refGetter = getPullAccessor().getCurrentStateAccessorFor(rc, in); + String[] sideEffects = new String[] {""}; + String refExp = refGetter.toImplementation(sideEffects); + String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); + input.addStatement(sideEffects[0] + langSpec.getVariableDeclaration(refTypeName, refVarName) + langSpec.getAssignment() + refExp + langSpec.getStatementDelimiter()); + } + params.add(refVarName); + } + } + String updateMethodName = null; + ResourceHierarchy dstRes = dstNode.getResourceHierarchy(); + if (!generatesComponent(dstRes)) { + updateMethodName = updateMethodPrefix + getComponentName(dstRes, langSpec) + from + resComponentName; + dstRes = dstRes.getParent(); + } else { + updateMethodName = updateMethodPrefix + from + resComponentName; + } + String dstCompName = langSpec.toVariableName(getComponentName(dstRes, langSpec)); + if (!outsideOutputResource2) { + // The destination resource is not outside. + if (srcRes != dstRes) { + input.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, params) + + langSpec.getStatementDelimiter()); // this.dst.updateDstFromSrc(value, refParams); + } else { + input.addStatement(langSpec.getMethodInvocation(updateMethodName, params) + + langSpec.getStatementDelimiter()); // this.updateDstFromSrc(value, refParams); + } + } else { + // Use the reference field to refer to outside destination resource. + input.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, params) + + langSpec.getStatementDelimiter()); // this.dst.updateDstFromSrc(value, refParams); + } + } + } + if (outsideInputMembers2.size() > 0) { + if (!generatesComponent(resourceNode.getResourceHierarchy())) { + ResourcePath srcRes2 = resourceNode.getOutSideResource(ch2); + for (ChannelMember out2: ch2.getOutputChannelMembers()) { + if (!generatesComponent(out2.getResource().getResourceHierarchy())) { + ResourcePath dstRes2 = out2.getResource(); + if (srcRes2.getParent().equals(dstRes2.getParent())) { + Map>> resourcePaths = null; + try { + resourcePaths = ch2.fillOutsideResourcePaths(out2, getPullAccessor()); + if (resourcePaths != null && resourcePaths.size() > 0) { + for (ChannelMember outsideMember: outsideInputMembers2) { + for (ChannelMember dependingMember: resourcePaths.get(outsideMember).getValue()) { + if (dependingMember.getResource().equals(srcRes2)) { + // An outside input resource path depends on srcRes. + ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey(); + if (!generatesComponent(outsidePath.getResourceHierarchy())) { + outsidePath = outsidePath.getParent(); + } + String outsideResName = langSpec.toVariableName(getComponentName(outsidePath.getResourceHierarchy(), langSpec)); + Expression outsideExp = getPullAccessor().getDirectStateAccessorFor(outsidePath, null); + if (generatesComponent(outsidePath.getResourceHierarchy())) { + outsideExp = ((Term) outsideExp).getChild(0); + } + Expression nextExp = dependingMember.getStateTransition().getNextStateExpression(); + if (nextExp != null && outsideExp instanceof Term) { + if (nextExp instanceof Variable) { + outsideExp = ((Term) outsideExp).substitute((Variable) nextExp, new Field(langSpec.toVariableName(getComponentName(dependingMember.getResource().getResourceHierarchy(), langSpec)))); + } else { + // ToDo. + } + } + String[] sideEffects = new String[] {""}; + String outsideAccessor = outsideExp.toImplementation(sideEffects); + input.addStatement(langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter()); // change the reference field. + // Update constructor. + String initializingStatement = langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter(); + if (component != null) { + MethodDeclaration constructor = getConstructor(component); + constructor.addStatement(initializingStatement); // initialize the reference field. + } else { + constructorStatements.add(initializingStatement); // initialize the reference field. + } + } + } + } + } + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork + | InvalidMessage | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + } + } + } + } + } + } + } + } + } + } + return new AbstractMap.SimpleEntry<>(constructorStatements, inputStatements); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/generators/DataTransferMethodAnalyzer.java b/AlgebraicDataflowArchitectureModel/src/generators/DataTransferMethodAnalyzer.java index 8e68103..518ec7f 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/DataTransferMethodAnalyzer.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/DataTransferMethodAnalyzer.java @@ -15,7 +15,7 @@ * */ public class DataTransferMethodAnalyzer { - static private HashSet reachableNodes = new HashSet<>(); + static private HashSet reachableNodes = new HashSet<>(); /** * Determine whether each resource state is stored or not depending on selected data transfer methods. @@ -24,7 +24,7 @@ */ static public void decideToStoreResourceStates(DataFlowGraph graph) { reachableNodes.clear(); - for (Node n : graph.getNodes()) { + for (ResourceNode n: graph.getResourceNodes()) { ResourceNode resource = (ResourceNode) n; trackNode(resource); } @@ -35,11 +35,13 @@ return; reachableNodes.add(resource); boolean flag = true; - for (Edge e : resource.getInEdges()) { - if (((PushPullAttribute) e.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { - // Traverse pull edges only. - trackNode((ResourceNode) e.getSource()); - flag = false; + for (Edge chToRes: resource.getInEdges()) { + for (Edge resToCh: chToRes.getSource().getInEdges()) { + if (((PushPullAttribute) resToCh.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { + // Traverse pull edges only. + trackNode((ResourceNode) resToCh.getSource()); + flag = false; + } } } ((StoreAttribute) resource.getAttribute()).setStored(flag); diff --git a/AlgebraicDataflowArchitectureModel/src/generators/ILanguageSpecific.java b/AlgebraicDataflowArchitectureModel/src/generators/ILanguageSpecific.java new file mode 100644 index 0000000..6c86540 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/generators/ILanguageSpecific.java @@ -0,0 +1,48 @@ +package generators; + +import java.util.ArrayList; +import java.util.List; + +import code.ast.CompilationUnit; +import code.ast.FieldDeclaration; +import code.ast.MethodDeclaration; +import code.ast.TypeDeclaration; +import code.ast.VariableDeclaration; +import models.algebra.Expression; +import models.algebra.Term; +import models.algebra.Type; +import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor; + +public interface ILanguageSpecific { + CompilationUnit newCompilationUnit(TypeDeclaration component); + TypeDeclaration newTypeDeclaration(String typeName); + VariableDeclaration newVariableDeclaration(Type type, String varName); + MethodDeclaration newMethodDeclaration(String methodName, Type returnType); + MethodDeclaration newMethodDeclaration(String methodName, boolean isConstructor, Type returnType, List parameters); + FieldDeclaration newFieldDeclaration(Type fieldType, String fieldName); + FieldDeclaration newFieldDeclaration(Type fieldType, String fieldName, String fieldInitializer); + Type newListType(String compTypeName); + Type newMapType(Type keyType, String compTypeName); + Type newTupleType(List compTypes); + String getVariableDeclaration(String typeName, String varName); + String getFieldInitializer(Type type, Expression initialValue); + boolean declareField(); + String getFieldAccessor(String fieldName); + String getMethodInvocation(String methodName); + String getMethodInvocation(String receiverName, List parameters); + String getMethodInvocation(String receiverName, String methodName); + String getMethodInvocation(String receiverName, String methodName, List parameters); + String getConstructorInvocation(String componentName, List parameters); + String getReturnStatement(String returnValue); + String getIfStatement(Term condition, String block); + String toComponentName(String name); + String toVariableName(String name); + String getMainComponentName(); + String getTupleGet(String tupleExp, int idx, int length); + String getDecomposedTuple(String tupleExp, VariableDeclaration tupleVar, List vars); + String getAssignment(); + String getStatementDelimiter(); + String getStringDelimiter(); + boolean isValueType(Type type); + boolean isVoidType(Type type); +} diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JavaCodeGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/JavaCodeGenerator.java index 314e81a..a15057e 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/JavaCodeGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/JavaCodeGenerator.java @@ -1,12 +1,17 @@ package generators; +import java.util.AbstractMap; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.Set; +import java.util.Stack; - +import code.ast.Annotation; import code.ast.Block; import code.ast.CompilationUnit; import code.ast.FieldDeclaration; @@ -19,6 +24,7 @@ import models.algebra.Expression; import models.algebra.Field; import models.algebra.Parameter; +import models.algebra.Position; import models.algebra.Symbol; import models.algebra.Term; import models.algebra.Type; @@ -26,12 +32,15 @@ import models.dataConstraintModel.Channel; import models.dataConstraintModel.ChannelMember; import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.ResourceHierarchy; import models.dataConstraintModel.ResourcePath; +import models.dataConstraintModel.Selector; import models.dataFlowModel.DataTransferModel; import models.dataFlowModel.DataTransferChannel; import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor; import models.dataFlowModel.PushPullAttribute; import models.dataFlowModel.PushPullValue; +import models.dataFlowModel.ChannelNode; import models.dataFlowModel.DataFlowEdge; import models.dataFlowModel.DataFlowGraph; import models.dataFlowModel.ResourceNode; @@ -60,165 +69,689 @@ 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; + 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); + 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<>(); - TypeDeclaration mainType = new TypeDeclaration(mainTypeName); - CompilationUnit mainCU = new CompilationUnit(mainType); + Map> dependedRootComponentGraph = getDependedRootComponentGraph(model); + ArrayList resources = determineResourceOrder(graph, dependedRootComponentGraph); + TypeDeclaration mainComponent = new TypeDeclaration(mainTypeName); + CompilationUnit mainCU = new CompilationUnit(mainComponent); mainCU.addImport(new ImportDeclaration("java.util.*")); codes.add(mainCU); // Declare the constructor of the main type. MethodDeclaration mainConstructor = new MethodDeclaration(mainTypeName, true); - mainType.addMethod(mainConstructor); + mainComponent.addMethod(mainConstructor); - // For each resource. - for (ResourceNode rn: resources) { - boolean f = false; - String resourceName = rn.getResource().getResourceName().substring(0, 1).toUpperCase() - + rn.getResource().getResourceName().substring(1); - TypeDeclaration type = new TypeDeclaration(resourceName); - - // Declare the field to refer to each resource in the main type. - String fieldInitializer = "new " + resourceName + "("; - Set depends = new HashSet<>(); - for (Edge e : rn.getOutEdges()) { - DataFlowEdge re = (DataFlowEdge) e; - ResourcePath dstRes = ((ResourceNode) re.getDestination()).getResource(); - String resName = dstRes.getResourceName().substring(0, 1).toUpperCase() + dstRes.getResourceName().substring(1); - if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { - depends.add(dstRes); - fieldInitializer += resName.toLowerCase() + ","; - f = true; - } - } - for (Edge e : rn.getInEdges()) { - DataFlowEdge re = (DataFlowEdge) e; - ResourcePath srcRes = ((ResourceNode) re.getSource()).getResource(); - String resName = srcRes.getResourceName().substring(0, 1).toUpperCase() + srcRes.getResourceName().substring(1); - if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { - depends.add(srcRes); - fieldInitializer += resName.toLowerCase() + ","; - f = true; - } else { - if (rn.getIndegree() > 1) { - // Declare a field to cash the state of the source resource in the type of the destination resource. - ResourcePath cashResId = ((ResourceNode) re.getSource()).getResource(); - type.addField(new FieldDeclaration( - cashResId.getResourceStateType(), ((ResourceNode) re.getSource()).getResource().getResourceName(), getInitializer(cashResId))); - } - } - } + // For each resource node. + for (ResourceNode resourceNode: resources) { + TypeDeclaration component = null; + Set depends = new HashSet<>(); Set refs = new HashSet<>(); - for (Channel cg : model.getChannels()) { - DataTransferChannel c = (DataTransferChannel) cg; - if (c.getInputResources().contains(rn.getResource())) { - for (ResourcePath id: c.getReferenceResources()) { - if (!refs.contains(id) && !depends.contains(id)) { - refs.add(id); - String refResName = id.getResourceName(); - fieldInitializer += refResName.toLowerCase() + ","; - f = true; + if (generatesComponent(resourceNode.getResourceHierarchy())) { + boolean f = false; + String resourceName = getComponentName(resourceNode.getResourceHierarchy()); + + component = resourceComponents.get(resourceNode.getResourceHierarchy()); + if (component == null) { + // Add compilation unit for each resource. + component = new TypeDeclaration(resourceName); + resourceComponents.put(resourceNode.getResourceHierarchy(), component); + CompilationUnit cu = new CompilationUnit(component); + cu.addImport(new ImportDeclaration("java.util.*")); + codes.add(cu); + + // Declare the field to refer to each resource in the main type. + if (resourceNode.getResourceHierarchy().getParent() == null) { + // For a root resource + String fieldInitializer = "new " + resourceName + "("; + for (Edge resToCh: resourceNode.getOutEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { + for (Edge chToRes: re.getDestination().getOutEdges()) { + ResourceHierarchy dstRes = ((ResourceNode) chToRes.getDestination()).getResourceHierarchy(); + String resName = getComponentName(dstRes); + depends.add(dstRes); + fieldInitializer += toVariableName(resName) + ","; + f = true; + } + } } + for (Edge chToRes : resourceNode.getInEdges()) { + for (Edge resToCh: chToRes.getSource().getInEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + ResourceHierarchy srcRes = ((ResourceNode) re.getSource()).getResourceHierarchy(); + String resName = getComponentName(srcRes); + if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { + depends.add(srcRes); + fieldInitializer += toVariableName(resName) + ","; + f = true; + } else { + ChannelNode cn = (ChannelNode) re.getDestination(); + if (cn.getIndegree() > 1 + || (cn.getIndegree() == 1 && cn.getChannel().getInputChannelMembers().iterator().next().getStateTransition().isRightPartial())) { + // Declare a field to cache the state of the source resource in the type of the destination resource. + ResourceHierarchy cacheRes = ((ResourceNode) re.getSource()).getResourceHierarchy(); + component.addField(new FieldDeclaration( + cacheRes.getResourceStateType(), cacheRes.getResourceName(), getInitializer(cacheRes))); + } + } + } + } + for (ResourceHierarchy dependedRes: dependedRootComponentGraph.keySet()) { + for (ResourceHierarchy dependingRes: dependedRootComponentGraph.get(dependedRes)) { + if (resourceNode.getResourceHierarchy().equals(dependingRes)) { + // Declare a field to refer to outside resources. + depends.add(dependedRes); + String resName = getComponentName(dependedRes); + fieldInitializer += toVariableName(resName) + ","; + f = true; + } + } + } + for (Channel ch : model.getChannels()) { + DataTransferChannel c = (DataTransferChannel) ch; + if (resourceNode.getOutSideResource(c) != null) { + for (ResourcePath res: c.getReferenceResources()) { + if (!refs.contains(res) && !depends.contains(res.getResourceHierarchy())) { + refs.add(res); + String refResName = res.getLeafResourceName(); + fieldInitializer += toVariableName(refResName) + ","; + f = true; + } + } + } + } + if (f) fieldInitializer = fieldInitializer.substring(0, fieldInitializer.length() - 1); + fieldInitializer += ")"; + FieldDeclaration field = new FieldDeclaration(new Type(resourceName, resourceName), resourceNode.getResourceName()); + mainComponent.addField(field); + Block mainConstructorBody = mainConstructor.getBody(); + if (mainConstructorBody == null) { + mainConstructorBody = new Block(); + mainConstructor.setBody(mainConstructorBody); + } + mainConstructorBody.addStatement(resourceNode.getResourceName() + " = " + fieldInitializer + ";"); + } + + // Declare the field to store the state in the type of each resource. + if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { + ResourceHierarchy res = resourceNode.getResourceHierarchy(); + Set children = res.getChildren(); + if (children == null || children.size() == 0) { + // leaf resource. + Type fieldType = getImplStateType(res); + component.addField(new FieldDeclaration(fieldType, "value", getInitializer(res))); + } else { + ResourceHierarchy child = children.iterator().next(); + if (children.size() == 1 && child.getNumParameters() > 0) { + // map or list. + component.addField(new FieldDeclaration(getImplStateType(res), "value", getInitializer(res))); + } else { + // class + for (ResourceHierarchy c: children) { + String childTypeName = getComponentName(c); + Type childType = null; + if (generatesComponent(c)) { + // The child has a component. + childType = new Type(childTypeName, childTypeName); + String fieldName = toVariableName(childTypeName); + component.addField(new FieldDeclaration(childType, fieldName, "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) { + // 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(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 in the parent component. + if (component == null) { + // Declare reference fields for push/pull data transfer. + boolean noPullTransfer = true; + for (Edge chToRes : resourceNode.getInEdges()) { + for (Edge resToCh: chToRes.getSource().getInEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(ch); + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().equals(srcRes) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + // Check if the output resource is outside of the channel scope. + boolean outsideOutputResource = false; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (resourceNode.getInSideResources().contains(cm.getResource()) && cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + if ((((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { + noPullTransfer = false; } } - type.addMethod(new MethodDeclaration("update" + srcResName, false, typeVoid, vars)); + } + // Declare the state field in the parent component. + ResourceHierarchy res = resourceNode.getResourceHierarchy(); + if (((StoreAttribute) resourceNode.getAttribute()).isStored() && noPullTransfer && res.getNumParameters() == 0) { + String resName = getComponentName(res); + FieldDeclaration stateField = new FieldDeclaration(res.getResourceStateType(), toVariableName(resName)); + fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), stateField)); + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), new VariableDeclaration(res.getResourceStateType(), toVariableName(resName)))); + } + } + + // Declare the getter method to obtain the resource state in an ancestor component. + 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; + DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (resourceNode.getOutSideResources().contains(cm.getResource()) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + for (Edge chToRes: re.getDestination().getOutEdges()) { + ResourceHierarchy dstRes = ((ResourceNode) chToRes.getDestination()).getResourceHierarchy(); + // Check if the output resource is outside of the channel scope. + boolean outsideOutputResource = false; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (((ResourceNode) chToRes.getDestination()).getInSideResources().contains(cm.getResource()) && cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + if ((((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) { + // Declare a field to refer to the destination resource of push transfer. + if (!generatesComponent(dstRes)) { + dstRes = dstRes.getParent(); + } + String dstResName = getComponentName(dstRes); + depends.add(dstRes); + FieldDeclaration dstRefField = new FieldDeclaration(new Type(dstResName, dstResName), toVariableName(dstResName)); + VariableDeclaration dstRefVar = new VariableDeclaration(new Type(dstResName, dstResName), toVariableName(dstResName)); + if (component != null) { + // A component is created for this resource. + if (resourceNode.getResourceHierarchy() != dstRes) { + component.addField(dstRefField); + if (!outsideOutputResource) { + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy(), dstRefVar)); + } + } + } else { + // No component is created for this resource. + if (resourceNode.getParent().getResourceHierarchy() != dstRes) { + fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), dstRefField)); + if (!outsideOutputResource) { + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), dstRefVar)); + } + } + } + if (outsideOutputResource) { + // When the reference to the destination resource can vary. + if (dstRes.getParent() != null) { + // Reference to root resource. + ResourceHierarchy dstRootRes = dstRes.getRoot(); + String dstRootResName = getComponentName(dstRootRes); + FieldDeclaration dstRootRefField = new FieldDeclaration(new Type(dstRootResName, dstRootResName), toVariableName(dstRootResName)); + VariableDeclaration dstRootRefVar = new VariableDeclaration(new Type(dstRootResName, dstRootResName), toVariableName(dstRootResName)); + if (component != null) { + // A component is created for this resource. + component.addField(dstRootRefField); + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy(), dstRootRefVar)); + } else { + // No component is created for this resource. + fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), dstRootRefField)); + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), dstRootRefVar)); + } + } + } + } + } + } + // Declare update methods for push data transfer and reference fields for pull data transfer. + for (Edge chToRes : resourceNode.getInEdges()) { + for (Edge resToCh: chToRes.getSource().getInEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(ch); + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().equals(srcRes) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + // Check if the output resource is outside of the channel scope. + boolean outsideOutputResource = false; + ChannelMember out = null; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (resourceNode.getInSideResources().contains(cm.getResource())) { + out = cm; + if (cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + } + String srcResName = getComponentName(srcRes.getResourceHierarchy()); + ResourcePath srcRes2 = srcRes; + String srcResName2 = srcResName; + if (!generatesComponent(srcRes.getResourceHierarchy())) { + srcRes2 = srcRes.getParent(); + srcResName2 = getComponentName(srcRes2.getResourceHierarchy()); + } + if ((((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { + // Declare a field to refer to the source resource of pull transfer. + depends.add(srcRes2.getResourceHierarchy()); + FieldDeclaration srcRefField = new FieldDeclaration(new Type(srcResName2, srcResName2), toVariableName(srcResName2)); + VariableDeclaration srcRefVar = new VariableDeclaration(new Type(srcResName2, srcResName2), toVariableName(srcResName2)); + if (component != null) { + // A component is created for this resource. + if (resourceNode.getResourceHierarchy() != srcRes2.getResourceHierarchy()) { + component.addField(srcRefField); + if (!outsideInputResource) { + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy(), srcRefVar)); + } + } + } else { + // No component is created for this resource. + if (resourceNode.getParent().getResourceHierarchy() != srcRes2.getResourceHierarchy()) { + fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), srcRefField)); + if (!outsideInputResource) { + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), srcRefVar)); + } + } + } + if (outsideInputResource) { + // When the reference to the source resource can vary. + if (srcRes2.getParent() != null) { + // Reference to its root resource. + ResourcePath srcRootRes = srcRes2.getRoot(); + String srcRootResName = getComponentName(srcRootRes.getResourceHierarchy()); + FieldDeclaration srcRootRefField = new FieldDeclaration(new Type(srcRootResName, srcRootResName), toVariableName(srcRootResName)); + VariableDeclaration srcRootRefVar = new VariableDeclaration(new Type(srcRootResName, srcRootResName), toVariableName(srcRootResName)); + if (component != null) { + // A component is created for this resource. + component.addField(srcRootRefField); + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy(), srcRootRefVar)); + } else { + // No component is created for this resource. + fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), srcRootRefField)); + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), srcRootRefVar)); + } + } + } + } else { + // Declare an update method in the type of the destination resource. + ArrayList params = new ArrayList<>(); + int v = 1; + for (Expression exp: out.getResource().getPathParams()) { + if (exp instanceof Variable) { + Variable pathVar = (Variable) exp; + String varName = "self" + (v > 1 ? v : ""); +// String varName = pathVar.getName(); + VariableDeclaration pathParam = new VariableDeclaration(pathVar.getType(), varName); + params.add(pathParam); // A path parameter to identify the self resource. + } + v++; + } + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + VariableDeclaration chParam = new VariableDeclaration(selVar.getType(), selVar.getName()); + params.add(chParam); // A channel parameter to specify the context of the collaboration. + } + } + params.add(new VariableDeclaration(srcRes.getResourceStateType(), srcRes.getLeafResourceName())); // The state of the source resource to carry the data-flow. + for (ResourcePath ref: ch.getReferenceResources()) { + if (!ref.equals(resourceNode.getInSideResource(ch))) { + params.add(new VariableDeclaration(ref.getResourceStateType(), ref.getLeafResourceName())); + } + } + MethodDeclaration update = null; + if (component != null) { + // A component is created for this resource. + update = new MethodDeclaration("updateFrom" + srcResName, false, typeVoid, params); + component.addMethod(update); + } else { + // No component is created for this resource. + String resourceName = getComponentName(resourceNode.getResourceHierarchy()); + String updateMethodName = "update" + resourceName + "From" + srcResName; + Map nameToMethod = updates.get(resourceNode.getParent().getResourceHierarchy()); + if (nameToMethod == null) { + nameToMethod = new HashMap<>(); + updates.put(resourceNode.getParent().getResourceHierarchy(), nameToMethod); + } + if (nameToMethod.get(updateMethodName) == null) { + update = new MethodDeclaration(updateMethodName, false, typeVoid, params); + nameToMethod.put(updateMethodName, update); + } + } + } + } + } + // Declare a field to refer to outside resources. + if (resourceNode.getParent() == null) { + for (ResourceHierarchy dependedRes: dependedRootComponentGraph.keySet()) { + for (ResourceHierarchy dependingRes: dependedRootComponentGraph.get(dependedRes)) { + if (resourceNode.getResourceHierarchy().equals(dependingRes)) { + // Declare a field to refer to outside resources. + depends.add(dependedRes); + String resName = getComponentName(dependedRes); + FieldDeclaration refField = new FieldDeclaration(new Type(resName, resName), toVariableName(resName)); + VariableDeclaration refVar = new VariableDeclaration(new Type(resName, resName), toVariableName(resName)); + if (component != null) { + // A component is created for this resource. + component.addField(refField); + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy(), refVar)); + } else { + // No component is created for this resource. + fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), refField)); + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), refVar)); + } + } + } } } // Declare a field to refer to the reference resource. - refs = new HashSet<>(); - for (Channel cg : model.getChannels()) { - DataTransferChannel c = (DataTransferChannel) cg; - if (c.getInputResources().contains(rn.getResource())) { - for (ResourcePath id: c.getReferenceResources()) { - if (!refs.contains(id) && !depends.contains(id)) { - refs.add(id); - String refResName = id.getResourceName(); - refResName = refResName.substring(0, 1).toUpperCase() + refResName.substring(1); - type.addField(new FieldDeclaration(new Type(refResName, refResName), id.getResourceName())); - constructor.addParameter(new VariableDeclaration(new Type(refResName, refResName), id.getResourceName())); - block.addStatement("this." + id.getResourceName() + " = " + id.getResourceName() + ";"); + for (Channel ch : model.getChannels()) { + DataTransferChannel c = (DataTransferChannel) ch; + if (resourceNode.getOutSideResource(c) != null) { + for (ResourcePath res: c.getReferenceResources()) { + if (!refs.contains(res) && !depends.contains(res.getResourceHierarchy())) { + refs.add(res); + String refResName = getComponentName(res.getResourceHierarchy()); + FieldDeclaration refResField = new FieldDeclaration(new Type(refResName, refResName), res.getLeafResourceName()); + VariableDeclaration refResVar = new VariableDeclaration(new Type(refResName, refResName), res.getLeafResourceName()); + if (component != null) { + // A component is created for this resource. + component.addField(refResField); + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy(), refResVar)); + } else { + // No component is created for this resource. + fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), refResField)); + constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), refResVar)); + } } } } } - constructor.setBody(block); - if (constructor.getParameters() != null) - type.addMethod(constructor); - - // Declare input methods in resources and the main type. - for (Channel cg : model.getIOChannels()) { - for (ChannelMember cm : ((DataTransferChannel) cg).getOutputChannelMembers()) { - if (cm.getResource().equals(rn.getResource())) { + + // Declare the input method in this component and the main component. + for (Channel ch : model.getInputChannels()) { + for (ChannelMember cm : ((DataTransferChannel) ch).getOutputChannelMembers()) { + if (resourceNode.getInSideResources().contains(cm.getResource())) { Expression message = cm.getStateTransition().getMessageExpression(); - if (message.getClass() == Term.class) { - ArrayList params = new ArrayList<>(); - for (Variable var: message.getVariables().values()) { - params.add(new VariableDeclaration(var.getType(), var.getName())); + if (message instanceof Term) { + // In each resource. + ArrayList resInputParams = new ArrayList<>(); + ArrayList mainInputParams = new ArrayList<>(); + // The path parameters are not to be passed to the input method of each resource (resInputParams) + // because they are always equal to either channel selectors or message parameters. + + // Channel parameters to specify the context of the collaboration. + int v = 1; + for (Selector selector: ch.getSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + resInputParams.add(new VariableDeclaration(selVar.getType(), selVar.getName())); + mainInputParams.add(new VariableDeclaration(selVar.getType(), selVar.getName())); + } else if (selector.getExpression() instanceof Term) { + Term var = (Term) selector.getExpression(); + resInputParams.add(new VariableDeclaration(var.getType(), "v" + v)); + mainInputParams.add(new VariableDeclaration(var.getType(), "v" + v)); + } + v++; } - MethodDeclaration input = new MethodDeclaration( - ((Term) cm.getStateTransition().getMessageExpression()).getSymbol().getImplName(), - false, typeVoid, params); - type.addMethod(input); - String str = ((Term) cm.getStateTransition().getMessageExpression()).getSymbol().getImplName(); - input = getMethod(mainType, str); + if (ch.getParent() != null) { + for (Selector selector: ch.getParent().getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + mainInputParams.add(new VariableDeclaration(selVar.getType(), selVar.getName())); + } else if (selector.getExpression() instanceof Term) { + Term var = (Term) selector.getExpression(); + mainInputParams.add(new VariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + } + // Message parameters to carry the data-flows. + for (Map.Entry varEnt: message.getVariables().entrySet()) { + Variable var = varEnt.getValue(); + String refVarName = null; + for (ChannelMember refCm: ((DataTransferChannel) ch).getReferenceChannelMembers()) { + Expression varExp = refCm.getStateTransition().getMessageExpression().getSubTerm(varEnt.getKey()); + if (varExp != null && varExp instanceof Variable) { + if (refCm.getStateTransition().getCurStateExpression().contains(varExp)) { + refVarName = refCm.getResource().getLeafResourceName(); + break; + } + } + } + if (refVarName != null) { + // var has come from a reference resource. + resInputParams.add(new VariableDeclaration(var.getType(), refVarName)); + } else { + // var has not come from a reference resource. + resInputParams.add(new VariableDeclaration(var.getType(), var.getName())); + boolean bExists = false; + for (VariableDeclaration mainParam: mainInputParams) { + if (mainParam.getName().equals(var.getName()) ) { + bExists = true; + break; + } + } + if (!bExists) { + mainInputParams.add(new VariableDeclaration(var.getType(), var.getName())); + } + } + } + String inputMethodName = ((Term) message).getSymbol().getImplName(); + if (((DataTransferChannel) ch).getOutputChannelMembers().size() > 1) { + inputMethodName += "For" + getComponentName(cm.getResource().getResourceHierarchy()); + } + MethodDeclaration input = new MethodDeclaration(inputMethodName, false, typeVoid, resInputParams); + if (component != null) { + // A component is created for this resource. + component.addMethod(input); + } else { + // No component is created for this resource. + Map nameToMethod = inputs.get(resourceNode.getParent().getResourceHierarchy()); + if (nameToMethod == null) { + nameToMethod = new HashMap<>(); + inputs.put(resourceNode.getParent().getResourceHierarchy(), nameToMethod); + } + if (nameToMethod.get(inputMethodName) == null) { + nameToMethod.put(inputMethodName, input); + } + } + + // In the main type. + String messageSymbol = ((Term) message).getSymbol().getImplName(); + input = getMethod(mainComponent, messageSymbol, mainInputParams); if (input == null) { - input = new MethodDeclaration(str, false, typeVoid, params); - mainType.addMethod(input); + input = new MethodDeclaration(messageSymbol, false, typeVoid, mainInputParams); + mainComponent.addMethod(input); } else { // Add type to a parameter without type. for (VariableDeclaration param: input.getParameters()) { if (param.getType() == null) { - for (VariableDeclaration p: params) { + for (VariableDeclaration p: mainInputParams) { if (param.getName().equals(p.getName()) && p.getType() != null) { param.setType(p.getType()); } @@ -226,43 +759,121 @@ } } } - } else if (message.getClass() == Variable.class) { - MethodDeclaration input = new MethodDeclaration( - ((Variable) cm.getStateTransition().getMessageExpression()).getName(), - false, typeVoid, null); - type.addMethod(input); - String str = ((Variable) cm.getStateTransition().getMessageExpression()).getName(); - input = getMethod(mainType, str); + } else if (message instanceof Variable) { + // In each resource. + ArrayList resInputParams = new ArrayList<>(); + ArrayList mainInputParams = new ArrayList<>(); + int v = 1; + if (cm.getResource().getLastParam() != null) { + Expression param = cm.getResource().getLastParam(); + if (param instanceof Variable) { + Variable var = (Variable) param; + resInputParams.add(new VariableDeclaration(var.getType(), var.getName())); + mainInputParams.add(new VariableDeclaration(var.getType(), var.getName())); + } else if (param instanceof Term) { + Term var = (Term) param; + resInputParams.add(new VariableDeclaration(var.getType(), "v" + v)); + mainInputParams.add(new VariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + if (cm.getResource().getParent() != null) { + for (Expression param: cm.getResource().getParent().getPathParams()) { + if (param instanceof Variable) { + Variable var = (Variable) param; + mainInputParams.add(new VariableDeclaration(var.getType(), var.getName())); + } else if (param instanceof Term) { + Term var = (Term) param; + mainInputParams.add(new VariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + } + String inputMethodName = ((Variable) message).getName(); + if (((DataTransferChannel) ch).getOutputChannelMembers().size() > 1) { + inputMethodName += "For" + getComponentName(cm.getResource().getResourceHierarchy()); + } + MethodDeclaration input = null; + if (resInputParams.size() > 0) { + input = new MethodDeclaration(inputMethodName, false, typeVoid, resInputParams); + } else { + input = new MethodDeclaration(inputMethodName, false, typeVoid, null); + } + if (component != null) { + // A component is created for this resource. + component.addMethod(input); + } else { + // No component is created for this resource. + Map nameToMethod = inputs.get(resourceNode.getParent().getResourceHierarchy()); + if (nameToMethod == null) { + nameToMethod = new HashMap<>(); + inputs.put(resourceNode.getParent().getResourceHierarchy(), nameToMethod); + } + if (nameToMethod.get(inputMethodName) == null) { + nameToMethod.put(inputMethodName, input); + } + } + + // In the main type. + String messageSymbol = ((Variable) message).getName(); + input = getMethod(mainComponent, messageSymbol, mainInputParams); if (input == null) { - input = new MethodDeclaration(str, false, typeVoid, null); - mainType.addMethod(input); + if (mainInputParams.size() > 0) { + input = new MethodDeclaration(messageSymbol, false, typeVoid, mainInputParams); + } else { + input = new MethodDeclaration(messageSymbol, false, typeVoid, null); + } + mainComponent.addMethod(input); } } } } } - - // Declare the field to store the state in the type of each resource. - if (((StoreAttribute) rn.getAttribute()).isStored()) { - ResourcePath resId = rn.getResource(); - type.addField(new FieldDeclaration(resId.getResourceStateType(), "value", getInitializer(resId))); + } + + // Add leaf getter methods to the parent components. + for (Map.Entry entry: getters) { + resourceComponents.get(entry.getKey()).addMethod(entry.getValue()); + } + + // Add leaf update methods to the parent components. + for (Map.Entry> entry: updates.entrySet()) { + for (MethodDeclaration update: entry.getValue().values()) { + resourceComponents.get(entry.getKey()).addMethod(update); } - - // Declare the getter method to obtain the state in the type of each resource. - type.addMethod(new MethodDeclaration("getValue", - rn.getResource().getResourceStateType())); - - // Add compilation unit for each resource. - CompilationUnit cu = new CompilationUnit(type); - cu.addImport(new ImportDeclaration("java.util.*")); - codes.add(cu); + } + + // Add leaf input methods to the parent components. + for (Map.Entry> entry: inputs.entrySet()) { + for (MethodDeclaration input: entry.getValue().values()) { + resourceComponents.get(entry.getKey()).addMethod(input); + } + } + + // Add leaf reference fields to the parent components. + for (Map.Entry entry: fields) { + boolean existsField = false; + for (FieldDeclaration field: resourceComponents.get(entry.getKey()).getFields()) { + if (field.getName().equals(entry.getValue().getName())) { + existsField = true; + break; + } + } + if (!existsField) { + resourceComponents.get(entry.getKey()).addField(entry.getValue()); + } + } + + // Add constructor parameters to the ancestor components. + for (ResourceNode root: graph.getRootResourceNodes()) { + addConstructorParameters(root.getResourceHierarchy(), resourceComponents, resourceConstructors, constructorParams); } // Declare the Pair class. boolean isCreatedPair = false; - for(ResourceNode rn : resources) { - if(isCreatedPair) continue; - if(model.getType("Pair").isAncestorOf(rn.getResource().getResourceStateType())) { + for (ResourceNode rn : resources) { + if (isCreatedPair) continue; + if (rn.getResourceStateType() != null && model.getType("Pair").isAncestorOf(rn.getResourceStateType())) { TypeDeclaration type = new TypeDeclaration("Pair"); type.addField(new FieldDeclaration(new Type("Double", "T"), "left")); type.addField(new FieldDeclaration(new Type("Double", "T"), "right")); @@ -293,48 +904,191 @@ } } - // 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()) { + DataTransferChannel dtCh = (DataTransferChannel) ch; + Set inRes = new HashSet<>(); + Set outRes = new HashSet<>(); + for (ChannelMember cm: dtCh.getChannelMembers()) { + if (cm.isOutside()) { + outRes.add(cm.getResource().getResourceHierarchy()); + } else { + inRes.add(cm.getResource().getResourceHierarchy()); + } } + if (outRes.size() > 0 && inRes.size() > 0) { + for (ResourceHierarchy out: outRes) { + for (ResourceHierarchy in: inRes) { + Set dependings = dependedComponentGraph.get(out.getRoot()); + if (dependings == null) { + dependings = new HashSet<>(); + dependedComponentGraph.put(out.getRoot(), dependings); + } + dependings.add(in.getRoot()); + } + } + } + } + return dependedComponentGraph; + } + + static private ArrayList determineResourceOrder(DataFlowGraph graph, Map> dependedRootComponentGraph) { + ArrayList resources = new ArrayList<>(); + Set visited = new HashSet<>(); + for (Node n : graph.getResourceNodes()) { + ResourceNode resNode = (ResourceNode) n; + topologicalSort(resNode, graph, dependedRootComponentGraph, visited, resources); + } + return resources; + } + + static private void topologicalSort(ResourceNode curResNode, DataFlowGraph graph, Map> dependedRootComponentGraph, Set visited, List orderedList) { + if (visited.contains(curResNode)) return; + visited.add(curResNode); + // For each incoming PUSH transfer. + for (Edge chToRes: curResNode.getInEdges()) { + for (Edge resToCh: chToRes.getSource().getInEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { + topologicalSort((ResourceNode) re.getSource(), graph, dependedRootComponentGraph, visited, orderedList); + } + } + } + // For each outgoing PULL transfer. + for (Edge resToCh: curResNode.getOutEdges()) { + DataFlowEdge de = (DataFlowEdge) resToCh; + if (((PushPullAttribute) de.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { + for (Edge chToRes : resToCh.getDestination().getOutEdges()) { + topologicalSort((ResourceNode) chToRes.getDestination(), graph, dependedRootComponentGraph, visited, orderedList); + } + } + } + // For each depending root node. + if (dependedRootComponentGraph.get(curResNode.getResourceHierarchy()) != null) { + for (ResourceHierarchy dependingRes: dependedRootComponentGraph.get(curResNode.getResourceHierarchy())) { + for (ResourceNode root: graph.getRootResourceNodes()) { + if (root.getResourceHierarchy().equals(dependingRes)) { + topologicalSort(root, graph, dependedRootComponentGraph, visited, orderedList); + } + } + } + } + // For each reference resource. + for (Node n: graph.getResourceNodes()) { + ResourceNode resNode = (ResourceNode) n; + for (Edge resToCh : resNode.getOutEdges()) { + ChannelNode chNode = (ChannelNode) resToCh.getDestination(); + for (ChannelMember m: chNode.getChannel().getReferenceChannelMembers()) { + if (curResNode.getOutSideResources().contains(m.getResource())) { + topologicalSort(resNode, graph, dependedRootComponentGraph, visited, orderedList); + } + } + } + } + orderedList.add(0, curResNode); + } + + private static void declareAccessorMethodInMainComponent(ResourceNode resourceNode, TypeDeclaration mainComponent) { + MethodDeclaration getterAccessor = null; + List mainGetterParams = new ArrayList<>(); + int v = 1; + for (Expression param: resourceNode.getPrimaryResourcePath().getPathParams()) { + if (param instanceof Variable) { + Variable var = (Variable) param; + mainGetterParams.add(new VariableDeclaration(var.getType(), var.getName())); + } else if (param instanceof Term) { + Term var = (Term) param; + mainGetterParams.add(new VariableDeclaration(var.getType(), "v" + v)); + } + v++; + } + if (mainGetterParams.size() > 0) { + getterAccessor = new MethodDeclaration("get" + getComponentName(resourceNode.getResourceHierarchy()), + false, + getImplStateType(resourceNode.getResourceHierarchy()), + mainGetterParams); + } else { + getterAccessor = new MethodDeclaration("get" + getComponentName(resourceNode.getResourceHierarchy()), + getImplStateType(resourceNode.getResourceHierarchy())); + } + getterAccessor.setBody(new Block()); + Expression getState = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(resourceNode.getPrimaryResourcePath(), null); + getterAccessor.getBody().addStatement("return " + getState.toImplementation(new String[] {null}) + ";"); + mainComponent.addMethod(getterAccessor); + } + + private static List addConstructorParameters(ResourceHierarchy resource, + Map resourceComponents, + Map resourceConstructors, + List> constructorParams) { + List params = new ArrayList<>(); + for (ResourceHierarchy child: resource.getChildren()) { + params.addAll(addConstructorParameters(child, resourceComponents, resourceConstructors, constructorParams)); + } + for (Entry paramEnt: constructorParams) { + if (paramEnt.getKey().equals(resource)) { + params.add(paramEnt.getValue()); + } + } + if (params.size() > 0) { + MethodDeclaration constructor = resourceConstructors.get(resource); + if (constructor == null) { + if (resourceComponents.get(resource) != null) { + String resourceName = getComponentName(resource); + constructor = new MethodDeclaration(resourceName, true); + Block body = new Block(); + constructor.setBody(body); + resourceComponents.get(resource).addMethod(constructor); + resourceConstructors.put(resource, constructor); + } + } + if (constructor != null) { + for (VariableDeclaration param: params) { + boolean existsParam = false; + if (constructor.getParameters() != null) { + for (VariableDeclaration constParam: constructor.getParameters()) { + if (constParam.getName().equals(param.getName())) { + existsParam = true; + break; + } + } + } + if (!existsParam) { + constructor.addParameter(param); + constructor.getBody().addStatement("this." + toVariableName(param.getName()) + " = " + toVariableName(param.getName()) + ";"); + } + } + } + } + if (resource.getNumParameters() > 0) params.clear(); + return params; + } + + private static String getInitializer(ResourceHierarchy res) { + Type stateType = res.getResourceStateType(); + String initializer = null; + if (res.getInitialValue() != null) { + initializer = res.getInitialValue().toImplementation(new String[] {""}); + } else if (stateType != null) { + initializer = DataConstraintModel.getDefaultValue(stateType); } return initializer; } @@ -384,92 +1138,252 @@ return codes; } - static private ArrayList determineResourceOrder(DataFlowGraph graph) { - ArrayList resources = new ArrayList<>(); - Set visited = new HashSet<>(); - for (Node n : graph.getNodes()) { - ResourceNode rn = (ResourceNode) n; - topologicalSort(graph, rn, visited, resources); - } - return resources; - } - - static private void topologicalSort(DataFlowGraph graph, ResourceNode curNode, Set visited, List orderedList) { - if (visited.contains(curNode)) return; - visited.add(curNode); - for (Edge e : curNode.getInEdges()) { - DataFlowEdge re = (DataFlowEdge) e; - if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { - topologicalSort(graph, (ResourceNode) re.getSource(), visited, orderedList); - } - } - for (Edge e : curNode.getOutEdges()) { - DataFlowEdge re = (DataFlowEdge) e; - if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { - topologicalSort(graph, (ResourceNode) re.getDestination(), visited, orderedList); - } - } - for (Node n: graph.getNodes()) { // for reference resources. - ResourceNode rn = (ResourceNode) n; - for (Edge e : rn.getOutEdges()) { - DataFlowEdge re = (DataFlowEdge) e; - for (ChannelMember m: re.getChannel().getReferenceChannelMembers()) { - if (m.getResource() == curNode.getResource()) { - topologicalSort(graph, rn, visited, orderedList); + private static MethodDeclaration getMethod(TypeDeclaration type, String methodName, List params) { + for (MethodDeclaration m: type.getMethods()) { + if (m.getName().equals(methodName)) { + if (m.getParameters() == null && (params == null || params.size() == 0)) return m; + if (m.getParameters() != null && params != null && m.getParameters().size() == params.size()) { + boolean matchParams = true; + for (int i = 0; i < m.getParameters().size(); i++) { + if (!m.getParameters().get(i).getType().equals(params.get(i).getType())) { + matchParams = false; + break; + } } + if (matchParams) return m; } } } - orderedList.add(0, curNode); - } - - private static MethodDeclaration getMethod(TypeDeclaration type, String methodName) { - for (MethodDeclaration m: type.getMethods()) { - if (m.getName().equals(methodName)) return m; - } return null; } static public IResourceStateAccessor pushAccessor = new IResourceStateAccessor() { @Override - public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) { - if (target.equals(from)) { + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + ResourcePath fromRes = from.getResource(); + if (targetRes.equals(fromRes)) { return new Field("value", - target.getResourceStateType() != null ? target.getResourceStateType() + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } - // for reference channel member - return new Parameter(target.getResourceName(), - target.getResourceStateType() != null ? target.getResourceStateType() + // use the cached value as the current state + return new Field(targetRes.getLeafResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } @Override - public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { - return new Parameter(target.getResourceName(), - target.getResourceStateType() != null ? target.getResourceStateType() + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + return new Parameter(targetRes.getLeafResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { + if (fromRes != null && targetRes.equals(fromRes)) { + return new Field("value", + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + return new Parameter(targetRes.getLeafResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } }; + static public IResourceStateAccessor pullAccessor = new IResourceStateAccessor() { @Override - public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) { - if (target.equals(from)) { - return new Field("value", - target.getResourceStateType() != null ? target.getResourceStateType() - : DataConstraintModel.typeInt); + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + if (from != null) { + ResourcePath fromRes = from.getResource(); + if (!target.isOutside()) { + return getDirectStateAccessorFor(targetRes, fromRes); + } + Term getter = null; + String targetComponentName = getComponentName(targetRes.getResourceHierarchy()); + if (generatesComponent(targetRes.getResourceHierarchy())) { + getter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); + getter.addChild(new Field(toVariableName(targetComponentName), targetRes.getResourceStateType())); + } else { + String parentName = toVariableName(getComponentName(targetRes.getResourceHierarchy().getParent())); + Type parentType = targetRes.getResourceHierarchy().getParent().getResourceStateType(); + getter = new Term(new Symbol("get" + targetComponentName, 1, Symbol.Type.METHOD)); + getter.addChild(new Field(parentName, parentType)); + } + return getter; + } else { + return getDirectStateAccessorFor(targetRes, null); } - // for reference channel member - Term getter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); - getter.addChild(new Field(target.getResourceName(), target.getResourceStateType())); - return getter; } @Override - public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { - Term getter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); - getter.addChild(new Field(target.getResourceName(), target.getResourceStateType())); - return getter; + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + if (from != null) { + ResourcePath fromRes = from.getResource(); + if (!target.isOutside()) { + return getDirectStateAccessorFor(targetRes, fromRes); + } + Term getter = null; + String targetComponentName = getComponentName(targetRes.getResourceHierarchy()); + if (generatesComponent(targetRes.getResourceHierarchy())) { + getter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); + getter.addChild(new Field(toVariableName(targetComponentName), targetRes.getResourceStateType())); + } else { + String parentName = toVariableName(getComponentName(targetRes.getResourceHierarchy().getParent())); + Type parentType = targetRes.getResourceHierarchy().getParent().getResourceStateType(); + getter = new Term(new Symbol("get" + targetComponentName, 1, Symbol.Type.METHOD)); + getter.addChild(new Field(parentName, parentType)); + } + return getter; + } else { + return getDirectStateAccessorFor(targetRes, null); + } + } + + @Override + public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { + if (fromRes != null) { + if (targetRes.equals(fromRes)) { + return new Field("value", + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + Term getter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); + getter.addChild(new Field(targetRes.getLeafResourceName(), targetRes.getResourceStateType())); + return getter; + } else { + // access from the outside of the hierarchy + Stack pathStack = new Stack<>(); + ResourcePath curPath = targetRes; + do { + pathStack.push(curPath); + curPath = curPath.getParent(); + } while (curPath != null); + // iterate from the root resource + Term getter = null; + int v = 1; + int arity = 2; + while (!pathStack.empty()) { + curPath = pathStack.pop(); + String typeName = getComponentName(curPath.getResourceHierarchy()); + if (getter == null) { + // root resource + String fieldName = toVariableName(typeName); + getter = new Field(fieldName, new Type(typeName, typeName)); + } else { + 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) { + Variable var = null; + Expression param = curPath.getLastParam(); + if (param instanceof Variable) { + var = (Variable) param; + } else if (param instanceof Term) { + var = new Variable("v" + v, ((Term) param).getType()); + } + if (var != null) { + newGetter.addChild(var); + newGetter.getSymbol().setArity(2); + } + v++; + } + getter = newGetter; + } else { + if (curPath.getResourceHierarchy().getNumParameters() > 0) { + Variable var = null; + Expression param = curPath.getLastParam(); + if (param instanceof Variable) { + var = (Variable) param; + } else if (param instanceof Term) { + var = new Variable("v" + v, ((Term) param).getType()); + } + if (var != null) { + getter.getSymbol().setArity(arity); + getter.addChild(var); + } + v++; + } + } + arity = 2; + } else { + // to get a descendant resource directly. + if (arity == 2) { + Term newGetter = new Term(new Symbol("get" + typeName, -1, Symbol.Type.METHOD)); + newGetter.addChild(getter); + getter = newGetter; + } + if (curPath.getResourceHierarchy().getNumParameters() > 0) { + Variable var = null; + Expression param = curPath.getLastParam(); + if (param instanceof Variable) { + var = (Variable) param; + } else if (param instanceof Term) { + var = new Variable("v" + v, ((Term) param).getType()); + } + if (var != null) { + getter.getSymbol().setArity(arity); + getter.addChild(var); + arity++; + } + v++; + } + } + } + } + if (generatesComponent(targetRes.getResourceHierarchy())) { + Term newGetter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); + newGetter.addChild(getter); + getter = newGetter; + } + return getter; + } + } + }; + + static public IResourceStateAccessor refAccessor = new IResourceStateAccessor() { + @Override + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + ResourcePath fromRes = from.getResource(); + if (targetRes.equals(fromRes)) { + return new Field("value", + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + return new Parameter(targetRes.getLeafResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + return new Parameter(targetRes.getLeafResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { + if (fromRes != null && targetRes.equals(fromRes)) { + return new Field("value", + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + return new Parameter(targetRes.getLeafResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); } }; } diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java index 86cd28a..283e16e 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java @@ -4,18 +4,25 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.Map.Entry; import code.ast.CompilationUnit; import code.ast.MethodDeclaration; import code.ast.TypeDeclaration; +import code.ast.VariableDeclaration; import models.Edge; -import models.Node; +import models.algebra.Constant; import models.algebra.Expression; +import models.algebra.Field; import models.algebra.InvalidMessage; +import models.algebra.Parameter; import models.algebra.ParameterizedIdentifierIsFutureWork; +import models.algebra.Position; +import models.algebra.Symbol; import models.algebra.Term; import models.algebra.Type; import models.algebra.UnificationFailed; @@ -24,12 +31,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.DataTransferChannel.IResourceStateAccessor; import models.dataFlowModel.PushPullAttribute; import models.dataFlowModel.PushPullValue; +import models.dataFlowModel.ChannelNode; import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork; import models.dataFlowModel.DataFlowEdge; import models.dataFlowModel.DataFlowGraph; @@ -39,10 +51,10 @@ public class JavaMethodBodyGenerator { public static ArrayList doGenerate(DataFlowGraph graph, DataTransferModel model, ArrayList codes) { // Create a map from type names (lower case) to their types. - Map typeMap = new HashMap<>(); + Map componentMap = new HashMap<>(); for (CompilationUnit code: codes) { - for (TypeDeclaration type: code.types()) { - typeMap.put(type.getTypeName().substring(0,1).toLowerCase() + type.getTypeName().substring(1), type); + for (TypeDeclaration component: code.types()) { + componentMap.put(component.getTypeName(), component); } } @@ -50,189 +62,702 @@ try { Map> referredResources = new HashMap<>(); for (Edge e: graph.getEdges()) { - DataFlowEdge d = (DataFlowEdge) e; - PushPullAttribute pushPull = (PushPullAttribute) d.getAttribute(); - ResourceNode src = (ResourceNode) d.getSource(); - ResourceNode dst = (ResourceNode) d.getDestination(); - String srcResourceName = src.getResource().getResourceName(); - String dstResourceName = dst.getResource().getResourceName(); - TypeDeclaration srcType = typeMap.get(srcResourceName); - TypeDeclaration dstType = typeMap.get(dstResourceName); - for (ChannelMember out: d.getChannel().getOutputChannelMembers()) { - if (out.getResource() == dst.getResource()) { - if (pushPull.getOptions().get(0) == PushPullValue.PUSH && srcType != null) { - // for push data transfer - MethodDeclaration update = getUpdateMethod(dstType, srcType); - if (((StoreAttribute) dst.getAttribute()).isStored()) { - // update stored state of dst side resource (when every incoming edge is in push style) - Expression updateExp = d.getChannel().deriveUpdateExpressionOf(out, JavaCodeGenerator.pushAccessor); - String[] sideEffects = new String[] {""}; - String curState = updateExp.toImplementation(sideEffects); - String updateStatement; - if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { - updateStatement = sideEffects[0]; - } else { - updateStatement = sideEffects[0] + "value = " + curState + ";"; + DataFlowEdge resToCh = (DataFlowEdge) e; + if (!resToCh.isChannelToResource()) { + PushPullAttribute pushPull = (PushPullAttribute) resToCh.getAttribute(); + ResourceNode src = (ResourceNode) resToCh.getSource(); + for (Edge chToRes: resToCh.getDestination().getOutEdges()) { + ResourceNode dst = (ResourceNode) chToRes.getDestination(); + String srcResourceName = JavaCodeGenerator.getComponentName(src.getResourceHierarchy()); + String dstResourceName = JavaCodeGenerator.getComponentName(dst.getResourceHierarchy()); + TypeDeclaration srcComponent = componentMap.get(srcResourceName); + TypeDeclaration dstComponent = componentMap.get(dstResourceName); + DataTransferChannel ch = ((ChannelNode) resToCh.getDestination()).getChannel(); + for (ChannelMember out: ch.getOutputChannelMembers()) { + if (dst.getInSideResources().contains(out.getResource())) { + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (src.getOutSideResources().contains(cm.getResource()) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } } - if (update.getBody() == null || !update.getBody().getStatements().contains(updateStatement)) { - update.addFirstStatement(updateStatement); - } - } - if (dst.getIndegree() > 1) { - // update a cash of src side resource (when incoming edges are multiple) - String cashStatement = "this." + srcResourceName + " = " + srcResourceName + ";"; - if (update.getBody() == null || !update.getBody().getStatements().contains(cashStatement)) { - update.addFirstStatement(cashStatement); - } - } - MethodDeclaration getter = getGetterMethod(dstType); - if (((StoreAttribute) dst.getAttribute()).isStored()) { - // returns the current state stored in a field. - if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { - Type resourceType = dst.getResource().getResourceStateType(); - if (model.isPrimitiveType(resourceType)) { - getter.addStatement("return value;"); + // Check if the output resource is outside of the channel scope. + boolean outsideOutputResource = out.isOutside(); + if ((pushPull.getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) { + // for push data transfer + MethodDeclaration update = null; + if (dstComponent == null) { + String dstParentResourceName = JavaCodeGenerator.getComponentName(dst.getResourceHierarchy().getParent()); + dstComponent = componentMap.get(dstParentResourceName); + update = getUpdateMethod(dstComponent, dstResourceName, srcResourceName); } else { - // copy the current state to be returned as a 'value' - String implTypeName = resourceType.getImplementationTypeName(); -// String interfaceTypeName = resourceType.getInterfaceTypeName(); -// String concreteTypeName; -// if (interfaceTypeName.contains("<")) { -// String typeName = implTypeName.substring(0, implTypeName.indexOf("<")); -//// String generics = interfaceTypeName.substring(interfaceTypeName.indexOf("<") + 1, interfaceTypeName.lastIndexOf(">")); -// concreteTypeName = typeName + "<>"; -// } else { -// concreteTypeName = implTypeName; -// } - getter.addStatement("return new " + implTypeName + "(value);"); + update = getUpdateMethod(dstComponent, null, srcResourceName); } - } - } - // src side (for a chain of update method invocations) - for (MethodDeclaration srcUpdate: getUpdateMethods(srcType)) { - String refParams = ""; - Set referredSet = referredResources.get(srcUpdate); - for (ChannelMember rc: d.getChannel().getReferenceChannelMembers()) { - // to get the value of reference member. - ResourcePath ref = rc.getResource(); - if (referredSet == null) { - referredSet = new HashSet<>(); - referredResources.put(srcUpdate, referredSet); - } - if (ref != dst.getResource()) { - String refVarName = ref.getResourceName(); - if (!referredSet.contains(ref)) { - referredSet.add(ref); - Expression refGetter = JavaCodeGenerator.pullAccessor.getCurrentStateAccessorFor(ref, src.getResource()); - String[] sideEffects = new String[] {""}; - String refExp = refGetter.toImplementation(sideEffects); - String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); - srcUpdate.addFirstStatement(sideEffects[0] + refTypeName + " " + refVarName + " = " + refExp + ";"); + if (((StoreAttribute) dst.getAttribute()).isStored()) { + // update stored state of dst side resource (when every incoming edge is in push style) + Expression updateExp = null; + if (ch.getReferenceChannelMembers().size() == 0) { + updateExp = ch.deriveUpdateExpressionOf(out, JavaCodeGenerator.pushAccessor).getKey(); + } else { + // if there exists one or more reference channel member. + HashMap inputResourceToStateAccessor = new HashMap<>(); + for (ChannelMember in: ch.getInputChannelMembers()) { + inputResourceToStateAccessor.put(in, JavaCodeGenerator.pushAccessor); + } + for (ChannelMember c: ch.getReferenceChannelMembers()) { + inputResourceToStateAccessor.put(c, JavaCodeGenerator.refAccessor); + } + updateExp = ch.deriveUpdateExpressionOf(out, JavaCodeGenerator.pushAccessor, inputResourceToStateAccessor).getKey(); } - refParams += ", " + refVarName; - } - } - srcUpdate.addStatement("this." + dstResourceName + ".update" + srcType.getTypeName() + "(value" + refParams + ");"); - } - for (MethodDeclaration srcInput: getInputMethods(srcType, src, model)) { - String refParams = ""; - Set referredSet = referredResources.get(srcInput); - for (ChannelMember rc: d.getChannel().getReferenceChannelMembers()) { - // to get the value of reference member. - ResourcePath ref = rc.getResource(); - if (referredSet == null) { - referredSet = new HashSet<>(); - referredResources.put(srcInput, referredSet); - } - if (ref != dst.getResource()) { - String refVarName = ref.getResourceName(); - if (!referredSet.contains(ref)) { - referredSet.add(ref); - Expression refGetter = JavaCodeGenerator.pullAccessor.getCurrentStateAccessorFor(ref, src.getResource()); - String[] sideEffects = new String[] {""}; - String refExp = refGetter.toImplementation(sideEffects); - String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); - srcInput.addFirstStatement(sideEffects[0] + refTypeName + " " + refVarName + " = " + refExp + ";"); + // Replace Json constructor with a constructor of 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); + } } - refParams += ", " + refVarName; + // 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]; + } else { + updateStatement = sideEffects[0] + "this.value = " + newState + ";"; + } + } else { + if (sideEffects[0] != null) { + updateStatement = sideEffects[0]; + updateStatement = updateStatement.replace(".value", "." + JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(outRes))); + } + if (DataConstraintModel.typeList.isAncestorOf(outRes.getParent().getResourceStateType())) { + Term selector = new Term(DataConstraintModel.set); + selector.addChild(new Field("value")); + selector.addChild(new Variable(update.getParameters().get(update.getParameters().size() - 2).getName())); + selector.addChild(new Constant(newState)); + String[] sideEffects2 = new String[] {""}; + String newList = selector.toImplementation(sideEffects2); + updateStatement += sideEffects2[0]; + } else if (DataConstraintModel.typeMap.isAncestorOf(outRes.getParent().getResourceStateType())) { + Term selector = new Term(DataConstraintModel.insert); + selector.addChild(new Field("value")); + selector.addChild(new Variable(update.getParameters().get(update.getParameters().size() - 2).getName())); + selector.addChild(new Constant(newState)); + String[] sideEffects2 = new String[] {""}; + String newMap = selector.toImplementation(sideEffects2); + updateStatement += sideEffects2[0]; + } else if (!(updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect())) { + updateStatement += "this." + JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(outRes)) + " = " + newState + ";"; + } + } + if (numOfOutResourcesWithTheSameHierarchy == 1) { + update.addFirstStatement(updateStatement); + } else { + Term conditions = null; + int v = 1; + Map>> resourcePaths = ch.fillOutsideResourcePaths(out, JerseyCodeGenerator.pushAccessor); + for (Expression pathParam: out.getResource().getPathParams()) { + if (pathParam instanceof Variable) { + String selfParamName = ((Variable) pathParam).getName(); + Expression arg = null; + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + if (selVar.getName().equals(selfParamName)) { + arg = selVar; + break; + } + } + } + if (arg == null) { + ResourcePath filledPath = resourcePaths.get(out).getKey(); + arg = filledPath.getPathParams().get(v - 1); + } + Term condition = new Term(DataConstraintModel.eq, new Expression[] { + new Parameter("self" + (v > 1 ? v : ""), 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); + if (resToCh.getDestination().getIndegree() > 1 + || (resToCh.getDestination().getIndegree() == 1 && ch.getInputChannelMembers().iterator().next().getStateTransition().isRightPartial())) { + // update a cache of src side resource (when incoming edges are multiple) + String cacheStatement = "this." + JavaCodeGenerator.toVariableName(srcResourceName) + " = " + JavaCodeGenerator.toVariableName(srcResourceName) + ";"; + if (update.getBody() == null || !update.getBody().getStatements().contains(cacheStatement)) { + update.addStatement(cacheStatement); + } + } + if (((StoreAttribute) dst.getAttribute()).isStored()) { + // returns the current state stored in a field. + MethodDeclaration getter = null; + if (JavaCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { + getter = getMethod(dstComponent, "getValue"); + } else { + getter = getGetterMethod(dstComponent, dstResourceName); + } + if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { + Type resourceType = dst.getResourceStateType(); + if (dst.getResourceHierarchy().getNumParameters() == 0) { + if (JavaCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { + // dst has a component. + if (model.isPrimitiveType(resourceType)) { + getter.addStatement("return value;"); + } else { + // copy the current state to be returned as a 'value' + String implTypeName = resourceType.getImplementationTypeName(); + getter.addStatement("return new " + implTypeName + "(value);"); + } + } else { + // dst has no component. + String dstResName = JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(dst.getResourceHierarchy())); + if (model.isPrimitiveType(resourceType)) { + getter.addStatement("return " + dstResName + ";"); + } else { + // copy the current state to be returned as a 'value' + String implTypeName = resourceType.getImplementationTypeName(); + getter.addStatement("return new " + implTypeName + "(" + dstResName + ");"); + } + } + } else { + String[] sideEffects = new String[] {""}; + if (DataConstraintModel.typeList.isAncestorOf(dst.getParent().getResourceStateType())) { + Term selector = new Term(DataConstraintModel.get); + selector.addChild(new Field("value")); + selector.addChild(dst.getSelectors().get(dst.getSelectors().size() - 1).getExpression()); + String curState = selector.toImplementation(sideEffects); + getter.addStatement(sideEffects[0] + "return " + curState + ";"); + } else if (DataConstraintModel.typeMap.isAncestorOf(dst.getParent().getResourceStateType())) { + Term selector = new Term(DataConstraintModel.lookup); + selector.addChild(new Field("value")); + selector.addChild(dst.getSelectors().get(dst.getSelectors().size() - 1).getExpression()); + String curState = selector.toImplementation(sideEffects); + getter.addStatement(sideEffects[0] + "return " + curState + ";"); + } + } + } + } + // src side (for a chain of update method invocations) + ChannelMember in = null; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (src.getOutSideResources().contains(cm.getResource())) { + in = cm; + break; + } + } + String srcResName = null; + if (srcComponent == null) { + String srcParentResourceName = JavaCodeGenerator.getComponentName(src.getResourceHierarchy().getParent()); + srcComponent = componentMap.get(srcParentResourceName); + srcResName = srcResourceName; + } + for (MethodDeclaration srcUpdate: getUpdateMethods(srcComponent, srcResName)) { + ResourcePath dstRes = out.getResource(); + // Values of path parameters. + String pathParams = ""; + for (Expression pathParam: dstRes.getPathParams()) { + if (pathParam instanceof Variable) { + Variable pathVar = (Variable) pathParam; + pathParams += pathVar.getName() + ", "; + } + } + // Values of channel parameters. + String chParams = ""; + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + chParams += selVar.getName() + ", "; + } + } + String refParams = ""; + Set referredSet = referredResources.get(srcUpdate); + for (ChannelMember rc: ch.getReferenceChannelMembers()) { + // to get the value of reference member. + ResourcePath ref = rc.getResource(); + if (referredSet == null) { + referredSet = new HashSet<>(); + referredResources.put(srcUpdate, referredSet); + } + if (!dst.getInSideResources().contains(ref)) { + String refVarName = ref.getLeafResourceName(); + if (!referredSet.contains(ref)) { + referredSet.add(ref); + Expression refGetter = JavaCodeGenerator.pullAccessor.getCurrentStateAccessorFor(rc, in); + String[] sideEffects = new String[] {""}; + String refExp = refGetter.toImplementation(sideEffects); + String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); + srcUpdate.addFirstStatement(sideEffects[0] + refTypeName + " " + refVarName + " = " + refExp + ";"); + } + refParams += ", " + refVarName; + } + } + String updateMethodName = null; + if (JavaCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { + updateMethodName = "updateFrom" + srcResourceName; + } else { + updateMethodName = "update" + dstResourceName + "From" + srcResourceName; + } + // Value of the source side (input side) resource. + String srcFieldName = "value"; + if (!JavaCodeGenerator.generatesComponent(src.getResourceHierarchy())) { + srcFieldName = JavaCodeGenerator.toVariableName(srcResourceName); + } + if (!outsideOutputResource) { + // The destination resource is not outside. + if (srcComponent != dstComponent) { + srcUpdate.addStatement("this." + JavaCodeGenerator.toVariableName(dstComponent.getTypeName()) + "." + updateMethodName + "(" + pathParams + chParams + srcFieldName + refParams + ");"); + } else { + srcUpdate.addStatement("this." + updateMethodName + "(" + pathParams + chParams + srcFieldName + refParams + ");"); + } + } else { + // Use the reference field to refer to outside destination resource. + srcUpdate.addStatement("this." + JavaCodeGenerator.toVariableName(dstComponent.getTypeName()) + "." + updateMethodName + "(" + pathParams + chParams + srcFieldName + refParams + ");"); + } + } + for (MethodDeclaration srcInput: getInputMethods(srcComponent, src, model)) { + ResourcePath dstRes = out.getResource(); + // Values of path parameters. + String pathParams = ""; + for (Expression pathParam: dstRes.getPathParams()) { + if (pathParam instanceof Variable) { + Variable pathVar = (Variable) pathParam; + pathParams += pathVar.getName() + ", "; + } + } + // Values of channel parameters. + String chParams = ""; + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + chParams += selVar.getName() + ", "; + } + } + String refParams = ""; + Set referredSet = referredResources.get(srcInput); + for (ChannelMember rc: ch.getReferenceChannelMembers()) { + // to get the value of reference member. + ResourcePath ref = rc.getResource(); + if (referredSet == null) { + referredSet = new HashSet<>(); + referredResources.put(srcInput, referredSet); + } + if (!dst.getInSideResources().contains(ref)) { + String refVarName = ref.getLeafResourceName(); + if (!referredSet.contains(ref)) { + referredSet.add(ref); + Expression refGetter = JavaCodeGenerator.pullAccessor.getCurrentStateAccessorFor(rc, in); + String[] sideEffects = new String[] {""}; + String refExp = refGetter.toImplementation(sideEffects); + String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); + srcInput.addFirstStatement(sideEffects[0] + refTypeName + " " + refVarName + " = " + refExp + ";"); + } + refParams += ", " + refVarName; + } + } + String updateMethodName = null; + if (JavaCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { + updateMethodName = "updateFrom" + srcResourceName; + } else { + updateMethodName = "update" + dstResourceName + "From" + srcResourceName; + } + String srcFieldName = "value"; + if (!JavaCodeGenerator.generatesComponent(src.getResourceHierarchy())) { + srcFieldName = JavaCodeGenerator.toVariableName(srcResourceName); + } + if (!outsideOutputResource) { + // The destination resource is not outside. + if (srcComponent != dstComponent) { + srcInput.addStatement("this." + JavaCodeGenerator.toVariableName(dstComponent.getTypeName()) + "." + updateMethodName + "(" + pathParams + chParams + srcFieldName + refParams + ");"); + } else { + srcInput.addStatement("this." + updateMethodName + "(" + pathParams + chParams + srcFieldName + refParams + ");"); + } + } else { + // Use the reference field to refer to outside destination resource. + srcInput.addStatement("this." + JavaCodeGenerator.toVariableName(dstComponent.getTypeName()) + "." + updateMethodName + "(" + pathParams + chParams + srcFieldName + refParams + ");"); + } + } + } else if ((pushPull.getOptions().get(0) != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { + // for pull (or push/pull) data transfer + if (dstComponent == null) { + String dstParentResourceName = JavaCodeGenerator.getComponentName(dst.getResourceHierarchy().getParent()); + dstComponent = componentMap.get(dstParentResourceName); + } + MethodDeclaration getter = null; + if (JavaCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { + getter = getMethod(dstComponent, "getValue"); } else { - inputResourceToStateAccessor.put(((ResourceNode) dIn.getSource()).getResource(), JavaCodeGenerator.pullAccessor); + getter = getGetterMethod(dstComponent, dstResourceName); + } + if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { + boolean isContainedPush = false; + Map inputResourceToStateAccessor = new HashMap<>(); + for (Edge chToRes2: dst.getInEdges()) { + DataTransferChannel ch2 = ((ChannelNode) chToRes2.getSource()).getChannel(); + for (Edge resToCh2: chToRes2.getSource().getInEdges()) { + DataFlowEdge dIn = (DataFlowEdge) resToCh2; + ChannelMember in = null; + for (ChannelMember cm: ch2.getInputChannelMembers()) { + if (((ResourceNode) dIn.getSource()).getOutSideResources().contains(cm.getResource())) { + in = cm; + break; + } + } + if (((PushPullAttribute) dIn.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { + isContainedPush = true; + inputResourceToStateAccessor.put(in, JavaCodeGenerator.pushAccessor); + } else { + inputResourceToStateAccessor.put(in, JavaCodeGenerator.pullAccessor); + } + } + } + // for reference channel members + for (ChannelMember c: ch.getReferenceChannelMembers()) { + inputResourceToStateAccessor.put(c, JavaCodeGenerator.pullAccessor); // by pull data transfer + } + String[] sideEffects = new String[] {""}; + // generate a return statement. + // An input resource is outside. + if (!isContainedPush) { + // All incoming edges are in PULL style. + String curState = ch.deriveUpdateExpressionOf(out, JavaCodeGenerator.pullAccessor).getKey().toImplementation(sideEffects); + getter.addStatement(sideEffects[0] + "return " + curState + ";"); + } else { + // At least one incoming edge is in PUSH style. + String curState = ch.deriveUpdateExpressionOf(out, JavaCodeGenerator.pullAccessor, inputResourceToStateAccessor).getKey().toImplementation(sideEffects); + getter.addStatement(sideEffects[0] + "return " + curState + ";"); + } + } + if (outsideInputResource) { + // Update fields to refer to outside resources. + Map>> resourcePaths = ch.fillOutsideResourcePaths(out, JavaCodeGenerator.pullAccessor); + if (resourcePaths != null && resourcePaths.size() > 0) { + for (ChannelMember outsideMember: resourcePaths.keySet()) { + ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey(); + if (!JavaCodeGenerator.generatesComponent(outsidePath.getResourceHierarchy())) { + outsidePath = outsidePath.getParent(); + } + String outsideResName = JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(outsidePath.getResourceHierarchy())); + Set dependingMembers = resourcePaths.get(outsideMember).getValue(); + for (ChannelMember dependingMember: dependingMembers) { + ResourcePath dependingRes = dependingMember.getResource(); + ResourceNode dependingNode = null; + PushPullAttribute pushPull2 = null; + for (Edge resToCh2: resToCh.getDestination().getInEdges()) { + if (((ResourceNode) resToCh2.getSource()).getOutSideResources().contains(dependingRes)) { + dependingNode = (ResourceNode) resToCh2.getSource(); + pushPull2 = (PushPullAttribute) resToCh.getAttribute(); + } + } + TypeDeclaration dependingComponent = null; + if (JavaCodeGenerator.generatesComponent(dependingRes.getResourceHierarchy())) { + String dependingResourceName = JavaCodeGenerator.getComponentName(dependingRes.getResourceHierarchy()); + dependingComponent = componentMap.get(dependingResourceName); + } else { + String dependingParentResourceName = JavaCodeGenerator.getComponentName(dependingRes.getParent().getResourceHierarchy()); + dependingComponent = componentMap.get(dependingParentResourceName); + } + Expression outsideExp = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(outsidePath, null); + if (JavaCodeGenerator.generatesComponent(outsidePath.getResourceHierarchy())) { + outsideExp = ((Term) outsideExp).getChild(0); + } + Expression nextExp = dependingMember.getStateTransition().getNextStateExpression(); + if (nextExp != null && outsideExp instanceof Term) { + if (nextExp instanceof Variable) { + outsideExp = ((Term) outsideExp).substitute((Variable) nextExp, new Field(JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(dependingRes.getResourceHierarchy())))); + } else { + // ToDo. + } + } + if (dstComponent == dependingComponent) { + // In the common parent. + if (dependingNode != null) { // Inspect further dependency. + for (Edge chToRes2: dependingNode.getInEdges()) { + DataTransferChannel ch2 = ((ChannelNode) chToRes2.getSource()).getChannel(); + if (ch2.getInputChannelMembers().size() == 0) { + // In an input method of the parent component. + Set outs = ch2.getOutputChannelMembers(); + MethodDeclaration input = getInputMethod(dependingComponent, outs.iterator().next(), outs.size()); + String[] sideEffects = new String[] {""}; + String outsideAccessor = outsideExp.toImplementation(sideEffects); + input.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // change the reference field. + // Update constructor. + MethodDeclaration constructor = getConstructor(dependingComponent); + constructor.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // initialize the reference field. + } else { + boolean isPush = true; + for (Edge resToCh2: chToRes2.getSource().getInEdges()) { + if (((PushPullAttribute) resToCh2.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { + isPush = false; + break; + } + } + if (isPush) { + String[] sideEffects = new String[] {""}; + String outsideAccessor = outsideExp.toImplementation(sideEffects); + for (Edge resToCh2: chToRes2.getSource().getInEdges()) { + // In an update method of the parent component. + ResourceNode dependingResSrc = (ResourceNode) resToCh2.getSource(); + MethodDeclaration update = null; + if (JavaCodeGenerator.generatesComponent(dependingRes.getResourceHierarchy())) { + update = getUpdateMethod(dependingComponent, null, JavaCodeGenerator.getComponentName(dependingResSrc.getResourceHierarchy())); + } else { + String dependingResName = JavaCodeGenerator.getComponentName(dependingRes.getResourceHierarchy()); + update = getUpdateMethod(dependingComponent, dependingResName, JavaCodeGenerator.getComponentName(dependingResSrc.getResourceHierarchy())); + } + update.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // change the reference field. + } + // Update constructor. + MethodDeclaration constructor = getConstructor(dependingComponent); + constructor.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // initialize the reference field. + } + } + } + } + } else { + if (pushPull2.getOptions().get(0) == PushPullValue.PUSH) { + // In an update method of the destination component. + MethodDeclaration update = null; + if (JavaCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { + update = getUpdateMethod(dstComponent, null, JavaCodeGenerator.getComponentName(dependingRes.getResourceHierarchy())); + } else { + String dstResName = JavaCodeGenerator.getComponentName(dst.getResourceHierarchy()); + update = getUpdateMethod(dstComponent, dstResName, JavaCodeGenerator.getComponentName(dependingRes.getResourceHierarchy())); + } + String[] sideEffects = new String[] {""}; + String outsideAccessor = outsideExp.toImplementation(sideEffects); + update.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // change the reference field. + // Update constructor. + MethodDeclaration constructor = getConstructor(dstComponent); + constructor.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // initialize the reference field. + } + } + } + } + } } } - // for reference channel members - for (ChannelMember c: d.getChannel().getReferenceChannelMembers()) { - inputResourceToStateAccessor.put(c.getResource(), JavaCodeGenerator.pullAccessor); // by pull data transfer - } - String[] sideEffects = new String[] {""}; - // generate a return statement. - if (!isContainedPush) { - // All incoming edges are in PULL style. - String curState = d.getChannel().deriveUpdateExpressionOf(out, JavaCodeGenerator.pullAccessor).toImplementation(sideEffects); - getter.addStatement(sideEffects[0] + "return " + curState + ";"); - } else { - // At least one incoming edge is in PUSH style. - String curState = d.getChannel().deriveUpdateExpressionOf(out, JavaCodeGenerator.pullAccessor, inputResourceToStateAccessor).toImplementation(sideEffects); - getter.addStatement(sideEffects[0] + "return " + curState + ";"); - } } - } + } } } } + // for source nodes - String mainTypeName = JavaCodeGenerator.mainTypeName.substring(0,1).toLowerCase() + JavaCodeGenerator.mainTypeName.substring(1); - TypeDeclaration mainType = typeMap.get(mainTypeName); - for (Node n: graph.getNodes()) { - ResourceNode resource = (ResourceNode) n; - String resourceName = resource.getResource().getResourceName(); - TypeDeclaration type = typeMap.get(resourceName); - if (type != null) { - // getter method - MethodDeclaration getter = getGetterMethod(type); - if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { - Type resourceType = resource.getResource().getResourceStateType(); - 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 ((child.getChildren() == null || child.getChildren().size() == 0) && child.getNumParameters() == 0) { + // the child is not a class + childGetter = new Term(new Symbol("get" + childTypeName, 1, Symbol.Type.METHOD)); + childGetter.addChild(new Constant("this")); + } else { + // the child is a class + childGetter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); + childGetter.addChild(new Field(fieldName, JavaCodeGenerator.getImplStateType(child))); + } + composer = new Term(DataConstraintModel.insert); + composer.addChild(composerSub); + composer.addChild(new Constant(fieldName, DataConstraintModel.typeString)); // key + composer.addChild(childGetter); // value + composer.setType(DataConstraintModel.typeMap); + composerSub = composer; + } + composer.setType(stateGetter.getReturnType()); + String[] sideEffects = new String[] {""}; + String returnValue = composer.toImplementation(sideEffects); + if (sideEffects[0] != null) { + stateGetter.addStatement(sideEffects[0] + "return " + returnValue+ ";"); + } else { + stateGetter.addStatement("return " + returnValue+ ";"); + } + } + } + } + } + + // descendant getter method + 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; + } + } + 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; + params++; + } 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; + params++; + } + 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; + 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]; @@ -242,20 +767,115 @@ if (input.getBody() == null || !input.getBody().getStatements().contains(updateStatement)) { input.addFirstStatement(updateStatement); } - if (mainType != null) { - MethodDeclaration mainInput = getMethod(mainType, input.getName()); - if (mainInput != null) { - String args = ""; - String delimitar = ""; - if (out.getStateTransition().getMessageExpression() instanceof Term) { - Term message = (Term) out.getStateTransition().getMessageExpression(); - for (Variable var: message.getVariables().values()) { - args += delimitar + var.getName(); - delimitar = ", "; + } else { + String updateStatement = ""; + if (sideEffects[0] != null) { + updateStatement = sideEffects[0]; + updateStatement = updateStatement.replace(".value", "." + JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(resource))); + } + if (DataConstraintModel.typeList.isAncestorOf(resource.getParent().getResourceStateType())) { + Term selector = new Term(DataConstraintModel.set); + selector.addChild(new Field("value")); + selector.addChild(new Variable(input.getParameters().get(input.getParameters().size() - 2).getName())); + selector.addChild(new Constant(newState)); + String[] sideEffects2 = new String[] {""}; + String newList = selector.toImplementation(sideEffects2); + updateStatement += sideEffects2[0]; + } else if (DataConstraintModel.typeMap.isAncestorOf(resource.getParent().getResourceStateType())) { + Term selector = new Term(DataConstraintModel.insert); + selector.addChild(new Field("value")); + selector.addChild(new Variable(input.getParameters().get(input.getParameters().size() - 2).getName())); + selector.addChild(new Constant(newState)); + String[] sideEffects2 = new String[] {""}; + String newMap = selector.toImplementation(sideEffects2); + updateStatement += sideEffects2[0]; + } else if (!(updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect())) { + updateStatement += "this." + JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(resource)) + " = " + newState + ";"; + } + if (updateStatement != null && (input.getBody() == null || !input.getBody().getStatements().contains(updateStatement))) { + input.addFirstStatement(updateStatement); + } + } + + // In the main type + if (mainComponent != null) { + Expression message = out.getStateTransition().getMessageExpression(); + String inputAccessorName = input.getName(); + if (message instanceof Term) { + inputAccessorName = ((Term) message).getSymbol().getImplName(); + } else if (message instanceof Variable) { + inputAccessorName = ((Variable) message).getName(); + } + MethodDeclaration inputAccessor = getMethod(mainComponent, inputAccessorName); + if (inputAccessor != null) { + Set referredSet = referredResources.get(input); + for (ChannelMember rc: ch.getReferenceChannelMembers()) { + // For each reference channel member, get the current state of the reference side resource by pull data transfer. + ResourcePath ref = rc.getResource(); + if (referredSet == null) { + referredSet = new HashSet<>(); + referredResources.put(input, referredSet); + } + if (!out.getResource().equals(ref)) { + String refVarName = ref.getLeafResourceName(); + if (!referredSet.contains(ref)) { + referredSet.add(ref); + Expression refGetter = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(ref, null); + sideEffects = new String[] {""}; + String refExp = refGetter.toImplementation(sideEffects); + String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); + inputAccessor.addFirstStatement(sideEffects[0] + refTypeName + " " + refVarName + " = " + refExp + ";"); } } - mainInput.addStatement("this." + resourceName + "." + input.getName() + "(" + args + ");"); } + Expression resExp = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(out.getResource(), null); + List args = new ArrayList<>(); + if (resExp instanceof Term) { + // to access the parent + if (((Term) resExp).getChildren().size() > 1 && ((Term) resExp).getChild(1) instanceof Variable) { + args.add(((Variable)((Term) resExp).getChild(1)).getName()); + } + resExp = ((Term) resExp).getChild(0); + } + String resourceAccess = resExp.toImplementation(new String[] {null}); + // Values of channel parameters. + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + if (!args.contains(selVar.getName())) { + args.add(selVar.getName()); + } + } + } + // Values of message parameters. + if (message instanceof Term) { + for (Map.Entry varEnt: message.getVariables().entrySet()) { + String refVarName = null; + for (ChannelMember rc: ch.getReferenceChannelMembers()) { + Expression varExp = rc.getStateTransition().getMessageExpression().getSubTerm(varEnt.getKey()); + if (varExp != null && rc.getStateTransition().getCurStateExpression().contains(varExp)) { + refVarName = rc.getResource().getLeafResourceName(); + break; + } + } + if (refVarName != null) { + if (!args.contains(refVarName)) { + args.add(refVarName); + } + } else { + if (!args.contains(varEnt.getValue().getName())) { + args.add(varEnt.getValue().getName()); + } + } + } + } + String argsStr = ""; + String delimiter = ""; + for (String arg: args) { + argsStr += delimiter + arg; + delimiter = ", "; + } + inputAccessor.addStatement(resourceAccess + "." + input.getName() + "(" + argsStr + ");"); } } } @@ -269,37 +889,119 @@ return codes; } - private static MethodDeclaration getUpdateMethod(TypeDeclaration type, TypeDeclaration from) { - for (MethodDeclaration m: type.getMethods()) { - if (m.getName().equals("update" + from.getTypeName())) return m; + private static void replaceJsonTermWithConstructorInvocation(Expression exp, Type replacedJsonType, String replacingClassName, TypeDeclaration descendantComponent) { + 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 = ""; + for (VariableDeclaration var: descendantConstructor.getParameters()) { + JsonAccessor jsonMember = new JsonAccessor(DataConstraintModel.dot); + jsonMember.addChild(jsonTerm); + jsonMember.addChild(new Constant(var.getName(), DataConstraintModel.typeString)); + Expression param = jsonMember.reduce(); + if (param != null) { + if (param instanceof Term) { + if (((Term) param).getType() == null) { + ((Term) param).setType(var.getType()); + } + } else if (param instanceof Variable) { + if (((Variable) param).getType() == null) { + ((Variable) param).setType(var.getType()); + } + } + constructorInvocation = constructorInvocation + delimiter + param.toImplementation(null); + } else { + constructorInvocation = constructorInvocation + delimiter + var.getName(); + } + delimiter = ", "; + } + constructorInvocation += ")"; + ((Term) exp).replaceSubTerm(termEnt.getKey(), new Constant(constructorInvocation, 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); + } + } + } + } + + private static MethodDeclaration getConstructor(TypeDeclaration component) { + for (MethodDeclaration m: component.getMethods()) { + if (m.isConstructor()) return m; } return null; } - private static List getUpdateMethods(TypeDeclaration type) { + private static MethodDeclaration getUpdateMethod(TypeDeclaration component, String dstResName, String srcResName) { + for (MethodDeclaration m: component.getMethods()) { + if (dstResName == null) { + if (m.getName().equals("updateFrom" + srcResName)) return m; + } else { + if (m.getName().equals("update" + dstResName + "From" + srcResName)) return m; + } + } + return null; + } + + private static List getUpdateMethods(TypeDeclaration component, String resName) { List updates = new ArrayList<>(); - for (MethodDeclaration m: type.getMethods()) { - if (m.getName().startsWith("update")) { - updates.add(m); + for (MethodDeclaration m: component.getMethods()) { + if (resName == null) { + if (m.getName().startsWith("updateFrom")) { + updates.add(m); + } + } else { + if (m.getName().startsWith("update" + resName + "From")) { + updates.add(m); + } } } return updates; } - - private static MethodDeclaration getGetterMethod(TypeDeclaration type) { - for (MethodDeclaration m: type.getMethods()) { - if (m.getName().startsWith("get")) return m; + + private static MethodDeclaration getGetterMethod(TypeDeclaration component, String resourceName) { + for (MethodDeclaration m: component.getMethods()) { + if (m.getName().startsWith("get" + resourceName)) return m; } return null; } - private static Map> getIOChannelsAndMembers(ResourceNode resource, DataTransferModel model) { + private static 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) { @@ -314,14 +1016,14 @@ return ioChannelsAndMembers; } - private static List getInputMethods(TypeDeclaration type, ResourceNode resource, DataTransferModel model) { + private static List getInputMethods(TypeDeclaration component, ResourceNode resource, DataTransferModel model) { List inputs = new ArrayList<>(); - for (Channel c: model.getIOChannels()) { + for (Channel c: model.getInputChannels()) { DataTransferChannel channel = (DataTransferChannel) c; // I/O channel for (ChannelMember out: channel.getOutputChannelMembers()) { - if (out.getResource().equals(resource.getResource())) { - MethodDeclaration input = getInputMethod(type, out); + if (resource.getInSideResources().contains(out.getResource())) { + MethodDeclaration input = getInputMethod(component, out, channel.getOutputChannelMembers().size()); inputs.add(input); } } @@ -329,20 +1031,39 @@ return inputs; } - private static MethodDeclaration getInputMethod(TypeDeclaration type, ChannelMember out) { - MethodDeclaration input = null; - if (out.getStateTransition().getMessageExpression() instanceof Term) { - Term message = (Term) out.getStateTransition().getMessageExpression(); - input = getMethod(type, message.getSymbol().getImplName()); - } else if (out.getStateTransition().getMessageExpression() instanceof Variable) { - Variable message = (Variable) out.getStateTransition().getMessageExpression(); - input = getMethod(type, message.getName()); + private static List getInputMethods(TypeDeclaration component, ResourceHierarchy resource, DataTransferModel model) { + List inputs = new ArrayList<>(); + for (Channel c: model.getInputChannels()) { + DataTransferChannel channel = (DataTransferChannel) c; + // I/O channel + for (ChannelMember out: channel.getOutputChannelMembers()) { + if (resource.equals(out.getResource().getResourceHierarchy())) { + MethodDeclaration input = getInputMethod(component, out, channel.getOutputChannelMembers().size()); + inputs.add(input); + } + } } + return inputs; + } + + private static MethodDeclaration getInputMethod(TypeDeclaration component, ChannelMember cm, int outNumber) { + String inputMethodName = null; + if (cm.getStateTransition().getMessageExpression() instanceof Term) { + Term message = (Term) cm.getStateTransition().getMessageExpression(); + inputMethodName =message.getSymbol().getImplName(); + } else if (cm.getStateTransition().getMessageExpression() instanceof Variable) { + Variable message = (Variable) cm.getStateTransition().getMessageExpression(); + inputMethodName = message.getName(); + } + if (outNumber > 1) { + inputMethodName += "For" + JavaCodeGenerator.getComponentName(cm.getResource().getResourceHierarchy()); + } + MethodDeclaration input = getMethod(component, inputMethodName); return input; } - private static MethodDeclaration getMethod(TypeDeclaration type, String methodName) { - for (MethodDeclaration m: type.getMethods()) { + private static MethodDeclaration getMethod(TypeDeclaration component, String methodName) { + for (MethodDeclaration m: component.getMethods()) { if (m.getName().equals(methodName)) return m; } return null; diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JavaSpecific.java b/AlgebraicDataflowArchitectureModel/src/generators/JavaSpecific.java new file mode 100644 index 0000000..6134a5f --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/generators/JavaSpecific.java @@ -0,0 +1,293 @@ +package generators; + +import java.util.ArrayList; +import java.util.List; + +import code.ast.CompilationUnit; +import code.ast.FieldDeclaration; +import code.ast.ImportDeclaration; +import code.ast.MethodDeclaration; +import code.ast.TypeDeclaration; +import code.ast.VariableDeclaration; +import models.algebra.Expression; +import models.algebra.Parameter; +import models.algebra.Term; +import models.algebra.Type; +import models.algebra.Variable; +import models.dataConstraintModel.DataConstraintModel; + +public class JavaSpecific implements ILanguageSpecific { + public static final Type typeVoid = new Type("Void", "void"); + public static final String self = "this"; + + @Override + public CompilationUnit newCompilationUnit(TypeDeclaration component) { + CompilationUnit cu = new CompilationUnit(component); + cu.addImport(new ImportDeclaration("java.util.*")); + return cu; + } + + @Override + public TypeDeclaration newTypeDeclaration(String typeName) { + return new TypeDeclaration(typeName); + } + + @Override + public VariableDeclaration newVariableDeclaration(Type type, String varName) { + return new VariableDeclaration(type, varName); + } + + @Override + public MethodDeclaration newMethodDeclaration(String methodName, Type returnType) { + if (returnType == null) { + returnType = typeVoid; + } + return new MethodDeclaration(methodName, returnType); + } + + @Override + public MethodDeclaration newMethodDeclaration(String methodName, boolean isConstructor, Type returnType, List parameters) { + if (returnType == null && !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 getFieldAccessor(String fieldName) { + return self + "." + fieldName; + } + + @Override + public String getMethodInvocation(String methodName) { + return self + "." + methodName + "()"; + } + + @Override + public String getMethodInvocation(String methodName, List parameters) { + if (parameters == null) return getMethodInvocation( methodName); + String invocation = self + "." + methodName + "("; + if (parameters.size() > 0) { + for (int i = 0; i < parameters.size(); i++) { + if (i < parameters.size() - 1) { + invocation += parameters.get(i) + ", "; + } else { + invocation += parameters.get(i); + } + } + } + invocation += ")"; + return invocation; + } + + @Override + public String getMethodInvocation(String receiverName, String methodName) { + return receiverName + "." + methodName + "()"; + } + + @Override + public String getMethodInvocation(String receiverName, String methodName, List parameters) { + if (parameters == null) return getMethodInvocation(receiverName, methodName); + String invocation = receiverName + "." + methodName + "("; + if (parameters.size() > 0) { + for (int i = 0; i < parameters.size(); i++) { + if (i < parameters.size() - 1) { + invocation += parameters.get(i) + ", "; + } else { + invocation += parameters.get(i); + } + } + } + invocation += ")"; + return invocation; + } + + @Override + public String getConstructorInvocation(String componentName, List parameters) { + String invocation = "new " + componentName + "("; + if (parameters.size() > 0) { + for (int i = 0; i < parameters.size(); i++) { + if (i < parameters.size() - 1) { + invocation += parameters.get(i) + ", "; + } else { + invocation += parameters.get(i); + } + } + } + invocation += ")"; + return invocation; + } + + @Override + public String getReturnStatement(String returnValue) { + return "return " + returnValue; + } + + @Override + public String getIfStatement(Term condition, String block) { + return "if (" + condition.toImplementation(new String[] {})+ ") {\n" + "\t" + block.replace("\n", "\n\t") + "\n}"; + } + + @Override + public String toComponentName(String name) { + return name.substring(0, 1).toUpperCase() + name.substring(1); + } + + @Override + public String toVariableName(String name) { + return name.substring(0, 1).toLowerCase() + name.substring(1); + } + + @Override + public String getMainComponentName() { + return "Main"; + } + + @Override + public String getAssignment() { + return " = "; + } + + @Override + public String getStatementDelimiter() { + return ";"; + } + + @Override + public String getStringDelimiter() { + return "\""; + } + + @Override + public String getTupleGet(String tupleExp, int idx, int length) { + Expression t = new Variable(tupleExp, DataConstraintModel.typeTuple); + for (int i = 0; i < idx; i++) { + Term next = new Term(DataConstraintModel.snd); + next.addChild(t); + t = next; + } + if (idx < length - 1) { + Term last = new Term(DataConstraintModel.fst); + last.addChild(t); + t = last; + } + return t.toImplementation(new String[]{}); + } + + @Override + public String getDecomposedTuple(String tupleExp, VariableDeclaration tupleVar, List vars) { + String statements = ""; + statements += getVariableDeclaration(tupleVar.getType().getInterfaceTypeName(), tupleVar.getName()) + + getAssignment() + tupleExp + getStatementDelimiter(); + for (int i = 0; i < vars.size(); i++) { + VariableDeclaration var = vars.get(i); + statements += "\n" + getVariableDeclaration(var.getType().getInterfaceTypeName(), var.getName()) + + getAssignment() + + getTupleGet(tupleVar.getName(), i, vars.size()) + + getStatementDelimiter(); + } + return statements; + } + + @Override + public boolean isValueType(Type type) { + if (type == DataConstraintModel.typeInt + || type == DataConstraintModel.typeLong + || type == DataConstraintModel.typeFloat + || type == DataConstraintModel.typeDouble + || type == DataConstraintModel.typeBoolean) { + return true; + } + return false; + } + + + @Override + public boolean isVoidType(Type type) { + if (type == typeVoid) { + return true; + } + return false; + } + + private String getImplementationTypeName(Type type) { + if (type == null) + return "Object"; + String wrapperType = DataConstraintModel.getWrapperType(type); + if (wrapperType != null) + return wrapperType; + return type.getImplementationTypeName(); + } + + private String getInterfaceTypeName(Type type) { + if (type == null) + return "Object"; + String wrapperType = DataConstraintModel.getWrapperType(type); + if (wrapperType != null) + return wrapperType; + return type.getInterfaceTypeName(); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JerseyCodeGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/JerseyCodeGenerator.java index dbd0b45..4f7d7ec 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/JerseyCodeGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/JerseyCodeGenerator.java @@ -1,8 +1,14 @@ package generators; +import java.util.AbstractMap; import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; +import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.Stack; import code.ast.Annotation; import code.ast.Block; @@ -17,6 +23,7 @@ import models.algebra.Expression; import models.algebra.Field; import models.algebra.Parameter; +import models.algebra.Position; import models.algebra.Symbol; import models.algebra.Term; import models.algebra.Type; @@ -24,7 +31,9 @@ import models.dataConstraintModel.Channel; import models.dataConstraintModel.ChannelMember; import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.ResourceHierarchy; import models.dataConstraintModel.ResourcePath; +import models.dataConstraintModel.Selector; import models.dataFlowModel.DataTransferModel; import models.dataFlowModel.DataTransferChannel; import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor; @@ -33,6 +42,7 @@ import models.dataFlowModel.DataFlowEdge; import models.dataFlowModel.DataFlowGraph; import models.dataFlowModel.ResourceNode; +import models.dataFlowModel.ChannelNode; import models.dataFlowModel.StoreAttribute; /** @@ -44,6 +54,7 @@ public class JerseyCodeGenerator { public static final Type typeVoid = new Type("Void", "void"); public static final Type typeClient = new Type("Client", "Client"); + public static boolean differentTreesAsDifferentServices = true; private static String defaultMainTypeName = "Main"; static String mainTypeName = defaultMainTypeName; @@ -59,154 +70,922 @@ 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; + 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("javax.ws.rs.*")); + cu.addImport(new ImportDeclaration("javax.ws.rs.client.*")); + cu.addImport(new ImportDeclaration("javax.ws.rs.core.*")); + cu.addImport(new ImportDeclaration("org.springframework.stereotype.Component")); + cu.addImport(new ImportDeclaration("com.fasterxml.jackson.databind.ObjectMapper")); + cu.addImport(new ImportDeclaration("com.fasterxml.jackson.core.JsonProcessingException")); + } + codes.add(cu); + + // Declare the field to store the state in the type of each resource. + if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { + ResourceHierarchy res = resourceNode.getResourceHierarchy(); + Set children = res.getChildren(); + if (children == null || children.size() == 0) { + // leaf resource. + Type fieldType = getImplStateType(res); + component.addField(new FieldDeclaration(fieldType, "value", getInitializer(res))); + Map nameToParam = constructorParams.get(res); + if (nameToParam == null) { + nameToParam = new HashMap<>(); + constructorParams.put(resourceNode.getResourceHierarchy(), nameToParam); + } + String varName = toVariableName(resourceName); + if (nameToParam.get(varName) == null) { + nameToParam.put(varName, new VariableDeclaration(fieldType, varName)); + } + } else { + ResourceHierarchy child = children.iterator().next(); + if (children.size() == 1 && child.getNumParameters() > 0) { + // map or list. + component.addField(new FieldDeclaration(getImplStateType(res), "value", getInitializer(res))); + } else { + // class + for (ResourceHierarchy c: children) { + String childTypeName = getComponentName(c); + Type childType = null; + if (generatesComponent(c)) { + // The child has a component. + childType = new Type(childTypeName, childTypeName); + String fieldName = toVariableName(childTypeName); + component.addField(new FieldDeclaration(childType, fieldName, "new " + childTypeName + "()")); + } + } + } } } - MethodDeclaration update = new MethodDeclaration("update" + srcResName, false, typeVoid, vars); - for (ChannelMember cm: re.getChannel().getOutputChannelMembers()) { - if (cm.getResource() == rn.getResource()) { - if (cm.getStateTransition().isRightUnary()) { + + // Declare the getter method to obtain the resource state in the component of each resource. + MethodDeclaration stateGetter = new MethodDeclaration("getValue", getImplStateType(resourceNode.getResourceHierarchy())); + if (resourceNode.getResourceHierarchy().getParent() == null) { + // Since this getter is also an accessor. + stateGetter.addAnnotation(new Annotation("Produces", "MediaType.APPLICATION_JSON")); + stateGetter.addAnnotation(new Annotation("GET")); + } + component.addMethod(stateGetter); + } + if (component != null) { + // 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 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; +// } +// } + } + + // Declare the state field and reference fields in the parent component. + boolean bDeclareClientField = false; + if (component == null) { + // Declare reference fields for push/pull data transfer. + boolean noPullTransfer = true; + for (Edge resToCh : resourceNode.getOutEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + for (Edge chToRes: re.getDestination().getOutEdges()) { + ResourceHierarchy dstRes = ((ResourceNode) chToRes.getDestination()).getResourceHierarchy(); + // Check if the output resource is outside of the channel scope. + boolean outsideOutputResource = false; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (((ResourceNode) chToRes.getDestination()).getInSideResources().contains(cm.getResource()) && cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + if (outsideOutputResource) { + // Declare a field in the parent component to refer to the destination resource of push transfer. + if (!generatesComponent(dstRes)) { + dstRes = dstRes.getParent(); + } + String dstResName = getComponentName(dstRes); +// if (resourceNode.getOutSideResource().getCommonPrefix(dstRes) == null && differentTreesAsDifferentServices) { + // Inter-service + if (!bDeclareClientField) { + // Declare a client field to connect to the destination resource of push transfer. + FieldDeclaration clientField = new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()"); + fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), clientField)); + bDeclareClientField = true; + } +// } else { +// // Inner-service +// // Declare a field to directly refer to the destination resource of push transfer. +// if (resourceNode.getParent().getResourceHierarchy() != dstRes.getResourceHierarchy()) { +// FieldDeclaration refFieldForPush = new FieldDeclaration(new Type(dstResName, dstResName), toVariableName(dstResName)); +// fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), refFieldForPush)); +// } +// } + } + } + } + for (Edge chToRes : resourceNode.getInEdges()) { + for (Edge resToCh: chToRes.getSource().getInEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(ch); + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().equals(srcRes) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + if (outsideInputResource) { + // Declare a field in the parent component to refer to the source resource of pull transfer. + if (!generatesComponent(srcRes.getResourceHierarchy())) { + srcRes = srcRes.getParent(); + } + String srcResName = getComponentName(srcRes.getResourceHierarchy()); +// if (resourceNode.getOutSideResource().getCommonPrefix(srcRes) == null && differentTreesAsDifferentServices) { + // Inter-service + if (!bDeclareClientField) { + // Declare a client field to connect to the source resource of pull transfer. + FieldDeclaration clientField = new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()"); + fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), clientField)); + bDeclareClientField = true; + } +// } else { +// // Inner-service +// // Declare a field to directly refer to the source resource of pull transfer. +// if (resourceNode.getParent().getResourceHierarchy() != srcRes.getResourceHierarchy()) { +// FieldDeclaration refFieldForPull = new FieldDeclaration(new Type(srcResName, srcResName), toVariableName(srcResName)); +// fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), refFieldForPull)); +// } +// } + noPullTransfer = false; + } + } + } + // Declare the state field in the parent component. + ResourceHierarchy res = resourceNode.getResourceHierarchy(); + if (((StoreAttribute) resourceNode.getAttribute()).isStored() && noPullTransfer && res.getNumParameters() == 0) { + String resName = getComponentName(res); + FieldDeclaration stateField = new FieldDeclaration(res.getResourceStateType(), toVariableName(resName)); + fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), stateField)); + + + Map nameToParam = constructorParams.get(resourceNode.getParent().getResourceHierarchy()); + if (nameToParam == null) { + nameToParam = new HashMap<>(); + constructorParams.put(resourceNode.getParent().getResourceHierarchy(), nameToParam); + } + String varName = toVariableName(resName); + if (nameToParam.get(varName) == null) { + nameToParam.put(varName, new VariableDeclaration(res.getResourceStateType(), varName)); + } + } + } + + // Declare the getter method to obtain the resource state in an ancestor component. + 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 = resourceNode.getPrimaryResourcePath(); + Expression getState = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(resPath, resPath.getRoot()); + getterAccessor.getBody().addStatement("return " + getState.toImplementation(new String[] {null}) + ";"); + + getterAccessor.addAnnotation(new Annotation("Produces", "MediaType.APPLICATION_JSON")); + getterAccessor.addAnnotation(new Annotation("GET")); + if (resourcePath.length() > 0) { + getterAccessor.addAnnotation(new Annotation("Path", "\"" + resourcePath + "\"")); + } + getterAccessors.put(resourceNode.getResourceHierarchy(), getterAccessor); + } + + // Declare a client field and update methods from other resources. + for (Edge resToCh: resourceNode.getOutEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().equals(resourceNode.getOutSideResource(ch)) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + for (ChannelMember cm: ch.getOutputChannelMembers()) { + ResourcePath dstRes = cm.getResource(); + // Check if the output resource is outside of the channel scope. + boolean outsideOutputResource = cm.isOutside(); + if (!bDeclareClientField && ((((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource)) { + // For push transfer. + if (!generatesComponent(dstRes.getResourceHierarchy())) { + dstRes = dstRes.getParent(); + } + String dstResName = getComponentName(dstRes.getResourceHierarchy()); + if (outsideOutputResource || (resourceNode.getOutSideResource(ch).getCommonPrefix(dstRes) == null && differentTreesAsDifferentServices)) { + // Inter-service + if (!bDeclareClientField) { + // Declare a client field to connect to the destination resource of push transfer. + FieldDeclaration clientField = new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()"); + if (component != null) { + // A component is created for this resource. + component.addField(clientField); + } else { + // No component is created for this resource. + fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), clientField)); + } + bDeclareClientField = true; + } + } else { + // Inner-service + // Declare a field to directly refer to the destination resource of push transfer. + FieldDeclaration dstRefField = new FieldDeclaration(new Type(dstResName, dstResName), toVariableName(dstResName)); + if (component != null) { + // A component is created for this resource. + if (resourceNode.getResourceHierarchy() != dstRes.getResourceHierarchy()) { + component.addField(dstRefField); + } + } else { + // No component is created for this resource. + if (resourceNode.getParent().getResourceHierarchy() != dstRes.getResourceHierarchy()) { + fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), dstRefField)); + } + } + } + } + } + } + for (Edge chToRes : resourceNode.getInEdges()) { + for (Edge resToCh: chToRes.getSource().getInEdges()) { + DataFlowEdge re = (DataFlowEdge) resToCh; + DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(ch); + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource = false; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.getResource().equals(srcRes) && cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } + } + // Check if the output resource is outside of the channel scope. + boolean outsideOutputResource = false; + ChannelMember out = null; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (resourceNode.getInSideResources().contains(cm.getResource())) { + out = cm; + if (cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } + } + } + String srcResName = getComponentName(srcRes.getResourceHierarchy()); + Type srcType = srcRes.getResourceStateType(); + if (!generatesComponent(srcRes.getResourceHierarchy())) { + srcRes = srcRes.getParent(); + } + if ((((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { + // For pull transfer. + if (outsideInputResource || (resourceNode.getInSideResource(ch).getCommonPrefix(srcRes) == null && differentTreesAsDifferentServices)) { + // Inter-service + if (!bDeclareClientField) { + // Declare a client field to connect to the source resource of pull transfer. + FieldDeclaration clientField = new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()"); + if (component != null) { + // A component is created for this resource. + component.addField(clientField); + } else { + // No component is created for this resource. + fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), clientField)); + } + bDeclareClientField = true; + } + } else { + // Inner-service + // Declare a field to directly refer to the source resource of pull transfer. + FieldDeclaration srcRefField = new FieldDeclaration(new Type(srcResName, srcResName), toVariableName(srcResName)); + if (component != null) { + // A component is created for this resource. + if (resourceNode.getResourceHierarchy() != srcRes.getResourceHierarchy()) { + component.addField(srcRefField); + } + } else { + // No component is created for this resource. + if (resourceNode.getParent().getResourceHierarchy() != srcRes.getResourceHierarchy()) { + fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), srcRefField)); + } + } + } + } else { + // For push transfer. + boolean hasRestAPI = false; + boolean isRestAPI = false; + if (outsideOutputResource || (resourceNode.getInSideResource(ch).getCommonPrefix(srcRes) == null && differentTreesAsDifferentServices)) { + // Inter-service + hasRestAPI = true; + if (resourceNode.getParent() == null) { + // A root resource + isRestAPI = true; + } + } + // Declare an update method in the type of the destination resource. + ArrayList params = new ArrayList<>(); + getUpdateResourcePathAndPathParams(out.getResource(), params, isRestAPI); // Path parameters to identify the self resource. + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + VariableDeclaration chParam = new VariableDeclaration(selVar.getType(), selVar.getName()); + if (isRestAPI) chParam.addAnnotation(new Annotation("FormParam", "\"" + selVar.getName() + "\"")); + params.add(chParam); // A channel parameter to specify the context of the collaboration. + } + } + String srcName = toVariableName(srcResName); + VariableDeclaration param = new VariableDeclaration(srcType, srcName); + if (isRestAPI) param.addAnnotation(new Annotation("FormParam", "\"" + srcName + "\"")); + params.add(param); // The state of the source resource to carry the data-flow. + for (ResourcePath refRes: ch.getReferenceResources()) { + if (!refRes.equals(resourceNode.getInSideResource(ch))) { + param = new VariableDeclaration(refRes.getResourceStateType(), refRes.getLeafResourceName()); + if (isRestAPI) param.addAnnotation(new Annotation("FormParam", "\"" + refRes.getLeafResourceName() + "\"")); + params.add(param); + } + } + MethodDeclaration update = null; + if (component != null) { + // A component is created for this resource. + update = new MethodDeclaration("updateFrom" + srcResName, false, typeVoid, params); + } else { + // No component is created for this resource. + String resourceName = getComponentName(resourceNode.getResourceHierarchy()); + update = new MethodDeclaration("update" + resourceName + "From" + srcResName, false, typeVoid, params); + } + // Determine whether the update method is put or post. + boolean isPut = false; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (resourceNode.getInSideResources().contains(cm.getResource())) { + if (cm.getStateTransition().isRightUnary()) { + isPut = true; + } else { + isPut = false; + } + } + } + if (isRestAPI) { + if (isPut) { update.addAnnotation(new Annotation("PUT")); } else { update.addAnnotation(new Annotation("POST")); } } - } - if (rn.getInEdges().size() > 1) { - // For each source resource, a child resource is defined in the destination resource so that its state can be updated separately. - update.addAnnotation(new Annotation("Path", "\"/" + srcName + "\"")); - // Declare a field to cash the state of the source resource in the type of the destination resource. - ResourcePath cashResId = ((ResourceNode) re.getSource()).getResource(); - type.addField(new FieldDeclaration(cashResId.getResourceStateType(), srcName, getInitializer(cashResId))); - } - type.addMethod(update); - } - } - -// // Declare a client field to connect to the source resource of reference transfer. -// if (!bDeclareClientField) { -// for (ChannelGenerator cg : model.getChannelGenerators()) { -// DataflowChannelGenerator dcg = ((DataflowChannelGenerator) cg); -// for (ChannelMember cm : dcg.getOutputChannelMembers()) { -// if (cm.getIdentifierTemplate().getResourceName().equals(type.getTypeName().toLowerCase())) { -// if (dcg.getReferenceChannelMembers().size() > 0) { -// // If there exists one or more reference channel member. -// type.addField(new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()")); -// bDeclareClientField = true; -// break; -// } -// } -// } -// if (bDeclareClientField) break; -// } -// } - - // Declare input methods in resources. - for (Channel cg : model.getIOChannels()) { - for (ChannelMember cm : ((DataTransferChannel) cg).getOutputChannelMembers()) { - if (cm.getResource().equals(rn.getResource())) { - Expression message = cm.getStateTransition().getMessageExpression(); - if (message.getClass() == Term.class) { - ArrayList params = new ArrayList<>(); - for (Variable var: message.getVariables().values()) { - String paramName = var.getName(); - VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); - param.addAnnotation(new Annotation("FormParam", "\"" + paramName + "\"")); - params.add(param); + // Calculate in-degree of the destination resource. + Set inResources = new HashSet<>(); + for (ResourceNode rn: graph.getResourceNodes(out.getResource().getResourceHierarchy())) { + // ResourceNodes that have the same ResourceHierarchy. + for (Edge chToRes2: rn.getInEdges()) { + for (Edge resToCh2: chToRes2.getSource().getInEdges()) { + inResources.add(((ResourceNode) resToCh2.getSource()).getResourceHierarchy()); + } } - MethodDeclaration input = new MethodDeclaration( - ((Term) cm.getStateTransition().getMessageExpression()).getSymbol().getImplName(), - false, typeVoid, params); - if (cm.getStateTransition().isRightUnary()) { - input.addAnnotation(new Annotation("PUT")); + } + int inDegree = inResources.size(); + if (inDegree > 1 + || (inDegree == 1 && ch.getInputChannelMembers().iterator().next().getStateTransition().isRightPartial())) { + // Declare a field to cache the state of the source resource in the type of the destination resource. + ResourceHierarchy cacheRes = ((ResourceNode) re.getSource()).getResourceHierarchy(); + FieldDeclaration cacheField = new FieldDeclaration(cacheRes.getResourceStateType(), srcName, getInitializer(cacheRes)); + if (component != null) { + // A component is created for this resource. + component.addField(cacheField); } else { - input.addAnnotation(new Annotation("POST")); + // No component is created for this resource. + fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), cacheField)); } - type.addMethod(input); - } else if (message.getClass() == Variable.class) { - MethodDeclaration input = new MethodDeclaration( - ((Variable) cm.getStateTransition().getMessageExpression()).getName(), - false, typeVoid, null); - if (cm.getStateTransition().isRightUnary()) { - input.addAnnotation(new Annotation("PUT")); + if (inDegree > 1) { + // For each source resource, a child resource is defined in the destination resource so that its state can be updated separately. + if (isRestAPI) update.addAnnotation(new Annotation("Path", "\"/" + srcName + "\"")); + } + } + if (component != null) { + // A component is created for this resource. + component.addMethod(update); + } else { + // No component is created for this resource. + String updateMethodName = update.getName(); + Map nameToMethod = updates.get(resourceNode.getParent().getResourceHierarchy()); + if (nameToMethod == null) { + nameToMethod = new HashMap<>(); + updates.put(resourceNode.getParent().getResourceHierarchy(), nameToMethod); + } + if (nameToMethod.get(updateMethodName) == null) { + nameToMethod.put(updateMethodName, update); + } + } + if (hasRestAPI && !isRestAPI) { + // Declare an update accessor method in the type of root resource. + String updateMethodName = update.getName(); + params = new ArrayList<>(); + String resourcePath = getUpdateResourcePathAndPathParams(out.getResource(), params, true); // Path parameters to identify the self resource. + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + VariableDeclaration chParam = new VariableDeclaration(selVar.getType(), selVar.getName()); + chParam.addAnnotation(new Annotation("FormParam", "\"" + selVar.getName() + "\"")); + params.add(chParam); // A channel parameter to specify the context of the collaboration. + } + } + param = new VariableDeclaration(srcType, srcName); + param.addAnnotation(new Annotation("FormParam", "\"" + srcName + "\"")); + params.add(param); // The state of the source resource to carry the data-flow. + for (ResourcePath refRes: ch.getReferenceResources()) { + if (!refRes.equals(resourceNode.getInSideResource(ch))) { + param = new VariableDeclaration(refRes.getResourceStateType(), refRes.getLeafResourceName()); + param.addAnnotation(new Annotation("FormParam", "\"" + refRes.getLeafResourceName() + "\"")); + params.add(param); + } + } + MethodDeclaration updateAccessor = new MethodDeclaration(updateMethodName, false, typeVoid, params); + if (isPut) { + updateAccessor.addAnnotation(new Annotation("PUT")); } else { - input.addAnnotation(new Annotation("POST")); + updateAccessor.addAnnotation(new Annotation("POST")); } - type.addMethod(input); + if (inDegree > 1) { + // For each source resource, a child resource is defined in the destination resource so that its state can be updated separately. + resourcePath += "/" + toVariableName(srcResName); + } + updateAccessor.addAnnotation(new Annotation("Path", "\"" + resourcePath + "\"")); + Map nameToMethod = updates.get(resourceNode.getResourceHierarchy().getRoot()); + if (nameToMethod == null) { + nameToMethod = new HashMap<>(); + updates.put(resourceNode.getResourceHierarchy().getRoot(), nameToMethod); + } + if (nameToMethod.get(updateMethodName) == null) { + nameToMethod.put(updateMethodName, updateAccessor); + } } } } } - // Declare the field to store the state in the type of each resource. - if (((StoreAttribute) rn.getAttribute()).isStored()) { - ResourcePath resId = rn.getResource(); - type.addField(new FieldDeclaration(resId.getResourceStateType(), "value", getInitializer(resId))); + // Declare the input method in each resource and the root resource. + for (Channel ch : model.getInputChannels()) { + for (ChannelMember cm : ((DataTransferChannel) ch).getOutputChannelMembers()) { + if (!cm.isOutside()) { + if (priorMemberForInputChannel.get(ch) == null) { + priorMemberForInputChannel.put(ch, cm); // The receiver of the input event when multiple output resources are defined for the channel. + } + } + } + for (ChannelMember cm : ((DataTransferChannel) ch).getOutputChannelMembers()) { + if (resourceNode.getInSideResources().contains(cm.getResource())) { + Expression message = cm.getStateTransition().getMessageExpression(); + if (message instanceof Term) { + // In each resource. + ArrayList resInputParams = new ArrayList<>(); + ArrayList rootInputParams = new ArrayList<>(); + String resourcePath = getInputMethodResourcePathAndPathParams(cm.getResource(), resInputParams, rootInputParams); // Path parameters for the input REST API. + if (resourcePath.indexOf('/') > 0) { + resourcePath = resourcePath.substring(resourcePath.indexOf('/')); + } else { + resourcePath = ""; + } + // The path parameters are not to be passed to the input method of each resource (resInputParams) + // because they are always equal to either channel selectors or message parameters. + + // Channel parameters to specify the context of the collaboration. + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + VariableDeclaration chParam = new VariableDeclaration(selVar.getType(), selVar.getName()); + resInputParams.add(chParam); + } + } + // Message parameters to carry the data-flows. + for (Map.Entry varEnt: message.getVariables().entrySet()) { + Variable var = varEnt.getValue(); + String refVarName = null; + for (ChannelMember refCm: ((DataTransferChannel) ch).getReferenceChannelMembers()) { + Expression varExp = refCm.getStateTransition().getMessageExpression().getSubTerm(varEnt.getKey()); + if (varExp != null && varExp instanceof Variable) { + if (refCm.getStateTransition().getCurStateExpression().contains(varExp)) { + refVarName = refCm.getResource().getLeafResourceName(); + break; + } + } + } + if (refVarName != null) { + // var has come from a reference resource. + VariableDeclaration param = new VariableDeclaration(var.getType(), refVarName); + resInputParams.add(param); + } else { + // var has not come from reference resource. + String paramName = var.getName(); + VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); + resInputParams.add(param); + if (!resourcePath.contains("{" + paramName+ "}")) { + param = new VariableDeclaration(var.getType(), paramName); + param.addAnnotation(new Annotation("FormParam", "\"" + paramName + "\"")); + rootInputParams.add(param); + } + } + } + + if (resourceNode.getResourceHierarchy().getParent() != null && resourceNode.getResourceHierarchy().getParent().getParent() != null) { + String inputMethodName = ((Term) message).getSymbol().getImplName(); + if (((DataTransferChannel) ch).getOutputChannelMembers().size() > 1) { + inputMethodName += "For" + getComponentName(cm.getResource().getResourceHierarchy()); + } + MethodDeclaration input = new MethodDeclaration(inputMethodName, false, typeVoid, resInputParams); + if (component != null) { + // A component is created for this resource. + component.addMethod(input); + } else { + // No component is created for this resource. + Map nameToMethod = inputs.get(resourceNode.getParent().getResourceHierarchy()); + if (nameToMethod == null) { + nameToMethod = new HashMap<>(); + inputs.put(resourceNode.getParent().getResourceHierarchy(), nameToMethod); + } + if (nameToMethod.get(inputMethodName) == null) { + nameToMethod.put(inputMethodName, input); + } + } + } + + // For the root resource. + if (priorMemberForInputChannel.get(ch) ==null || cm == priorMemberForInputChannel.get(ch)) { + // If cm is the receiver of the input event. + priorMemberForInputChannel.put(ch, cm); + String messageSymbol = ((Term) message).getSymbol().getImplName(); + MethodDeclaration inputAccessor = new MethodDeclaration(messageSymbol, false, typeVoid, rootInputParams); + if (cm.getStateTransition().isRightUnary()) { + inputAccessor.addAnnotation(new Annotation("PUT")); + } else { + inputAccessor.addAnnotation(new Annotation("POST")); + } + if (resourcePath.length() > 0) { + inputAccessor.addAnnotation(new Annotation("Path", "\"" + resourcePath + "\"")); + } + 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 { + inputAccessor.addAnnotation(new Annotation("POST")); + } + 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("javax.ws.rs.client.*")); + break; + } + } + } + } + } + + // Add constructor parameters to the ancestor components. + for (ResourceNode root: graph.getRootResourceNodes()) { + addConstructorParameters(root.getResourceHierarchy(), resourceComponents, resourceConstructors, constructorParams); + } + + // Add accessors. + for (ResourceHierarchy rootRes: model.getResourceHierarchies()) { + if (rootRes.getParent() == null) { + // root resource + TypeDeclaration rootComponent = resourceComponents.get(rootRes); + // Add getter accessors. + for (ResourceHierarchy res: getterAccessors.keySet()) { + if (rootRes.isAncestorOf(res)) { + rootComponent.addMethod(getterAccessors.get(res)); + } + } + // Add input accessors. + for (ResourceHierarchy res: inputAccessors.keySet()) { + if (rootRes.isAncestorOf(res)) { + for (MethodDeclaration inputAccessor: inputAccessors.get(res).values()) { + rootComponent.addMethod(inputAccessor); + } + } + } + } } // Declare the Pair class. @@ -214,7 +993,7 @@ for(Node n : resources) { ResourceNode rn = (ResourceNode) n; if(isCreatedPair) continue; - if(model.getType("Pair").isAncestorOf(rn.getResource().getResourceStateType())) { + if(model.getType("Pair").isAncestorOf(rn.getResourceStateType())) { TypeDeclaration type = new TypeDeclaration("Pair"); type.addField(new FieldDeclaration(new Type("Double", "T"), "left")); type.addField(new FieldDeclaration(new Type("Double", "T"), "right")); @@ -253,19 +1032,154 @@ 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 resInputParams, ArrayList rootInputParams) { + int v = 1; + List params = new ArrayList<>(); + if (resPath.getLastParam() != null) { + Expression pathParam = resPath.getLastParam(); + if (pathParam instanceof Variable) { + Variable var = (Variable) pathParam; + String paramName = var.getName(); + params.add("{" + paramName + "}"); + VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); + param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\"")); + rootInputParams.add(param); + } else if (pathParam instanceof Term) { + Term var = (Term) pathParam; + String paramName = "v" + v; + params.add("{" + paramName + "}"); + VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); + param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\"")); + rootInputParams.add(param); + } + v++; + } + if (resPath.getParent() != null) { + for (Expression pathParam: resPath.getParent().getPathParams()) { + if (pathParam instanceof Variable) { + Variable var = (Variable) pathParam; + String paramName = var.getName(); + params.add("{" + paramName + "}"); + VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); + param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\"")); + rootInputParams.add(param); + } else if (pathParam instanceof Term) { + Term var = (Term) pathParam; + String paramName = "v" + v; + params.add("{" + paramName + "}"); + VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); + param.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\"")); + rootInputParams.add(param); + } + v++; + } + } + return resPath.getResourceHierarchy().toResourcePath(params); + } + + private static String getInitializer(ResourceHierarchy res) { + Type stateType = res.getResourceStateType(); + String initializer = null; + if (res.getInitialValue() != null) { + initializer = res.getInitialValue().toImplementation(new String[] {""}); + } else if (stateType != null) { + initializer = DataConstraintModel.getDefaultValue(stateType); + } return initializer; } @@ -316,41 +1230,204 @@ static public IResourceStateAccessor pushAccessor = new IResourceStateAccessor() { @Override - public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) { - if (target.equals(from)) { + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + ResourcePath fromRes = from.getResource(); + if (targetRes.equals(fromRes)) { return new Field("value", - target.getResourceStateType() != null ? target.getResourceStateType() + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + // use the cached value as the current state + return new Field(targetRes.getLeafResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + return new Parameter(targetRes.getLeafResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { + if (fromRes != null && targetRes.equals(fromRes)) { + return new Field("value", + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } return null; } - - @Override - public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { - return new Parameter(target.getResourceName(), - target.getResourceStateType() != null ? target.getResourceStateType() - : DataConstraintModel.typeInt); - } }; static public IResourceStateAccessor pullAccessor = new IResourceStateAccessor() { @Override - public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) { - if (target.equals(from)) { - return new Field("value", - target.getResourceStateType() != null ? target.getResourceStateType() - : DataConstraintModel.typeInt); + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + if (from != null && !target.isOutside()) { + ResourcePath fromRes = from.getResource(); + if (targetRes.getCommonPrefix(fromRes) != null) { + return getDirectStateAccessorFor(targetRes, fromRes); + } } // for reference channel member - return new Parameter(target.getResourceName(), - target.getResourceStateType() != null ? target.getResourceStateType() + return new Parameter(targetRes.getLeafResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } @Override - public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { - return new Parameter(target.getResourceName(), - target.getResourceStateType() != null ? target.getResourceStateType() + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + if (from != null && !target.isOutside()) { + ResourcePath fromRes = from.getResource(); + if (targetRes.getCommonPrefix(fromRes) != null) { + return getDirectStateAccessorFor(targetRes, fromRes); + } + } + return new Parameter(targetRes.getLeafResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } + + @Override + public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { + if (fromRes != null && !fromRes.getResourceHierarchy().isAncestorOf(targetRes.getResourceHierarchy())) { + if (targetRes.equals(fromRes)) { + return new Field("value", + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + return new Parameter(targetRes.getLeafResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } else { + // access from an ancestor or outside of the hierarchy + Stack pathStack = new Stack<>(); + ResourcePath curPath = targetRes; + do { + if (fromRes != null && curPath.equals(fromRes)) break; + pathStack.push(curPath); + curPath = curPath.getParent(); + } while (curPath != null); + // iterate from the `from' resource + Term getter = null; + int v = 1; + int arity = 2; + 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) { + Variable var = null; + Expression param = curPath.getLastParam(); + if (param instanceof Variable) { + var = (Variable) param; + } else if (param instanceof Term) { + var = new Variable("v" + v, ((Term) param).getType()); + } + if (var != null) { + newGetter.addChild(var); + newGetter.getSymbol().setArity(2); + } + v++; + } + getter = newGetter; + } else { + if (curPath.getResourceHierarchy().getNumParameters() > 0) { + Variable var = null; + Expression param = curPath.getLastParam(); + if (param instanceof Variable) { + var = (Variable) param; + } else if (param instanceof Term) { + var = new Variable("v" + v, ((Term) param).getType()); + } + if (var != null) { + getter.getSymbol().setArity(arity); + getter.addChild(var); + } + v++; + } + } + arity = 2; + } else { + // to get a descendant resource directly. + if (arity == 2) { + Term newGetter = new Term(new Symbol("get" + typeName, -1, Symbol.Type.METHOD)); + newGetter.addChild(getter); + getter = newGetter; + } + if (curPath.getResourceHierarchy().getNumParameters() > 0) { + Variable var = null; + Expression param = curPath.getLastParam(); + if (param instanceof Variable) { + var = (Variable) param; + } else if (param instanceof Term) { + var = new Variable("v" + v, ((Term) param).getType()); + } + if (var != null) { + getter.getSymbol().setArity(arity); + getter.addChild(var); + arity++; + } + v++; + } + } + } + } + + if (generatesComponent(targetRes.getResourceHierarchy())) { + Term newGetter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); + newGetter.addChild(getter); + getter = newGetter; + } + return getter; + } + } + }; + static public IResourceStateAccessor refAccessor = new IResourceStateAccessor() { + @Override + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + ResourcePath fromRes = from.getResource(); + if (targetRes.equals(fromRes)) { + return new Field("value", + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + return new Parameter(targetRes.getLeafResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + return new Parameter(targetRes.getLeafResourceName(), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { + if (fromRes != null && targetRes.equals(fromRes)) { + return new Field("value", + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + return null; + } }; } diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java index cdaf898..621bd82 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java @@ -4,8 +4,10 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import algorithms.TypeInference; @@ -15,10 +17,14 @@ import code.ast.TypeDeclaration; import code.ast.VariableDeclaration; import models.Edge; -import models.Node; +import models.algebra.Constant; import models.algebra.Expression; +import models.algebra.Field; import models.algebra.InvalidMessage; +import models.algebra.Parameter; import models.algebra.ParameterizedIdentifierIsFutureWork; +import models.algebra.Position; +import models.algebra.Symbol; import models.algebra.Term; import models.algebra.Type; import models.algebra.UnificationFailed; @@ -27,12 +33,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 +55,10 @@ public static ArrayList doGenerate(DataFlowGraph graph, DataTransferModel model, ArrayList codes) { // Create a map from type names (lower case) to their types. - Map typeMap = new HashMap<>(); + Map componentMap = new HashMap<>(); for (CompilationUnit code: codes) { - for (TypeDeclaration type: code.types()) { - typeMap.put(type.getTypeName().substring(0,1).toLowerCase() + type.getTypeName().substring(1), type); + for (TypeDeclaration component: code.types()) { + componentMap.put(component.getTypeName(), component); } } @@ -56,263 +67,854 @@ Set chainedCalls = new HashSet<>(); Map> referredResources = new HashMap<>(); for (Edge e: graph.getEdges()) { - DataFlowEdge d = (DataFlowEdge) e; - PushPullAttribute pushPull = (PushPullAttribute) d.getAttribute(); - ResourceNode src = (ResourceNode) d.getSource(); - ResourceNode dst = (ResourceNode) d.getDestination(); - String srcResourceName = src.getResource().getResourceName(); - String dstResourceName = dst.getResource().getResourceName(); - TypeDeclaration srcType = typeMap.get(srcResourceName); - TypeDeclaration dstType = typeMap.get(dstResourceName); - for (ChannelMember out: d.getChannel().getOutputChannelMembers()) { - if (out.getResource() == dst.getResource()) { - if (pushPull.getOptions().get(0) == PushPullValue.PUSH && srcType != null) { - // for push data transfer - MethodDeclaration update = getUpdateMethod(dstType, srcType); - if (((StoreAttribute) dst.getAttribute()).isStored()) { - // update stored state of dst side resource (when every incoming edge is in push style) - Expression updateExp = null; - if (d.getChannel().getReferenceChannelMembers().size() == 0) { - updateExp = d.getChannel().deriveUpdateExpressionOf(out, JerseyCodeGenerator.pushAccessor); - } else { - // if there exists one or more reference channel member. - HashMap inputResourceToStateAccessor = new HashMap<>(); - for (Edge eIn: dst.getInEdges()) { - DataFlowEdge dIn = (DataFlowEdge) eIn; - inputResourceToStateAccessor.put(((ResourceNode) dIn.getSource()).getResource(), JerseyCodeGenerator.pushAccessor); + DataFlowEdge resToCh = (DataFlowEdge) e; + if (!resToCh.isChannelToResource()) { + PushPullAttribute pushPull = (PushPullAttribute) resToCh.getAttribute(); + ResourceNode src = (ResourceNode) resToCh.getSource(); + for (Edge chToRes: resToCh.getDestination().getOutEdges()) { + ResourceNode dst = (ResourceNode) chToRes.getDestination(); + String srcResourceName = JerseyCodeGenerator.getComponentName(src.getResourceHierarchy()); + String dstResourceName = JerseyCodeGenerator.getComponentName(dst.getResourceHierarchy()); + TypeDeclaration srcComponent = componentMap.get(srcResourceName); + TypeDeclaration dstComponent = componentMap.get(dstResourceName); + DataTransferChannel ch = ((ChannelNode) resToCh.getDestination()).getChannel(); + for (ChannelMember out: ch.getOutputChannelMembers()) { + if (dst.getInSideResources().contains(out.getResource())) { + // Check if the input resource is outside of the channel scope. + boolean outsideInputResource = false; + ChannelMember in = null; + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (src.getOutSideResources().contains(cm.getResource())) { + in = cm; + if (cm.isOutside()) { + outsideInputResource = true; // Regarded as pull transfer. + break; + } } - for (ChannelMember c: d.getChannel().getReferenceChannelMembers()) { - inputResourceToStateAccessor.put(c.getResource(), JerseyCodeGenerator.pullAccessor); + } + // Check if the output resource is outside of the channel scope. + boolean outsideOutputResource = out.isOutside(); + if ((pushPull.getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) { + // for push data transfer + MethodDeclaration update = null; + if (dstComponent == null) { + String dstParentResourceName = JerseyCodeGenerator.getComponentName(dst.getResourceHierarchy().getParent()); + dstComponent = componentMap.get(dstParentResourceName); + update = getUpdateMethod(dstComponent, dstResourceName, srcResourceName); + } else { + update = getUpdateMethod(dstComponent, null, srcResourceName); } - updateExp = d.getChannel().deriveUpdateExpressionOf(out, JerseyCodeGenerator.pushAccessor, inputResourceToStateAccessor); - } - String[] sideEffects = new String[] {""}; - String curState = updateExp.toImplementation(sideEffects); - String updateStatement; - if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { - updateStatement = sideEffects[0]; - } else { - updateStatement = sideEffects[0] + "this.value = " + curState + ";"; - } - if (update.getBody() == null || !update.getBody().getStatements().contains(updateStatement)) { - // add an update statement of the state of dst side resource. - update.addFirstStatement(updateStatement); - } - } - if (dst.getIndegree() > 1) { - // update a cash of src side resource (when incoming edges are multiple) - String cashStatement = "this." + srcResourceName + " = " + srcResourceName + ";"; - if (update.getBody() == null || !update.getBody().getStatements().contains(cashStatement)) { - update.addFirstStatement(cashStatement); - } - } - // to convert a json param to a tuple, pair or map object. - for (VariableDeclaration param: update.getParameters()) { - Type paramType = param.getType(); - String paramName = param.getName(); - String paramConverter = ""; - if (DataConstraintModel.typeList.isAncestorOf(paramType) && paramType != DataConstraintModel.typeList) { - Type compType = TypeInference.getListComponentType(paramType); - if (DataConstraintModel.typeTuple.isAncestorOf(compType)) { - param.setType(DataConstraintModel.typeListStr); - param.setName(paramName + "_json"); - paramConverter += paramType.getInterfaceTypeName() + " " + paramName + " = new " + paramType.getImplementationTypeName() + "();\n"; - paramConverter += "for (String str: " + param.getName() + ") {\n"; - String mapTypeName = convertFromEntryToMapType(compType); - paramConverter += "\t" + mapTypeName + " i = new ObjectMapper().readValue(str, HashMap.class);\n"; - paramConverter += "\t" + paramName + ".add(" + getCodeForConversionFromMapToTuple(compType, "i") + ");\n"; - paramConverter += "}"; - update.addThrow("JsonProcessingException"); - } else if (DataConstraintModel.typePair.isAncestorOf(compType)) { - param.setType(DataConstraintModel.typeListStr); - param.setName(paramName + "_json"); - paramConverter += paramType.getInterfaceTypeName() + " " + paramName + " = new " + paramType.getImplementationTypeName() + "();\n"; - paramConverter += "for (String str: " + param.getName() + ") {\n"; - String mapTypeName = convertFromEntryToMapType(compType); - paramConverter += "\t" + mapTypeName + " i = new ObjectMapper().readValue(str, HashMap.class);\n"; - paramConverter += "\t" + paramName + ".add(" + getCodeForConversionFromMapToPair(compType, "i") + ");\n"; - paramConverter += "}"; - update.addThrow("JsonProcessingException"); - } else if (DataConstraintModel.typeMap.isAncestorOf(compType)) { - param.setType(DataConstraintModel.typeListStr); - // To do. + if (((StoreAttribute) dst.getAttribute()).isStored()) { + // update stored state of dst side resource (when every incoming edge is in push style) + Expression updateExp = null; + if (ch.getReferenceChannelMembers().size() == 0) { + updateExp = ch.deriveUpdateExpressionOf(out, JerseyCodeGenerator.pushAccessor).getKey(); + } else { + // if there exists one or more reference channel member. + HashMap inputResourceToStateAccessor = new HashMap<>(); + for (Edge chToRes2: dst.getInEdges()) { + DataTransferChannel ch2 = ((ChannelNode) chToRes2.getSource()).getChannel(); + for (Edge resToCh2: chToRes2.getSource().getInEdges()) { + DataFlowEdge dIn = (DataFlowEdge) resToCh2; + ChannelMember in2 = null; + for (ChannelMember cm: ch2.getInputChannelMembers()) { + if (((ResourceNode) dIn.getSource()).getOutSideResources().contains(cm.getResource())) { + in2 = cm; + break; + } + } + inputResourceToStateAccessor.put(in2, JerseyCodeGenerator.pushAccessor); + } + } + for (ChannelMember c: ch.getReferenceChannelMembers()) { + inputResourceToStateAccessor.put(c, JerseyCodeGenerator.refAccessor); + } + updateExp = ch.deriveUpdateExpressionOf(out, JerseyCodeGenerator.pushAccessor, inputResourceToStateAccessor).getKey(); + } + // Replace Json constructor with a constructor of 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]; + } else { + updateStatement = sideEffects[0] + "this.value = " + newState + ";"; + } + } else { + if (sideEffects[0] != null) { + updateStatement = sideEffects[0]; + updateStatement = updateStatement.replace(".value", "." + JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(outRes))); + } + if (DataConstraintModel.typeList.isAncestorOf(outRes.getParent().getResourceStateType())) { + Term selector = new Term(DataConstraintModel.set); + selector.addChild(new Field("value")); + selector.addChild(new Variable(update.getParameters().get(update.getParameters().size() - 2).getName())); + selector.addChild(new Constant(newState)); + String[] sideEffects2 = new String[] {""}; + String newList = selector.toImplementation(sideEffects2); + updateStatement += sideEffects2[0]; + } else if (DataConstraintModel.typeMap.isAncestorOf(outRes.getParent().getResourceStateType())) { + Term selector = new Term(DataConstraintModel.insert); + selector.addChild(new Field("value")); + selector.addChild(new Variable(update.getParameters().get(update.getParameters().size() - 2).getName())); + selector.addChild(new Constant(newState)); + String[] sideEffects2 = new String[] {""}; + String newMap = selector.toImplementation(sideEffects2); + updateStatement += sideEffects2[0]; + } else if (!(updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect())) { + updateStatement += "this." + JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(outRes)) + " = " + newState + ";"; + } + } + // add an update statement of the state of dst side resource. + if (numOfOutResourcesWithTheSameHierarchy == 1) { + update.addFirstStatement(updateStatement); + } else { + Term conditions = null; + int v = 1; + Map>> resourcePaths = ch.fillOutsideResourcePaths(out, JerseyCodeGenerator.pushAccessor); + for (Expression pathParam: out.getResource().getPathParams()) { + if (pathParam instanceof Variable) { + String selfParamName = ((Variable) pathParam).getName(); + Expression arg = null; + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + if (selVar.getName().equals(selfParamName)) { + arg = selVar; + break; + } + } + } + if (arg == null) { + ResourcePath filledPath = resourcePaths.get(out).getKey(); + arg = filledPath.getPathParams().get(v - 1); + } + Term condition = new Term(DataConstraintModel.eq, new Expression[] { + new Parameter("self" + (v > 1 ? v : ""), 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 of the destination resource. + Set inResources = new HashSet<>(); + for (ResourceNode rn: graph.getResourceNodes(out.getResource().getResourceHierarchy())) { + // ResourceNodes that have the same ResourceHierarchy. + for (Edge chToRes2: rn.getInEdges()) { + for (Edge resToCh2: chToRes2.getSource().getInEdges()) { + inResources.add(((ResourceNode) resToCh2.getSource()).getResourceHierarchy()); + } + } + } + int inDegree = inResources.size(); + if (inDegree > 1 + || (inDegree == 1 && ch.getInputChannelMembers().iterator().next().getStateTransition().isRightPartial())) { + // update a cache of src side resource (when incoming edges are multiple) + String cacheStatement = "this." + JerseyCodeGenerator.toVariableName(srcResourceName) + " = " + JerseyCodeGenerator.toVariableName(srcResourceName) + ";"; + if (update.getBody() == null || !update.getBody().getStatements().contains(cacheStatement)) { + update.addStatement(cacheStatement); + } + } + // For a post/put REST API. + if (outsideOutputResource + || (in.getResource().getCommonPrefix(out.getResource()) == null && JerseyCodeGenerator.differentTreesAsDifferentServices)) { + // Inter-services + if (dst.getResourceHierarchy().getParent() != null) { + // If not a root resource. + TypeDeclaration rootComponent = componentMap.get(JerseyCodeGenerator.getComponentName(dst.getResourceHierarchy().getRoot())); + MethodDeclaration update2 = update; + update = getMethod(rootComponent, update2.getName()); // get the accessor to the update method. + // To make the accessor call the update method. + Expression resExp = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(out.getResource(), out.getResource().getRoot()); + String args = ""; + String delimiter = ""; + if (resExp instanceof Term) { + // to access the parent + if (((Term) resExp).getChildren().size() > 1 && ((Term) resExp).getChild(1) instanceof Variable) { + args += delimiter + ((Variable)((Term) resExp).getChild(1)).getName(); + delimiter = ", "; + } + resExp = ((Term) resExp).getChild(0); + } + String resourceAccess = resExp.toImplementation(new String[] {""}); + int v = 0; + for (VariableDeclaration var: update2.getParameters()) { + if (v < out.getResource().getPathParams().size()) { + args += delimiter + ((Variable) out.getResource().getPathParams().get(v)).getName(); + } else { + args += delimiter + var.getName(); + } + delimiter = ", "; + v++; + } + update.addStatement(resourceAccess + "." + update2.getName() + "(" + args + ");"); + } + // to convert a json param to a tuple, pair or map object. + for (VariableDeclaration param: update.getParameters()) { + Type paramType = param.getType(); + String paramName = param.getName(); + String paramConverter = ""; + if (DataConstraintModel.typeList.isAncestorOf(paramType) && paramType != DataConstraintModel.typeList) { + Type compType = TypeInference.getListComponentType(paramType); + if (DataConstraintModel.typeTuple.isAncestorOf(compType)) { + param.setType(DataConstraintModel.typeListStr); + param.setName(paramName + "_json"); + paramConverter += paramType.getInterfaceTypeName() + " " + paramName + " = new " + paramType.getImplementationTypeName() + "();\n"; + paramConverter += "for (String str: " + param.getName() + ") {\n"; + String mapTypeName = convertFromEntryToMapType(compType); + paramConverter += "\t" + mapTypeName + " i = new ObjectMapper().readValue(str, HashMap.class);\n"; + paramConverter += "\t" + paramName + ".add(" + getCodeForConversionFromMapToTuple(compType, "i") + ");\n"; + paramConverter += "}"; + update.addThrow("JsonProcessingException"); + } else if (DataConstraintModel.typePair.isAncestorOf(compType)) { + param.setType(DataConstraintModel.typeListStr); + param.setName(paramName + "_json"); + paramConverter += paramType.getInterfaceTypeName() + " " + paramName + " = new " + paramType.getImplementationTypeName() + "();\n"; + paramConverter += "for (String str: " + param.getName() + ") {\n"; + String mapTypeName = convertFromEntryToMapType(compType); + paramConverter += "\t" + mapTypeName + " i = new ObjectMapper().readValue(str, HashMap.class);\n"; + paramConverter += "\t" + paramName + ".add(" + getCodeForConversionFromMapToPair(compType, "i") + ");\n"; + paramConverter += "}"; + update.addThrow("JsonProcessingException"); + } else if (DataConstraintModel.typeMap.isAncestorOf(compType)) { + param.setType(DataConstraintModel.typeListStr); + // To do. + } + } else if (DataConstraintModel.typeTuple.isAncestorOf(paramType)) { + param.setType(DataConstraintModel.typeString); + param.setName(paramName + "_json"); + paramConverter += paramType.getInterfaceTypeName() + " " + paramName + ";\n"; + paramConverter += "{\n"; + String mapTypeName = convertFromEntryToMapType(paramType); + paramConverter += "\t" + mapTypeName + " i = new ObjectMapper().readValue(" + paramName + "_json" + ", HashMap.class);\n"; + paramConverter += "\t" + paramName + " = " + getCodeForConversionFromMapToTuple(paramType, "i") + ";\n"; + paramConverter += "}"; + update.addThrow("JsonProcessingException"); + } else if (DataConstraintModel.typePair.isAncestorOf(paramType)) { + param.setType(DataConstraintModel.typeString); + param.setName(paramName + "_json"); + paramConverter += paramType.getInterfaceTypeName() + " " + paramName + ";\n"; + paramConverter += "{\n"; + String mapTypeName = convertFromEntryToMapType(paramType); + paramConverter += "\t" + mapTypeName + " i = new ObjectMapper().readValue(" + paramName + "_json" + ", HashMap.class);\n"; + paramConverter += "\t" + paramName + " = " + getCodeForConversionFromMapToPair(paramType, "i") + ";\n"; + paramConverter += "}"; + update.addThrow("JsonProcessingException"); + } else if (DataConstraintModel.typeMap.isAncestorOf(paramType)) { + param.setType(DataConstraintModel.typeString); + param.setName(paramName + "_json"); + paramConverter += paramType.getInterfaceTypeName() + " " + paramName + " = " + "new " + paramType.getImplementationTypeName() + "();\n"; + paramConverter += "{\n"; + String mapTypeName = convertFromEntryToMapType(paramType); + paramConverter += "\t" + mapTypeName + " i = new ObjectMapper().readValue(" + paramName + "_json" + ", HashMap.class);\n"; + paramConverter += "\t" + getCodeForConversionFromMapToMap(paramType, "i", paramName) + "\n"; + paramConverter += "}"; + update.addThrow("JsonProcessingException"); + } + if (paramConverter.length() > 0 && !update.getBody().getStatements().contains(paramConverter)) { + update.addFirstStatement(paramConverter); + } + } + } + if (((StoreAttribute) dst.getAttribute()).isStored()) { + // returns the state stored in a field. + MethodDeclaration getter = null; + if (JerseyCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { + getter = getMethod(dstComponent, "getValue"); + } else { + getter = getGetterMethod(dstComponent, dstResourceName); + } + if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { + if (dst.getResourceHierarchy().getNumParameters() == 0) { + if (JerseyCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { + // dst has a component. + getter.addStatement("return value;"); + } else { + // dst has no component. + String dstResName = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(dst.getResourceHierarchy())); + getter.addStatement("return " + dstResName + ";"); + } + } else { + if (DataConstraintModel.typeList.isAncestorOf(dst.getParent().getResourceStateType())) { + Term selector = new Term(DataConstraintModel.get); + selector.addChild(new Field("value")); + selector.addChild(dst.getSelectors().get(dst.getSelectors().size() - 1).getExpression()); + getter.addStatement("return " + selector.toImplementation(new String[] {}) + ";"); + } else if (DataConstraintModel.typeMap.isAncestorOf(dst.getParent().getResourceStateType())) { + Term selector = new Term(DataConstraintModel.lookup); + selector.addChild(new Field("value")); + selector.addChild(dst.getSelectors().get(dst.getSelectors().size() - 1).getExpression()); + getter.addStatement("return " + selector.toImplementation(new String[] {}) + ";"); + } + } + } + } + // src side (for a chain of update method invocations) + String httpMethod = null; + if (out.getStateTransition().isRightUnary()) { + httpMethod = "put"; + } else { + httpMethod = "post"; + } + String srcName = null; + if (srcComponent == null) { + String srcParentResourceName = JerseyCodeGenerator.getComponentName(src.getResourceHierarchy().getParent()); + srcComponent = componentMap.get(srcParentResourceName); + srcName = srcResourceName; + } + for (MethodDeclaration srcUpdate: getUpdateMethods(srcComponent, srcName)) { + if (srcUpdate != null) { + List>> params = new ArrayList<>(); + ResourcePath dstRes = out.getResource(); + // Values of channel parameters. + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + params.add(new AbstractMap.SimpleEntry<>(selVar.getType(), + new AbstractMap.SimpleEntry<>(selVar.getName(), selVar.getName()))); + } + } + // Value of the source side (input side) resource. + String srcFieldName = "this.value"; + if (!JerseyCodeGenerator.generatesComponent(src.getResourceHierarchy())) { + srcFieldName = JerseyCodeGenerator.toVariableName(srcResourceName); + } + params.add(new AbstractMap.SimpleEntry<>(src.getResourceStateType(), + new AbstractMap.SimpleEntry<>(JerseyCodeGenerator.toVariableName(srcResourceName), srcFieldName))); + Set referredSet = referredResources.get(srcUpdate); + if (ch.getReferenceChannelMembers().size() > 0) { + for (ChannelMember rc: ch.getReferenceChannelMembers()) { + // For each reference channel member, get the current state of the reference side resource by pull data transfer. + ResourcePath ref = rc.getResource(); + if (referredSet == null) { + referredSet = new HashSet<>(); + referredResources.put(srcUpdate, referredSet); + } + if (!dst.getInSideResources().contains(ref)) { + String refResourceName = ref.getLeafResourceName(); + Type refResourceType = ref.getResourceStateType(); + if (!referredSet.contains(ref)) { + referredSet.add(ref); + String[] sideEffects = new String[] {""}; + if (rc.isOutside()) { + List pathParams = new ArrayList<>(); + for (Expression pathExp: ref.getPathParams()) { + pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); + } + generatePullDataTransfer(srcUpdate, refResourceName, ref.getResourceHierarchy().toResourcePath(pathParams), refResourceType); + } else { + ResourcePath srcRes = in.getResource(); + if (!JerseyCodeGenerator.generatesComponent(srcRes.getResourceHierarchy())) { + srcRes = srcRes.getParent(); + } + Expression refGetter = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(ref, srcRes); + String refExp = refGetter.toImplementation(sideEffects); + String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); + srcUpdate.addFirstStatement(sideEffects[0] + refTypeName + " " + refResourceName + " = " + refExp + ";"); + } + } + // Value of a reference side resource. + params.add(new AbstractMap.SimpleEntry<>(refResourceType, new AbstractMap.SimpleEntry<>(refResourceName, refResourceName))); + } + } + } + if (outsideOutputResource || (in.getResource().getCommonPrefix(dstRes) == null && JerseyCodeGenerator.differentTreesAsDifferentServices)) { + // Inter-servces + String[] sideEffects = new String[] {""}; + List pathParams = new ArrayList<>(); + for (Expression pathExp: dstRes.getPathParams()) { + pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); + } + String srcResName = JerseyCodeGenerator.toVariableName(srcResourceName); + if (inDegree <= 1) { + srcResName = null; + } + if (!chainedCalls.contains(srcUpdate)) { + // The first call to an update method in this method + srcUpdate.addStatement(getHttpMethodParamsStatement(srcComponent.getTypeName(), params, true)); + srcUpdate.addStatement("String result = " + getHttpMethodCallStatement(baseURL, + dstRes.getResourceHierarchy().toResourcePath(pathParams), + srcResName, + httpMethod)); + chainedCalls.add(srcUpdate); + } else { + // After the second time of call to update methods in this method + srcUpdate.addStatement(getHttpMethodParamsStatement(srcComponent.getTypeName(), params, false)); + srcUpdate.addStatement("result = " + getHttpMethodCallStatement(baseURL, + dstRes.getResourceHierarchy().toResourcePath(pathParams), + srcResName, + httpMethod)); + } + srcUpdate.addThrow("JsonProcessingException"); + } else { + // Inner-service + String updateMethodName = null; + if (JerseyCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { + updateMethodName = "updateFrom" + srcResourceName; + } else { + updateMethodName = "update" + dstResourceName + "From" + srcResourceName; + } + String callParams = ""; + String delimiter = ""; + // Values of path parameters. + for (Expression pathParam: dstRes.getPathParams()) { + if (pathParam instanceof Variable) { + Variable pathVar = (Variable) pathParam; + callParams += delimiter + pathVar.getName(); + delimiter = ", "; + } + } + // Values of other parameters. + for (Map.Entry> paramEnt: params) { + callParams += delimiter + paramEnt.getValue().getValue(); + delimiter = ", "; + } + if (srcComponent != dstComponent) { + srcUpdate.addStatement("this." + JerseyCodeGenerator.toVariableName(dstResourceName) + "." + updateMethodName + "(" + callParams + ");"); + } else { + srcUpdate.addStatement("this." + updateMethodName + "(" + callParams + ");"); + } + if (update != null && update.getThrows() != null && update.getThrows().getExceptions().contains("JsonProcessingException")) { + srcUpdate.addThrow("JsonProcessingException"); + } + } + } + } + for (MethodDeclaration srcInput: getInputMethods(srcComponent, src, model)) { + List>> params = new ArrayList<>(); + ResourcePath dstRes = out.getResource(); + // Values of channel parameters. + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + params.add(new AbstractMap.SimpleEntry<>(selVar.getType(), + new AbstractMap.SimpleEntry<>(selVar.getName(), selVar.getName()))); + } + } + // Value of the source side (input side) resource. + String srcFieldName = "this.value"; + if (!JerseyCodeGenerator.generatesComponent(src.getResourceHierarchy())) { + srcFieldName = JerseyCodeGenerator.toVariableName(srcResourceName); + } + params.add(new AbstractMap.SimpleEntry<>(src.getResourceStateType(), + new AbstractMap.SimpleEntry<>(JerseyCodeGenerator.toVariableName(srcResourceName), srcFieldName))); + Set referredSet = referredResources.get(srcInput); + for (ChannelMember rc: ch.getReferenceChannelMembers()) { // For each reference channel member, get the current state of the reference side resource by pull data transfer. ResourcePath ref = rc.getResource(); if (referredSet == null) { referredSet = new HashSet<>(); - referredResources.put(srcUpdate, referredSet); + referredResources.put(srcInput, referredSet); } - if (ref != dst.getResource()) { - String refResourceName = ref.getResourceName(); + if (!dst.getInSideResources().contains(ref)) { + String refResourceName = ref.getLeafResourceName(); Type refResourceType = ref.getResourceStateType(); if (!referredSet.contains(ref)) { referredSet.add(ref); - generatePullDataTransfer(srcUpdate, refResourceName, refResourceType); + String[] sideEffects = new String[] {""}; + if (rc.isOutside()) { + List pathParams = new ArrayList<>(); + for (Expression pathExp: ref.getPathParams()) { + pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); + } + generatePullDataTransfer(srcInput, refResourceName, ref.getResourceHierarchy().toResourcePath(pathParams), refResourceType); + } else { + ResourcePath srcRes = in.getResource(); + if (!JerseyCodeGenerator.generatesComponent(srcRes.getResourceHierarchy())) { + srcRes = srcRes.getParent(); + } + Expression refGetter = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(ref, srcRes); + String refExp = refGetter.toImplementation(sideEffects); + String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); + srcInput.addFirstStatement(sideEffects[0] + refTypeName + " " + ref.getLeafResourceName() + " = " + refExp + ";"); + } } // Value of a reference side resource. params.add(new AbstractMap.SimpleEntry<>(refResourceType, new AbstractMap.SimpleEntry<>(refResourceName, refResourceName))); + } + } + if (outsideOutputResource || (in.getResource().getCommonPrefix(dstRes) == null && JerseyCodeGenerator.differentTreesAsDifferentServices)) { + // Inter-services + String[] sideEffects = new String[] {""}; + List pathParams = new ArrayList<>(); + for (Expression pathExp: dstRes.getPathParams()) { + pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); + } + String srcResName = JerseyCodeGenerator.toVariableName(srcResourceName); + if (inDegree <= 1) { + srcResName = null; + } + if (!chainedCalls.contains(srcInput)) { + // First call to an update method in this method + srcInput.addStatement(getHttpMethodParamsStatement(srcComponent.getTypeName(), params, true)); + srcInput.addStatement("String result = " + getHttpMethodCallStatement(baseURL, + dstRes.getResourceHierarchy().toResourcePath(pathParams), + srcResName, + httpMethod)); + chainedCalls.add(srcInput); + } else { + // After the second time of call to update methods in this method + srcInput.addStatement(getHttpMethodParamsStatement(srcComponent.getTypeName(), params, false)); + srcInput.addStatement("result = " + getHttpMethodCallStatement(baseURL, + dstRes.getResourceHierarchy().toResourcePath(pathParams), + srcResName, + httpMethod)); + } + srcInput.addThrow("JsonProcessingException"); + } else { + // Inner-service + String updateMethodName = null; + if (JerseyCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { + updateMethodName = "updateFrom" + srcResourceName; + } else { + updateMethodName = "update" + dstResourceName + "From" + srcResourceName; + } + String callParams = ""; + String delimiter = ""; + // Values of path parameters. + for (Expression pathParam: dstRes.getPathParams()) { + if (pathParam instanceof Variable) { + Variable pathVar = (Variable) pathParam; + callParams += delimiter + pathVar.getName(); + delimiter = ", "; + } + } + // Values of other parameters. + for (Map.Entry> paramEnt: params) { + callParams += delimiter + paramEnt.getValue().getValue(); + delimiter = ", "; + } + if (srcComponent != dstComponent) { + srcInput.addStatement("this." + JerseyCodeGenerator.toVariableName(dstResourceName) + "." + updateMethodName + "(" + callParams + ");"); + } else { + srcInput.addStatement("this." + updateMethodName + "(" + callParams + ");"); + } + if (update != null && update.getThrows() != null && update.getThrows().getExceptions().contains("JsonProcessingException")) { + srcInput.addThrow("JsonProcessingException"); } } } - String srcResName = null; - if (dst.getIndegree() > 1) { - srcResName = srcResourceName; + } else if ((pushPull.getOptions().get(0) != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { + // for pull (or push/pull) data transfer + if (dstComponent == null) { + String dstParentResourceName = JerseyCodeGenerator.getComponentName(dst.getResourceHierarchy().getParent()); + dstComponent = componentMap.get(dstParentResourceName); } - if (!chainedCalls.contains(srcUpdate)) { - // The first call to an update method in this method - // Value of the source side (input side) resource. - params.add(0, new AbstractMap.SimpleEntry<>(src.getResource().getResourceStateType(), new AbstractMap.SimpleEntry<>(srcResourceName, "this.value"))); - srcUpdate.addStatement(getHttpMethodParamsStatement(srcType.getTypeName(), params, true)); - srcUpdate.addStatement("String result = " + getHttpMethodCallStatement(baseURL, dstResourceName, srcResName, httpMethod)); - chainedCalls.add(srcUpdate); + MethodDeclaration getter = null; + if (JerseyCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { + getter = getMethod(dstComponent, "getValue"); } else { - // After the second time of call to update methods in this method - // Value of the source side (input side) resource. - params.add(0, new AbstractMap.SimpleEntry<>(src.getResource().getResourceStateType(), new AbstractMap.SimpleEntry<>(srcResourceName, "this.value"))); - srcUpdate.addStatement(getHttpMethodParamsStatement(srcType.getTypeName(), params, false)); - srcUpdate.addStatement("result = " + getHttpMethodCallStatement(baseURL, dstResourceName, srcResName, httpMethod)); + getter = getGetterMethod(dstComponent, dstResourceName); } - srcUpdate.addThrow("JsonProcessingException"); - } - } - for (MethodDeclaration srcInput: getInputMethods(srcType, src, model)) { - List>> params = new ArrayList<>(); - Set referredSet = referredResources.get(srcInput); - for (ChannelMember rc: d.getChannel().getReferenceChannelMembers()) { - // For each reference channel member, get the current state of the reference side resource by pull data transfer. - ResourcePath ref = rc.getResource(); - if (referredSet == null) { - referredSet = new HashSet<>(); - referredResources.put(srcInput, referredSet); - } - if (ref != dst.getResource()) { - String refResourceName = ref.getResourceName(); - Type refResourceType = ref.getResourceStateType(); - if (!referredSet.contains(ref)) { - referredSet.add(ref); - generatePullDataTransfer(srcInput, refResourceName, refResourceType); + if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { + // generate a return statement. + Expression curExp = ch.deriveUpdateExpressionOf(out, JerseyCodeGenerator.pullAccessor).getKey(); // no pull data transfer is included. + Map>> resourcePaths = ch.fillOutsideResourcePaths(out, JerseyCodeGenerator.pullAccessor); + String[] sideEffects = new String[] {""}; + String curState = curExp.toImplementation(sideEffects); + getter.addStatement(sideEffects[0] + "return " + curState + ";"); + // For each reference channel member, get the current state of the reference side resource by pull data transfer. + for (ChannelMember rc: ch.getReferenceChannelMembers()) { + ResourcePath refRes = rc.getResource(); + String refResourceName = refRes.getLeafResourceName(); + Type refResourceType = refRes.getResourceStateType(); + if (rc.isOutside()) { + List pathParams = new ArrayList<>(); + for (Expression pathExp: refRes.getPathParams()) { + pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); + } + generatePullDataTransfer(getter, refResourceName, refRes.getResourceHierarchy().toResourcePath(pathParams), refResourceType); + } else { + ResourcePath dstRes = out.getResource(); + if (!JerseyCodeGenerator.generatesComponent(dstRes.getResourceHierarchy())) { + dstRes = dstRes.getParent(); + } + Expression refGetter = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(refRes, dstRes); + String refExp = refGetter.toImplementation(sideEffects); + String refTypeName = refResourceType.getInterfaceTypeName(); + getter.addFirstStatement(sideEffects[0] + refTypeName + " " + refResourceName + " = " + refExp + ";"); + } } - // Value of a reference side resource. - params.add(new AbstractMap.SimpleEntry<>(refResourceType, new AbstractMap.SimpleEntry<>(refResourceName, refResourceName))); - } - } - String srcResName = null; - if (dst.getIndegree() > 1) { - srcResName = srcResourceName; - } - if (!chainedCalls.contains(srcInput)) { - // First call to an update method in this method - // Value of the source side (input side) resource. - params.add(0, new AbstractMap.SimpleEntry<>(src.getResource().getResourceStateType(), new AbstractMap.SimpleEntry<>(srcResourceName, "this.value"))); - srcInput.addStatement(getHttpMethodParamsStatement(srcType.getTypeName(), params, true)); - srcInput.addStatement("String result = " + getHttpMethodCallStatement(baseURL, dstResourceName, srcResName, httpMethod)); - chainedCalls.add(srcInput); - } else { - // After the second time of call to update methods in this method - // Value of the source side (input side) resource. - params.add(0, new AbstractMap.SimpleEntry<>(src.getResource().getResourceStateType(), new AbstractMap.SimpleEntry<>(srcResourceName, "this.value"))); - srcInput.addStatement(getHttpMethodParamsStatement(srcType.getTypeName(), params, false)); - srcInput.addStatement("result = " + getHttpMethodCallStatement(baseURL, dstResourceName, srcResName, httpMethod)); - } - srcInput.addThrow("JsonProcessingException"); + for (Entry>> pathEnt: resourcePaths.entrySet()) { + ChannelMember cm = pathEnt.getKey(); + ResourcePath src2 = pathEnt.getValue().getKey(); + // get outside src resource state by pull data transfer. + if (cm.isOutside() || src2.getCommonPrefix(dst.getInSideResource(ch)) == null) { + Type srcResourceType = src2.getResourceStateType(); + List pathParams = new ArrayList<>(); + for (Expression pathExp: src2.getPathParams()) { + pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); + } + generatePullDataTransfer(getter, src2.getLeafResourceName(), src2.getResourceHierarchy().toResourcePath(pathParams), srcResourceType); + } + } + } + // get src resource state by pull data transfer. + if (src.getNumberOfParameters() == 0 && src.getOutSideResource(ch).getCommonPrefix(dst.getInSideResource(ch)) == null) { + Type srcResourceType = src.getResourceStateType(); + List pathParams = new ArrayList<>(); + generatePullDataTransfer(getter, src.getResourceName(), src.getResourceHierarchy().toResourcePath(pathParams), srcResourceType); + } + } } - } else { - // for pull (or push/pull) data transfer - MethodDeclaration getter = getGetterMethod(dstType); - if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { - // generate a return statement. - String[] sideEffects = new String[] {""}; - String curState = d.getChannel().deriveUpdateExpressionOf(out, JerseyCodeGenerator.pullAccessor).toImplementation(sideEffects); // no pull data transfer is included. - getter.addStatement(sideEffects[0] + "return " + curState + ";"); - // For each reference channel member, get the current state of the reference side resource by pull data transfer. - for (ChannelMember c: d.getChannel().getReferenceChannelMembers()) { - String refResourceName = c.getResource().getResourceName(); - Type refResourceType = c.getResource().getResourceStateType(); - generatePullDataTransfer(getter, refResourceName, refResourceType); - } - } - // get src side resource state by pull data transfer. - Type srcResourceType = src.getResource().getResourceStateType(); - generatePullDataTransfer(getter, srcResourceName, srcResourceType); - } + } } } } + // for source nodes - for (Node n: graph.getNodes()) { - ResourceNode resource = (ResourceNode) n; - String resourceName = resource.getResource().getResourceName(); - TypeDeclaration type = typeMap.get(resourceName); - if (type != null) { - // getter method - MethodDeclaration getter = getGetterMethod(type); - if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { - getter.addStatement("return value;"); + for (ResourceHierarchy resource: model.getResourceHierarchies()) { + String resourceName = JerseyCodeGenerator.getComponentName(resource); + TypeDeclaration component = componentMap.get(resourceName); + if (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 ((child.getChildren() == null || child.getChildren().size() == 0) && child.getNumParameters() == 0) { + // the child is not a class + childGetter = new Term(new Symbol("get" + childTypeName, 1, Symbol.Type.METHOD)); + childGetter.addChild(new Constant("this")); + } else { + // the child is a class + childGetter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); + childGetter.addChild(new Field(fieldName, JerseyCodeGenerator.getImplStateType(child))); + } + composer = new Term(DataConstraintModel.insert); + composer.addChild(composerSub); + composer.addChild(new Constant(fieldName, DataConstraintModel.typeString)); // key + composer.addChild(childGetter); // value + composer.setType(DataConstraintModel.typeMap); + composerSub = composer; + } + composer.setType(stateGetter.getReturnType()); + String[] sideEffects = new String[] {null}; + String returnValue = composer.toImplementation(sideEffects); + if (sideEffects[0] != null) { + stateGetter.addStatement(sideEffects[0] + "return " + returnValue+ ";"); + } else { + stateGetter.addStatement("return " + returnValue+ ";"); + } + } + } + } + } + + // descendant getter method + 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; + } + } + 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; + params++; + } 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; + params++; + } + 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; + 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[] {""}; + if (rc.isOutside()) { + List pathParams = new ArrayList<>(); + for (Expression pathExp: ref.getPathParams()) { + pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); + } + generatePullDataTransfer(input, refResourceName, ref.getResourceHierarchy().toResourcePath(pathParams), refResourceType); + } else { + ResourcePath dstRes = out.getResource(); + if (!JerseyCodeGenerator.generatesComponent(dstRes.getResourceHierarchy())) { + dstRes = dstRes.getParent(); + } + Expression refGetter = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(ref, dstRes); + String refExp = refGetter.toImplementation(sideEffects); + String refTypeName = refResourceType.getInterfaceTypeName(); + input.addFirstStatement(sideEffects[0] + refTypeName + " " + refResourceName + " = " + refExp + ";"); + } + } + } + } + Expression updateExp = ch.deriveUpdateExpressionOf(out, JerseyCodeGenerator.refAccessor).getKey(); + // Replace Json constructor with a constructor of 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]; @@ -320,7 +922,81 @@ updateStatement = sideEffects[0] + "this.value = " + newState + ";"; } if (input.getBody() == null || !input.getBody().getStatements().contains(updateStatement)) { - input.addFirstStatement(updateStatement); + input.addStatement(updateStatement); + } + } else { + String updateStatement = ""; + if (sideEffects[0] != null) { + updateStatement = sideEffects[0]; + updateStatement = updateStatement.replace(".value", "." + JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(resource))); + } + if (DataConstraintModel.typeList.isAncestorOf(resource.getParent().getResourceStateType())) { + Term selector = new Term(DataConstraintModel.set); + selector.addChild(new Field("value")); + selector.addChild(new Variable(input.getParameters().get(input.getParameters().size() - 2).getName())); + selector.addChild(new Constant(newState)); + String[] sideEffects2 = new String[] {""}; + String newList = selector.toImplementation(sideEffects2); + updateStatement += sideEffects2[0]; + } else if (DataConstraintModel.typeMap.isAncestorOf(resource.getParent().getResourceStateType())) { + Term selector = new Term(DataConstraintModel.insert); + selector.addChild(new Field("value")); + selector.addChild(new Variable(input.getParameters().get(input.getParameters().size() - 2).getName())); + selector.addChild(new Constant(newState)); + String[] sideEffects2 = new String[] {""}; + String newMap = selector.toImplementation(sideEffects2); + updateStatement += sideEffects2[0]; + } else if (!(updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect())) { + updateStatement += "this." + JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(resource)) + " = " + newState + ";"; + } + if (updateStatement != null && (input.getBody() == null || !input.getBody().getStatements().contains(updateStatement))) { + input.addStatement(updateStatement); + } + } + + if (out.getResource().getParent() != null && out.getResource().getParent().getParent() != null) { + // In the root resource + Expression message = out.getStateTransition().getMessageExpression(); + String inputAccessorName = input.getName(); + if (message instanceof Term) { + inputAccessorName = ((Term) message).getSymbol().getImplName(); + } else if (message instanceof Variable) { + inputAccessorName = ((Variable) message).getName(); + } + MethodDeclaration inputAccessor = getMethod(componentMap.get(JerseyCodeGenerator.getComponentName(resource.getRoot())), inputAccessorName); + if (inputAccessor != null) { + // The expression of the receiver (resource) of the input method. + Expression resExp = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(out.getResource(), out.getResource().getRoot()); + String args = ""; + String delimiter = ""; + if (resExp instanceof Term) { + // to access the parent + if (((Term) resExp).getChildren().size() > 1 && ((Term) resExp).getChild(1) instanceof Variable) { + args += delimiter + ((Variable)((Term) resExp).getChild(1)).getName(); + delimiter = ", "; + } + resExp = ((Term) resExp).getChild(0); + } + String resourceAccess = resExp.toImplementation(new String[] {""}); + // 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 +1010,62 @@ return codes; } - private static void generatePullDataTransfer(MethodDeclaration methodBody, String fromResourceName, Type fromResourceType) { + private static void replaceJsonTermWithConstructorInvocation(Expression exp, Type replacedJsonType, String replacingClassName, TypeDeclaration descendantComponent) { + 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 = ""; + for (VariableDeclaration var: descendantConstructor.getParameters()) { + JsonAccessor jsonMember = new JsonAccessor(DataConstraintModel.dot); + jsonMember.addChild(jsonTerm); + jsonMember.addChild(new Constant(var.getName(), DataConstraintModel.typeString)); + Expression param = jsonMember.reduce(); + if (param != null) { + if (param instanceof Term) { + if (((Term) param).getType() == null) { + ((Term) param).setType(var.getType()); + } + } else if (param instanceof Variable) { + if (((Variable) param).getType() == null) { + ((Variable) param).setType(var.getType()); + } + } + constructorInvocation = constructorInvocation + delimiter + param.toImplementation(null); + } else { + constructorInvocation = constructorInvocation + delimiter + var.getName(); + } + delimiter = ", "; + } + constructorInvocation += ")"; + ((Term) exp).replaceSubTerm(termEnt.getKey(), new Constant(constructorInvocation)); + subTerms = ((Term) exp).getSubTerms(Term.class); + termEntItr = subTerms.entrySet().iterator(); + } 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 +1104,7 @@ if (respConverter.length() > 0) { methodBody.addFirstStatement(respConverter); } - methodBody.addFirstStatement(respTypeName + " " + varName + " = " + getHttpMethodCallStatementWithResponse(baseURL, fromResourceName, "get", respImplTypeName)); + methodBody.addFirstStatement(respTypeName + " " + varName + " = " + getHttpMethodCallStatementWithResponse(baseURL, fromResourcePath, "get", respImplTypeName)); } private static String convertFromEntryToMapType(Type type) { @@ -516,37 +1247,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 +1319,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 +1334,24 @@ return inputs; } - private static MethodDeclaration getInputMethod(TypeDeclaration type, ChannelMember out) { - MethodDeclaration input = null; - if (out.getStateTransition().getMessageExpression() instanceof Term) { - Term message = (Term) out.getStateTransition().getMessageExpression(); - input = getMethod(type, message.getSymbol().getImplName()); - } else if (out.getStateTransition().getMessageExpression() instanceof Variable) { - Variable message = (Variable) out.getStateTransition().getMessageExpression(); - input = getMethod(type, message.getName()); + private static MethodDeclaration getInputMethod(TypeDeclaration component, ChannelMember cm, int outNumber) { + String inputMethodName = null; + if (cm.getStateTransition().getMessageExpression() instanceof Term) { + Term message = (Term) cm.getStateTransition().getMessageExpression(); + inputMethodName = message.getSymbol().getImplName(); + } else if (cm.getStateTransition().getMessageExpression() instanceof Variable) { + Variable message = (Variable) cm.getStateTransition().getMessageExpression(); + inputMethodName = message.getName(); } + if (outNumber > 1) { + inputMethodName += "For" + JerseyCodeGenerator.getComponentName(cm.getResource().getResourceHierarchy()); + } + MethodDeclaration input = getMethod(component, inputMethodName); return input; } - private static MethodDeclaration getMethod(TypeDeclaration type, String methodName) { - for (MethodDeclaration m: type.getMethods()) { + private static MethodDeclaration getMethod(TypeDeclaration component, String methodName) { + for (MethodDeclaration m: component.getMethods()) { if (m.getName().equals(methodName)) return m; } return null; diff --git a/AlgebraicDataflowArchitectureModel/src/models/algebra/Constant.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/Constant.java index 440145e..50207b6 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/algebra/Constant.java +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Constant.java @@ -9,7 +9,7 @@ } public Constant(String value, Type type) { - super(new Symbol(value, 0), new ArrayList()); + super(new Symbol((type == null ? value: type.valueToRepresentation(value)), 0), new ArrayList()); symbol.setSignature(new Type[] {type}); } @@ -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..7695979 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/algebra/Expression.java +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Expression.java @@ -4,7 +4,18 @@ public abstract class Expression implements Cloneable { public abstract Expression getSubTerm(Position pos); + /** + * Get the unification between this expression and another expression. + * @param another another expression + * @return unified expression + */ public abstract Expression unify(Expression another); + /** + * Get the inverse map to obtain a sub-term of a given output value back from the output value itself. + * @param outputValue an output value (usually a term) + * @param targetPos a position in outputValue + * @return inverse map + */ public abstract Expression getInverseMap(Expression outputValue, Position targetPos); public abstract boolean contains(Expression exp); public abstract Object clone(); diff --git a/AlgebraicDataflowArchitectureModel/src/models/algebra/Field.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/Field.java index f51b3c4..6702d3f 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/algebra/Field.java +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Field.java @@ -14,7 +14,8 @@ } public Field(String name, Type type) { - super(name, type); + super(name); + symbol.setSignature(new Type[] {type}); } public Field(Symbol symbol) { @@ -22,7 +23,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/LambdaAbstraction.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/LambdaAbstraction.java new file mode 100644 index 0000000..9898224 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/LambdaAbstraction.java @@ -0,0 +1,30 @@ +package models.algebra; + +import java.util.ArrayList; +import java.util.List; + +public class LambdaAbstraction extends Symbol { + private List variables = null; + private Term term = null; + + public LambdaAbstraction(Variable variable, Term term) { + super("($" + variable.getName() + ")->" + term.toString(), 1, Type.LAMBDA); + this.variables = new ArrayList<>(); + this.variables.add(variable); + this.term = term; + } + + public LambdaAbstraction(List variables, Term term) { + super("($" + variables + ")->" + term.toString(), variables.size(), Type.LAMBDA); + this.variables = variables; + this.term = term; + } + + public List getVariables() { + return variables; + } + + public Term getTerm() { + return term; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/algebra/Parameter.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/Parameter.java index 739ab80..ab84998 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/algebra/Parameter.java +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Parameter.java @@ -12,7 +12,8 @@ } public Parameter(String name, Type type) { - super(name, type); + super(name); + symbol.setSignature(new Type[] {type}); } public Parameter(Symbol symbol) { @@ -20,7 +21,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..de90a73 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/algebra/Position.java +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Position.java @@ -28,6 +28,14 @@ public boolean isEmpty() { return (orders == null || orders.size() == 0); } + + public boolean isAncestorOf(Position another) { + if (another.orders.size() < this.orders.size()) return false; + for (int i = 0; i < orders.size(); i++) { + if (this.orders.get(i) != another.orders.get(i)) return false; + } + return true; + } public Object clone() { return new Position((ArrayList) orders.clone()); diff --git a/AlgebraicDataflowArchitectureModel/src/models/algebra/Symbol.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/Symbol.java index 5a06873..980fc53 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,6 +71,41 @@ } } + 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; } @@ -89,6 +129,10 @@ public boolean isMethod() { return (operatorType == Type.METHOD || operatorType == Type.METHOD_WITH_SIDE_EFFECT); } + + public boolean isLambda() { + return (operatorType == Type.LAMBDA); + } public Symbol[] getInverses() { return inverses; @@ -155,14 +199,26 @@ /** * Generate the implementation of this symbol * @param type the type of this symbol + * @param childrenTypes * @param childrenImpl the implementations of the children * @param childrenSideEffects (input) an array of the side effects of the children * @param sideEffect (output) an array of the side effect of this symbol * @return the implementation */ - public String generate(models.algebra.Type type, String[] childrenImpl, String[] childrenSideEffects, String[] sideEffect) { + public String generate(models.algebra.Type type, models.algebra.Type[] childrenTypes, String[] childrenImpl, String[] childrenSideEffects, String[] sideEffect) { if (generator != null) { - return generator.generate(type, childrenImpl, childrenSideEffects, sideEffect); + return generator.generate(type, childrenTypes, childrenImpl, childrenSideEffects, sideEffect); + } + return null; + } + + public boolean isCalculatable() { + return (calculator != null); + } + + public Expression calculate(List args) { + if (calculator != null) { + return calculator.calculate(args); } return null; } @@ -219,11 +275,16 @@ /** * Generate the implementation * @param type the type of this expression + * @param childrenTypes * @param children the implementations of the children * @param childrenSideEffects (input) an array of the side effects of the children * @param sideEffect (output) an array of the side effect of this generator * @return the generated implementation */ - public String generate(models.algebra.Type type, String children[], String[] childrenSideEffects, String[] sideEffect); + public String generate(models.algebra.Type type, models.algebra.Type[] childrenTypes, String children[], String[] childrenSideEffects, String[] sideEffect); + } + + public interface ICalculator { + public Expression calculate(List args); } } diff --git a/AlgebraicDataflowArchitectureModel/src/models/algebra/Term.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/Term.java index b7cad09..07993fa 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/algebra/Term.java +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Term.java @@ -1,12 +1,14 @@ package models.algebra; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map.Entry; public class Term extends Expression { protected Symbol symbol = null; - protected ArrayList children = new ArrayList<>(); + protected List children = new ArrayList<>(); protected Type type = null; public Term(Symbol symbol) { @@ -14,12 +16,18 @@ this.symbol = symbol; } - public Term(Symbol symbol, ArrayList children) { + public Term(Symbol symbol, List children) { super(); this.symbol = symbol; this.children = children; } + public Term(Symbol symbol, Expression[] children) { + super(); + this.symbol = symbol; + this.children = new ArrayList<>(Arrays.asList(children)); + } + public Symbol getSymbol() { return symbol; } @@ -45,6 +53,12 @@ children.add(child); return true; } + + public boolean setChild(int n, Expression child) { + if (getArity() != -1 && n >= getArity()) return false; + children.set(n, child); + return true; + } public void addChild(Expression child, boolean bForced) { if (!bForced && getArity() != -1 && children.size() >= getArity()) return; @@ -55,21 +69,28 @@ 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; @@ -109,6 +130,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; @@ -123,6 +146,69 @@ } } + 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 public Expression getInverseMap(Expression outputValue, Position targetPos) { if (targetPos.isEmpty()) return outputValue; @@ -175,7 +261,11 @@ public Object clone() { Term newTerm = new Term(symbol); for (Expression e: children) { - newTerm.addChild((Expression) e.clone()); + if (e != null) { + newTerm.addChild((Expression) e.clone()); + } else { + newTerm.addChild(null); + } } newTerm.type = type; return newTerm; @@ -185,7 +275,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++) { @@ -198,7 +288,11 @@ String exp = symbol.toString() + "("; String delimiter = ""; for (Expression e: children) { - exp += (delimiter + e.toString()); + if (e != null) { + exp += (delimiter + e.toString()); + } else { + exp += (delimiter + "null"); + } delimiter = ","; } return exp + ")"; @@ -213,50 +307,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++) { + Expression child = children.get(i); + if (child instanceof Variable) { + childrenTypes[i] = ((Variable) child).getType(); + } else if (child instanceof Term) { + childrenTypes[i] = ((Term) child).getType(); + } String childSideEffect[] = new String[] {""}; - childrenImpl[i] = children.get(i).toImplementation(childSideEffect); + childrenImpl[i] = child.toImplementation(childSideEffect); childrenSideEffects[i] = childSideEffect[0]; } - String exp = symbol.generate(getType(), childrenImpl, childrenSideEffects, sideEffects); + String exp = symbol.generate(getType(), childrenTypes, childrenImpl, childrenSideEffects, sideEffects); if (symbol.isImplWithSideEffect()) { sideEffects[0] = sideEffects[0] + exp; - exp = children.get(0).toImplementation(new String[] {""}); // the value of this term + exp = childrenImpl[0]; // the value of this term } return exp; } else { for (int i = 0; i < children.size(); i++) { + Expression child = children.get(implParamOrder[i]); + if (child instanceof Variable) { + childrenTypes[i] = ((Variable) child).getType(); + } else if (child instanceof Term) { + childrenTypes[i] = ((Term) child).getType(); + } String childSideEffect[] = new String[] {""}; - childrenImpl[i] = children.get(implParamOrder[i]).toImplementation(childSideEffect); + childrenImpl[i] = child.toImplementation(childSideEffect); childrenSideEffects[i] = childSideEffect[0]; } - String exp = symbol.generate(getType(), childrenImpl, childrenSideEffects, sideEffects); + String exp = symbol.generate(getType(), childrenTypes, childrenImpl, childrenSideEffects, sideEffects); if (symbol.isImplWithSideEffect()) { sideEffects[0] = sideEffects[0] + exp; - exp = children.get(implParamOrder[0]).toImplementation(new String[] {""}); // the value of this term + exp = childrenImpl[0]; // the value of this term } return exp; } @@ -265,12 +373,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 +395,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 +410,7 @@ exp += ")"; if (symbol.isImplWithSideEffect()) { sideEffects[0] = sideEffects[0] + exp + ";\n"; - exp = children.get(implParamOrder[0]).toImplementation(new String[] {""}); + exp = receiver; } return exp; } diff --git a/AlgebraicDataflowArchitectureModel/src/models/algebra/Type.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/Type.java index 50b0285..9e3eecb 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/algebra/Type.java +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Type.java @@ -9,28 +9,28 @@ private String interfaceTypeName; private List parentTypes = new ArrayList<>(); - public Type(String typeName, String implementastionTypeName) { + public Type(String typeName, String implementationTypeName) { this.typeName = typeName; - this.implementationTypeName = implementastionTypeName; - this.interfaceTypeName = implementastionTypeName; + this.implementationTypeName = implementationTypeName; + this.interfaceTypeName = implementationTypeName; } - public Type(String typeName, String implementastionTypeName, String interfaceTypeName) { + public Type(String typeName, String implementationTypeName, String interfaceTypeName) { this.typeName = typeName; - this.implementationTypeName = implementastionTypeName; + this.implementationTypeName = implementationTypeName; this.interfaceTypeName = interfaceTypeName; } - public Type(String typeName, String implementastionTypeName, Type parentType) { + public Type(String typeName, String implementationTypeName, Type parentType) { this.typeName = typeName; - this.implementationTypeName = implementastionTypeName; - this.interfaceTypeName = implementastionTypeName; + this.implementationTypeName = implementationTypeName; + this.interfaceTypeName = implementationTypeName; this.parentTypes.add(parentType); } - public Type(String typeName, String implementastionTypeName, String interfaceTypeName, Type parentType) { + public Type(String typeName, String implementationTypeName, String interfaceTypeName, Type parentType) { this.typeName = typeName; - this.implementationTypeName = implementastionTypeName; + this.implementationTypeName = implementationTypeName; this.interfaceTypeName = interfaceTypeName; this.parentTypes.add(parentType); } @@ -73,12 +73,32 @@ public boolean isAncestorOf(Type another) { if (this.equals(another)) return true; - if (another.getParentTypes() == null) return false; + 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/dataConstraintModel/Channel.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Channel.java index 8bf74da..2bec47e 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Channel.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Channel.java @@ -1,25 +1,44 @@ package models.dataConstraintModel; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Set; +import models.algebra.Expression; import models.algebra.Variable; public class Channel { protected String channelName; - protected Set selectors = null; + protected Channel parent = null; + protected List children = null; + protected List selectors = null; protected Set channelMembers = null; protected String sourceText = null; + protected boolean isNative = false; public Channel(String channelName) { this.channelName = channelName; - selectors = new HashSet<>(); + this.parent = null; + this.children = new ArrayList<>(); + selectors = new ArrayList<>(); channelMembers = new HashSet<>(); } - public Channel(String channelName, Set variables) { + public Channel(String channelName, Variable variable) { this.channelName = channelName; - selectors = new HashSet<>(); + this.parent = null; + this.children = new ArrayList<>(); + selectors = new ArrayList<>(); + selectors.add(new Selector(variable)); + channelMembers = new HashSet<>(); + } + + public Channel(String channelName, List variables) { + this.channelName = channelName; + this.parent = null; + this.children = new ArrayList<>(); + selectors = new ArrayList<>(); for (Variable var: variables) { selectors.add(new Selector(var)); } @@ -30,17 +49,51 @@ 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.refleshOutside(); + } + + 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; @@ -48,23 +101,55 @@ 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) { + public void refleshOutside() { + 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() == id) { + if (cm.getResource() == res) { channelMembers.remove(cm); break; } @@ -78,6 +163,17 @@ } 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..370c3f7 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ChannelMember.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ChannelMember.java @@ -5,13 +5,13 @@ public class ChannelMember { private ResourcePath resourcePath = null; - private List selectors = null; private StateTransition stateTransition = null; + private boolean isOutside = false; public ChannelMember(ResourcePath resourcePath) { this.resourcePath = resourcePath; - selectors = new ArrayList<>(); stateTransition = new StateTransition(); + isOutside = false; } public ResourcePath getResource() { @@ -22,19 +22,6 @@ this.resourcePath = resourcePath; } - public List getSelectors() { - return selectors; - } - - public void setSelectors(List selectors) { - this.selectors = selectors; - } - - public ChannelMember addSelector(Selector selector) { - selectors.add(selector); - return this; - } - public StateTransition getStateTransition() { return stateTransition; } @@ -43,16 +30,24 @@ this.stateTransition = stateTransition; } + public boolean isOutside() { + return this.isOutside; + } + + public void setOutside(boolean isOutside) { + this.isOutside = isOutside; + } + @Override public String toString() { if (stateTransition.getNextStateExpression() == null) { - return resourcePath.getResourceName() + "(" + return resourcePath.toString() + "(" + stateTransition.getCurStateExpression() + "," + stateTransition.getMessageExpression() + ")"; } - return resourcePath.getResourceName() + "(" + return resourcePath.toString() + "(" + stateTransition.getCurStateExpression() + "," + stateTransition.getMessageExpression() + ")" - + " == " + stateTransition.getNextStateExpression(); + + " = " + stateTransition.getNextStateExpression(); } } diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/DataConstraintModel.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/DataConstraintModel.java index 35b2aa7..b40602b 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/DataConstraintModel.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/DataConstraintModel.java @@ -1,415 +1,1283 @@ -package models.dataConstraintModel; - -import java.util.Collection; -import java.util.HashMap; - -import models.algebra.Symbol; -import models.algebra.Type; -import parser.Parser; - -public class DataConstraintModel { - protected HashMap resourcePaths = null; - protected HashMap channels = null; - protected HashMap ioChannels = null; - protected HashMap types = null; - protected HashMap symbols = null; - public static final Type typeInt = new Type("Int", "int"); - public static final Type typeLong = new Type("Long", "long", typeInt); - 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 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); - public static final Type typeTuple = new Type("Tuple", "AbstractMap.SimpleEntry", "Map.Entry"); - public static final Type typePair = new Type("Pair", "Pair", "Pair"); - 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 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() { - @Override - public String generate(Type type, String[] children, String[] childrenSideEffects, String[] sideEffect) { - String compType = ""; - if (type != null) { - String interfaceType = type.getInterfaceTypeName(); - if (interfaceType.contains("<")) { - compType = interfaceType.substring(interfaceType.indexOf("<") + 1, interfaceType.lastIndexOf(">")); - } - String implType = type.getImplementationTypeName(); - if (implType.indexOf('<') >= 0) { - implType = implType.substring(0, implType.indexOf('<')); - } - return "new " + implType + "<" + compType + ">()"; - } - return "new ArrayList<" + compType + ">()"; - } - }); - public static final Symbol null_ = new Symbol("null", 0, Symbol.Type.PREFIX, "null", 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) { - String temp = "temp_if" + count[0]; - String impl = ""; - - impl += type.getInterfaceTypeName() + " " + temp + ";\n"; - if (childrenSideEffects[0] != null && childrenSideEffects[0].length() > 0) impl += childrenSideEffects[0]; - impl += "if (" + children[0] + ") {\n"; - if (childrenSideEffects[1] != null && childrenSideEffects[1].length() > 0) impl += "\t" + childrenSideEffects[1]; - impl += "\t" + temp + " = " + children[1] + ";\n"; - impl += "} else {\n"; - if (childrenSideEffects[2] != null && childrenSideEffects[2].length() > 0) impl += "\t" + childrenSideEffects[2]; - impl += "\t" + temp + " = " + children[2] + ";\n"; - impl += "}\n"; - - sideEffect[0] += impl; - - count[0]++; - return temp; - } - }); - - - 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) { - sideEffect[0] += s; - } - String impl = "new Pair<>(" + childrenImpl[0] + "," + childrenImpl[1] + ")"; - return impl; - } - }); - 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) { - sideEffect[0] += s; - } - String impl = "new AbstractMap.SimpleEntry<>(" + childrenImpl[0] + "$x)"; - for (int i = 1; i < childrenImpl.length - 1; i++) { - impl = impl.replace("$x", ", new AbstractMap.SimpleEntry<>(" + childrenImpl[i] + "$x)"); - } - impl = impl.replace("$x", ", " + childrenImpl[childrenImpl.length - 1]); - return impl; - } - }); - public static final Symbol fst = new Symbol("fst", 1, Symbol.Type.PREFIX, "getKey", Symbol.Type.METHOD); - 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}; - @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; - } - }); - - 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}); - } - - public DataConstraintModel() { - resourcePaths = new HashMap<>(); - channels = new HashMap<>(); - ioChannels = new HashMap<>(); - types = new HashMap<>(); - addType(typeInt); - addType(typeLong); - addType(typeFloat); - addType(typeDouble); - addType(typeBoolean); - addType(typeString); - addType(typeList); - addType(typePair); - addType(typeTuple); - addType(typeMap); - symbols = new HashMap<>(); - addSymbol(add); - addSymbol(mul); - addSymbol(sub); - 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); - addSymbol(lt); - addSymbol(ge); - addSymbol(le); - addSymbol(and); - addSymbol(or); - addSymbol(neg); - addSymbol(true_); - addSymbol(false_); - addSymbol(null_); - addSymbol(pair); - addSymbol(left); - addSymbol(right); - addSymbol(tuple); - addSymbol(fst); - addSymbol(snd); - addSymbol(insert); - addSymbol(lookup); - } - - public Collection getResourcePaths() { - return resourcePaths.values(); - } - - public ResourcePath getResourcePath(String resourceName) { - return resourcePaths.get(resourceName); - } - - public void addResourcePath(ResourcePath resourcePath) { - resourcePaths.put(resourcePath.getResourceName(), resourcePath); - } - - public void setResourcePaths(HashMap 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); - } - for (Channel ch: ioChannels.values()) { - ch.removeChannelMember(id); - } - } - - 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 Channel getIOChannel(String channelName) { - return ioChannels.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 addIOChannel(Channel ioChannel) { - ioChannels.put(ioChannel.getChannelName(), ioChannel); - for (ResourcePath id: ioChannel.getResources()) { - resourcePaths.put(id.getResourceName(), id); - } - } - - public void removeIOChannel(String ioChannelName) { - ioChannels.remove(ioChannelName); - } - - public void addType(Type type) { - types.put(type.getTypeName(), type); - } - - public Type getType(String name) { - return types.get(name); - } - - public void addSymbol(Symbol symbol) { - symbols.put(symbol.getName(), symbol); - } - - public Symbol getSymbol(String name) { - return symbols.get(name); - } - - public static String getWrapperType(Type type) { - if (type == typeInt) { - return "Integer"; - } else if (type == typeLong) { - return "Long"; - } else if (type == typeFloat) { - return "Float"; - } else if (type == typeDouble) { - return "Double"; - } else if (type == typeBoolean) { - return "Boolean"; - } - return null; - } - - public boolean isPrimitiveType(Type type) { - 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"; - } else if (type == typeLong) { - return "0L"; - } else if (type == typeFloat) { - return "0.0f"; - } else if (type == typeDouble) { - return "0.0"; - } else if (type == typeBoolean) { - return "false"; - } else if (type == typeString) { - return "\"\""; - } - return "new " + type.getImplementationTypeName() + "()"; - } - - @Override - public String toString() { - String out = ""; - for (Channel channel: ioChannels.values()) { - out += channel.toString(); - } - for (Channel channel: channels.values()) { - out += channel.toString(); - } - return out; - } - - public String getSourceText() { - String out = ""; - String init = ""; - for (ResourcePath resource: resourcePaths.values()) { - String initializer = resource.getInitText(); - if (initializer != null) { - init += initializer; - } - } - if (init.length() > 0) { - out += "init {\n" + init + "}\n"; - } - for (Channel channel: ioChannels.values()) { - out += channel.getSourceText(); - } - for (Channel channel: channels.values()) { - out += channel.getSourceText(); - } - return out; - } -} +package models.dataConstraintModel; + +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Stack; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.LambdaAbstraction; +import models.algebra.Symbol; +import models.algebra.Term; +import models.algebra.Type; +import models.algebra.Variable; +import parser.Parser; + +public class DataConstraintModel { + protected List resourcePaths = null; + protected HashMap resourceHierarchies = null; + protected HashMap channels = null; + protected HashMap inputChannels = null; + protected HashMap types = null; + protected HashMap symbols = null; + public static final Type typeInt = new Type("Int", "int"); + public static final Type typeLong = new Type("Long", "long", typeInt); + 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 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); + public static final Type typeTuple = new Type("Tuple", "AbstractMap.SimpleEntry", "Map.Entry"); + public static final Type typePair = new Type("Pair", "Pair", "Pair"); + 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 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); + } + } + 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, 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("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); + } + } + } + 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(); + if (interfaceType.contains("<")) { + compType = interfaceType.substring(interfaceType.indexOf("<") + 1, interfaceType.lastIndexOf(">")); + } + String implType = type.getImplementationTypeName(); + if (implType.indexOf('<') >= 0) { + implType = implType.substring(0, implType.indexOf('<')); + } + 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, Type[] childrenTypes, String[] childrenImpl, String[] childrenSideEffects, String[] sideEffect) { + String temp = "temp_if" + count[0]; + String impl = ""; + + impl += type.getInterfaceTypeName() + " " + temp + ";\n"; + if (childrenSideEffects[0] != null && childrenSideEffects[0].length() > 0) impl += childrenSideEffects[0]; + impl += "if (" + 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 + " = " + childrenImpl[2] + ";\n"; + impl += "}\n"; + + sideEffect[0] += impl; + + 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 pair = new Symbol("pair", -1, 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; + } + String impl = "new Pair<>(" + childrenImpl[0] + "," + childrenImpl[1] + ")"; + return impl; + } + }); + public static final Symbol tuple = new Symbol("tuple", -1, 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; + } + String impl = "new AbstractMap.SimpleEntry<>(" + childrenImpl[0] + "$x)"; + for (int i = 1; i < childrenImpl.length - 1; i++) { + impl = impl.replace("$x", ", new AbstractMap.SimpleEntry<>(" + childrenImpl[i] + "$x)"); + } + impl = impl.replace("$x", ", " + childrenImpl[childrenImpl.length - 1]); + return impl; + } + }); + public static final Symbol fst = new Symbol("fst", 1, Symbol.Type.PREFIX, "getKey", Symbol.Type.METHOD); + 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); // 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 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}); + 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 ArrayList<>(); + resourceHierarchies = new HashMap<>(); + channels = new HashMap<>(); + inputChannels = new HashMap<>(); + types = new HashMap<>(); + addType(typeInt); + addType(typeLong); + addType(typeFloat); + addType(typeDouble); + addType(typeBoolean); + addType(typeString); + addType(typeList); + addType(typePair); + addType(typeTuple); + addType(typeMap); + addType(typeJson); + symbols = new HashMap<>(); + addSymbol(add); + addSymbol(mul); + addSymbol(sub); + addSymbol(div); + addSymbol(minus); + addSymbol(mod); + addSymbol(eq); + addSymbol(neq); + addSymbol(gt); + addSymbol(lt); + addSymbol(ge); + addSymbol(le); + 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(pair); + addSymbol(left); + addSymbol(right); + addSymbol(tuple); + 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 getResourceHierarchies() { + return resourceHierarchies.values(); + } + + 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.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(List resourcePaths) { + this.resourcePaths = resourcePaths; + } + + public ResourcePath getResourcePath(String path) { + for (ResourcePath resourcePath: resourcePaths) { + if (resourcePath.toString().equals(path)) return resourcePath; + } + return null; + } + + public void removeResourcePath(String path) { + ResourcePath resourcePath = getResourcePath(path); + if (resourcePath == null) return; + resourcePaths.remove(resourcePath); + for (Channel ch: channels.values()) { + ch.removeChannelMember(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; + } + + public void addChannel(Channel channel) { + channels.put(channel.getChannelName(), channel); + } + + public void removeChannel(String channelName) { + channels.remove(channelName); + } + + public Collection getInputChannels() { + return inputChannels.values(); + } + + public Channel getInputChannel(String channelName) { + return inputChannels.get(channelName); + } + + public void setInputChannels(HashMap inputChannels) { + this.inputChannels = inputChannels; + } + + public void addInputChannel(Channel inputChannel) { + inputChannels.put(inputChannel.getChannelName(), inputChannel); + } + + public void removeInputChannel(String inputChannelName) { + inputChannels.remove(inputChannelName); + } + + public void addType(Type type) { + types.put(type.getTypeName(), type); + } + + public Type getType(String name) { + return types.get(name); + } + + public void addSymbol(Symbol symbol) { + symbols.put(symbol.getName(), symbol); + } + + public Symbol getSymbol(String name) { + return symbols.get(name); + } + + public static String getWrapperType(Type type) { + if (type == typeInt) { + return "Integer"; + } else if (type == typeLong) { + return "Long"; + } else if (type == typeFloat) { + return "Float"; + } else if (type == typeDouble) { + return "Double"; + } else if (type == typeBoolean) { + return "Boolean"; + } + return null; + } + + public boolean isPrimitiveType(Type type) { + 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"; + } else if (type == typeLong) { + return "0L"; + } else if (type == typeFloat) { + return "0.0f"; + } else if (type == typeDouble) { + return "0.0"; + } else if (type == typeBoolean) { + return "false"; + } else if (type == typeString) { + return "\"\""; + } + 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: inputChannels.values()) { + out += channel.toString(); + } + for (Channel channel: channels.values()) { + out += channel.toString(); + } + return out; + } + + public String getSourceText() { + String out = ""; + String init = ""; + for (ResourceHierarchy resource: resourceHierarchies.values()) { + String initializer = resource.getInitText(); + if (initializer != null) { + init += resource.toString() + " := " + initializer + "\n"; + } + } + if (init.length() > 0) { + out += "init {\n" + init + "}\n"; + } + for (Channel channel: inputChannels.values()) { + out += channel.getSourceText(); + } + for (Channel channel: channels.values()) { + out += channel.getSourceText(); + } + return out; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonAccessor.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonAccessor.java new file mode 100644 index 0000000..30a7465 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonAccessor.java @@ -0,0 +1,182 @@ +package models.dataConstraintModel; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.LambdaAbstraction; +import models.algebra.Position; +import models.algebra.Symbol; +import models.algebra.Term; +import models.algebra.Type; +import models.algebra.Variable; + +public class JsonAccessor extends Term { + + public JsonAccessor(Symbol symbol) { + super(symbol); + } + + @Override + public Expression reduce() { + Expression reducedTerm = super.reduce(); + if (reducedTerm instanceof Term) { + if ((symbol == DataConstraintModel.dot || symbol == DataConstraintModel.dotParam) && getChildren().size() >= 2) { + // this term is `json.key`. + Expression expJson = getChild(0); + Expression expKey = getChild(1); + if (expKey instanceof Constant && expJson instanceof Term) { + reducedTerm = getValue((Term) expJson, (Constant) expKey); + } + } + } + return reducedTerm; + } + + private Expression getValue(Term json, Constant key) { + if (json instanceof JsonTerm) { + return ((JsonTerm) json).get(key); + } + if (json.getSymbol().equals(DataConstraintModel.addMember) || 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_); + } + + @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..ee98148 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonTerm.java @@ -0,0 +1,112 @@ +package models.dataConstraintModel; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Symbol; +import models.algebra.Term; +import parser.Parser; + +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())); + } + + @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 + "}"; + } + + private String toString(int indentLevel) { + StringBuilder jsonStr = new StringBuilder("{"); + String delim = "\n"; + String indent = " ".repeat(indentLevel + 1); + + for (String key : keyToIndex.keySet()) { + jsonStr.append(delim).append(indent) + .append(Parser.DOUBLE_QUOT).append(key).append(Parser.DOUBLE_QUOT).append(": ") + .append(formatValue(getChild(keyToIndex.get(key)), indentLevel + 1)); + delim = ",\n"; + } + + jsonStr.append("\n").append(" ".repeat(indentLevel)).append("}"); + return jsonStr.toString(); + } + + private String formatValue(Expression value, int indentLevel) { + if (value instanceof JsonTerm) { + return ((JsonTerm) value).toString(indentLevel); + } else { + return value.toString(); // Handle constants or other types of expressions + } + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonType.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonType.java new file mode 100644 index 0000000..9f9e835 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonType.java @@ -0,0 +1,65 @@ +package models.dataConstraintModel; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import models.algebra.Type; + +public class JsonType extends Type { + protected boolean isListOrMap = false; + protected Map memberTypes = null; + protected Type listOrMapElementType = null; + + public JsonType(String typeName, String implementationTypeName) { + super(typeName, implementationTypeName); + memberTypes = new HashMap<>(); + } + + public JsonType(String typeName, String implementationTypeName, String interfaceTypeName) { + super(typeName, implementationTypeName, interfaceTypeName); + memberTypes = new HashMap<>(); + } + + public JsonType(String typeName, String implementationTypeName, Type parentType) { + super(typeName, implementationTypeName, parentType); + memberTypes = new HashMap<>(); + } + + public JsonType(String typeName, String implementationTypeName, String interfaceTypeName, Type parentType) { + super(typeName, implementationTypeName, interfaceTypeName, parentType); + memberTypes = new HashMap<>(); + } + + public Map getMemberTypes() { + return memberTypes; + } + + public Type getMemberType(String key) { + return memberTypes.get(key); + } + + public Set getKeys() { + return memberTypes.keySet(); + } + + public void addMemberType(String key, Type valueType) { + memberTypes.put(key, valueType); + } + +// public boolean isListOrMap() { +// return isListOrMap; +// } +// +// public void setListOrMap(boolean isListOrMap) { +// this.isListOrMap = isListOrMap; +// } + + public Type getElementType() { + return listOrMapElementType; + } + + public void setElementType(Type listElementType) { + this.listOrMapElementType = listElementType; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ListTerm.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ListTerm.java new file mode 100644 index 0000000..afc4212 --- /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..18f2c9b --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/MapTerm.java @@ -0,0 +1,87 @@ +package models.dataConstraintModel; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Symbol; +import models.algebra.Term; +import parser.Parser; + +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..9c48d1c --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourceHierarchy.java @@ -0,0 +1,257 @@ +package models.dataConstraintModel; + +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import models.algebra.Expression; +import models.algebra.Term; +import models.algebra.Type; +import models.algebra.Variable; + +public class ResourceHierarchy { + private ResourceHierarchy parent = null; + private Set children = null; + private String resourceName = null; + private Type resourceStateType = null; + private int numParameters = 0; + protected Expression initialValue; + protected String initText; + 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..97f5607 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourcePath.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourcePath.java @@ -1,70 +1,181 @@ package models.dataConstraintModel; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.AbstractMap; +import java.util.AbstractMap.SimpleEntry; + import models.algebra.Expression; -import models.algebra.Term; import models.algebra.Type; +import models.algebra.Symbol; +import models.algebra.Term; -public class ResourcePath { - private String resourceName = null; - private Type resourceStateType = null; - private int numParameters = 0; - private Expression initialValue = null; - protected String initText = null; +public class ResourcePath extends Symbol { + protected ResourcePath parent = null; + protected ResourceHierarchy resourceHierarchy = null; + protected List> pathParams = null; + + public ResourcePath(String fullResourceName) { + super(fullResourceName); + this.parent = null; + this.resourceHierarchy = new ResourceHierarchy(null, fullResourceName); + this.pathParams = new ArrayList<>(); + } - public ResourcePath(String resourceName, int numParameters) { - this.resourceName = resourceName; - this.numParameters = numParameters; + public ResourcePath(String fullResourceName, ResourceHierarchy resourceHierarchy) { + super(fullResourceName); + this.parent = null; + this.resourceHierarchy = resourceHierarchy; + this.pathParams = new ArrayList<>(); + } + + public ResourcePath(ResourcePath parent, String leafResourceName) { + super(parent.toString() + "." + leafResourceName); + this.parent = parent; + this.resourceHierarchy = new ResourceHierarchy(parent.getResourceHierarchy(), leafResourceName); + this.pathParams = parent.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); + this.parent = another.parent; + this.resourceHierarchy = another.resourceHierarchy; + this.pathParams = new ArrayList<>(another.getPathParamsAndConstraints()); } - public ResourcePath(String resourceName, Type resourceStateType, int numParameters) { - this.resourceName = resourceName; - this.resourceStateType = resourceStateType; - this.numParameters = numParameters; + public ResourceHierarchy getResourceHierarchy() { + return resourceHierarchy; } - public String getResourceName() { - return resourceName; + public void setResourceHierarchy(ResourceHierarchy resourceHierarchy) { + this.resourceHierarchy = resourceHierarchy; + } + + public String getLeafResourceName() { + return resourceHierarchy.getResourceName(); } public int getNumberOfParameters() { - return numParameters; + return resourceHierarchy.getTotalNumParameters(); } - public Type getResourceStateType() { - return resourceStateType; + public models.algebra.Type getResourceStateType() { + return resourceHierarchy.getResourceStateType(); } - public void setResourceStateType(Type resourceStateType) { - this.resourceStateType = resourceStateType; - if (initialValue != null) { - if (initialValue instanceof Term) { - ((Term) initialValue).setType(resourceStateType); - } + public void setResourceStateType(models.algebra.Type resourceStateType) { + this.resourceHierarchy.setResourceStateType(resourceStateType); + } + + public List getPathParams() { + List params = new ArrayList<>(); + for (Map.Entry paramEnt: this.pathParams) { + params.add(paramEnt.getKey()); } + return params; } - public Expression getInitialValue() { - return initialValue; + public List> getPathParamsAndConstraints() { + return pathParams; } - public void setInitialValue(Expression initialValue) { - this.initialValue = initialValue; + public void setPathParams(List> pathParams) { + this.pathParams = pathParams; } - public void setInitText(String initText) { - this.initText = initText; + 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 String getInitText() { - return initText; - } - - public boolean equals(Object another) { - if (!(another instanceof ResourcePath)) return false; - return resourceName.equals(((ResourcePath) another).resourceName); + public boolean endsWithParam() { + if (resourceHierarchy.getNumParameters() > 0) return true; + return false; } - public int hashCode() { - return resourceName.hashCode(); + public Expression getLastParam() { + if (endsWithParam()) { + return pathParams.get(pathParams.size() - 1).getKey(); + } + return null; + } + + 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 String toString() { + return resourceHierarchy.toString(pathParams); } } diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Selector.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Selector.java index a29f8e3..f170a07 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Selector.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Selector.java @@ -1,19 +1,19 @@ package models.dataConstraintModel; -import models.algebra.Variable; +import models.algebra.Expression; public class Selector { - private Variable variable = null; + private Expression exp = null; - public Selector(Variable variable) { - this.setVariable(variable); + public Selector(Expression exp) { + this.exp = exp; } - public Variable getVariable() { - return variable; + public Expression getExpression() { + return exp; } - public void setVariable(Variable variable) { - this.variable = variable; + public void setExpression(Expression exp) { + this.exp = exp; } } diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/StateTransition.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/StateTransition.java index 011045a..997d1f6 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/StateTransition.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/StateTransition.java @@ -3,7 +3,9 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.Map.Entry; +import java.util.Set; +import models.algebra.Constant; import models.algebra.Expression; import models.algebra.InvalidMessage; import models.algebra.Position; @@ -42,13 +44,24 @@ } public boolean isRightUnary() { - for (Position pos : curStateExpression.getVariables().keySet()) { - if (nextStateExpression.contains(curStateExpression.getVariables().get(pos))) return false; + for (Variable var: curStateExpression.getVariables().values()) { + if (nextStateExpression.contains(var)) return false; } return true; } + + public boolean isRightPartial() { + for (Variable var: curStateExpression.getVariables().values()) { + if (messageExpression.contains(var)) return true; + } + if (isRightUnary()) return false; + for (Variable var: messageExpression.getVariables().values()) { + if (nextStateExpression.contains(var)) return true; + } + return false; + } - public Expression deriveMessageConstraintFor(Expression curStateValue, Expression nextStateValue) throws InvalidMessage, ResolvingMultipleDefinitionIsFutureWork { + public Expression deriveMessageConstraintFor(Expression curStateValue, Expression nextStateValue, Set substitutedPositions) throws InvalidMessage, ResolvingMultipleDefinitionIsFutureWork { HashMap> bindings = new HashMap<>(); Expression curStateTerm = getCurStateExpression(); @@ -58,6 +71,9 @@ Position varPos = curStateVarEnt.getKey(); Expression valueCalc = curStateTerm.getInverseMap(curStateValue, varPos); if (valueCalc != null) { + if (valueCalc instanceof Term && !(valueCalc instanceof Constant)) { + valueCalc = ((Term) valueCalc).reduce(); + } ArrayList values = bindings.get(var); if (values == null) { values = new ArrayList(); @@ -87,6 +103,9 @@ Position varPos = nextStateVarEnt.getKey(); Expression valueCalc = nextStateTerm.getInverseMap(nextStateValue, varPos); if (valueCalc != null) { + if (valueCalc instanceof Term) { + valueCalc = ((Term) valueCalc).reduce(); + } ArrayList values = bindings.get(var); if (values == null) { values = new ArrayList(); @@ -97,18 +116,10 @@ } Expression messageTerm = getMessageExpression(); - if (!(messageTerm instanceof Term)) throw new InvalidMessage(); - HashMap messageVars = messageTerm.getVariables(); - for (Variable var: messageVars.values()) { - if (bindings.get(var) != null) { - if (bindings.get(var).size() > 1) throw new ResolvingMultipleDefinitionIsFutureWork(); - messageTerm = ((Term) messageTerm).substitute(var, bindings.get(var).iterator().next()); - } - } - return messageTerm; + return substituteValues(messageTerm, bindings, substitutedPositions); } - public Expression deriveMessageConstraintFor(Expression curStateValue) throws InvalidMessage, ResolvingMultipleDefinitionIsFutureWork { + public Expression deriveMessageConstraintFor(Expression curStateValue, Set substitutedPositions) throws InvalidMessage, ResolvingMultipleDefinitionIsFutureWork { HashMap> bindings = new HashMap<>(); Expression curStateTerm = getCurStateExpression(); @@ -118,6 +129,9 @@ Position varPos = curStateVarEnt.getKey(); Expression valueCalc = curStateTerm.getInverseMap(curStateValue, varPos); if (valueCalc != null) { + if (valueCalc instanceof Term && !(valueCalc instanceof Constant)) { + valueCalc = ((Term) valueCalc).reduce(); + } ArrayList values = bindings.get(var); if (values == null) { values = new ArrayList(); @@ -128,12 +142,26 @@ } Expression messageTerm = getMessageExpression(); - if (!(messageTerm instanceof Term)) throw new InvalidMessage(); + return substituteValues(messageTerm, bindings, substitutedPositions); + } + + private Expression substituteValues(Expression messageTerm, HashMap> bindings, Set substitutedPositions) + throws InvalidMessage, ResolvingMultipleDefinitionIsFutureWork { + if (!(messageTerm instanceof Term) && !(messageTerm instanceof Variable)) throw new InvalidMessage(); HashMap messageVars = messageTerm.getVariables(); - for (Variable var: messageVars.values()) { + for (Entry varEnt: messageVars.entrySet()) { + Variable var = varEnt.getValue(); if (bindings.get(var) != null) { if (bindings.get(var).size() > 1) throw new ResolvingMultipleDefinitionIsFutureWork(); - messageTerm = ((Term) messageTerm).substitute(var, bindings.get(var).iterator().next()); + if (messageTerm instanceof Term) { + substitutedPositions.add(varEnt.getKey()); + messageTerm = ((Term) messageTerm).substitute(var, bindings.get(var).iterator().next()); + } else if (messageTerm instanceof Variable) { + if (messageTerm.equals(var)) { + substitutedPositions.add(new Position()); + return bindings.get(var).iterator().next(); + } + } } } return messageTerm; @@ -150,6 +178,9 @@ Position varPos = curStateVarEnt.getKey(); Expression valueCalc = curStateTerm.getInverseMap(curStateValue, varPos); if (valueCalc != null) { + if (valueCalc instanceof Term && !(valueCalc instanceof Constant)) { + valueCalc = ((Term) valueCalc).reduce(); + } if (bindings.get(var) != null) throw new ResolvingMultipleDefinitionIsFutureWork(); bindings.put(var, valueCalc); } @@ -178,6 +209,9 @@ nextStateTerm = ((Term) nextStateTerm).substitute(var, bindings.get(var)); } } + if (nextStateTerm instanceof Term && !(nextStateTerm instanceof Constant)) { + nextStateTerm = ((Term) nextStateTerm).reduce(); + } return nextStateTerm; } } diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/StateTransitionTerm.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/StateTransitionTerm.java new file mode 100644 index 0000000..6baeff1 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/StateTransitionTerm.java @@ -0,0 +1,12 @@ +package models.dataConstraintModel; + +import models.algebra.Symbol; +import models.algebra.Term; + +public class StateTransitionTerm extends Term { + + public StateTransitionTerm(ResourcePath path) { + super(path); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ChannelNode.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ChannelNode.java new file mode 100644 index 0000000..108bc21 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ChannelNode.java @@ -0,0 +1,39 @@ +package models.dataFlowModel; + +import java.util.HashSet; +import java.util.Set; + +import models.Node; + +public class ChannelNode extends Node { + protected ChannelNode parent = null; + protected Set children = null; + protected DataTransferChannel channel = null; + + public ChannelNode(ChannelNode parent, DataTransferChannel channel) { + this.parent = parent; + this.channel = channel; + this.children = new HashSet<>(); + } + + public ChannelNode getParent() { + return parent; + } + + public Set getChildren() { + return children; + } + + public void addChild(ChannelNode child) { + children.add(child); + child.parent = this; + } + + public DataTransferChannel getChannel() { + return channel; + } + + public void setChannel(DataTransferChannel channel) { + this.channel = channel; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataFlowEdge.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataFlowEdge.java index 9de9359..6b35380 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataFlowEdge.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataFlowEdge.java @@ -3,18 +3,23 @@ import models.*; public class DataFlowEdge extends Edge { - protected DataTransferChannel channel = null; + protected boolean channelToResource = false; - public DataFlowEdge(ResourceNode src, ResourceNode dst, DataTransferChannel channel) { + public DataFlowEdge(ResourceNode src, ChannelNode dst) { super(src, dst); - this.channel = channel; + channelToResource = false; } - - public DataTransferChannel getChannel() { - return channel; + + public DataFlowEdge(ChannelNode src, ResourceNode dst) { + super(src, dst); + channelToResource = true; } - - public String toString() { - return channel.getChannelName(); + + public boolean isChannelToResource() { + return channelToResource; } + +// public String toString() { +// return channel.getChannelName(); +// } } diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataFlowGraph.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataFlowGraph.java index 2c540ed..a86dc97 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataFlowGraph.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataFlowGraph.java @@ -1,40 +1,173 @@ package models.dataFlowModel; +import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import models.DirectedGraph; +import models.dataConstraintModel.ResourceHierarchy; import models.dataConstraintModel.ResourcePath; public class DataFlowGraph extends DirectedGraph { - protected Map nodeMap = null; + protected Set rootResourceNodes = null; + protected Set rootChannelNodes = null; + protected Map channelNodeMap = null; + protected Map resourceNodeMap = null; public DataFlowGraph() { super(); - nodeMap = new HashMap<>(); + rootResourceNodes = new HashSet<>(); + rootChannelNodes = new HashSet<>(); + channelNodeMap = new HashMap<>(); + resourceNodeMap = new HashMap<>(); } - public void addNode(ResourcePath id) { - if (nodeMap.get(id) == null) { - ResourceNode node = new ResourceNode(id); - addNode(node); - nodeMap.put(id, node); + public ResourceNode addResourceNode(ResourceNode parent, + ResourcePath outSideResource, + DataTransferChannel outSideChannel) { + ResourceNode node = null; + if (outSideResource.getNumberOfParameters() == 0) { + // a global (possibly non-root) resource node + Collection nodes = getResourceNodes(outSideResource.getResourceHierarchy()); + if (nodes.size() > 0) { + node = nodes.iterator().next(); + if (outSideChannel != null) { + // not a terminal node + node.addOutSideResource(outSideChannel, outSideResource); + } + } + } else { + // a channel local resource node + node = resourceNodeMap.get(System.identityHashCode(outSideResource)); } + if (node != null) return node; + node = new ResourceNode(parent, outSideResource, outSideChannel); + addNode(node); + if (parent == null) { + rootResourceNodes.add(node); + } else { + parent.addChild(node); + } + resourceNodeMap.put(System.identityHashCode(outSideResource), node); + return node; + } + + public ResourceNode addTerminalResourceNode(ResourceNode parent, + DataTransferChannel inSideChannel, + ResourcePath inSideResource) { + ResourceNode node = null; + if (inSideResource.getNumberOfParameters() == 0) { + // a global (possibly non-root) terminal resource node + Collection nodes = getResourceNodes(inSideResource.getResourceHierarchy()); + if (nodes.size() > 0) { + node = nodes.iterator().next(); + } + } else { + // a channel local terminal resource node + node = resourceNodeMap.get(System.identityHashCode(inSideResource)); + } + if (node != null) return node; + node = new ResourceNode(parent, inSideResource, inSideChannel, true); + addNode(node); + if (parent == null) { + rootResourceNodes.add(node); + } else { + parent.addChild(node); + } + resourceNodeMap.put(System.identityHashCode(inSideResource), node); + return node; + } + + public ResourceNode addResourceNode(ResourceNode parent, + Map inSide, + Map outSide) { + ResourceNode node = null; + for (ResourcePath outRes: outSide.values()) { + node = resourceNodeMap.get(System.identityHashCode(outRes)); + if (node != null) return node; + } + for (ResourcePath inRes: inSide.values()) { + node = resourceNodeMap.get(System.identityHashCode(inRes)); + if (node != null) return node; + } + node = new ResourceNode(parent, inSide, outSide); + addNode(node); + if (parent == null) { + rootResourceNodes.add(node); + } else { + parent.addChild(node); + } + for (ResourcePath outRes: outSide.values()) { + resourceNodeMap.put(System.identityHashCode(outRes), node); + } + for (ResourcePath inRes: inSide.values()) { + resourceNodeMap.put(System.identityHashCode(inRes), node); + } + return node; + } + + public ChannelNode addChannelNode(ChannelNode parent, DataTransferChannel ch) { + ChannelNode node = channelNodeMap.get(ch); + if (node != null) return node; + node = new ChannelNode(parent, ch); + addNode(node); + if (parent == null) { + rootChannelNodes.add(node); + } else { + parent.addChild(node); + } + channelNodeMap.put(ch, node); + return node; + } + + public ResourceNode getResourceNode(ResourcePath resPath) { + return resourceNodeMap.get(System.identityHashCode(resPath)); + } + + public ChannelNode getChannelNode(DataTransferChannel channel) { + return channelNodeMap.get(channel); + } + + public Collection getResourceNodes() { + HashSet result = new HashSet<>(resourceNodeMap.values()); + return result; + } + + public Collection getResourceNodes(ResourceHierarchy resourceHierarchy) { + Collection resourceNodes = new ArrayList<>(); + for (ResourceNode rn: resourceNodeMap.values()) { + if (rn.getResourceHierarchy() == resourceHierarchy) { + resourceNodes.add(rn); + } + } + return resourceNodes; } - public void addEdge(ResourcePath in, ResourcePath out, DataTransferChannel dfChannelGen) { - ResourceNode srcNode = nodeMap.get(in); - if (srcNode == null) { - srcNode = new ResourceNode(in); - addNode(srcNode); - nodeMap.put(in, srcNode); - } - ResourceNode dstNode = nodeMap.get(out); - if (dstNode == null) { - dstNode = new ResourceNode(out); - addNode(dstNode); - nodeMap.put(out, dstNode); - } - addEdge(new DataFlowEdge(srcNode, dstNode, dfChannelGen)); + + public Collection getChannelNodes() { + return channelNodeMap.values(); + } + + public Set getRootResourceNodes() { + return rootResourceNodes; + } + + public Set getRootChannelNodes() { + return rootChannelNodes; + } + + public DataFlowEdge addEdge(ResourceNode srcNode, ChannelNode dstNode) { + DataFlowEdge edge =new DataFlowEdge(srcNode, dstNode); + addEdge(edge); + return edge; + } + + public DataFlowEdge addEdge(ChannelNode srcNode, ResourceNode dstNode) { + DataFlowEdge edge = new DataFlowEdge(srcNode, dstNode); + addEdge(edge); + return edge; } } diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferChannel.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferChannel.java index b3f3bf0..d6a81d9 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferChannel.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferChannel.java @@ -1,9 +1,14 @@ package models.dataFlowModel; +import java.util.AbstractMap; import java.util.HashMap; import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.Set; +import models.algebra.Constant; import models.algebra.Expression; import models.algebra.InvalidMessage; import models.algebra.Parameter; @@ -13,7 +18,11 @@ import models.algebra.UnificationFailed; import models.algebra.ValueUndefined; import models.algebra.Variable; -import models.dataConstraintModel.*; +import models.dataConstraintModel.Channel; +import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.ResourcePath; +import models.dataConstraintModel.Selector; +import parser.Parser; public class DataTransferChannel extends Channel { protected Set inputChannelMembers = null; @@ -27,6 +36,20 @@ referenceChannelMembers = new HashSet<>(); } + public DataTransferChannel(String channelName, Variable variable) { + super(channelName, variable); + inputChannelMembers = new HashSet<>(); + outputChannelMembers = new HashSet<>(); + referenceChannelMembers = new HashSet<>(); + } + + public DataTransferChannel(String channelName, List variables) { + super(channelName, variables); + inputChannelMembers = new HashSet<>(); + outputChannelMembers = new HashSet<>(); + referenceChannelMembers = new HashSet<>(); + } + public Set getInputChannelMembers() { return inputChannelMembers; } @@ -35,7 +58,7 @@ this.inputChannelMembers = inputChannelMembers; } - public void addInputChannelMember(ChannelMember inputChannelMember) { + private void addInputChannelMember(ChannelMember inputChannelMember) { inputChannelMembers.add(inputChannelMember); } @@ -47,7 +70,7 @@ this.outputChannelMembers = outputChannelMembers; } - public void addOutputChannelMember(ChannelMember outputChannelMember) { + private void addOutputChannelMember(ChannelMember outputChannelMember) { outputChannelMembers.add(outputChannelMember); } @@ -59,23 +82,23 @@ this.referenceChannelMembers = referenceChannelMembers; } - public void addReferenceChannelMember(ChannelMember referenceChannelMember) { + private void addReferenceChannelMember(ChannelMember referenceChannelMember) { referenceChannelMembers.add(referenceChannelMember); } - public void addChannelMemberAsInput(ChannelMember groupDependentResource) { - addChannelMember(groupDependentResource); - addInputChannelMember(groupDependentResource); + public void addChannelMemberAsInput(ChannelMember inputChannelMember) { + addChannelMember(inputChannelMember); + addInputChannelMember(inputChannelMember); } - public void addChannelMemberAsOutput(ChannelMember groupDependentResource) { - addChannelMember(groupDependentResource); - addOutputChannelMember(groupDependentResource); + public void addChannelMemberAsOutput(ChannelMember outputChannelMember) { + addChannelMember(outputChannelMember); + addOutputChannelMember(outputChannelMember); } - public void addChannelMemberAsReference(ChannelMember groupDependentResource) { - addChannelMember(groupDependentResource); - addReferenceChannelMember(groupDependentResource); + public void addChannelMemberAsReference(ChannelMember referenceChannelMember) { + addChannelMember(referenceChannelMember); + addReferenceChannelMember(referenceChannelMember); } public void removeChannelMember(ResourcePath id) { @@ -142,8 +165,8 @@ HashMap nextStateParams = new HashMap<>(); @Override - public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) { - String resource = target.getResourceName(); + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + String resource = target.getResource().getLeafResourceName(); Parameter curStateParam = curStateParams.get(resource); if (curStateParam == null) { curStateParam = new Parameter("cur" + resource); @@ -153,72 +176,259 @@ } @Override - public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { - String resource = target.getResourceName(); + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + String resource = target.getResource().getLeafResourceName(); Parameter nextStateParam = nextStateParams.get(resource); if (nextStateParam == null) { - nextStateParam = new Parameter("next" + resource); + nextStateParam = new Parameter("next" + target); nextStateParams.put(resource, nextStateParam); } return nextStateParam; } + + @Override + public Expression getDirectStateAccessorFor(ResourcePath target, ResourcePath from) { + String resource = target.getLeafResourceName(); + Parameter curStateParam = curStateParams.get(resource); + if (curStateParam == null) { + curStateParam = new Parameter("cur" + resource); + curStateParams.put(resource, curStateParam); + } + return curStateParam; + } }; - return deriveUpdateExpressionOf(targetMember, defaultStateAccessor); + return deriveUpdateExpressionOf(targetMember, defaultStateAccessor).getKey(); } /** - * Derive the update expression of the state of the target channel member with a given resource state accessor. + * Derive the state update calculation of the target channel member with a given resource push/pull state accessor. * @param targetMember a channel member whose state is to be updated - * @param stateAccessor a resource state accessor - * @return the derived update expression + * @param stateAccessor a push/pull resource state accessor + * @return the derived update calculation and the unified message * @throws ParameterizedIdentifierIsFutureWork * @throws ResolvingMultipleDefinitionIsFutureWork * @throws InvalidMessage * @throws UnificationFailed * @throws ValueUndefined */ - public Expression deriveUpdateExpressionOf(ChannelMember targetMember, IResourceStateAccessor stateAccessor) + public Map.Entry deriveUpdateExpressionOf(ChannelMember targetMember, IResourceStateAccessor stateAccessor) throws ParameterizedIdentifierIsFutureWork, ResolvingMultipleDefinitionIsFutureWork, InvalidMessage, UnificationFailed, ValueUndefined { return deriveUpdateExpressionOf(targetMember, stateAccessor, null); } - public Expression deriveUpdateExpressionOf(ChannelMember targetMember, IResourceStateAccessor stateAccessor, HashMap inputResourceToStateAccessor) + public Map.Entry deriveUpdateExpressionOf(ChannelMember targetMember, IResourceStateAccessor stateAccessor, Map inputResourceToStateAccessor) throws ParameterizedIdentifierIsFutureWork, ResolvingMultipleDefinitionIsFutureWork, InvalidMessage, UnificationFailed, ValueUndefined { if (!getOutputChannelMembers().contains(targetMember)) return null; - HashSet messageConstraints = new HashSet<>(); + + // Calculate unified message constraints + Map> substitutedPositionsFromChannels = new HashMap<>(); + Term unifiedMessage = calcUnifiedMessage(targetMember, stateAccessor, inputResourceToStateAccessor, substitutedPositionsFromChannels); + + // Calculate the next state of target resource from the unified message and the current resource state + Expression curOutputStateAccessor = stateAccessor.getCurrentStateAccessorFor(targetMember, targetMember); + if (unifiedMessage == null) { + // for IOChannel + if (targetMember.getStateTransition().getMessageExpression() instanceof Term) { + unifiedMessage = (Term) targetMember.getStateTransition().getMessageExpression(); + } + } + return new AbstractMap.SimpleEntry<>(targetMember.getStateTransition().deriveNextStateExpressionFor(curOutputStateAccessor, unifiedMessage), unifiedMessage); + } + + /** + * Derive the state update calculation of the target channel member with a message and a given resource push/pull state accessor. + * @param targetMember a channel member whose state is to be updated + * @param message a message on the channel + * @param stateAccessor a push/pull resource state accessor + * @return the derived update calculation + * @throws ParameterizedIdentifierIsFutureWork + * @throws ResolvingMultipleDefinitionIsFutureWork + * @throws InvalidMessage + * @throws UnificationFailed + * @throws ValueUndefined + */ + public Expression deriveUpdateExpressionOf(ChannelMember targetMember, Term message, IResourceStateAccessor stateAccessor) + throws ParameterizedIdentifierIsFutureWork, ResolvingMultipleDefinitionIsFutureWork, InvalidMessage, UnificationFailed, ValueUndefined { + if (!getOutputChannelMembers().contains(targetMember)) return null; + + // Calculate the next state of target resource from the unified message and the current resource state + Expression curOutputStateAccessor = stateAccessor.getCurrentStateAccessorFor(targetMember, targetMember); + return targetMember.getStateTransition().deriveNextStateExpressionFor(curOutputStateAccessor, message); + } + + /** + * Fill outside resource paths with a given resource push/pull state accessor. + * @param targetMember a channel member whose state is to be updated + * @param stateAccessor a push/pull resource state accessor + * @return map from a depending channel member to the pair of the filled resource path and the set of the depended channel members + * @throws ParameterizedIdentifierIsFutureWork + * @throws ResolvingMultipleDefinitionIsFutureWork + * @throws InvalidMessage + * @throws UnificationFailed + * @throws ValueUndefined + */ + public Map>> fillOutsideResourcePaths(ChannelMember targetMember, IResourceStateAccessor stateAccessor) + throws ParameterizedIdentifierIsFutureWork, ResolvingMultipleDefinitionIsFutureWork, InvalidMessage, UnificationFailed, ValueUndefined { + return fillOutsideResourcePaths(targetMember, stateAccessor, null); + } + + public Map>> fillOutsideResourcePaths(ChannelMember targetMember, IResourceStateAccessor stateAccessor, Map inputResourceToStateAccessor) + throws ParameterizedIdentifierIsFutureWork, ResolvingMultipleDefinitionIsFutureWork, InvalidMessage, UnificationFailed, ValueUndefined { + if (!getOutputChannelMembers().contains(targetMember)) return null; + Map>> resourcePaths = new HashMap<>(); + + // Calculate unified message constraints from input and reference state transitions + Map> substitutedPositionsInMessageFromChannels = new HashMap<>(); + Term unifiedMessage = calcUnifiedMessage(targetMember, stateAccessor, inputResourceToStateAccessor, substitutedPositionsInMessageFromChannels); + + // Fill outside resource paths + if (unifiedMessage != null) { + for (ChannelMember cm: getInputChannelMembers()) { + if (cm.isOutside()) { + Set dependingVarPosInMessage = new HashSet<>(); + ResourcePath filledResPath = fillOutsideResourcePath(cm.getResource(), unifiedMessage, targetMember.getStateTransition().getMessageExpression(), dependingVarPosInMessage); + Set dependingChannelMembers = new HashSet<>(); + for (ChannelMember otherCm: substitutedPositionsInMessageFromChannels.keySet()) { + for (Position otherPos: substitutedPositionsInMessageFromChannels.get(otherCm)) { + for (Position thisPos: dependingVarPosInMessage) { + if (thisPos.isAncestorOf(otherPos)) { + dependingChannelMembers.add(otherCm); + break; + } + } + if (dependingChannelMembers.contains(otherCm)) break; + } + } + resourcePaths.put(cm, new AbstractMap.SimpleEntry<>(filledResPath, dependingChannelMembers)); + } + } + for (ChannelMember cm: getReferenceChannelMembers()) { + if (cm.isOutside()) { + Set dependingVarPosInMessage = new HashSet<>(); + ResourcePath filledResPath = fillOutsideResourcePath(cm.getResource(), unifiedMessage, targetMember.getStateTransition().getMessageExpression(), dependingVarPosInMessage); + Set dependingChannelMembers = new HashSet<>(); + for (ChannelMember otherCm: substitutedPositionsInMessageFromChannels.keySet()) { + for (Position otherPos: substitutedPositionsInMessageFromChannels.get(otherCm)) { + for (Position thisPos: dependingVarPosInMessage) { + if (thisPos.isAncestorOf(otherPos)) { + dependingChannelMembers.add(otherCm); + break; + } + } + if (dependingChannelMembers.contains(otherCm)) break; + } + } + resourcePaths.put(cm, new AbstractMap.SimpleEntry<>(filledResPath, dependingChannelMembers)); + } + } + for (ChannelMember cm: getOutputChannelMembers()) { + if (cm.isOutside()) { + Set dependingVarPosInMessage = new HashSet<>(); + ResourcePath filledResPath = fillOutsideResourcePath(cm.getResource(), unifiedMessage, targetMember.getStateTransition().getMessageExpression(), dependingVarPosInMessage); + Set dependingChannelMembers = new HashSet<>(); + for (ChannelMember otherCm: substitutedPositionsInMessageFromChannels.keySet()) { + for (Position otherPos: substitutedPositionsInMessageFromChannels.get(otherCm)) { + for (Position thisPos: dependingVarPosInMessage) { + if (thisPos.isAncestorOf(otherPos)) { + dependingChannelMembers.add(otherCm); + break; + } + } + if (dependingChannelMembers.contains(otherCm)) break; + } + } + resourcePaths.put(cm, new AbstractMap.SimpleEntry<>(filledResPath, dependingChannelMembers)); + } + } + } + return resourcePaths; + } + + /** + * 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); + Expression messageConstraintByInput = calcMessageConstraintForInputMember(inputMember, targetMember, stateAccessor, inputResourceToStateAccessor, substitutedPositionsInMessageFromChannels); messageConstraints.add((Term) messageConstraintByInput); } // Calculate message constraints from reference state transitions for (ChannelMember referenceMember: getReferenceChannelMembers()) { - ResourcePath referenceResource = referenceMember.getResource(); - if (referenceResource.getNumberOfParameters() > 0) { - throw new ParameterizedIdentifierIsFutureWork(); - } - Expression curInputStateAccessor = null; - if (inputResourceToStateAccessor == null) { - curInputStateAccessor = stateAccessor.getCurrentStateAccessorFor(referenceResource, targetMember.getResource()); - } else { - curInputStateAccessor = inputResourceToStateAccessor.get(referenceResource).getCurrentStateAccessorFor(referenceResource, targetMember.getResource()); - } - Expression messageConstraintByReference = referenceMember.getStateTransition().deriveMessageConstraintFor(curInputStateAccessor); + Expression messageConstraintByReference = calcMessageConstraintForReferenceMember(referenceMember, targetMember, stateAccessor, inputResourceToStateAccessor, substitutedPositionsInMessageFromChannels); messageConstraints.add((Term) messageConstraintByReference); } @@ -234,40 +444,219 @@ } } } - - // 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) { + dstParams.set(i, new AbstractMap.SimpleEntry<>(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(); + } + dstParams.set(i, new AbstractMap.SimpleEntry<>(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(); + } + dstParams.set(i, new AbstractMap.SimpleEntry<>(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"; + String channelSource = ""; + if (isNative()) { + channelSource += Parser.NATIVE + " "; + } + if (parent == null) { + channelSource += Parser.CHANNEL + " " + getChannelName(); + } else { + channelSource += Parser.SUB_CHANNEL + " " + getChannelName(); + } + 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 in " + inputMember + "\n"; + channelSource += "\t " + Parser.IN + " " + inputMember + "\n"; } for (ChannelMember refMember: referenceChannelMembers) { - channelSource += "\t ref " + refMember + "\n"; + channelSource += "\t " + Parser.REF + " " + refMember + "\n"; } for (ChannelMember outputMember: outputChannelMembers) { - channelSource += "\t out " + outputMember + "\n"; + 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..b27156b 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferModel.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferModel.java @@ -1,31 +1,263 @@ package models.dataFlowModel; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; import java.util.Set; import models.dataConstraintModel.Channel; import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.ResourceHierarchy; import models.dataConstraintModel.ResourcePath; public class DataTransferModel extends DataConstraintModel { public DataFlowGraph getDataFlowGraph() { DataFlowGraph dataFlowGraph = new DataFlowGraph(); + Map>> channelLocalResMap = new HashMap<>(); for (Channel channel: getChannels()) { - DataTransferChannel dfChannel = (DataTransferChannel)channel; - Set inputResources = dfChannel.getInputResources(); - Set outputResources = dfChannel.getOutputResources(); - for (ResourcePath in: inputResources) { - for (ResourcePath out: outputResources) { - dataFlowGraph.addEdge(in ,out, dfChannel); - } - } + addResourceToChannelEdges(dataFlowGraph, channel, null, channelLocalResMap); } - for (Channel channel: getIOChannels()) { - DataTransferChannel dfChannel = (DataTransferChannel)channel; - Set outputResources = dfChannel.getOutputResources(); - for (ResourcePath out: outputResources) { - dataFlowGraph.addNode(out); - } + for (Channel channel: getChannels()) { + addChannelToResourceEdges(dataFlowGraph, channel, null, channelLocalResMap); + } + for (Channel channel: getInputChannels()) { + addChannelToResourceEdges(dataFlowGraph, channel, null, channelLocalResMap); } return dataFlowGraph; } + + private void addResourceToChannelEdges(DataFlowGraph dataFlowGraph, Channel dstChannel, ChannelNode parentChannelNode, + Map>> channelLocalResMap) { + DataTransferChannel dstDfChannel = (DataTransferChannel) dstChannel; + ChannelNode dstChannelNode = dataFlowGraph.addChannelNode(parentChannelNode, dstDfChannel); + for (ResourcePath srcRes: dstDfChannel.getInputResources()) { + Map> chLocalResNodes = channelLocalResMap.get(srcRes.getResourceHierarchy()); + if (srcRes.getNumberOfParameters() == 0) { + // ResourcePath without parameter corresponds to a global ResourceNode. + ResourceNode srcResNode = addResourceNodes(dataFlowGraph, srcRes, dstDfChannel, channelLocalResMap, false); + dataFlowGraph.addEdge(srcResNode, dstChannelNode); + } else { + if (chLocalResNodes == null || chLocalResNodes.get(dstChannel) == null) { + // There is no channel-local ResourcePath. + // Then, create a new channel-local ResourceNode. + ResourceNode srcResNode = addResourceNodes(dataFlowGraph, srcRes, dstDfChannel, channelLocalResMap, false); + dataFlowGraph.addEdge(srcResNode, dstChannelNode); + } else { + // There already has been a channel-local ResourceNode. + Set nodes = chLocalResNodes.get(dstChannel); // channel-local ResourceNodes. + boolean bExists = false; + for (ResourceNode node: nodes) { + ResourcePath r = node.getOutSideResource(dstDfChannel); + if (r.toString().equals(srcRes.toString())) { + bExists = true; // There exists a textually identical ResourecPath within the same channel. + break; + } + } + if (!bExists) { + // Create a new channel-local ResourceNode. + ResourceNode srcResNode = addResourceNodes(dataFlowGraph, srcRes, dstDfChannel, channelLocalResMap, false); + dataFlowGraph.addEdge(srcResNode, dstChannelNode); + } + } + } + } + for (Channel childChannel: dstDfChannel.getChildren()) { + addResourceToChannelEdges(dataFlowGraph, childChannel, dstChannelNode, channelLocalResMap); + } + } + + private void addChannelToResourceEdges(DataFlowGraph dataFlowGraph, Channel srcChannel, ChannelNode parentChannelNode, + Map>> channelLocalResMap) { + DataTransferChannel srcDfChannel = (DataTransferChannel) srcChannel; + ChannelNode srcChannelNode = dataFlowGraph.addChannelNode(parentChannelNode, srcDfChannel); + for (ResourcePath dstRes: srcDfChannel.getOutputResources()) { + Map> chLocalResNodes = channelLocalResMap.get(dstRes.getResourceHierarchy()); // ResourceNodes that have the same ResourceHierarchy. + Set dstResSet = new HashSet<>(); + if (dstRes.getNumberOfParameters() == 0) { + // ResourcePath without parameter corresponds to a global ResourceNode. + if (chLocalResNodes == null) { + // Create a new global terminal ResourceNode. + ResourceNode dstResNode = addResourceNodes(dataFlowGraph, dstRes, srcDfChannel, channelLocalResMap, true); + dstResSet.add(dstResNode); + } else { + // Select all (usually one) global ResourceNodes. + for (Set resSet: chLocalResNodes.values()) { + dstResSet.addAll(resSet); + } + } + } else { + if (chLocalResNodes == null) { + // There is no corresponding ResourceNode. + // Create a new channel-local terminal ResourceNode. + ResourceNode dstResNode = addResourceNodes(dataFlowGraph, dstRes, srcDfChannel, channelLocalResMap, true); + dstResSet.add(dstResNode); + } else { + if (chLocalResNodes.get(srcDfChannel) == null) { + // Select all non-terminal ResourceNodes. + for (Set resSet: chLocalResNodes.values()) { + dstResSet.addAll(resSet); + } + } else { + // There already has been a channel-local terminal ResourceNode. + if (chLocalResNodes.size() > 0) { + // There already has been a channel-local ResourceNode. + for (Set resNodes: chLocalResNodes.values()) { + for (ResourceNode localResNode: resNodes) { + for (ResourcePath localResPath: localResNode.getInSideResources()) { + if (localResPath.toString().equals(dstRes.toString())) { + // Channel-local ResourcePath should be identical, and the identical ResourcePath is selected on top priority. + dstResSet.add(localResNode); + } + } + } + } + } + // Search a common channel-local ancestor. + if (dstResSet.size() == 0) { + ResourcePath dstParent = dstRes.getParent(); + while (dstParent != null && dstParent.getNumberOfParameters() > 0) { + Map> chToLocalDstNodes = channelLocalResMap.get(dstParent.getResourceHierarchy()); + if (chToLocalDstNodes != null) { + for (Channel dstCh: chToLocalDstNodes.keySet()) { + for (ResourceNode localParentNode: chToLocalDstNodes.get(dstCh)) { + for (ResourcePath localParentPath: localParentNode.getInSideResources()) { + if (localParentPath.toString().equals(dstParent.toString())) { + // There already has been a common channel-local ancestor. + ResourceNode dstResNode = addResourceNodes(dataFlowGraph, dstRes, (DataTransferChannel)dstCh, channelLocalResMap, false); + dstResSet.add(dstResNode); + break; + } + } + if (dstResSet.size() > 0) break; + } + if (dstResSet.size() > 0) break; + } + if (dstResSet.size() > 0) break; + } + dstParent = dstParent.getParent(); + } + } + if (dstResSet.size() == 0) { + for (Set nodes: chLocalResNodes.values()) { + // Select all corresponding ResourceNodes. + dstResSet.addAll(nodes); + } + } + if (dstResSet.size() == 0) { + // Otherwise create a new ResourceNode. + ResourceNode dstResNode = addResourceNodes(dataFlowGraph, dstRes, srcDfChannel, channelLocalResMap, false); + dstResSet.add(dstResNode); + } + } + } + } + for (ResourceNode dstResNode: dstResSet) { + dstResNode.addInSideResource(srcDfChannel, dstRes); + dataFlowGraph.addEdge(srcChannelNode, dstResNode); // Connect to each ResourceNode that has the same ResourceHierarchy. + } + } + for (Channel childChannel: srcDfChannel.getChildren()) { + addChannelToResourceEdges(dataFlowGraph, childChannel, srcChannelNode, channelLocalResMap); + } + } + + private ResourceNode addResourceNodes(DataFlowGraph dataFlowGraph, ResourcePath resPath, DataTransferChannel dfChannel, + Map>> channelLocalResMap, boolean isTerminal) { + ResourceNode resNode = null; + if (resPath.getParent() == null) { + if (!isTerminal) { + resNode = dataFlowGraph.addResourceNode(null, resPath, dfChannel); + } else { + resNode = dataFlowGraph.addTerminalResourceNode(null, dfChannel, resPath); + } + } else { + // Search an identical parent ResourceNode. + ResourceNode parent = null; + DataTransferChannel parentDfChannel = dfChannel; + if (resPath.getResourceHierarchy().getParent().getNumParameters() == 0) { + parentDfChannel = null; + } + if (channelLocalResMap.get(resPath.getResourceHierarchy().getParent()) != null) { + Set chLocalNodes = channelLocalResMap.get(resPath.getResourceHierarchy().getParent()).get(parentDfChannel); + if (chLocalNodes != null) { + for (ResourceNode node: chLocalNodes) { + for (ResourcePath r: node.getOutSideResources()) { + if (r.toString().equals(resPath.getParent().toString())) { + parent = node; + break; + } + } + if (parent != null) break; + for (ResourcePath r: node.getInSideResources()) { + if (r.toString().equals(resPath.getParent().toString())) { + parent = node; + break; + } + } + if (parent != null) break; + } + } + } + if (parent == null) { + parent = addResourceNodes(dataFlowGraph, resPath.getParent(), parentDfChannel, channelLocalResMap, false); + } + if (!isTerminal) { + resNode = dataFlowGraph.addResourceNode(parent, resPath, dfChannel); + } else { + resNode = dataFlowGraph.addTerminalResourceNode(parent, dfChannel, resPath); + } + } + Map> chToLocalNodes = channelLocalResMap.get(resPath.getResourceHierarchy()); + if (chToLocalNodes == null) { + chToLocalNodes = new HashMap<>(); + channelLocalResMap.put(resPath.getResourceHierarchy(), chToLocalNodes); + } + Set chLocalNodes = chToLocalNodes.get(dfChannel); + if (chLocalNodes == null) { + chLocalNodes = new HashSet<>(); + chToLocalNodes.put(dfChannel, chLocalNodes); + } + chLocalNodes.add(resNode); + return resNode; + } + +// private ResourceNode addResourceNodes(DataFlowGraph dataFlowGraph, ResourcePath resPath, DataTransferChannel dfChannel, +// Map>> resourceMap) { +// if (resPath.getNumberOfParameters() == 0) { +// // ResourcePath without parameter corresponds to a global ResourceNode. +// dfChannel = null; +// } +// if (resourceMap.get(resPath.getResourceHierarchy()) != null && resourceMap.get(resPath.getResourceHierarchy()).get(dfChannel) != null) { +// Set nodes = resourceMap.get(resPath.getResourceHierarchy()).get(dfChannel); +// for (ResourceNode node: nodes) { +// if (node.getOutSideResource().toString().equals(resPath.toString())) { +// return node; +// } +// for (ResourcePath res: node.getInSideResources()) { +// if (res.toString().toString().equals(resPath.toString())) { +// return node; +// } +// } +// } +// } +// ResourceNode resNode = null; +// if (resPath.getParent() == null) { +// resNode = dataFlowGraph.addResourceNode(null, dfChannel, resPath); +// } else { +// ResourceNode parent = addResourceNodes(dataFlowGraph, resPath.getParent(), dfChannel, resourceMap); +// resNode = dataFlowGraph.addResourceNode(parent, dfChannel, resPath); +// } +// Map> chToNodes = resourceMap.get(resPath.getResourceHierarchy()); +// if (chToNodes == null) { +// chToNodes = new HashMap<>(); +// resourceMap.put(resPath.getResourceHierarchy(), chToNodes); +// } +// Set nodes = chToNodes.get(dfChannel); +// if (nodes == null) { +// nodes = new HashSet<>(); +// chToNodes.put(dfChannel, nodes); +// } +// nodes.add(resNode); +// return resNode; +// } } diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ModelExtension.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ModelExtension.java index d8aa4af..bff95f8 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ModelExtension.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ModelExtension.java @@ -26,7 +26,7 @@ final int[] count = new int[] {0}; sum.setGenerator(new Symbol.IImplGenerator() { @Override - public String generate(Type type, String[] children, String[] childrenSideEffects, String[] sideEffect) { + public String generate(Type type, Type[] childrenTypes, String[] children, String[] childrenSideEffects, String[] sideEffect) { String compType = "Integer"; if (type != null) { String interfaceType = type.getInterfaceTypeName(); @@ -56,8 +56,8 @@ final int[] count = new int[] {0}; merge.setGenerator(new Symbol.IImplGenerator() { @Override - public String generate(Type type, String[] childrenImpl, String[] childrenSideEffects, String[] sideEffect) { - String implType = "Arrayist<>"; + public String generate(Type type, Type[] childrenTypes, String[] childrenImpl, String[] childrenSideEffects, String[] sideEffect) { + String implType = "ArrayList<>"; String interfaceType = "List"; String compType = "Integer"; if (type != null) { @@ -109,7 +109,7 @@ extractFaceDown.setArity(1); extractFaceDown.setGenerator(new Symbol.IImplGenerator() { @Override - public String generate(Type type, String[] children, String[] childrenSideEffects, String[] sideEffect) { + public String generate(Type type, Type[] childrenTypes, String[] children, String[] childrenSideEffects, String[] sideEffect) { return children[0]+".stream().filter(item -> item.getValue()==false).collect(Collectors.toList())"; } }); @@ -124,7 +124,7 @@ sortByKey.setArity(1); sortByKey.setGenerator(new Symbol.IImplGenerator() { @Override - public String generate(Type type, String[] children, String[] childrenSideEffects, String[] sideEffect) { + public String generate(Type type, Type[] childrenTypes, String[] children, String[] childrenSideEffects, String[] sideEffect) { String compType = ""; String temp_sort="temp_sort"; if (type != null) { diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ReferenceEdge.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ReferenceEdge.java new file mode 100644 index 0000000..c8ff371 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ReferenceEdge.java @@ -0,0 +1,11 @@ +package models.dataFlowModel; + +import models.Edge; + +public class ReferenceEdge extends Edge { + + public ReferenceEdge(ResourceNode src, ResourceNode dst) { + super(src, dst); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ResourceNode.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ResourceNode.java index ef286af..d382534 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ResourceNode.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ResourceNode.java @@ -1,30 +1,189 @@ package models.dataFlowModel; +import java.util.AbstractMap; +import java.util.AbstractMap.SimpleEntry; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + import models.Node; +import models.algebra.Expression; +import models.algebra.Type; +import models.dataConstraintModel.ResourceHierarchy; import models.dataConstraintModel.ResourcePath; +import models.dataConstraintModel.Selector; public class ResourceNode extends Node { - protected ResourcePath resourcePath = null; + protected ResourceNode parent = null; + protected Set children = null; + protected ResourceHierarchy resourceHierarchy = null; + protected Map inSide = null; + protected Map outSide = null; + protected List selectors = null; - public ResourceNode(ResourcePath resourcePath) { - this.resourcePath = resourcePath; + public ResourceNode(ResourceNode parent, + ResourcePath outSideResource, + DataTransferChannel outSideChannel) { + this.parent = parent; + this.children = new HashSet<>(); + this.inSide = new HashMap<>(); + this.outSide = new HashMap<>(); + this.outSide.put(outSideChannel, outSideResource); + this.resourceHierarchy = outSideResource.getResourceHierarchy(); + this.selectors = new ArrayList<>(); + if (resourceHierarchy.getNumParameters() > 0) { + if (outSideChannel != null) { + selectors.addAll(outSideChannel.getSelectors()); + } else { + List pathParams = outSideResource.getPathParams(); + selectors.add(new Selector(pathParams.get(pathParams.size() - 1))); + } + } + } + + public ResourceNode(ResourceNode parent, + ResourcePath resource, + DataTransferChannel channel, boolean isInside) { + this.parent = parent; + this.children = new HashSet<>(); + this.inSide = new HashMap<>(); + this.outSide = new HashMap<>(); + if (isInside) { + this.inSide.put(channel, resource); + } else { + this.outSide.put(channel, resource); + } + this.resourceHierarchy = resource.getResourceHierarchy(); + this.selectors = new ArrayList<>(); + if (resourceHierarchy.getNumParameters() > 0) { + if (channel != null) { + selectors.addAll(channel.getSelectors()); + } else { + List pathParams = resource.getPathParams(); + selectors.add(new Selector(pathParams.get(pathParams.size() - 1))); + } + } } - public ResourcePath getResource() { - return resourcePath; + public ResourceNode(ResourceNode parent, + Map outSide, + Map inSide) { + this.parent = parent; + this.children = new HashSet<>(); + this.inSide = inSide; + this.outSide = outSide; + this.selectors = new ArrayList<>(); + for (Map.Entry outsideEnt: outSide.entrySet()) { + DataTransferChannel outSideChannel = outsideEnt.getKey(); + ResourcePath outSideResource = outsideEnt.getValue(); + if (this.resourceHierarchy == null) { + this.resourceHierarchy = outSideResource.getResourceHierarchy(); + } + if (resourceHierarchy.getNumParameters() > 0) { + if (outSideChannel != null) { + selectors.addAll(outSideChannel.getSelectors()); + } else { + List pathParams = outSideResource.getPathParams(); + selectors.add(new Selector(pathParams.get(pathParams.size() - 1))); + } + } + } } - public boolean equals(Object another) { - if (this == another) return true; - if (!(another instanceof ResourceNode)) return false; - return resourcePath.equals(((ResourceNode)another).resourcePath); + public ResourceHierarchy getResourceHierarchy() { + return resourceHierarchy; } - public int hashCode() { - return resourcePath.hashCode(); + public String getResourceName() { + return resourceHierarchy.getResourceName(); } - public String toString() { - return resourcePath.getResourceName(); + public Type getResourceStateType() { + return resourceHierarchy.getResourceStateType(); } + + public int getNumberOfParameters() { + return resourceHierarchy.getTotalNumParameters(); + } + + public ResourceNode getParent() { + return parent; + } + + public Set getChildren() { + return children; + } + + public void addChild(ResourceNode child) { + children.add(child); + child.parent = this; + } + + public ResourcePath getPrimaryResourcePath() { + if (outSide.size() > 0) return outSide.values().iterator().next(); + return inSide.values().iterator().next(); + } + + public Collection getInSideResources() { + return inSide.values(); + } + + public ResourcePath getInSideResource(DataTransferChannel channel) { + return inSide.get(channel); + } + + public Collection getOutSideResources() { + return outSide.values(); + } + + public ResourcePath getOutSideResource(DataTransferChannel channel) { + return outSide.get(channel); + } + + public Set getOutSideChannel() { + return outSide.keySet(); + } + + public void addInSideResource(DataTransferChannel channel, ResourcePath inResource) { + inSide.put(channel, inResource); + } + + public void addOutSideResource(DataTransferChannel channel, ResourcePath outResource) { + outSide.put(channel, outResource); + } + + public List getSelectors() { + return selectors; + } + + public 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 boolean equals(Object another) { +// if (this == another) return true; +// if (!(another instanceof ResourceNode)) return false; +// return resourcePath.equals(((ResourceNode)another).resourcePath); +// } +// +// public int hashCode() { +// return resourcePath.hashCode(); +// } +// +// public String toString() { +// return resourcePath.toString(); +// } } diff --git a/AlgebraicDataflowArchitectureModel/src/models/visualModel/FormulaChannel.java b/AlgebraicDataflowArchitectureModel/src/models/visualModel/FormulaChannel.java index 581d31e..2fb57ee 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/visualModel/FormulaChannel.java +++ b/AlgebraicDataflowArchitectureModel/src/models/visualModel/FormulaChannel.java @@ -32,24 +32,24 @@ // channelMember.setStateTransition(st); super.addChannelMemberAsInput(channelMember); if (formula != null && getInputChannelMembers().size() > 1) { - formula += " " + defaultOperator + " " + channelMember.getResource().getResourceName(); + formula += " " + defaultOperator + " " + channelMember.getResource().getLeafResourceName(); if (formulaRhs != null) { if (formulaRhs instanceof Variable) { Term newTerm = new Term(defaultOperator); newTerm.addChild(formulaRhs); - newTerm.addChild(new Variable(channelMember.getResource().getResourceName()), true); + newTerm.addChild(new Variable(channelMember.getResource().getLeafResourceName()), true); formulaRhs = newTerm; } else if (formulaRhs instanceof Term) { Term newTerm = new Term(defaultOperator); newTerm.addChild(formulaRhs); - newTerm.addChild(new Variable(channelMember.getResource().getResourceName())); + newTerm.addChild(new Variable(channelMember.getResource().getLeafResourceName())); formulaRhs = newTerm; } } } else { if (formula == null) formula = ""; - formula += channelMember.getResource().getResourceName(); - formulaRhs = new Variable(channelMember.getResource().getResourceName()); + formula += channelMember.getResource().getLeafResourceName(); + formulaRhs = new Variable(channelMember.getResource().getLeafResourceName()); } if (formulaRhs != null) { setFormulaTerm(formulaRhs); @@ -63,8 +63,8 @@ super.addChannelMemberAsOutput(channelMember); if (getOutputChannelMembers().size() == 1) { if (formula == null) formula = ""; - if (!formula.contains("==")) { - formula = channelMember.getResource().getResourceName() + " == " + formula; + if (!formula.contains("=")) { + formula = channelMember.getResource().getLeafResourceName() + " = " + formula; } } if (formulaRhs != null) { @@ -96,7 +96,7 @@ Map resToNextVar = new HashMap<>(); for (ChannelMember cm: this.getInputChannelMembers()) { ResourcePath id = cm.getResource(); - String resName = id.getResourceName(); + String resName = id.getLeafResourceName(); Variable curVar = new Variable(resName + "1"); Variable nextVar = new Variable(resName + "2"); curStates.put(id, curVar); @@ -134,7 +134,7 @@ for (ChannelMember cm: getOutputChannelMembers()) { ResourcePath id = cm.getResource(); StateTransition st = new StateTransition(); - String resName = id.getResourceName(); + String resName = id.getLeafResourceName(); Variable curVar = new Variable(resName + "1"); st.setCurStateExpression(curVar); st.setNextStateExpression(rhs); diff --git a/AlgebraicDataflowArchitectureModel/src/parser/Parser.java b/AlgebraicDataflowArchitectureModel/src/parser/Parser.java index 876ee6f..90b1174 100644 --- a/AlgebraicDataflowArchitectureModel/src/parser/Parser.java +++ b/AlgebraicDataflowArchitectureModel/src/parser/Parser.java @@ -1,6 +1,5 @@ package parser; -import java.awt.image.DataBufferDouble; import java.io.BufferedReader; import java.io.IOException; import java.util.ArrayList; @@ -14,20 +13,29 @@ import models.algebra.Type; import models.algebra.Variable; import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.JsonAccessor; +import models.dataConstraintModel.JsonTerm; +import models.dataConstraintModel.ResourceHierarchy; import models.dataConstraintModel.ResourcePath; import models.dataConstraintModel.StateTransition; +import models.dataConstraintModel.StateTransitionTerm; import models.dataFlowModel.DataTransferModel; import models.dataFlowModel.DataTransferChannel; import parser.exceptions.ExpectedAssignment; import parser.exceptions.ExpectedChannel; import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; +import parser.exceptions.ExpectedDoubleQuotation; import parser.exceptions.ExpectedEquals; -import parser.exceptions.ExpectedInOrOutOrRefKeyword; +import parser.exceptions.ExpectedInOrOutOrRefOrSubKeyword; import parser.exceptions.ExpectedLeftCurlyBracket; import parser.exceptions.ExpectedRHSExpression; import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.ExpectedRightCurlyBracket; import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; import parser.exceptions.WrongLHSExpression; +import parser.exceptions.WrongPathExpression; import parser.exceptions.WrongRHSExpression; public class Parser { @@ -35,6 +43,11 @@ public static final String CHANNEL = "channel"; public static final String INIT = "init"; + public static final String IN = "in"; + public static final String OUT = "out"; + public static final String REF = "ref"; + public static final String SUB_CHANNEL = "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,23 +56,37 @@ 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; @@ -79,17 +106,21 @@ } public DataTransferModel doParse() - throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedInOrOutOrRefKeyword, ExpectedStateTransition, ExpectedEquals, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment { + throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedRightCurlyBracket, + ExpectedInOrOutOrRefOrSubKeyword, ExpectedStateTransition, ExpectedEquals, + ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, WrongPathExpression, WrongJsonExpression, ExpectedColon, ExpectedDoubleQuotation { return parseDataFlowModel(); } public DataTransferModel parseDataFlowModel() - throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedInOrOutOrRefKeyword, ExpectedStateTransition, ExpectedEquals, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment { + throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedRightCurlyBracket, + ExpectedInOrOutOrRefOrSubKeyword, ExpectedStateTransition, ExpectedEquals, + ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, WrongPathExpression, WrongJsonExpression, ExpectedColon, ExpectedDoubleQuotation { DataTransferModel model = new DataTransferModel(); DataTransferChannel channel; while ((channel = parseChannel(model)) != null) { if (channel.getInputChannelMembers().size() == 0) { - model.addIOChannel(channel); + model.addInputChannel(channel); } else { model.addChannel(channel); } @@ -98,20 +129,29 @@ } public DataTransferChannel parseChannel(DataTransferModel model) - throws - ExpectedLeftCurlyBracket, ExpectedRightBracket, ExpectedAssignment, - ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, - ExpectedChannel, ExpectedChannelName, ExpectedInOrOutOrRefKeyword, - ExpectedStateTransition, ExpectedEquals - { + throws ExpectedLeftCurlyBracket, ExpectedRightBracket, ExpectedRightCurlyBracket, ExpectedAssignment, + ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, + ExpectedChannel, ExpectedChannelName, ExpectedInOrOutOrRefOrSubKeyword, + ExpectedStateTransition, ExpectedEquals, WrongPathExpression, WrongJsonExpression, ExpectedColon, 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()); @@ -120,50 +160,68 @@ 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 - { + 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(); @@ -178,18 +236,17 @@ rightTerm = parseTerm(stream, model); if (rightTerm == null) throw new WrongRHSExpression(stream.getLine()); - resourcePath.setInitialValue(rightTerm); - resourcePath.setInitText(stream.getSourceText(fromLine, toLine)); + resourceHierarchy.setInitialValue(rightTerm); + resourceHierarchy.setInitText(stream.getSourceText(fromLine, toLine)); } + if (stream.hasNext()) stream.next(); } public ChannelMember parseChannelMember(DataTransferModel model, final String inOrOutOrRef) - throws - ExpectedRightBracket, ExpectedStateTransition, ExpectedEquals, - ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression - { + throws ExpectedRightBracket, ExpectedRightCurlyBracket, ExpectedStateTransition, ExpectedEquals, + ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, WrongPathExpression, WrongJsonExpression, ExpectedColon, 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; @@ -203,12 +260,7 @@ if (rightTerm == null) throw new WrongRHSExpression(stream.getLine()); } - String resourceName = ((Term) leftTerm).getSymbol().getName(); - ResourcePath resourcePath = model.getResourcePath(resourceName); - if (resourcePath == null) { - resourcePath = new ResourcePath(resourceName, 0); - model.addResourcePath(resourcePath); - } + ResourcePath resourcePath = (ResourcePath) leftTerm.getSymbol(); ChannelMember channelMember = new ChannelMember(resourcePath); StateTransition stateTransition = new StateTransition(); stateTransition.setCurStateExpression(((Term) leftTerm).getChild(0)); @@ -239,27 +291,42 @@ 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)) { + 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)) { @@ -273,6 +340,7 @@ int arity = 0; do { 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 +351,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 +393,136 @@ break; } else if (operator.equals(ADD)) { operators.add(DataTransferModel.add); + stream.next(); } else if (operator.equals(MUL)) { operators.add(DataTransferModel.mul); + stream.next(); } else if (operator.equals(SUB)) { operators.add(DataTransferModel.sub); // not minus + stream.next(); } else if (operator.equals(DIV)) { operators.add(DataTransferModel.div); + stream.next(); + } else if (operator.equals(MOD)) { + operators.add(DataTransferModel.mod); + stream.next(); + } else if (operator.equals(EQ)) { + operators.add(DataTransferModel.eq); + stream.next(); + } else if (operator.equals(NEQ)) { + operators.add(DataTransferModel.neq); + stream.next(); + } else if (operator.equals(GT)) { + operators.add(DataTransferModel.gt); + stream.next(); + } else if (operator.equals(LT)) { + operators.add(DataTransferModel.lt); + stream.next(); + } else if (operator.equals(GE)) { + operators.add(DataTransferModel.ge); + stream.next(); + } else if (operator.equals(LE)) { + operators.add(DataTransferModel.le); + stream.next(); + } else if (operator.equals(AND)) { + operators.add(DataTransferModel.and); + stream.next(); + } else if (operator.equals(OR)) { + operators.add(DataTransferModel.or); + stream.next(); + } else if (operator.equals(DOT)) { + // json accessor + while (operator.equals(DOT)) { + Expression exp = expressions.remove(0); + stream.next(); // DOT + if (stream.checkNext() == null) throw new WrongJsonExpression(stream.getLine()); + String literalOrLeftCurlyBracket = stream.next(); + Expression paramTerm = null; + if (literalOrLeftCurlyBracket.equals(LEFT_CURLY_BRACKET)) { + // parameter + paramTerm = parseTerm(stream, model); + String rightCurlyBracket = stream.next(); + if (rightCurlyBracket == null || !rightCurlyBracket.equals(RIGHT_CURLY_BRACKET)) throw new WrongJsonExpression(stream.getLine()); + } else { + // literal + paramTerm = new Constant(literalOrLeftCurlyBracket, DataTransferModel.typeString); + } + Type paramType = null; + if (paramTerm instanceof Variable) { + paramType = ((Variable) paramTerm).getType(); + } else if (paramTerm instanceof Term) { + paramType = ((Term) paramTerm).getType(); + } + Term term = null; + if (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 +532,41 @@ ArrayList addSubs = new ArrayList<>(); Expression first = expressions.get(0); int i = 1; + Term rootTerm = null; for (Symbol op: operators) { Expression second = expressions.get(i); - if (op.getName().equals(MUL) || op.getName().equals(DIV)) { + if (op.getName().equals(MUL) || op.getName().equals(DIV) || op.getName().equals(MOD)) { + // higher priority than add and sub Term term = new Term(op); term.addChild(first); term.addChild(second); first = term; + } else if (op.getName().equals(EQ) || op.getName().equals(NEQ) || op.getName().equals(GT) || op.getName().equals(LT) + || op.getName().equals(GE) || op.getName().equals(LE) || op.getName().equals(AND) || op.getName().equals(OR)) { + // lower priority than add and sub + if (first != null) monomials.add(first); + Expression firstMonomial = monomials.get(0); + int j = 1; + for (Symbol op2: addSubs) { + Expression secondMonomial = monomials.get(j); + Term term = new Term(op2); + term.addChild(firstMonomial); + term.addChild(secondMonomial); + firstMonomial = term; + j++; + } + if (rootTerm == null) { + rootTerm = new Term(op); + rootTerm.addChild(firstMonomial); + } else { + rootTerm.addChild(firstMonomial); + firstMonomial = rootTerm; + rootTerm = new Term(op); + rootTerm.addChild(firstMonomial); + } + monomials.clear(); + addSubs.clear(); + first = second; } else { // add or sub ==> new monomial monomials.add(first); @@ -369,29 +586,171 @@ firstMonomial = term; i++; } - return firstMonomial; + if (rootTerm == null) { + return firstMonomial; + } else { + rootTerm.addChild(firstMonomial); + return rootTerm; + } } - /**-------------------------------------------------------------------------------- - * [protected] - /**-------------------------------------------------------------------------------- - * checking the token has a token. - * - * @param token - * @param specificTokenName - */ - protected Boolean isMatchKeyword(final String token, final String specificTokenName) { +// private Expression parseJsonTerm(TokenStream stream, DataTransferModel model) throws ExpectedRightBracket, WrongJsonExpression, ExpectedColon { +// Term jsonTerm = new Constant(DataConstraintModel.nil); +// jsonTerm.setType(DataConstraintModel.typeJson); +// while (stream.checkNext() != null && !stream.checkNext().equals(RIGHT_CURLY_BRACKET)) { +// String key = stream.next(); +// Constant keyExp = new Constant(key); +// keyExp.setType(DataConstraintModel.typeString); +// if (stream.checkNext() == null || !stream.checkNext().equals(COLON)) throw new ExpectedColon(stream.getLine()); +// String colon = stream.next(); +// Expression value = parseTerm(stream, model); +// Term nextTerm = new Term(DataConstraintModel.addMember); +// nextTerm.addChild(jsonTerm); +// nextTerm.addChild(keyExp); +// nextTerm.addChild(value); +// jsonTerm = nextTerm; +// if (stream.checkNext() == null || !stream.checkNext().equals(COMMA)) break; +// String comma = stream.next(); +// } +// return jsonTerm; +// } + + private Expression 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 { + Term listTerm = new Constant(DataTransferModel.nil); + listTerm.setType(DataTransferModel.typeList); + while (stream.checkNext() != null && !stream.checkNext().equals(RIGHT_SQUARE_BRACKET)) { + Expression element = parseTerm(stream, model); + Term nextTerm = new Term(DataTransferModel.cons); + nextTerm.addChild(element); + nextTerm.addChild(listTerm); + listTerm = nextTerm; + if (stream.checkNext() == null || !stream.checkNext().equals(COMMA)) break; + String comma = stream.next(); + } + return listTerm; + } + + private Variable parseVariable(TokenStream stream, DataTransferModel model, String symbolName) { + Variable var; + if (stream.checkNext() != null && stream.checkNext().equals(COLON)) { + // when a type is specified. + stream.next(); + String typeName = stream.next(); + Type type = model.getType(typeName); + if (type == null) { + type = new Type(typeName, typeName); + } + var = new Variable(symbolName, type); + } else { + var = new Variable(symbolName); + } + return var; + } + + public StateTransitionTerm parseStateTransitionTerm(TokenStream stream, DataTransferModel model) + throws ExpectedRightBracket, ExpectedRightCurlyBracket, WrongPathExpression, WrongJsonExpression, ExpectedColon, 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; @@ -404,61 +763,83 @@ 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; } @@ -470,7 +851,7 @@ n = 0; if (line >= tokens.size()) return null; } - String token = tokens.get(line).get(n); + String token = tokens.get(line).get(n).getTokenStr(); n++; return token; } @@ -482,7 +863,7 @@ n = 0; if (line >= tokens.size()) return null; } - return tokens.get(line).get(n); + return tokens.get(line).get(n).getTokenStr(); } public boolean hasNext() { @@ -507,4 +888,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..eae7ac7 100644 --- a/AlgebraicDataflowArchitectureModel/src/parser/ParserDTRAM.java +++ b/AlgebraicDataflowArchitectureModel/src/parser/ParserDTRAM.java @@ -14,10 +14,12 @@ import parser.exceptions.ExpectedAssignment; import parser.exceptions.ExpectedChannel; import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; +import parser.exceptions.ExpectedDoubleQuotation; import parser.exceptions.ExpectedEquals; import parser.exceptions.ExpectedFormulaChannel; import parser.exceptions.ExpectedGeometry; -import parser.exceptions.ExpectedInOrOutOrRefKeyword; +import parser.exceptions.ExpectedInOrOutOrRefOrSubKeyword; import parser.exceptions.ExpectedIoChannel; import parser.exceptions.ExpectedLeftCurlyBracket; import parser.exceptions.ExpectedModel; @@ -25,8 +27,11 @@ import parser.exceptions.ExpectedRHSExpression; import parser.exceptions.ExpectedResource; import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.ExpectedRightCurlyBracket; import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; import parser.exceptions.WrongLHSExpression; +import parser.exceptions.WrongPathExpression; import parser.exceptions.WrongRHSExpression; public class ParserDTRAM extends Parser { @@ -61,9 +66,12 @@ /**-------------------------------------------------------------------------------- * * @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 { + throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedInOrOutOrRefOrSubKeyword, ExpectedStateTransition, ExpectedEquals, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, ExpectedModel, ExpectedGeometry, ExpectedRightCurlyBracket, WrongPathExpression, WrongJsonExpression, ExpectedColon, ExpectedDoubleQuotation { DataTransferModel model = getParsedModel(); return model; } @@ -73,7 +81,7 @@ * @param graph */ public void doParseGeometry(mxGraph graph) - throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedInOrOutOrRefKeyword, ExpectedStateTransition, ExpectedEquals, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, ExpectedModel, ExpectedGeometry, ExpectedNode, ExpectedResource, ExpectedFormulaChannel, ExpectedIoChannel{ + throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedInOrOutOrRefOrSubKeyword, ExpectedStateTransition, ExpectedEquals, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, ExpectedModel, ExpectedGeometry, ExpectedNode, ExpectedResource, ExpectedFormulaChannel, ExpectedIoChannel{ parseGeometry(graph); } @@ -83,9 +91,12 @@ /**-------------------------------------------------------------------------------- * * @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(); @@ -110,11 +121,11 @@ * @param graph */ private void parseGeometry(mxGraph graph) - throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedInOrOutOrRefKeyword, ExpectedStateTransition, ExpectedEquals, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment,ExpectedModel, ExpectedGeometry, ExpectedNode, ExpectedResource, ExpectedFormulaChannel, ExpectedIoChannel { + throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedInOrOutOrRefOrSubKeyword, ExpectedStateTransition, ExpectedEquals, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment,ExpectedModel, ExpectedGeometry, ExpectedNode, ExpectedResource, ExpectedFormulaChannel, ExpectedIoChannel { - if (!isMatchKeyword(stream.next(), GEOMETRY_GROUP)) throw new ExpectedGeometry(stream.getLine()); + if (!doesMatchToKeyword(stream.next(), GEOMETRY_GROUP)) throw new ExpectedGeometry(stream.getLine()); - if (!isMatchKeyword(stream.next(), LEFT_CURLY_BRACKET)) throw new ExpectedLeftCurlyBracket(stream.getLine()); + if (!doesMatchToKeyword(stream.next(), LEFT_CURLY_BRACKET)) throw new ExpectedLeftCurlyBracket(stream.getLine()); String node = stream.next(); while (node.equals(GEOMETORY_NODE)) { @@ -128,22 +139,22 @@ String name = stream.next(); - if (!isMatchKeyword(stream.next(), COLON)) throw new ExpectedAssignment(stream.getLine()); + if (!doesMatchToKeyword(stream.next(), COLON)) throw new ExpectedAssignment(stream.getLine()); String x = stream.next(); int xC = Integer.parseInt(x); // C = Coordinate(x,y,w,h) - if (!isMatchKeyword(stream.next(), COMMA))throw new ExpectedAssignment(stream.getLine()); + if (!doesMatchToKeyword(stream.next(), COMMA))throw new ExpectedAssignment(stream.getLine()); String y = stream.next(); int yC = Integer.parseInt(y); - if (!isMatchKeyword(stream.next(), COMMA))throw new ExpectedAssignment(stream.getLine()); + if (!doesMatchToKeyword(stream.next(), COMMA))throw new ExpectedAssignment(stream.getLine()); String w = stream.next(); int wC = Integer.parseInt(w); - if (!isMatchKeyword(stream.next(), COMMA))throw new ExpectedAssignment(stream.getLine()); + if (!doesMatchToKeyword(stream.next(), COMMA))throw new ExpectedAssignment(stream.getLine()); String h = stream.next(); int hC = Integer.parseInt(h); diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedColon.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedColon.java new file mode 100644 index 0000000..426aaf1 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedColon.java @@ -0,0 +1,8 @@ +package parser.exceptions; + +public class ExpectedColon extends ParseException { + + public ExpectedColon(int line) { + super(line); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/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/ExpectedInOrOutOrRefKeyword.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedInOrOutOrRefKeyword.java deleted file mode 100644 index a44a604..0000000 --- a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedInOrOutOrRefKeyword.java +++ /dev/null @@ -1,9 +0,0 @@ -package parser.exceptions; - -public class ExpectedInOrOutOrRefKeyword extends ParseException { - - public ExpectedInOrOutOrRefKeyword(int line) { - super(line); - } - -} diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedInOrOutOrRefOrSubKeyword.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedInOrOutOrRefOrSubKeyword.java new file mode 100644 index 0000000..8d99767 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedInOrOutOrRefOrSubKeyword.java @@ -0,0 +1,9 @@ +package parser.exceptions; + +public class ExpectedInOrOutOrRefOrSubKeyword extends ParseException { + + public ExpectedInOrOutOrRefOrSubKeyword(int line) { + super(line); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedRightCurlyBracket.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedRightCurlyBracket.java new file mode 100644 index 0000000..df87cb3 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedRightCurlyBracket.java @@ -0,0 +1,9 @@ +package parser.exceptions; + +public class ExpectedRightCurlyBracket extends ParseException { + + public ExpectedRightCurlyBracket(int line) { + super(line); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/WrongJsonExpression.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/WrongJsonExpression.java new file mode 100644 index 0000000..6a84d6f --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/WrongJsonExpression.java @@ -0,0 +1,9 @@ +package parser.exceptions; + +public class WrongJsonExpression extends ParseException { + + public WrongJsonExpression(int line) { + super(line); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/WrongPathExpression.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/WrongPathExpression.java new file mode 100644 index 0000000..7f580a5 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/WrongPathExpression.java @@ -0,0 +1,9 @@ +package parser.exceptions; + +public class WrongPathExpression extends ParseException { + + public WrongPathExpression(int line) { + super(line); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/ChannelState.java b/AlgebraicDataflowArchitectureModel/src/simulator/ChannelState.java new file mode 100644 index 0000000..4178d73 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/ChannelState.java @@ -0,0 +1,167 @@ +package simulator; + +import java.util.Map; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.dataConstraintModel.Selector; +import models.dataFlowModel.DataTransferChannel; + +/** + * 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..23dfafa --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/Event.java @@ -0,0 +1,671 @@ +package simulator; + +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.InvalidMessage; +import models.algebra.Position; +import models.algebra.Term; +import models.algebra.UnificationFailed; +import models.algebra.Variable; +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; + +/** + * 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; + 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..8cd943f --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/Resource.java @@ -0,0 +1,215 @@ +package simulator; + +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Type; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.ResourceHierarchy; +import simulator.states.*; + +/** + * 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..13b2c92 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/ResourceIdentifier.java @@ -0,0 +1,137 @@ +package simulator; + +import java.util.HashMap; +import java.util.Map; +import java.util.AbstractMap; +import java.util.AbstractMap.SimpleEntry; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.dataConstraintModel.ResourceHierarchy; +import models.dataConstraintModel.ResourcePath; +import models.dataConstraintModel.Selector; + +public class ResourceIdentifier extends ResourcePath { + + public ResourceIdentifier(String fullResourceName, ResourceHierarchy resourceHierarchy) { + super(fullResourceName, resourceHierarchy); + } + + public ResourceIdentifier(ResourceIdentifier parentResId, String leafResourceName, ResourceHierarchy resourceHierarchy) { + super(parentResId, leafResourceName, resourceHierarchy); + } + + public ResourceIdentifier(ResourceIdentifier parentResId, Expression exp, ResourceHierarchy resourceHierarchy) { + super(parentResId, exp, resourceHierarchy); + } + + public void setPathParam(int paramIdx, 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..29399dd --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/Simulator.java @@ -0,0 +1,606 @@ +package simulator; + +import java.util.AbstractMap; +import java.util.AbstractMap.SimpleEntry; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.InvalidMessage; +import models.algebra.ParameterizedIdentifierIsFutureWork; +import models.algebra.Term; +import models.algebra.UnificationFailed; +import models.algebra.ValueUndefined; +import models.algebra.Variable; +import models.dataConstraintModel.Channel; +import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.ResourceHierarchy; +import models.dataConstraintModel.ResourcePath; +import models.dataConstraintModel.Selector; +import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor; +import simulator.Event.IResourceStateValueProvider; +import simulator.interfaces.INativeInitializer; +import simulator.interfaces.INativeReceiver; + +/** + * 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); + inputEvent.constructMessageAndDescendantEvents(new ResourceStateValueProvider(curState, nextSystemState), true); + 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); + nextEvents.add(nextEvent); + } + } + 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..1b19ac6 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/SystemState.java @@ -0,0 +1,1060 @@ +package simulator; + +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Term; +import models.algebra.Type; +import models.algebra.Variable; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.JsonTerm; +import models.dataConstraintModel.ListTerm; +import models.dataConstraintModel.MapTerm; +import models.dataConstraintModel.ResourceHierarchy; +import models.dataFlowModel.DataTransferChannel; +import simulator.states.CompositeResourceState; +import simulator.states.JsonResourceState; +import simulator.states.ListResourceState; +import simulator.states.MapResourceState; +import simulator.states.PrimitiveResourceState; +import simulator.states.ResourceState; + +/** + * 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())); + 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(); + 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; + } 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); + 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..3117943 --- /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); +} 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..7e74929 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/NativeSender.java @@ -0,0 +1,41 @@ +package simulator.interfaces; + +import java.util.HashSet; +import java.util.Set; + +import models.algebra.Expression; +import models.algebra.InvalidMessage; +import models.algebra.ParameterizedIdentifierIsFutureWork; +import models.algebra.UnificationFailed; +import models.algebra.ValueUndefined; +import models.dataConstraintModel.Channel; +import models.dataConstraintModel.ResourcePath; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork; +import simulator.Event; +import simulator.Resource; +import simulator.Simulator; + +abstract public class NativeSender { + protected Simulator simulator; + protected DataTransferChannel channel; + protected ResourcePath resourcePath; + protected Resource resource; + + public NativeSender(Simulator simulator, DataTransferChannel channel, ResourcePath resourcePath, Resource resource) { + this.simulator = simulator; + this.channel = channel; + this.resourcePath = resourcePath; + this.resource = resource; + } + + public void sendToModel(Expression message) { + try { + Event event = new Event(channel, message, resourcePath, resource); + simulator.transition(event); + System.out.println(System.nanoTime()); //終了 + } 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..9d6aad1 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentHeightReceiver.java @@ -0,0 +1,35 @@ +package simulator.interfaces.swing; + +import java.awt.Component; + +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.text.JTextComponent; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Term; +import simulator.Event; +import simulator.SystemState; +import simulator.interfaces.INativeReceiver; + +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..f90d46b --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentMouseSender.java @@ -0,0 +1,47 @@ +package simulator.interfaces.swing; + +import java.awt.Component; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Term; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.ResourcePath; +import models.dataFlowModel.DataTransferChannel; +import simulator.Resource; +import simulator.Simulator; +import simulator.interfaces.NativeSender; + +public class ComponentMouseSender extends NativeSender implements MouseListener { + + public ComponentMouseSender(Simulator simulator, DataTransferChannel channel, ResourcePath resourcePath, Resource resource) { + super(simulator, channel, resourcePath, resource); + } + + public void mouseClicked(MouseEvent e) { + } + + public void mousePressed(MouseEvent e) { + Constant one = new Constant("1", DataConstraintModel.typeInt); + Expression message = channel.getOutputChannelMembers().iterator().next().getStateTransition().getMessageExpression(); + message = (Term) message.clone(); + ((Term) message).setChild(0, one); + sendToModel(message); + } + + public void mouseReleased(MouseEvent e) { + Constant zero = new Constant("0", DataConstraintModel.typeInt); + Expression message = channel.getOutputChannelMembers().iterator().next().getStateTransition().getMessageExpression(); + message = (Term) message.clone(); + ((Term) message).setChild(0, zero); + sendToModel(message); + } + + public void mouseEntered(MouseEvent e) { + } + + public void mouseExited(MouseEvent e) { + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentTextReceiver.java b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentTextReceiver.java new file mode 100644 index 0000000..5ab8b63 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentTextReceiver.java @@ -0,0 +1,40 @@ +package simulator.interfaces.swing; + +import java.awt.Component; + +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.text.JTextComponent; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Term; +import simulator.Event; +import simulator.SystemState; +import simulator.interfaces.INativeReceiver; + +public class ComponentTextReceiver implements INativeReceiver { + protected Component component; + + public ComponentTextReceiver(Component component) { + this.component = component; + } + + @Override + public void onReceiveFromModel(Event event, 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..c6580fd --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentTextSender.java @@ -0,0 +1,66 @@ +package simulator.interfaces.swing; + +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Term; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.ResourcePath; +import models.dataFlowModel.DataTransferChannel; +import simulator.Resource; +import simulator.Simulator; +import simulator.interfaces.NativeSender; + +public class ComponentTextSender extends NativeSender implements 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..447dae1 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentVisibilityReceiver.java @@ -0,0 +1,35 @@ +package simulator.interfaces.swing; + +import java.awt.Component; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Term; +import models.dataConstraintModel.DataConstraintModel; +import simulator.Event; +import simulator.SystemState; +import simulator.interfaces.INativeReceiver; + +public class ComponentVisibilityReceiver implements INativeReceiver { + protected Component component; + + public ComponentVisibilityReceiver(Component component) { + this.component = component; + } + + @Override + public void onReceiveFromModel(Event event, 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..76c0dda --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentWidthReceiver.java @@ -0,0 +1,35 @@ +package simulator.interfaces.swing; + +import java.awt.Component; + +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.text.JTextComponent; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Term; +import simulator.Event; +import simulator.SystemState; +import simulator.interfaces.INativeReceiver; + +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..2867e97 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentXReceiver.java @@ -0,0 +1,35 @@ +package simulator.interfaces.swing; + +import java.awt.Component; + +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.text.JTextComponent; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Term; +import simulator.Event; +import simulator.SystemState; +import simulator.interfaces.INativeReceiver; + +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..a964f6b --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/ComponentYReceiver.java @@ -0,0 +1,35 @@ +package simulator.interfaces.swing; + +import java.awt.Component; + +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.text.JTextComponent; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Term; +import simulator.Event; +import simulator.SystemState; +import simulator.interfaces.INativeReceiver; + +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..e59ed64 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/swing/SwingPresenter.java @@ -0,0 +1,402 @@ +package simulator.interfaces.swing; + +import java.awt.Component; +import java.awt.FlowLayout; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; + +import models.algebra.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; + +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"; + + 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 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); + 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); + 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); + 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); + 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); + 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("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); + 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/timers/TimerEventSender.java b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/timers/TimerEventSender.java new file mode 100644 index 0000000..acdb333 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/timers/TimerEventSender.java @@ -0,0 +1,25 @@ +package simulator.interfaces.timers; + +import models.algebra.Expression; +import models.algebra.Term; +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() { + System.out.println(System.nanoTime()); //開始 + 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..1ef8061 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/interfaces/timers/TimerService.java @@ -0,0 +1,139 @@ +package simulator.interfaces.timers; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.ScheduledThreadPoolExecutor; + +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; + +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..b445d14 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/states/CompositeResourceState.java @@ -0,0 +1,14 @@ +package simulator.states; + +import java.util.Map; + +public abstract class CompositeResourceState extends ResourceState { + @Override + boolean hasChildren() { + return true; + } + + public abstract Map getChildStates(); + public abstract void 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..6ba7256 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/states/JsonResourceState.java @@ -0,0 +1,58 @@ +package simulator.states; + +import java.util.HashMap; +import java.util.Map; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Term; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.JsonTerm; + +public class JsonResourceState extends CompositeResourceState { + private Map children = new HashMap<>(); + + @Override + public Expression getValue() { + JsonTerm value = new JsonTerm(); + value.setType(DataConstraintModel.typeJson); + for (Map.Entry childEnt: children.entrySet()) { + value.addMember(childEnt.getKey(), childEnt.getValue().getValue()); + } + return value; + } + + @Override + public Map getChildStates() { + return children; + } + + public void addChildState(String param, ResourceState childState) { + children.put(param, childState); + } + + 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..d45f9d2 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/states/ListResourceState.java @@ -0,0 +1,67 @@ +package simulator.states; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Term; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.ListTerm; + +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..6ceecf4 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/states/MapResourceState.java @@ -0,0 +1,58 @@ +package simulator.states; + +import java.util.HashMap; +import java.util.Map; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Term; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.MapTerm; + +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..19db331 --- /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..00883d9 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/simulator/states/State.java @@ -0,0 +1,13 @@ +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 index a89ea3c..8ee6b46 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/CodeGeneratorTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/CodeGeneratorTest.java @@ -17,13 +17,18 @@ import parser.exceptions.ExpectedAssignment; import parser.exceptions.ExpectedChannel; import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; +import parser.exceptions.ExpectedDoubleQuotation; import parser.exceptions.ExpectedEquals; -import parser.exceptions.ExpectedInOrOutOrRefKeyword; +import parser.exceptions.ExpectedInOrOutOrRefOrSubKeyword; import parser.exceptions.ExpectedLeftCurlyBracket; import parser.exceptions.ExpectedRHSExpression; import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.ExpectedRightCurlyBracket; import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; import parser.exceptions.WrongLHSExpression; +import parser.exceptions.WrongPathExpression; import parser.exceptions.WrongRHSExpression; public class CodeGeneratorTest { @@ -39,9 +44,9 @@ DataTransferMethodAnalyzer.decideToStoreResourceStates(graph); ArrayList codetree = JavaMethodBodyGenerator.doGenerate(graph, model, JavaCodeGenerator.doGenerate(graph, model)); System.out.println(codetree); - } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefKeyword + } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression - | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment e) { + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { e.printStackTrace(); } } catch (FileNotFoundException e) { diff --git a/AlgebraicDataflowArchitectureModel/src/tests/DataConstraintModelTest.java b/AlgebraicDataflowArchitectureModel/src/tests/DataConstraintModelTest.java index afe405a..b84e563 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/DataConstraintModelTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/DataConstraintModelTest.java @@ -4,6 +4,7 @@ import org.junit.Test; +import models.algebra.Variable; import models.dataConstraintModel.*; public class DataConstraintModelTest { @@ -12,47 +13,47 @@ public void test() { // Construct a data constraint architecture model. DataConstraintModel model = new DataConstraintModel(); - ResourcePath customer_off = new ResourcePath("customers.{customer_id}.off", 1); - ResourcePath customer_add = new ResourcePath("customers.{customer_id}.add", 1); - ResourcePath company_add = new ResourcePath("companies.{company_id}.add", 1); - Channel gin_1 = new Channel("gin_1"); // set customer's office - GroupSelector x1 = new GroupSelector(); + ResourcePath customers = new ResourcePath("customers"); // "customers" + ResourcePath customer = new ResourcePath(customers, new Variable("uid")); // "customers.{uid}" + ResourcePath customer_off = new ResourcePath(customer, "off"); // "customers.{uid}.off" + ResourcePath customer_add = new ResourcePath(customer, "add"); // "customers.{uid}.add" + ResourcePath companies = new ResourcePath("companies"); // "companies" + ResourcePath company = new ResourcePath(companies, new Variable("cid")); // "companies.{cid}" + ResourcePath company_add = new ResourcePath(company, "add"); // "companies.{cid}.add" + model.addResourcePath(customer_off); + model.addResourcePath(customer_add); + model.addResourcePath(company_add); + + Channel cio_setCustomerOff = new Channel("CIO_SetCustomerOff", new Variable("uid")); // set customer's office ChannelMember customer_off_1 = new ChannelMember(customer_off); - customer_off_1.addSelector(x1); - gin_1.addChannelMember(customer_off_1); - assertEquals(customer_off.getNumberOfParameters(), customer_off_1.getSelectors().size()); + cio_setCustomerOff.addChannelMember(customer_off_1); + assertEquals(customer_off.getPathParams().get(0), cio_setCustomerOff.getSelectors().iterator().next().getExpression()); - Channel gin_2 = new Channel("gin_2"); // set companie's address - GroupSelector x2 = new GroupSelector(); + Channel cio_setCompanyAdd = new Channel("CIO_SetCompanyAdd", new Variable("cid")); // set companie's address ChannelMember company_add_1 = new ChannelMember(company_add); - company_add_1.addSelector(x2); - gin_2.addChannelMember(company_add_1); - assertEquals(company_add.getNumberOfParameters(), company_add_1.getSelectors().size()); + cio_setCompanyAdd.addChannelMember(company_add_1); + assertEquals(company_add.getPathParams().get(0), cio_setCompanyAdd.getSelectors().iterator().next().getExpression()); - Channel g = new Channel("g"); // update customer's address - GroupSelector x3 = new GroupSelector(); - ChannelSelector y = new ChannelSelector(); + Channel c = new Channel("c", new Variable("uid")); // update customer's address ChannelMember customer_off_2 = new ChannelMember(customer_off); ChannelMember company_add_2 = new ChannelMember(company_add); ChannelMember customer_add_2 = new ChannelMember(customer_add); - customer_off_2.addSelector(x3); - company_add_2.addSelector(y); - customer_add_2.addSelector(x3); - g.addChannelMember(customer_off_2); - g.addChannelMember(customer_add_2); - g.addChannelMember(company_add_2); - assertEquals(customer_off.getNumberOfParameters(), customer_off_2.getSelectors().size()); - assertEquals(customer_add.getNumberOfParameters(), customer_add_2.getSelectors().size()); - assertEquals(company_add.getNumberOfParameters(), company_add_2.getSelectors().size()); + c.addChannelMember(customer_off_2); + c.addChannelMember(company_add_2); + c.addChannelMember(customer_add_2); + assertEquals(customer_off.getPathParams().get(0), c.getSelectors().iterator().next().getExpression()); + assertEquals(customer_add.getPathParams().get(0), c.getSelectors().iterator().next().getExpression()); + assertEquals(company_add.getPathParams().get(0), new Variable("cid")); - model.addIOChannel(gin_1); - model.addIOChannel(gin_2); - model.addChannel(g); + model.addInputChannel(cio_setCustomerOff); + model.addInputChannel(cio_setCompanyAdd); + model.addChannel(c); // Check the model. assertEquals(3, model.getResourcePaths().size()); - assertEquals(2, model.getIOChannels().size()); + assertEquals(7, model.getResourceHierarchies().size()); + assertEquals(2, model.getInputChannels().size()); assertEquals(1, model.getChannels().size()); } diff --git a/AlgebraicDataflowArchitectureModel/src/tests/DataFlowModelTest.java b/AlgebraicDataflowArchitectureModel/src/tests/DataFlowModelTest.java deleted file mode 100644 index 605b006..0000000 --- a/AlgebraicDataflowArchitectureModel/src/tests/DataFlowModelTest.java +++ /dev/null @@ -1,88 +0,0 @@ -package tests; - -import static org.junit.Assert.*; - -import org.junit.Test; - -import models.*; -import models.dataConstraintModel.*; -import models.dataFlowModel.*; - -public class DataFlowModelTest { - - @Test - public void test() { - // Construct a data-flow architecture model. - DataTransferModel model = new DataTransferModel(); - ResourcePath customer_off = new ResourcePath("customers.{x1}.off", 1); // an identifier template to specify a customer's office resource - ResourcePath company_add = new ResourcePath("companies.{x2}.add", 1); // an identifier template to specify a companie's address resource - ResourcePath customer_add = new ResourcePath("customers.{x1}.add", 1); // an identifier template to specify a customer's address resource - - // === gin_1 === - // - // customers.{x1}.off(c, set(x)) == x - // customers.{x1}.off(c, e) == c - // - DataTransferChannel gin_1 = new DataTransferChannel("gin_1"); // set customer's office (an input channel) - GroupSelector x1 = new GroupSelector(); - ChannelMember customer_off_1 = new ChannelMember(customer_off); - customer_off_1.addSelector(x1); // x1 is determined by the path parameter in customer's office template, and serves as a group selector in this channel - gin_1.addChannelMember(customer_off_1); - assertEquals(customer_off.getNumberOfParameters(), customer_off_1.getSelectors().size()); - - // === gin_2 === - // - // companies.{x2}.add(a, set(y)) == y - // companies.{x2}.add(a, e) == a - // - DataTransferChannel gin_2 = new DataTransferChannel("gin_2"); // set companie's address (an input channel) - GroupSelector x2 = new GroupSelector(); - ChannelMember company_add_1 = new ChannelMember(company_add); - company_add_1.addSelector(x2); // x2 is determined by the path parameter in companie's address template, and serves as a group selector in this channel - gin_2.addChannelMember(company_add_1); - assertEquals(company_add.getNumberOfParameters(), company_add_1.getSelectors().size()); - - // === g === - // - // customers.{x3}.off( c, update(y, z)) == y - // companies.{y}.add( a1, update(y, z)) == z - // customers.{x3}.add(a2, update(y, z)) == z - // - DataTransferChannel g = new DataTransferChannel("g"); // update customer's address - GroupSelector x3 = new GroupSelector(); - ChannelSelector y = new ChannelSelector(); - ChannelMember customer_off_2 = new ChannelMember(customer_off); - ChannelMember company_add_2 = new ChannelMember(company_add); - ChannelMember customer_add_2 = new ChannelMember(customer_add); - customer_off_2.addSelector(x3); // x3 is determined by the path parameter in customer's office template, and serves as a group selector in this channel - company_add_2.addSelector(y); // y is determined by the value of the customer's office resource, and serves as a channel selector in this channel - customer_add_2.addSelector(x3); // x3 determines the path parameter in customer's address template to update - g.addChannelMemberAsInput(customer_off_2); - g.addChannelMemberAsInput(customer_add_2); - g.addChannelMemberAsOutput(company_add_2); - assertEquals(customer_off.getNumberOfParameters(), customer_off_2.getSelectors().size()); - assertEquals(customer_add.getNumberOfParameters(), customer_add_2.getSelectors().size()); - assertEquals(company_add.getNumberOfParameters(), company_add_2.getSelectors().size()); - - // Construct a data-flow architecture model. - model.addIOChannel(gin_1); - model.addIOChannel(gin_2); - model.addChannel(g); - - // Check the model. - assertEquals(3, model.getResourcePaths().size()); - assertEquals(2, model.getIOChannels().size()); - assertEquals(1, model.getChannels().size()); - - // Extract the resource dependency graph. - DataFlowGraph resourceDependencyGraph = model.getDataFlowGraph(); - - // Check the graph. - assertEquals(3, resourceDependencyGraph.getNodes().size()); - assertEquals(2, resourceDependencyGraph.getEdges().size()); - for (Edge e: resourceDependencyGraph.getEdges()) { - System.out.println(e.getSource() + "-(" + e + ")->" + e.getDestination()); - } - } - -} diff --git a/AlgebraicDataflowArchitectureModel/src/tests/DataStorageDecisionTest.java b/AlgebraicDataflowArchitectureModel/src/tests/DataStorageDecisionTest.java index 9d28936..0db9f4b 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/DataStorageDecisionTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/DataStorageDecisionTest.java @@ -13,13 +13,18 @@ import parser.exceptions.ExpectedAssignment; import parser.exceptions.ExpectedChannel; import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; +import parser.exceptions.ExpectedDoubleQuotation; import parser.exceptions.ExpectedEquals; -import parser.exceptions.ExpectedInOrOutOrRefKeyword; +import parser.exceptions.ExpectedInOrOutOrRefOrSubKeyword; import parser.exceptions.ExpectedLeftCurlyBracket; import parser.exceptions.ExpectedRHSExpression; import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.ExpectedRightCurlyBracket; import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; import parser.exceptions.WrongLHSExpression; +import parser.exceptions.WrongPathExpression; import parser.exceptions.WrongRHSExpression; public class DataStorageDecisionTest { @@ -34,12 +39,12 @@ DataFlowGraph graph = DataTransferModelAnalyzer.createDataFlowGraphWithStateStoringAttribute(model); DataTransferModelAnalyzer.annotateWithSelectableDataTransferAttiribute(graph); DataTransferMethodAnalyzer.decideToStoreResourceStates(graph); - for(Node n:graph.getNodes()) { - System.out.println(((ResourceNode) n).getResource().getResourceName() + ":" + ((StoreAttribute) ((ResourceNode) n).getAttribute()).isStored()); + for(Node resNode: graph.getResourceNodes()) { + System.out.println(((ResourceNode) resNode).getResourceName() + ":" + ((StoreAttribute) ((ResourceNode) resNode).getAttribute()).isStored()); } - } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefKeyword + } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression - | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment e) { + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { e.printStackTrace(); } } catch (FileNotFoundException e) { diff --git a/AlgebraicDataflowArchitectureModel/src/tests/DataStorageNecessityTest.java b/AlgebraicDataflowArchitectureModel/src/tests/DataStorageNecessityTest.java index 9a7c300..d95fb08 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/DataStorageNecessityTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/DataStorageNecessityTest.java @@ -12,13 +12,18 @@ import parser.exceptions.ExpectedAssignment; import parser.exceptions.ExpectedChannel; import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; +import parser.exceptions.ExpectedDoubleQuotation; import parser.exceptions.ExpectedEquals; -import parser.exceptions.ExpectedInOrOutOrRefKeyword; +import parser.exceptions.ExpectedInOrOutOrRefOrSubKeyword; import parser.exceptions.ExpectedLeftCurlyBracket; import parser.exceptions.ExpectedRHSExpression; import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.ExpectedRightCurlyBracket; import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; import parser.exceptions.WrongLHSExpression; +import parser.exceptions.WrongPathExpression; import parser.exceptions.WrongRHSExpression; public class DataStorageNecessityTest { @@ -37,9 +42,9 @@ System.out.println(resource.toString() + ":" + ((StoreAttribute)resource.getAttribute()).isNeeded()); } } - } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefKeyword + } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression - | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment e) { + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket | WrongPathExpression | WrongJsonExpression | ExpectedColon | 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..0f4575b --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/tests/DataTransferModelTest.java @@ -0,0 +1,88 @@ +package tests; + +import static org.junit.Assert.*; + +import org.junit.Test; + +import models.*; +import models.algebra.Variable; +import models.dataConstraintModel.*; +import models.dataFlowModel.*; + +public class DataTransferModelTest { + + @Test + public void test() { + // Construct a data-flow architecture model. + DataTransferModel model = new DataTransferModel(); + + ResourcePath customers = new ResourcePath("customers"); // "customers" + ResourcePath customer = new ResourcePath(customers, new Variable("uid")); // "customers.{uid}" + ResourcePath customer_off = new ResourcePath(customer, "off"); // "customers.{uid}.off" + ResourcePath customer_add = new ResourcePath(customer, "add"); // "customers.{uid}.add" + ResourcePath companies = new ResourcePath("companies"); // "companies" + ResourcePath company = new ResourcePath(companies, new Variable("cid")); // "companies.{cid}" + ResourcePath company_add = new ResourcePath(company, "add"); // "companies.{cid}.add" + model.addResourcePath(customer_off); + model.addResourcePath(customer_add); + model.addResourcePath(company_add); + + // === cio_setCustomerOff(uid) === + // + // out customers.{uid}.off(c, set(x)) = x + // + DataTransferChannel cio_setCustomerOff = new DataTransferChannel("CIO_SetCustomerOff", new Variable("uid")); // set customer's office (an input channel) + ChannelMember customer_off_1 = new ChannelMember(customer_off); + cio_setCustomerOff.addChannelMemberAsOutput(customer_off_1); + assertEquals(customer_off.getPathParams().get(0), cio_setCustomerOff.getSelectors().iterator().next().getExpression()); + + // === cio_setCompanyAdd(cid) === + // + // out companies.{cid}.add(a, set(y)) = y + // + DataTransferChannel cio_setCompanyAdd = new DataTransferChannel("CIO_SetCompanyAdd", new Variable("cid")); // set companie's address (an input channel) + ChannelMember company_add_1 = new ChannelMember(company_add); + cio_setCompanyAdd.addChannelMemberAsOutput(company_add_1); + assertEquals(company_add.getPathParams().get(0), cio_setCompanyAdd.getSelectors().iterator().next().getExpression()); + + // === c === + // + // in customers.{uid}.off( c, update(cid, a2)) = cid + // in companies.{cid}.add( a, update(cid, a2)) = a2 + // out customers.{uid}.add(b, update(cid, a2)) = a2 + // + DataTransferChannel c = new DataTransferChannel("c", new Variable("uid")); // update customer's address + ChannelMember customer_off_2 = new ChannelMember(customer_off); + ChannelMember company_add_2 = new ChannelMember(company_add); + ChannelMember customer_add_2 = new ChannelMember(customer_add); + c.addChannelMemberAsInput(customer_off_2); + c.addChannelMemberAsInput(company_add_2); + c.addChannelMemberAsOutput(customer_add_2); + assertEquals(customer_off.getPathParams().get(0), c.getSelectors().iterator().next().getExpression()); + assertEquals(customer_add.getPathParams().get(0), c.getSelectors().iterator().next().getExpression()); + assertEquals(company_add.getPathParams().get(0), new Variable("cid")); + + // Construct a data-flow architecture model. + model.addInputChannel(cio_setCustomerOff); + model.addInputChannel(cio_setCompanyAdd); + model.addChannel(c); + + // Check the model. + assertEquals(3, model.getResourcePaths().size()); + assertEquals(7, model.getResourceHierarchies().size()); + assertEquals(2, model.getInputChannels().size()); + assertEquals(1, model.getChannels().size()); + + // Extract the resource dependency graph. + DataFlowGraph resourceDependencyGraph = model.getDataFlowGraph(); + + // Check the graph. + assertEquals(7, resourceDependencyGraph.getResourceNodes().size()); + assertEquals(3, resourceDependencyGraph.getChannelNodes().size()); + assertEquals(5, resourceDependencyGraph.getEdges().size()); + for (Edge e: resourceDependencyGraph.getEdges()) { + System.out.println(e.getSource() + "-(" + e + ")->" + e.getDestination()); + } + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/tests/EdgeTransitionSelectableTest.java b/AlgebraicDataflowArchitectureModel/src/tests/EdgeTransitionSelectableTest.java index 847fb6f..b88f7c3 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/EdgeTransitionSelectableTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/EdgeTransitionSelectableTest.java @@ -12,13 +12,18 @@ import parser.exceptions.ExpectedAssignment; import parser.exceptions.ExpectedChannel; import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; +import parser.exceptions.ExpectedDoubleQuotation; import parser.exceptions.ExpectedEquals; -import parser.exceptions.ExpectedInOrOutOrRefKeyword; +import parser.exceptions.ExpectedInOrOutOrRefOrSubKeyword; import parser.exceptions.ExpectedLeftCurlyBracket; import parser.exceptions.ExpectedRHSExpression; import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.ExpectedRightCurlyBracket; import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; import parser.exceptions.WrongLHSExpression; +import parser.exceptions.WrongPathExpression; import parser.exceptions.WrongRHSExpression; public class EdgeTransitionSelectableTest { @@ -36,9 +41,9 @@ DataFlowEdge re = (DataFlowEdge) e; System.out.println(re.getSource() + "-" + re.getDestination() + ":" + ((PushPullAttribute)(re.getAttribute())).getOptions()); } - } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefKeyword + } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression - | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment e) { + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { e.printStackTrace(); } } catch (FileNotFoundException e) { diff --git a/AlgebraicDataflowArchitectureModel/src/tests/FormulaChannelTest.java b/AlgebraicDataflowArchitectureModel/src/tests/FormulaChannelTest.java index a8f3503..6bf26f4 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/FormulaChannelTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/FormulaChannelTest.java @@ -5,6 +5,7 @@ import models.algebra.Symbol; import models.dataConstraintModel.ChannelMember; import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.ResourceHierarchy; import models.dataConstraintModel.ResourcePath; import models.visualModel.FormulaChannel; @@ -12,23 +13,23 @@ @Test public void test() { - ResourcePath id1 = new ResourcePath("r1", 0); - ResourcePath id2 = new ResourcePath("r2", 0); - ResourcePath id3 = new ResourcePath("r3", 0); + ResourcePath res1 = new ResourcePath("r1"); + ResourcePath res2 = new ResourcePath("r2"); + ResourcePath res3 = new ResourcePath("r3"); FormulaChannel ch1 = new FormulaChannel("ch1", DataConstraintModel.add); System.out.println(ch1.getFormula()); System.out.println(ch1.getFormulaTerm()); System.out.println(ch1.getSourceText()); - ch1.addChannelMemberAsInput(new ChannelMember(id1)); + ch1.addChannelMemberAsInput(new ChannelMember(res1)); System.out.println(ch1.getFormula()); System.out.println(ch1.getFormulaTerm()); System.out.println(ch1.getSourceText()); - ch1.addChannelMemberAsInput(new ChannelMember(id2)); + ch1.addChannelMemberAsInput(new ChannelMember(res2)); System.out.println(ch1.getFormula()); System.out.println(ch1.getFormulaTerm()); System.out.println(ch1.getSourceText()); - ch1.addChannelMemberAsOutput(new ChannelMember(id3)); + ch1.addChannelMemberAsOutput(new ChannelMember(res3)); System.out.println(ch1.getFormula()); System.out.println(ch1.getFormulaTerm()); System.out.println(ch1.getSourceText()); @@ -37,15 +38,15 @@ System.out.println(ch2.getFormula()); System.out.println(ch2.getFormulaTerm()); System.out.println(ch2.getSourceText()); - ch2.addChannelMemberAsOutput(new ChannelMember(id3)); + ch2.addChannelMemberAsOutput(new ChannelMember(res3)); System.out.println(ch2.getFormula()); System.out.println(ch2.getFormulaTerm()); System.out.println(ch2.getSourceText()); - ch2.addChannelMemberAsInput(new ChannelMember(id1)); + ch2.addChannelMemberAsInput(new ChannelMember(res1)); System.out.println(ch2.getFormula()); System.out.println(ch2.getFormulaTerm()); System.out.println(ch2.getSourceText()); - ch2.addChannelMemberAsInput(new ChannelMember(id2)); + ch2.addChannelMemberAsInput(new ChannelMember(res2)); System.out.println(ch2.getFormula()); System.out.println(ch2.getFormulaTerm()); System.out.println(ch2.getSourceText()); diff --git a/AlgebraicDataflowArchitectureModel/src/tests/InverseTest.java b/AlgebraicDataflowArchitectureModel/src/tests/InverseTest.java index bfa8957..0d0efe1 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/InverseTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/InverseTest.java @@ -14,18 +14,24 @@ import models.algebra.Expression; import models.algebra.Position; +import models.algebra.Term; import models.algebra.Variable; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.JsonType; import models.dataFlowModel.DataTransferModel; import parser.Parser; import parser.Parser.TokenStream; +import parser.exceptions.ExpectedColon; +import parser.exceptions.ExpectedDoubleQuotation; import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.WrongJsonExpression; public class InverseTest { @Test public void test() { - String lhs = "y"; DataTransferModel model = new DataTransferModel(); try { + String lhs = "y"; String rhs = "(a * x + b) * c"; TokenStream stream = new Parser.TokenStream(); @@ -33,7 +39,7 @@ stream.addLine(rhs); Expression rhsExp = parser.parseTerm(stream, model); - System.out.println(lhs + " = " + rhsExp); + System.out.println("=== solve{" + lhs + " = " + rhsExp + "} for a, b, d, x ==="); HashMap rhsVars = rhsExp.getVariables(); assertEquals(4, rhsVars.size()); @@ -47,7 +53,66 @@ assertFalse(inv.contains(v)); System.out.println(rhsVars.get(vPos) + " = " + inv); } - } catch (ExpectedRightBracket e) { + + // Extract an element in a tuple + TokenStream stream2 = new Parser.TokenStream(); + Parser parser2 = new Parser(stream2); + stream2.addLine("fst(tuple(x, y))"); + Expression tupleExp = parser2.parseTerm(stream2, model); + stream2.addLine("snd(tuple(x, y))"); + Expression tupleExp2 = parser2.parseTerm(stream2, model); + Expression reduced = ((Term) tupleExp).reduce(); + Expression reduced2 = ((Term) tupleExp2).reduce(); + Variable x = new Variable("x"); + assertEquals(reduced, x); + assertEquals(reduced2, y); + System.out.println("=== simplify ==="); + System.out.println(tupleExp + " = " + reduced); + System.out.println(tupleExp2 + " = " + reduced2); + + // Solve {z = fst(x)} for x + TokenStream stream3 = new Parser.TokenStream(); + Parser parser3 = new Parser(stream3); + stream3.addLine("fst(x)"); + Expression rhsExp3 = parser3.parseTerm(stream3, model); + Variable z = new Variable("z"); + System.out.println("=== solve{" + z + " = " + rhsExp3 + "} for x ==="); + HashMap rhsVars3 = rhsExp3.getVariables(); + for (Position vPos: rhsVars3.keySet()) { + Variable v = rhsVars3.get(vPos); + Expression inv = rhsExp3.getInverseMap(z, vPos); // inverse map to get v back from the output value z + if (inv instanceof Term) { + inv = ((Term) inv).reduce(); + } + assertTrue(inv.contains(z)); + assertFalse(inv.contains(v)); + System.out.println(rhsVars3.get(vPos) + " = " + inv); + } + + // Solve {z = x.id} for x + TokenStream stream4 = new Parser.TokenStream(); + Parser parser4 = new Parser(stream4); + stream4.addLine("x.id"); + Expression rhsExp4 = parser4.parseTerm(stream4, model); + System.out.println("=== solve{" + z + " = " + rhsExp4 + "} for x ==="); + HashMap rhsVars4 = rhsExp4.getVariables(); + for (Position vPos: rhsVars4.keySet()) { + Variable v = rhsVars4.get(vPos); + if (x.getName().equals("x")) { + JsonType jsonType = new JsonType("Json", "HashMap<>", DataConstraintModel.typeJson); + jsonType.addMemberType("id", DataConstraintModel.typeInt); + jsonType.addMemberType("name", DataConstraintModel.typeString); + v.setType(jsonType); + } + Expression inv = rhsExp4.getInverseMap(z, vPos); // inverse map to get v back from the output value z + if (inv instanceof Term) { + inv = ((Term) inv).reduce(); + } + System.out.println(rhsVars4.get(vPos) + " = " + inv); + assertTrue(inv.contains(z)); + assertFalse(inv.contains(v)); + } + } catch (ExpectedRightBracket | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { e.printStackTrace(); } } diff --git a/AlgebraicDataflowArchitectureModel/src/tests/NativeAccessTest.java b/AlgebraicDataflowArchitectureModel/src/tests/NativeAccessTest.java new file mode 100644 index 0000000..a91d089 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/tests/NativeAccessTest.java @@ -0,0 +1,102 @@ +package tests; + +import org.junit.Test; + +import algorithms.TypeInference; +import models.algebra.Expression; +import models.algebra.InvalidMessage; +import models.algebra.ParameterizedIdentifierIsFutureWork; +import models.algebra.UnificationFailed; +import models.algebra.ValueUndefined; +import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.ResourcePath; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork; +import parser.Parser; +import parser.Parser.TokenStream; +import parser.exceptions.ExpectedColon; +import parser.exceptions.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..099c6a6 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/SimplifiedDataFlowModelTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/SimplifiedDataFlowModelTest.java @@ -14,10 +14,14 @@ public void test() { // Construct a data-flow architecture model. DataTransferModel model = new DataTransferModel(); - ResourcePath payment = new ResourcePath("payment", 0); // a resource to specify the payment resource - ResourcePath loyalty = new ResourcePath("loyalty", 0); // a resource to specify the loyalty resource - ResourcePath history = new ResourcePath("history", 0); // a resource to specify the payment history resource - ResourcePath total = new ResourcePath("total", 0); // a resource to specify the total payment resource + ResourcePath payment = new ResourcePath("payment"); // a resource to specify the payment resource + ResourcePath points = new ResourcePath("points"); // a resource to specify the points resource + ResourcePath history = new ResourcePath("history"); // a resource to specify the payment history resource + ResourcePath total = new ResourcePath("total"); // a resource to specify the total payment resource + model.addResourcePath(payment); + model.addResourcePath(points); + model.addResourcePath(history); + model.addResourcePath(total); // === cin === // @@ -31,11 +35,11 @@ // === c1 === // // payment(p1, update1(y)) == y - // loyalty(l, update1(y)) == floor(y * 0.05) + // points(l, update1(y)) == floor(y * 0.05) // DataTransferChannel c1 = new DataTransferChannel("c1"); ChannelMember c1_payment = new ChannelMember(payment); - ChannelMember c1_loyalty = new ChannelMember(loyalty); + ChannelMember c1_loyalty = new ChannelMember(points); c1.addChannelMemberAsInput(c1_payment); c1.addChannelMemberAsOutput(c1_loyalty); assertEquals(c1.getChannelMembers().size(), 2); @@ -71,22 +75,23 @@ assertEquals(c3.getOutputChannelMembers().size(), 1); // Construct a data-flow architecture model. - model.addIOChannel(cin); + model.addInputChannel(cin); model.addChannel(c1); model.addChannel(c2); model.addChannel(c3); // Check the model. assertEquals(4, model.getResourcePaths().size()); - assertEquals(1, model.getIOChannels().size()); + assertEquals(1, model.getInputChannels().size()); assertEquals(3, model.getChannels().size()); // Extract the resource dependency graph. DataFlowGraph resourceDependencyGraph = model.getDataFlowGraph(); // Check the graph. - assertEquals(4, resourceDependencyGraph.getNodes().size()); - assertEquals(3, resourceDependencyGraph.getEdges().size()); + assertEquals(4, resourceDependencyGraph.getResourceNodes().size()); + assertEquals(4, resourceDependencyGraph.getChannelNodes().size()); + assertEquals(6, resourceDependencyGraph.getEdges().size()); for (Edge e: resourceDependencyGraph.getEdges()) { System.out.println(e.getSource() + "-(" + e + ")->" + e.getDestination()); } diff --git a/AlgebraicDataflowArchitectureModel/src/tests/SimulatorTest.java b/AlgebraicDataflowArchitectureModel/src/tests/SimulatorTest.java new file mode 100644 index 0000000..a6c2817 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/tests/SimulatorTest.java @@ -0,0 +1,227 @@ +package tests; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import models.algebra.Expression; +import models.algebra.InvalidMessage; +import models.algebra.ParameterizedIdentifierIsFutureWork; +import models.algebra.UnificationFailed; +import models.algebra.ValueUndefined; +import models.algebra.Variable; +import models.algebra.Constant; +import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.ResourcePath; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork; +import parser.Parser; +import parser.Parser.TokenStream; +import parser.exceptions.ExpectedColon; +import parser.exceptions.ExpectedDoubleQuotation; +import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.WrongJsonExpression; +import simulator.Event; +import simulator.Resource; +import simulator.Simulator; +import simulator.SystemState; +import simulator.ResourceIdentifier; +import simulator.states.MapResourceState; + +public class SimulatorTest { + @Test + public void test() { + try { + TokenStream stream = new Parser.TokenStream(); + Parser parser = new Parser(stream); + + // Construct a data transfer architecture model. + DataTransferModel model = new DataTransferModel(); + + ResourcePath customers = new ResourcePath("customers"); // "customers" + ResourcePath customer = new ResourcePath(customers, + new Variable("uid", DataConstraintModel.typeString)); // "customers.{uid}" + ResourcePath customer_off = new ResourcePath(customer, "off"); // "customers.{uid}.off" + ResourcePath customer_add = new ResourcePath(customer, "add"); // "customers.{uid}.add" + ResourcePath companies = new ResourcePath("companies"); // "companies" + ResourcePath company = new ResourcePath(companies, + new Variable("cid", DataConstraintModel.typeString)); // "companies.{cid}" + ResourcePath company_add = new ResourcePath(company, "add"); // "companies.{cid}.add" + ResourcePath company2 = new ResourcePath(companies, + new Variable("cid2", DataConstraintModel.typeString)); // "companies.{cid2}" + ResourcePath company2_add = new ResourcePath(company2, "add"); // "companies.{cid2}.add" + ResourcePath companyA = new ResourcePath(companies, + new Constant("A", DataConstraintModel.typeString)); // "companies.A" + ResourcePath companyA_add = new ResourcePath(companyA, "add"); // "companies.{cid2}.add" + model.addResourcePath(customer_off); + model.addResourcePath(customer_add); + model.addResourcePath(company_add); + model.addResourcePath(company2_add); + model.addResourcePath(customers); + model.addResourcePath(companies); + + // For channel CIO_AddCustomer + DataTransferChannel cio_addCustomer = new DataTransferChannel("CIO_AddCustomer"); // add a customer + ChannelMember customers1 = new ChannelMember(customers); + stream.addLine("db:Map"); + Expression curStateExp0 = parser.parseTerm(stream, model); + customers1.getStateTransition().setCurStateExpression(curStateExp0); + stream.addLine("addCustomer(uid:Str, off:Str)"); + Expression messageExp0 = parser.parseTerm(stream, model); + customers1.getStateTransition().setMessageExpression(messageExp0); + stream.addLine("insert(db, uid, {\"off\": off})"); + Expression nextStateExp0 = parser.parseTerm(stream, model); + customers1.getStateTransition().setNextStateExpression(nextStateExp0); + cio_addCustomer.addChannelMemberAsOutput(customers1); + + // For channel CIO_AddCompany + DataTransferChannel cio_addCompany = new DataTransferChannel("CIO_AddCompany"); // add a company + ChannelMember companies1 = new ChannelMember(companies); + stream.addLine("db:Map"); + Expression curStateExp1 = parser.parseTerm(stream, model); + companies1.getStateTransition().setCurStateExpression(curStateExp1); + stream.addLine("addCampany(cid:Str, add:Str)"); + Expression messageExp1 = parser.parseTerm(stream, model); + companies1.getStateTransition().setMessageExpression(messageExp1); + stream.addLine("insert(db, cid, {\"add\": add})"); + Expression nextStateExp1 = parser.parseTerm(stream, model); + companies1.getStateTransition().setNextStateExpression(nextStateExp1); + cio_addCompany.addChannelMemberAsOutput(companies1); + + // For channel CIO_SetCustomerOff + DataTransferChannel cio_setCustomerOff = new DataTransferChannel("CIO_SetCustomerOff", new Variable("uid")); // set customer's office + ChannelMember customer_off_1 = new ChannelMember(customer_off); + stream.addLine("cid:Str"); + Expression curStateExp2 = parser.parseTerm(stream, model); + customer_off_1.getStateTransition().setCurStateExpression(curStateExp2); + stream.addLine("setOff(cid2:Str)"); + Expression messageExp2 = parser.parseTerm(stream, model); + customer_off_1.getStateTransition().setMessageExpression(messageExp2); + stream.addLine("cid2"); + Expression nextStateExp2 = parser.parseTerm(stream, model); + customer_off_1.getStateTransition().setNextStateExpression(nextStateExp2); + cio_setCustomerOff.addChannelMemberAsOutput(customer_off_1); + + // For channel CIO_SetCompanyAdd + DataTransferChannel cio_setCompanyAdd = new DataTransferChannel("CIO_SetCompanyAdd", new Variable("cid")); // set companie's address + ChannelMember company_add_1 = new ChannelMember(company_add); + stream.addLine("a1:Str"); + Expression curStateExp3 = parser.parseTerm(stream, model); + company_add_1.getStateTransition().setCurStateExpression(curStateExp3); + stream.addLine("setAdd(a2:Str)"); + Expression messageExp3 = parser.parseTerm(stream, model); + company_add_1.getStateTransition().setMessageExpression(messageExp3); + stream.addLine("a2"); + Expression nextStateExp3 = parser.parseTerm(stream, model); + company_add_1.getStateTransition().setNextStateExpression(nextStateExp3); + cio_setCompanyAdd.addChannelMemberAsOutput(company_add_1); + + // For channel C + DataTransferChannel c = new DataTransferChannel("C", new Variable("uid")); // update customer's address + ChannelMember customer_off_2 = new ChannelMember(customer_off); + stream.addLine("cid:Str"); + Expression curStateExp4 = parser.parseTerm(stream, model); + customer_off_2.getStateTransition().setCurStateExpression(curStateExp4); + stream.addLine("sync(cid2:Str, add2:Str)"); + Expression messageExp4 = parser.parseTerm(stream, model); + customer_off_2.getStateTransition().setMessageExpression(messageExp4); + stream.addLine("cid2"); + Expression nextStateExp4 = parser.parseTerm(stream, model); + customer_off_2.getStateTransition().setNextStateExpression(nextStateExp4); + c.addChannelMemberAsInput(customer_off_2); + + ChannelMember company_add_2 = new ChannelMember(company2_add); + stream.addLine("a1:Str"); + Expression curStateExp5 = parser.parseTerm(stream, model); + company_add_2.getStateTransition().setCurStateExpression(curStateExp5); + stream.addLine("sync(cid2:Str, add2:Str)"); + Expression messageExp5 = parser.parseTerm(stream, model); + company_add_2.getStateTransition().setMessageExpression(messageExp5); + stream.addLine("add2"); + Expression nextStateExp5 = parser.parseTerm(stream, model); + company_add_2.getStateTransition().setNextStateExpression(nextStateExp5); + c.addChannelMemberAsInput(company_add_2); + + ChannelMember customer_add_2 = new ChannelMember(customer_add); + stream.addLine("a3:Str"); + Expression curStateExp6 = parser.parseTerm(stream, model); + customer_add_2.getStateTransition().setCurStateExpression(curStateExp6); + stream.addLine("sync(cid2:Str, add2:Str)"); + Expression messageExp6 = parser.parseTerm(stream, model); + customer_add_2.getStateTransition().setMessageExpression(messageExp6); + stream.addLine("add2"); + Expression nextStateExp6 = parser.parseTerm(stream, model); + customer_add_2.getStateTransition().setNextStateExpression(nextStateExp6); + c.addChannelMemberAsOutput(customer_add_2); + + model.addInputChannel(cio_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/UpdateCodeGenerationTest.java b/AlgebraicDataflowArchitectureModel/src/tests/UpdateCodeGenerationTest.java index 69a5b1f..bc1db89 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/UpdateCodeGenerationTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/UpdateCodeGenerationTest.java @@ -25,92 +25,110 @@ Symbol sum = new Symbol("sum", 1, Symbol.Type.PREFIX, "stream().mapToInt(x->x).sum", Symbol.Type.METHOD); // resources - ResourcePath payment = new ResourcePath("payment", DataConstraintModel.typeInt, 0); // a resource to specify the payment resource - ResourcePath loyalty = new ResourcePath("loyalty", DataConstraintModel.typeInt, 0); // a resource to specify the loyalty resource - ResourcePath history = new ResourcePath("history", DataConstraintModel.typeList, 0);// a resource to specify the payment history resource - ResourcePath total = new ResourcePath("total", DataConstraintModel.typeInt, 0); // a resource to specify the total payment resource + ResourcePath payment = new ResourcePath("payment"); // a resource to specify the payment resource + ResourcePath points = new ResourcePath("points"); // a resource to specify the loyalty resource + ResourcePath history = new ResourcePath("history"); // a resource to specify the payment history resource + ResourcePath total = new ResourcePath("total"); // a resource to specify the total payment resource + payment.setResourceStateType(DataConstraintModel.typeInt); + points.setResourceStateType(DataConstraintModel.typeInt); + history.setResourceStateType(DataConstraintModel.typeInt); + total.setResourceStateType(DataConstraintModel.typeInt); // fields in the Java program final Field fPayment = new Field("payment", DataConstraintModel.typeInt); - final Field fLoyalty = new Field("loyalty", DataConstraintModel.typeInt); + final Field fPoints = new Field("points", DataConstraintModel.typeInt); final Field fHistory = new Field("history", DataConstraintModel.typeList); final Field fTotal = new Field("total", DataConstraintModel.typeInt); // parameters in the Java program final Parameter pPayment = new Parameter("payment", DataConstraintModel.typeInt); - final Parameter pLoyalty = new Parameter("loyalty", DataConstraintModel.typeInt); + final Parameter pPoints = new Parameter("points", DataConstraintModel.typeInt); final Parameter pHistory = new Parameter("history", DataConstraintModel.typeList); final Parameter pTotal = new Parameter("total", DataConstraintModel.typeInt); IResourceStateAccessor pushAccessor = new IResourceStateAccessor() { @Override - public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) { - if (target.equals(from)) { - if (target.equals(payment)) return fPayment; - if (target.equals(loyalty)) return fLoyalty; - if (target.equals(history)) return fHistory; - if (target.equals(total)) return fTotal; + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + ResourcePath fromRes = from.getResource(); + if (targetRes.equals(fromRes)) { + if (targetRes.equals(payment)) return fPayment; + if (targetRes.equals(points)) return fPoints; + if (targetRes.equals(history)) return fHistory; + if (targetRes.equals(total)) return fTotal; } return null; } @Override - public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { - if (target.equals(payment)) return pPayment; - if (target.equals(loyalty)) return pLoyalty; - if (target.equals(history)) return pHistory; - if (target.equals(total)) return pTotal; + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + if (targetRes.equals(payment)) return pPayment; + if (targetRes.equals(points)) return pPoints; + if (targetRes.equals(history)) return pHistory; + if (targetRes.equals(total)) return pTotal; + return null; + } + @Override + public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { return null; } }; // methods in the Java program final Symbol paymentGetter = new Symbol("getPayment", 1, Symbol.Type.METHOD); - final Symbol loyltyGetter = new Symbol("getLoyalty", 1, Symbol.Type.METHOD); + final Symbol pointsGetter = new Symbol("getPoints", 1, Symbol.Type.METHOD); final Symbol historyGetter = new Symbol("getHistory", 1, Symbol.Type.METHOD); final Symbol totalGetter = new Symbol("getTotal", 1, Symbol.Type.METHOD); IResourceStateAccessor pullAccessor = new IResourceStateAccessor() { @Override - public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) { - if (target.equals(from)) { - if (target.equals(payment)) return fPayment; - if (target.equals(loyalty)) return fLoyalty; - if (target.equals(history)) return fHistory; - if (target.equals(total)) return fTotal; + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + ResourcePath fromRes = from.getResource(); + if (targetRes.equals(fromRes)) { + if (targetRes.equals(payment)) return fPayment; + if (targetRes.equals(points)) return fPoints; + if (targetRes.equals(history)) return fHistory; + if (targetRes.equals(total)) return fTotal; } return null; } @Override - public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { - if (target.equals(payment)) { + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + if (targetRes.equals(payment)) { Term getter = new Term(paymentGetter); getter.addChild(fPayment); return getter; } - if (target.equals(loyalty)) { - Term getter = new Term(loyltyGetter); - getter.addChild(fLoyalty); + if (targetRes.equals(points)) { + Term getter = new Term(pointsGetter); + getter.addChild(fPoints); return getter; } - if (target.equals(history)) { + if (targetRes.equals(history)) { Term getter = new Term(historyGetter); getter.addChild(fHistory); return getter; } - if (target.equals(total)) { + if (targetRes.equals(total)) { Term getter = new Term(totalGetter); getter.addChild(fTotal); return getter; } return null; + } + @Override + public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { + return null; } }; // === c1 === // - // payment(p1, update1(y)) == y - // loyalty(l, update1(y)) == floor(y * 0.05) + // payment(p1, update1(y)) = y + // points(l, update1(y)) = floor(y * 0.05) // DataTransferChannel c1 = new DataTransferChannel("c1"); ChannelMember c1_payment = new ChannelMember(payment); - ChannelMember c1_loyalty = new ChannelMember(loyalty); + ChannelMember c1_loyalty = new ChannelMember(points); c1.addChannelMemberAsInput(c1_payment); c1.addChannelMemberAsOutput(c1_loyalty); @@ -121,11 +139,11 @@ Symbol update1 = new Symbol("update1", 1); Term c1_message = new Term(update1); // update1(y) c1_message.addChild(y); - Term rawLoyality = new Term(DataConstraintModel.mul); // y*0.05 - rawLoyality.addChild(y); - rawLoyality.addChild(c_0_05); - Term nextLoyality = new Term(floor); // floor(y*0.05) - nextLoyality.addChild(rawLoyality); + Term rawPoints = new Term(DataConstraintModel.mul); // y*0.05 + rawPoints.addChild(y); + rawPoints.addChild(c_0_05); + Term nextPoints = new Term(floor); // floor(y*0.05) + nextPoints.addChild(rawPoints); StateTransition c1_payment_transition = new StateTransition(); c1_payment_transition.setCurStateExpression(p1); @@ -133,11 +151,11 @@ c1_payment_transition.setNextStateExpression(y); c1_payment.setStateTransition(c1_payment_transition); - StateTransition c1_loyalty_transition = new StateTransition(); - c1_loyalty_transition.setCurStateExpression(l); - c1_loyalty_transition.setMessageExpression(c1_message); - c1_loyalty_transition.setNextStateExpression(nextLoyality); - c1_loyalty.setStateTransition(c1_loyalty_transition); + StateTransition c1_points_transition = new StateTransition(); + c1_points_transition.setCurStateExpression(l); + c1_points_transition.setMessageExpression(c1_message); + c1_points_transition.setNextStateExpression(nextPoints); + c1_loyalty.setStateTransition(c1_points_transition); System.out.println(c1); @@ -147,21 +165,21 @@ System.out.println(c1.deriveUpdateExpressionOf(c1_loyalty).toImplementation(sideEffects)); System.out.println("-- PUSH --"); - Expression loyaltyPushUpdate = c1.deriveUpdateExpressionOf(c1_loyalty, pushAccessor); + Expression loyaltyPushUpdate = c1.deriveUpdateExpressionOf(c1_loyalty, pushAccessor).getKey(); Parameter param = null; for (Parameter p: loyaltyPushUpdate.getSubTerms(Parameter.class).values()) { - if (p.equals(pPayment) || p.equals(pLoyalty) || p.equals(pHistory) || p.equals(pTotal)) { + if (p.equals(pPayment) || p.equals(pPoints) || p.equals(pHistory) || p.equals(pTotal)) { param = p; break; } } System.out.println("void update(" + param.getType().getImplementationTypeName() + " " + param.toImplementation(sideEffects) + ") {"); - System.out.println("\t" + fLoyalty + " = " + loyaltyPushUpdate.toImplementation(sideEffects) + ";"); + System.out.println("\t" + fPoints + " = " + loyaltyPushUpdate.toImplementation(sideEffects) + ";"); System.out.println("}"); System.out.println("-- PULL --"); - System.out.println(loyalty.getResourceStateType().getImplementationTypeName() + " " + loyltyGetter.toImplementation() + "() {"); - System.out.println("\t return " + c1.deriveUpdateExpressionOf(c1_loyalty, pullAccessor).toImplementation(sideEffects) + ";"); + System.out.println(points.getResourceStateType().getImplementationTypeName() + " " + pointsGetter.toImplementation() + "() {"); + System.out.println("\t return " + c1.deriveUpdateExpressionOf(c1_loyalty, pullAccessor).getKey().toImplementation(sideEffects) + ";"); System.out.println("}"); } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage | UnificationFailed | ValueUndefined e) { @@ -172,8 +190,8 @@ // === c2 === // - // payment(p1, update2(z)) == z - // history(h, update2(z)) == cons(z, h) + // payment(p1, update2(z)) = z + // history(h, update2(z)) = cons(z, h) // DataTransferChannel c2 = new DataTransferChannel("c2"); ChannelMember c2_payment = new ChannelMember(payment); @@ -210,10 +228,10 @@ System.out.println(c2.deriveUpdateExpressionOf(c2_history).toImplementation(sideEffects)); System.out.println("-- PUSH --"); - Expression historyPushUpdate = c2.deriveUpdateExpressionOf(c2_history, pushAccessor); + Expression historyPushUpdate = c2.deriveUpdateExpressionOf(c2_history, pushAccessor).getKey(); Parameter param = null; for (Parameter p: historyPushUpdate.getSubTerms(Parameter.class).values()) { - if (p.equals(pPayment) || p.equals(pLoyalty) || p.equals(pHistory) || p.equals(pTotal)) { + if (p.equals(pPayment) || p.equals(pPoints) || p.equals(pHistory) || p.equals(pTotal)) { param = p; break; } @@ -224,7 +242,7 @@ System.out.println("-- PULL --"); System.out.println(history.getResourceStateType().getImplementationTypeName() + " " + historyGetter.toImplementation() + "() {"); - System.out.println("\t return " + c2.deriveUpdateExpressionOf(c2_history, pullAccessor).toImplementation(sideEffects) + ";"); + System.out.println("\t return " + c2.deriveUpdateExpressionOf(c2_history, pullAccessor).getKey().toImplementation(sideEffects) + ";"); System.out.println("}"); } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage | UnificationFailed | ValueUndefined e) { @@ -273,10 +291,10 @@ System.out.println(c3.deriveUpdateExpressionOf(c3_total).toImplementation(sideEffects)); System.out.println("-- PUSH --"); - Expression totalPushUpdate = c3.deriveUpdateExpressionOf(c3_total, pushAccessor); + Expression totalPushUpdate = c3.deriveUpdateExpressionOf(c3_total, pushAccessor).getKey(); Parameter param = null; for (Parameter p: totalPushUpdate.getSubTerms(Parameter.class).values()) { - if (p.equals(pPayment) || p.equals(pLoyalty) || p.equals(pHistory) || p.equals(pTotal)) { + if (p.equals(pPayment) || p.equals(pPoints) || p.equals(pHistory) || p.equals(pTotal)) { param = p; break; } @@ -287,7 +305,7 @@ System.out.println("-- PULL --"); System.out.println(total.getResourceStateType().getImplementationTypeName() + " " + totalGetter.toImplementation() + "() {"); - System.out.println("\t return " + c3.deriveUpdateExpressionOf(c3_total, pullAccessor).toImplementation(sideEffects) + ";"); + System.out.println("\t return " + c3.deriveUpdateExpressionOf(c3_total, pullAccessor).getKey().toImplementation(sideEffects) + ";"); System.out.println("}"); } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage | UnificationFailed | ValueUndefined e) { diff --git a/AlgebraicDataflowArchitectureModel/src/tests/UpdateConflictCheckTest.java b/AlgebraicDataflowArchitectureModel/src/tests/UpdateConflictCheckTest.java index 5f51af7..de362c9 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/UpdateConflictCheckTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/UpdateConflictCheckTest.java @@ -11,13 +11,18 @@ import parser.exceptions.ExpectedAssignment; import parser.exceptions.ExpectedChannel; import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; +import parser.exceptions.ExpectedDoubleQuotation; import parser.exceptions.ExpectedEquals; -import parser.exceptions.ExpectedInOrOutOrRefKeyword; +import parser.exceptions.ExpectedInOrOutOrRefOrSubKeyword; import parser.exceptions.ExpectedLeftCurlyBracket; import parser.exceptions.ExpectedRHSExpression; import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.ExpectedRightCurlyBracket; import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; import parser.exceptions.WrongLHSExpression; +import parser.exceptions.WrongPathExpression; import parser.exceptions.WrongRHSExpression; public class UpdateConflictCheckTest { @@ -25,41 +30,14 @@ File file = new File("models/POS2.model"); try { Parser parser = new Parser(new BufferedReader(new FileReader(file))); + DataTransferModel model; try { - DataTransferModel model = parser.doParse(); + model = parser.doParse(); System.out.println(Validation.checkUpdateConflict(model)); - } catch (ExpectedRightBracket e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (ExpectedChannel e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (ExpectedChannelName e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (ExpectedLeftCurlyBracket e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (ExpectedInOrOutOrRefKeyword e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (ExpectedStateTransition e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (ExpectedEquals e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (ExpectedRHSExpression e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (WrongLHSExpression e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (WrongRHSExpression e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (ExpectedAssignment e) { - // TODO Auto-generated catch block + } catch (ExpectedRightBracket | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket + | ExpectedRightCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword | ExpectedStateTransition | ExpectedEquals + | ExpectedRHSExpression | WrongLHSExpression | WrongRHSExpression | ExpectedAssignment + | WrongPathExpression | WrongJsonExpression | ExpectedColon | 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..8c5eae4 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/parser/ParseTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/parser/ParseTest.java @@ -17,13 +17,18 @@ import parser.exceptions.ExpectedAssignment; import parser.exceptions.ExpectedChannel; import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; +import parser.exceptions.ExpectedDoubleQuotation; import parser.exceptions.ExpectedEquals; -import parser.exceptions.ExpectedInOrOutOrRefKeyword; +import parser.exceptions.ExpectedInOrOutOrRefOrSubKeyword; import parser.exceptions.ExpectedLeftCurlyBracket; import parser.exceptions.ExpectedRHSExpression; import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.ExpectedRightCurlyBracket; import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; import parser.exceptions.WrongLHSExpression; +import parser.exceptions.WrongPathExpression; import parser.exceptions.WrongRHSExpression; public class ParseTest { @@ -40,7 +45,7 @@ for (Channel c: model.getChannels()) { for (ChannelMember out: ((DataTransferChannel) c).getOutputChannelMembers()) { String[] sideEffects = new String[] {""}; - System.out.println("next" + out.getResource().getResourceName() + " = " + ((DataTransferChannel) c).deriveUpdateExpressionOf(out).toImplementation(sideEffects)); + System.out.println("next" + out.getResource().getLeafResourceName() + " = " + ((DataTransferChannel) c).deriveUpdateExpressionOf(out).toImplementation(sideEffects)); } } @@ -50,11 +55,11 @@ for (Edge e: resourceDependencyGraph.getEdges()) { System.out.println(e.getSource() + "-(" + e + ")->" + e.getDestination()); } - } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefKeyword + } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression | WrongRHSExpression | ExpectedRightBracket | ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage - | UnificationFailed | ValueUndefined | ExpectedAssignment e) { + | UnificationFailed | ValueUndefined | ExpectedAssignment | ExpectedRightCurlyBracket | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { e.printStackTrace(); } } catch (FileNotFoundException e) { diff --git a/GameEngine.iml b/GameEngine.iml new file mode 100644 index 0000000..af8fa66 --- /dev/null +++ b/GameEngine.iml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/GameEngine/pom.xml b/GameEngine/pom.xml new file mode 100644 index 0000000..4d99d29 --- /dev/null +++ b/GameEngine/pom.xml @@ -0,0 +1,100 @@ + + + 4.0.0 + + org.example + GameEngine + 1.0-SNAPSHOT + + + 21 + 21 + UTF-8 + 3.3.4 + 1.10.7 + natives-windows + + + + + + org.lwjgl + lwjgl-bom + ${lwjgl.version} + import + pom + + + + + + + org.lwjgl + lwjgl + + + org.lwjgl + lwjgl-assimp + + + org.lwjgl + lwjgl-glfw + + + org.lwjgl + lwjgl-openal + + + org.lwjgl + lwjgl-opengl + + + org.lwjgl + lwjgl-stb + + + org.lwjgl + lwjgl + ${lwjgl.natives} + + + org.lwjgl + lwjgl-assimp + ${lwjgl.natives} + + + org.lwjgl + lwjgl-glfw + ${lwjgl.natives} + + + org.lwjgl + lwjgl-openal + ${lwjgl.natives} + + + org.lwjgl + lwjgl-opengl + ${lwjgl.natives} + + + org.lwjgl + lwjgl-stb + ${lwjgl.natives} + + + org.joml + joml + ${joml.version} + + + org.jetbrains + annotations + RELEASE + compile + + + + \ No newline at end of file diff --git a/GameEngine/resources/CameraView.png b/GameEngine/resources/CameraView.png new file mode 100644 index 0000000..1201f49 --- /dev/null +++ b/GameEngine/resources/CameraView.png Binary files differ diff --git a/GameEngine/resources/ComponentView.png b/GameEngine/resources/ComponentView.png new file mode 100644 index 0000000..124ec17 --- /dev/null +++ b/GameEngine/resources/ComponentView.png Binary files differ diff --git a/GameEngine/resources/EditorFrame.png b/GameEngine/resources/EditorFrame.png new file mode 100644 index 0000000..daa5955 --- /dev/null +++ b/GameEngine/resources/EditorFrame.png Binary files differ diff --git a/GameEngine/resources/EntityView.png b/GameEngine/resources/EntityView.png new file mode 100644 index 0000000..bc09cef --- /dev/null +++ b/GameEngine/resources/EntityView.png Binary files differ diff --git a/GameEngine/resources/GameEngineTest.model b/GameEngine/resources/GameEngineTest.model new file mode 100644 index 0000000..aeb6c4c --- /dev/null +++ b/GameEngine/resources/GameEngineTest.model @@ -0,0 +1,163 @@ +init { +scene := { + "entities": { + "1": { + "eid": "1", + "transform": { + "rotation": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "scale": { + "x": 1.0, + "y": 1.0, + "z": 1.0 + }, + "position": { + "x": 718.0, + "y": 442.0, + "z": 0.0 + } + }, + "move": { + "type": "straight", + "speed": 5.0 + }, + "mesh": { + "sprite": "GameEngine/resources/enemy1.png", + "type": "sprite" + } + } + } +} +player := { + "transform": { + "rotation": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "scale": { + "x": 1.0, + "y": 1.0, + "z": 1.0 + }, + "position": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + } + }, + "mesh": { + "sprite": "GameEngine/resources/enemy1.png", + "type": "sprite" + } +} +enemy1 := { + "eid": "1", + "transform": { + "rotation": { + "x": 0.0, + "y": 0.0, + "z": 0.0 + }, + "scale": { + "x": 1.0, + "y": 1.0, + "z": 1.0 + }, + "position": { + "x": 718.0, + "y": 442.0, + "z": 0.0 + } + }, + "move": { + "type": "straight", + "speed": 5.0 + }, + "mesh": { + "sprite": "GameEngine/resources/enemy1.png", + "type": "sprite" + } +} +} +native channel KeyEvent(kno:Int) { + out scene.keys.{kno:Int}.state(curState:Int,keyEvent(nextState)) = nextState +} +native channel SceneUpdateEvent { + out scene.time(time:Long,updateEvent(dt:Long)) = (time+dt) +} +native channel TimerEvent(tid:Str) { + out timers.{tid:Str}.count(count:Long,tick()) = (count+1) +} +channel ClearTimer { + out timers(timers:Map,clearTimer(tid:Str)) = delete(timers,tid) +} +channel StartTimer { + out timers(timers:Map,startTimer(tid:Str,interval:Long)) = insert(timers,tid,{ + "count": 0, + "interval": interval +}) +} +channel UpdateCameraPosition { + out scene.camera.transform.position(curPos:Json,updateCameraPosition(x:Double,y:Double,z:Double)) = { + "x": x, + "y": y, + "z": z +} +} +native channel EntityPositionUpdate(eid:Str) { + in scene.entities.{eid:Str}.transform.position(curPos:Json,updatePosition(nextPos.x,nextPos.y,nextPos.z)) = nextPos +} +channel MoveEnemy1 { + in enemy1.transform.position(curPos:Json,moveEnemy1(nextPos,eid)) = nextPos + ref enemy1.eid(eid,moveEnemy1(nextPos,eid)) + out scene.entities.{eid:Str}.transform.position(curPos:Json,moveEnemy1(nextPos,eid)) = nextPos +} +native channel CameraProjectionUpdate { + in scene.camera.projection(curProj:Json,updateProjection(curProj,nextProj)) = nextProj +} +native channel CameraScaleUpdate { + in scene.camera.transform.scale(curScale:Json,updateScale(nextScale.x,nextScale.y,nextScale.z)) = nextScale +} +channel UpdateEnemy1(tid:Str) { + in timers.{tid:Str}.count(curCount:Long,updateEnemy1(type,speed)) = nextCount + ref enemy1.move.type(type:Str,updateEnemy1(type,speed)) + ref enemy1.move.speed(speed:Float,updateEnemy1(type,speed)) + out enemy1.transform.position(curPos:Json,updateEnemy1(type,speed)) = { + "x": (curPos.x-speed), + "y": curPos.y, + "z": curPos.z +} +} +native channel EntitySpriteUpdate(eid:Str) { + in scene.entities.{eid:Str}.mesh(curMesh:Json,updateSprite(spritePath:Str)) = { + "sprite": spritePath, + "type": "sprite" +} +} +native channel EntityScaleUpdate(eid:Str) { + in scene.entities.{eid:Str}.transform.scale(curScale:Json,updateScale(nextScale.x,nextScale.y,nextScale.z)) = nextScale +} +native channel TimersUpdated { + in timers(curTimers:Map,update(curTimers,nextTimers)) = nextTimers +} +channel RotateEnemy1 { + in enemy1.transform.rotation(curRot:Json,rotateEnemy1(nextRot,eid)) = nextRot + ref enemy1.eid(eid,rotateEnemy1(nextRot,eid)) + out scene.entities.{eid:Str}.transform.rotation(curRot:Json,rotateEnemy1(nextRot,eid)) = nextRot +} +native channel CameraPositionUpdate { + in scene.camera.transform.position(curPos:Json,updatePosition(nextPos.x,nextPos.y,nextPos.z)) = nextPos +} +native channel SceneUpdate { + in scene(curSc:Json,update(curSc,nextSc)) = nextSc +} +native channel EntityRotationUpdate(eid:Str) { + in scene.entities.{eid:Str}.transform.rotation(curRot:Json,updateRotation(nextRot.x,nextRot.y,nextRot.z)) = nextRot +} +native channel CameraRotationUpdate { + in scene.camera.transform.rotation(curRot:Json,updateRotation(nextRot.x,nextRot.y,nextRot.z)) = nextRot +} diff --git a/GameEngine/resources/InPort.png b/GameEngine/resources/InPort.png new file mode 100644 index 0000000..03b7081 --- /dev/null +++ b/GameEngine/resources/InPort.png Binary files differ diff --git a/GameEngine/resources/Line.png b/GameEngine/resources/Line.png new file mode 100644 index 0000000..0279819 --- /dev/null +++ b/GameEngine/resources/Line.png Binary files differ diff --git a/GameEngine/resources/OutPort.png b/GameEngine/resources/OutPort.png new file mode 100644 index 0000000..ec8bc03 --- /dev/null +++ b/GameEngine/resources/OutPort.png Binary files differ diff --git a/GameEngine/resources/PortB.png b/GameEngine/resources/PortB.png new file mode 100644 index 0000000..e6c1186 --- /dev/null +++ b/GameEngine/resources/PortB.png Binary files differ diff --git a/GameEngine/resources/RemoveButton.png b/GameEngine/resources/RemoveButton.png new file mode 100644 index 0000000..05bfdc6 --- /dev/null +++ b/GameEngine/resources/RemoveButton.png Binary files differ diff --git a/GameEngine/resources/button.png b/GameEngine/resources/button.png new file mode 100644 index 0000000..9d84a62 --- /dev/null +++ b/GameEngine/resources/button.png Binary files differ diff --git a/GameEngine/resources/cameraFrameView.png b/GameEngine/resources/cameraFrameView.png new file mode 100644 index 0000000..3487028 --- /dev/null +++ b/GameEngine/resources/cameraFrameView.png Binary files differ diff --git a/GameEngine/resources/empty.png b/GameEngine/resources/empty.png new file mode 100644 index 0000000..d4b75de --- /dev/null +++ b/GameEngine/resources/empty.png Binary files differ diff --git a/GameEngine/resources/enemy1.png b/GameEngine/resources/enemy1.png new file mode 100644 index 0000000..a6b7443 --- /dev/null +++ b/GameEngine/resources/enemy1.png Binary files differ diff --git a/GameEngine/resources/enemy2.png b/GameEngine/resources/enemy2.png new file mode 100644 index 0000000..e885a4e --- /dev/null +++ b/GameEngine/resources/enemy2.png Binary files differ diff --git a/GameEngine/resources/test.png b/GameEngine/resources/test.png new file mode 100644 index 0000000..75891d3 --- /dev/null +++ b/GameEngine/resources/test.png Binary files differ diff --git a/GameEngine/src/main/java/gameEngine/ConnectionManager.java b/GameEngine/src/main/java/gameEngine/ConnectionManager.java new file mode 100644 index 0000000..48122cc --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/ConnectionManager.java @@ -0,0 +1,88 @@ +package gameEngine; + +import java.util.ArrayList; +import java.util.List; + +import gameEngine.entites.EditorEntity; +import gameEngine.entites.editorComponents.*; +import gameEngine.input.Input; +import gameEngine.input.MouseInput; + +public class ConnectionManager { + private final List connections = new ArrayList<>(); + private PortView pressedPort; + + public void handlePortPress(PortView port) { + pressedPort = port; + } + + public void handlePortRelease(PortView port) { + if (pressedPort != null && pressedPort != port && pressedPort.getPortType() != port.getPortType()) { + if (pressedPort.isConnectedTo(port)) { + System.out.println("Already connected"); + } else { + System.out.println("Connect!"); + Connection newConnection = new Connection(pressedPort, port); + connections.add(newConnection); + pressedPort.addConnectedPort(port); + port.addConnectedPort(pressedPort); + + EditorEntity portParentA = pressedPort.getParent(); + EditorEntity portParentB = port.getParent(); + if (portParentA.getEditorComponent(EntityView.class) != null || portParentA.getEditorComponent(CameraView.class) != null) { + portParentA.ComponentConnections.add( + portParentB.getEditorComponent(ComponentView.class).connectionType + ); + } else if (portParentB.getEditorComponent(EntityView.class) != null || portParentB.getEditorComponent(CameraView.class) != null) { + portParentB.ComponentConnections.add( + portParentA.getEditorComponent(ComponentView.class).connectionType + ); + } + } + } + pressedPort = null; + } + + public void update() { + for (Connection connection : connections) { + connection.render(); + + if (Input.GetMouseButtonDown(0)) { + float mouseX = MouseInput.getX(); + float mouseY = MouseInput.getY(); + + if (connection.isRemoveButtonClicked(mouseX, mouseY)) { + System.out.println("Connection removed"); + removeConnection(connection.getPortA(), connection.getPortB()); + break; + } + } + } + } + + public void removeConnection(PortView portA, PortView portB) { + Connection toRemove = null; + for (Connection connection : connections) { + if ((connection.getPortA() == portA && connection.getPortB() == portB) || + (connection.getPortA() == portB && connection.getPortB() == portA)) { + toRemove = connection; + break; + } + } + + if (toRemove != null) { + connections.remove(toRemove); + portA.removeConnectedPort(portB); + portB.removeConnectedPort(portA); + + EditorEntity portParentA = portA.getParent(); + EditorEntity portParentB = portB.getParent(); + + if (portParentA.getEditorComponent(EntityView.class) != null) { + portParentA.ComponentConnections.remove(portParentB.getEditorComponent(ComponentView.class).connectionType); + } else { + portParentB.ComponentConnections.remove(portParentA.getEditorComponent(ComponentView.class).connectionType); + } + } + } +} \ No newline at end of file diff --git a/GameEngine/src/main/java/gameEngine/GameEditor.java b/GameEngine/src/main/java/gameEngine/GameEditor.java new file mode 100644 index 0000000..81db05c --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/GameEditor.java @@ -0,0 +1,203 @@ +package gameEngine; + +import gameEngine.entites.EditorEntity; +import gameEngine.geometry.Transform; +import gameEngine.scenes.EditorScene; +import gameEngine.views.*; +import gameEngine.scenes.Scene; +import org.joml.Vector3f; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class GameEditor { + private EditorScene scene; + private float windowWidth, windowHeight; + private final String frameImagePath = Window.RESOURCE_PATH + "EditorFrame.png"; + + private List updatableviews = new ArrayList<>(); + + private Sprite[] EditorFrameSprite = new Sprite[4]; + + private Button dtramButton; + private Button playButton; + + private Button createMeshComponentViewButton; + private Button createMoveImageComponentViewButton; + private Button createMoveStraightComponentViewButton; + + private Button createEntityViewButton; + + public InputField[][] inspectorInputFields; + + public GameEditor(Scene scene, float windowWidth, float windowHeight) { + this.scene = (EditorScene) scene; + this.windowWidth = windowWidth; + this.windowHeight = windowHeight; + initializeEditorComponents(); + } + + private void initializeEditorComponents() { + createFrame(); + + playButton = new Button(windowWidth/ 2 -16 , 3, 1, 0.4f,"Play"); + dtramButton = new Button(100 - 16 , 3, 2.5f, 0.4f,"Play DTRAM"); + + createMeshComponentViewButton = new Button(20, 33, 3.2f, 0.5f, "Add Mesh"); + createMoveImageComponentViewButton = new Button(20, 73, 3.2f, 0.5f, "Add MoveImage"); + createMoveStraightComponentViewButton = new Button(20, 113, 3.2f, 0.5f, "Add Move"); + + createEntityViewButton = new Button(240, 33, 3.2f, 0.5f, "Add Entity"); + + setButtonListeners(); + setInspector(); + } + + private void registerUpdatable(IUpdatable... components) { + Collections.addAll(updatableviews, components); + } + + private void setInspector() { + String[] labels = {"Position", "Rotation", "Scale"}; + String[] axes = {"x", "y", "z"}; + float startX = 900; + float startY = 40; + float fieldWidth = 70; + float fieldHeight = 22; + float spacingX = 100; + float spacingY = 40; + float labelOffsetX = -100; + float labelOffsetY = 5; + + InputField[][] inputFields = new InputField[3][3]; + + for (int i = 0; i < labels.length; i++) { + float labelX = startX + labelOffsetX; + float labelY = startY + i * spacingY + labelOffsetY; + + Text labelText = new Text(labelX, labelY - 6, labels[i] + ":", 18); + updatableviews.add(labelText); + + for (int j = 0; j < axes.length; j++) { + float fieldX = startX + j * spacingX; + float fieldY = startY + i * spacingY; + + Text axisText = new Text(fieldX - 20, fieldY, axes[j] + ":", 18); + InputField field = new InputField(fieldX, fieldY, fieldWidth, fieldHeight, "0", 18); + + inputFields[i][j] = field; + + int transformType = i; + int axisIndex = j; + + //InputFieldの値変更時 + field.setOnChangeListener(newValue -> { + EditorEntity clickedEntity = scene.getClickedEntity(); + + System.out.println("うおおお!" + clickedEntity); + + if (clickedEntity != null) { + Transform transform = clickedEntity.transform; + + try { + // 入力を解析して数値に変換 + float value = Float.parseFloat(newValue); + + switch (transformType) { + case 0: + if (axisIndex == 0) transform.position.x = value; + if (axisIndex == 1) transform.position.y = value; + if (axisIndex == 2) transform.position.z = value; + break; + case 1: + if (axisIndex == 0) transform.rotation.x = value; + if (axisIndex == 1) transform.rotation.y = value; + if (axisIndex == 2) transform.rotation.z = value; + break; + case 2: + if (axisIndex == 0) transform.scale.x = value; + if (axisIndex == 1) transform.scale.y = value; + if (axisIndex == 2) transform.scale.z = value; + break; + } + + // 変化を適用 + clickedEntity.transform = transform; + + } catch (NumberFormatException e) { + // 入力が不正な場合は何もしない + System.err.println("Invalid input for transform value: " + newValue); + field.copyDisplayedTextToCurrent(); + } + } + }); + + updatableviews.add(axisText); + updatableviews.add(field); + } + } + this.inspectorInputFields = inputFields; + } + + private void setButtonListeners() { + playButton.clearListeners(); + playButton.addListener(scene::changeSceneStart); + + dtramButton.clearListeners(); + GameEngineModelFileGenerator modelFileGenerator = new GameEngineModelFileGenerator(); + dtramButton.addListener(modelFileGenerator::generate); + + createMeshComponentViewButton.clearListeners(); + createMeshComponentViewButton.addListener(scene::addNewMeshComponent); + + createMoveImageComponentViewButton.clearListeners(); + createMoveImageComponentViewButton.addListener(scene::addNewMoveImageComponent); + + createMoveStraightComponentViewButton.clearListeners(); + createMoveStraightComponentViewButton.addListener(scene::addMoveComponent); + + createEntityViewButton.clearListeners(); + createEntityViewButton.addListener(scene::addNewEntity); + + registerUpdatable(playButton, dtramButton, createMeshComponentViewButton, createMoveImageComponentViewButton, createEntityViewButton, createMoveStraightComponentViewButton); + } + + public void setScene(Scene newScene) { + this.scene = (EditorScene) newScene; + setButtonListeners(); + } + + public void update() { + for(Sprite editorFrameSprites : EditorFrameSprite) editorFrameSprites.update(); + + for (IUpdatable updatable : updatableviews) { + updatable.update(); + } + + if (scene.getClickedEntity() != null) { + Transform transform = scene.getClickedEntity().transform; + + updateInspectorField(inspectorInputFields[0], transform.position); // Position + updateInspectorField(inspectorInputFields[1], transform.rotation); // Rotation + updateInspectorField(inspectorInputFields[2], transform.scale); // Scale + } + } + + public void updateInspectorField(InputField[] fields, Vector3f values) { + fields[0].setText(String.valueOf(values.x)); + fields[1].setText(String.valueOf(values.y)); + fields[2].setText(String.valueOf(values.z)); + } + + private void createFrame(){ + EditorFrameSprite[0] = new Sprite(frameImagePath, 0, 0, 20, 0.5f); + EditorFrameSprite[1] = new Sprite(frameImagePath, 0, 0, 0.25f, 20); + EditorFrameSprite[2] = new Sprite(frameImagePath, windowWidth-16, 0, 0.25f, 20); + EditorFrameSprite[3] = new Sprite(frameImagePath, 0, windowHeight-16, 20, 0.5f); + + for (Sprite sprite : EditorFrameSprite) { + updatableviews.add(sprite); + } + } +} diff --git a/GameEngine/src/main/java/gameEngine/GameEngineModelFileGenerator.java b/GameEngine/src/main/java/gameEngine/GameEngineModelFileGenerator.java new file mode 100644 index 0000000..f995f88 --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/GameEngineModelFileGenerator.java @@ -0,0 +1,626 @@ +package gameEngine; + +import algorithms.TypeInference; +import gameEngine.entites.EditorEntity; +import gameEngine.entites.Entity; +import gameEngine.entites.editorComponents.ComponentView; +import gameEngine.entites.editorComponents.EntityView; +import gameEngine.entites.gameComponents.Move; +import gameEngine.geometry.Transform; +import gameEngine.scenes.EditorScene; +import gameEngine.views.Window; +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Variable; +import models.dataConstraintModel.*; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.DataTransferModel; +import parser.Parser; +import parser.Parser.TokenStream; +import parser.exceptions.*; +import simulator.Event; +import simulator.ResourceIdentifier; +import simulator.Simulator; +import gameEngine.simulator.interfaces.GameEnginePresenter; +import simulator.SystemState; +import simulator.interfaces.timers.TimerService; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class GameEngineModelFileGenerator { + private DataTransferModel model; + private final String modelFilePath = "GameEngine/resources/"; + Parser.TokenStream stream = new Parser.TokenStream(); + Parser parser = new Parser(stream); + + public void generate(){ + try { + EditorScene editorScene = (EditorScene) Window.getInstance().getScene(); + model = new DataTransferModel(); + ResourcePath scene = new ResourcePath("scene"); // "scene" + ResourcePath scene_time = new ResourcePath(scene, "time"); // "scene.time" + ResourcePath scene_camera = new ResourcePath(scene, "camera"); // "scene.camera" + ResourcePath scene_camera_transform = new ResourcePath(scene_camera, "transform"); // "scene.camera.transform" + ResourcePath scene_camera_transform_position = new ResourcePath(scene_camera_transform, "position"); // "scene.camera.transform.position" + ResourcePath scene_camera_transform_rotation = new ResourcePath(scene_camera_transform, "rotation"); // "scene.camera.transform.rotation" + ResourcePath scene_camera_transform_scale = new ResourcePath(scene_camera_transform, "scale"); // "scene.camera.transform.scale" + ResourcePath scene_camera_projection = new ResourcePath(scene_camera, "projection"); // "scene.camera.projection" + + ResourcePath entities = new ResourcePath(scene, "entities"); // "scene.entities" + ResourcePath entity = new ResourcePath(entities, new Variable("eid", DataConstraintModel.typeString)); // "scene.entities.{eid}" + ResourcePath entity_transform = new ResourcePath(entity, "transform"); // "scene.entities.{eid}.transform" + ResourcePath entity_transform_position = new ResourcePath(entity_transform, "position"); // "scene.entities.{eid}.transform.position" + ResourcePath entity_transform_rotation = new ResourcePath(entity_transform, "rotation"); // "scene.entities.{eid}.transform.rotation" + ResourcePath entity_transform_scale = new ResourcePath(entity_transform, "scale"); // "scene.entities.{eid}.transform.scale" + ResourcePath entity_mesh = new ResourcePath(entity, "mesh"); // "scene.entities.{eid}.mesh" + + ResourcePath keys = new ResourcePath(scene, "keys"); // "scene.keys" + ResourcePath key = new ResourcePath(keys, new Variable("kno", DataConstraintModel.typeInt)); // "scene.keys.{kno}" + ResourcePath key_state = new ResourcePath(key, "state"); // "scene.keys.{kno}.state + + ResourcePath timers = new ResourcePath("timers"); // "timers" + ResourcePath timer = new ResourcePath(timers, new Variable("tid", DataConstraintModel.typeString)); // "timers.{tid}" + ResourcePath timer_count = new ResourcePath(timer, "count"); //"timer.{tid}.count + + List enemy = new ArrayList<>(); + List enemy_transform = new ArrayList<>(); + List enemy_transform_position = new ArrayList<>(); + List enemy_transform_rotation = new ArrayList<>(); + List enemy_move = new ArrayList<>(); + List enemy_move_type = new ArrayList<>(); + List enemy_move_direction = new ArrayList<>(); + List enemy_move_speed = new ArrayList<>(); + List enemy_eid = new ArrayList<>(); + for (int i = 1; i < editorScene.editorEntities.size(); i++) { + EditorEntity editorEntity = (EditorEntity) editorScene.getEditorEntity("" + i); + String id = editorEntity.getId(); + + ResourcePath e = new ResourcePath("enemy" + id); + ResourcePath et = new ResourcePath(e, "transform"); + ResourcePath etp = new ResourcePath(et, "position"); + ResourcePath etr = new ResourcePath(et, "rotation"); + ResourcePath em = new ResourcePath(e,"move"); + ResourcePath emt = new ResourcePath(em,"type"); + ResourcePath emd = new ResourcePath(em, "direction"); + ResourcePath ems = new ResourcePath(em,"speed"); + ResourcePath eid = new ResourcePath(e, "eid"); + enemy.add(e); + enemy_transform.add(et); + enemy_transform_position.add(etp); + enemy_transform_rotation.add(etr); + enemy_move.add(em); + enemy_move_type.add(emt); + enemy_move_direction.add(emd); + enemy_move_speed.add(ems); + enemy_eid.add(eid); + } + + model.addResourcePath(scene_time); + model.addResourcePath(scene_camera); + model.addResourcePath(scene_camera_transform); + model.addResourcePath(scene_camera_transform_position); + model.addResourcePath(scene_camera_transform_rotation); + model.addResourcePath(scene_camera_transform_scale); + model.addResourcePath(scene_camera_projection); + model.addResourcePath(entity_transform); + model.addResourcePath(entity_transform_position); + model.addResourcePath(entity_transform_rotation); + model.addResourcePath(entity_transform_scale); + model.addResourcePath(entity_mesh); + model.addResourcePath(key_state); + model.addResourcePath(timers); + model.addResourcePath(timer_count); + + for (ResourcePath et : enemy_transform) model.addResourcePath(et); + for (ResourcePath etp : enemy_transform) model.addResourcePath(etp); + for (ResourcePath etr : enemy_transform) model.addResourcePath(etr); + for (ResourcePath em : enemy_transform) model.addResourcePath(em); + for (ResourcePath emt : enemy_transform) model.addResourcePath(emt); + for (ResourcePath ems : enemy_transform) model.addResourcePath(ems); + + addInit(); + + addNativeChannel( + new DataTransferChannel("SceneUpdateEvent"), + new ChannelMemberDefinition( + ChannelMemberDefinition.ChannelMemberType.Out, + scene_time, + "time: Long", + "updateEvent(dt: Long)", + "time + dt" + ) + ); + + addNativeChannel( + new DataTransferChannel("SceneUpdate"), + new ChannelMemberDefinition( + ChannelMemberDefinition.ChannelMemberType.In, + scene, + "curSc: Json", + "update(curSc, nextSc)", + "nextSc" + ) + ); + + addNativeChannel( + new DataTransferChannel("CameraPositionUpdate"), + new ChannelMemberDefinition( + ChannelMemberDefinition.ChannelMemberType.In, + scene_camera_transform_position, + "curPos: Json", + "updatePosition(nextPos.x, nextPos.y, nextPos.z)", + "nextPos" + ) + ); + + addNativeChannel( + new DataTransferChannel("CameraRotationUpdate"), + new ChannelMemberDefinition( + ChannelMemberDefinition.ChannelMemberType.In, + scene_camera_transform_rotation, + "curRot: Json", + "updateRotation(nextRot.x, nextRot.y, nextRot.z)", + "nextRot" + ) + ); + + addNativeChannel( + new DataTransferChannel("CameraScaleUpdate"), + new ChannelMemberDefinition( + ChannelMemberDefinition.ChannelMemberType.In, + scene_camera_transform_scale, + "curScale: Json", + "updateScale(nextScale.x, nextScale.y, nextScale.z)", + "nextScale" + ) + ); + + addNativeChannel( + new DataTransferChannel("CameraProjectionUpdate"), + new ChannelMemberDefinition( + ChannelMemberDefinition.ChannelMemberType.In, + scene_camera_projection, + "curProj: Json", + "updateProjection(curProj, nextProj)", + "nextProj" + ) + ); + + addNativeChannel( + new DataTransferChannel("EntityPositionUpdate", new Variable("eid", DataConstraintModel.typeString)), + new ChannelMemberDefinition( + ChannelMemberDefinition.ChannelMemberType.In, + entity_transform_position, + "curPos: Json", + "updatePosition(nextPos.x, nextPos.y, nextPos.z)", + "nextPos" + ) + ); + + addNativeChannel( + new DataTransferChannel("EntityRotationUpdate", new Variable("eid", DataConstraintModel.typeString)), + new ChannelMemberDefinition( + ChannelMemberDefinition.ChannelMemberType.In, + entity_transform_rotation, + "curRot: Json", + "updateRotation(nextRot.x, nextRot.y, nextRot.z)", + "nextRot" + ) + ); + + addNativeChannel( + new DataTransferChannel("EntityScaleUpdate", new Variable("eid", DataConstraintModel.typeString)), + new ChannelMemberDefinition( + ChannelMemberDefinition.ChannelMemberType.In, + entity_transform_scale, + "curScale: Json", + "updateScale(nextScale.x, nextScale.y, nextScale.z)", + "nextScale" + ) + ); + + addNativeChannel( + new DataTransferChannel("EntitySpriteUpdate", new Variable("eid", DataConstraintModel.typeString)), + new ChannelMemberDefinition( + ChannelMemberDefinition.ChannelMemberType.In, + entity_mesh, + "curMesh: Json", + "updateSprite(spritePath: Str)", + "{\"type\": \"sprite\", \"sprite\": spritePath}" + ) + ); + + addNativeChannel( + new DataTransferChannel("KeyEvent", new Variable("kno", DataConstraintModel.typeInt)), + new ChannelMemberDefinition( + ChannelMemberDefinition.ChannelMemberType.Out, + key_state, + "curState: Int", + "keyEvent(nextState)", + "nextState" + ) + ); + + addNativeChannel( + new DataTransferChannel("TimersUpdated"), + new ChannelMemberDefinition( + ChannelMemberDefinition.ChannelMemberType.In, + timers, + "curTimers: Map", + "update(curTimers, nextTimers)", + "nextTimers" + ) + ); + + addNativeChannel( + new DataTransferChannel("TimerEvent", new Variable("tid", DataConstraintModel.typeString)), + new ChannelMemberDefinition( + ChannelMemberDefinition.ChannelMemberType.Out, + timer_count, + "count: Long", + "tick()", + "count + 1" + ) + ); + + addChannel( + new DataTransferChannel("UpdateCameraPosition"), + new ChannelMemberDefinition( + ChannelMemberDefinition.ChannelMemberType.Out, + scene_camera_transform_position, + "curPos: Json", + "updateCameraPosition(x: Double, y: Double, z: Double)", + "{\"x\": x, \"y\": y, \"z\": z}") + ); + + for (int i = 1; i < editorScene.editorEntities.size(); i++) { + EditorEntity editorEntity = (EditorEntity) editorScene.getEditorEntity("" + i); + String id = editorEntity.getId(); + if (editorEntity.getEditorComponent(EntityView.class) != null) { + + addChannel( + new DataTransferChannel("UpdateEnemy" + id, new Variable("tid", DataConstraintModel.typeString)), + new ChannelMemberDefinition( + ChannelMemberDefinition.ChannelMemberType.In, + timer_count, + "curCount: Long", + "updateEnemy" + id + "(type, speed)", + "nextCount"), + new ChannelMemberDefinition( + ChannelMemberDefinition.ChannelMemberType.Ref, + enemy_move_type.get(i - 1), + "type: Str", + "updateEnemy" + id + "(type, speed)"), + new ChannelMemberDefinition( + ChannelMemberDefinition.ChannelMemberType.Ref, + enemy_move_speed.get(i - 1), + "speed: Float", + "updateEnemy" + id + "(type, speed)"), + new ChannelMemberDefinition( + ChannelMemberDefinition.ChannelMemberType.Out, + enemy_transform_position.get(i - 1), + "curPos: Json", + "updateEnemy" + id + "(type, speed)", + setMoveDirection(editorEntity)) + ); + + addChannel( + new DataTransferChannel("MoveEnemy" + id), + new ChannelMemberDefinition( + ChannelMemberDefinition.ChannelMemberType.In, + enemy_transform_position.get(i - 1), + "curPos: Json", + "moveEnemy" + id + "(nextPos, eid)", + "nextPos"), + new ChannelMemberDefinition( + ChannelMemberDefinition.ChannelMemberType.Ref, + enemy_eid.get(i - 1), + "eid", + "moveEnemy" + id + "(nextPos, eid)"), + new ChannelMemberDefinition( + ChannelMemberDefinition.ChannelMemberType.Out, + entity_transform_position, + "curPos: Json", + "moveEnemy" + id + "(nextPos, eid)", + "nextPos") + ); + + addChannel( + new DataTransferChannel("RotateEnemy" + id), + new ChannelMemberDefinition( + ChannelMemberDefinition.ChannelMemberType.In, + enemy_transform_rotation.get(i - 1), + "curRot: Json", + "rotateEnemy" + id + "(nextRot, eid)", + "nextRot"), + new ChannelMemberDefinition( + ChannelMemberDefinition.ChannelMemberType.Ref, + enemy_eid.get(i - 1), + "eid", + "rotateEnemy" + id + "(nextRot, eid)"), + new ChannelMemberDefinition( + ChannelMemberDefinition.ChannelMemberType.Out, + entity_transform_rotation, + "curRot: Json", + "rotateEnemy" + id + "(nextRot, eid)", + "nextRot") + ); + + } + } + + DataTransferChannel startTimerChannel = addChannel( + new DataTransferChannel("StartTimer"), + new ChannelMemberDefinition( + ChannelMemberDefinition.ChannelMemberType.Out, + timers, + "timers: Map", + "startTimer(tid: Str, interval: Long)", + "insert(timers, tid, {\"interval\": interval, \"count\": 0})") + ); + + addChannel( + new DataTransferChannel("ClearTimer"), + new ChannelMemberDefinition( + ChannelMemberDefinition.ChannelMemberType.Out, + timers, + "timers: Map", + "clearTimer(tid: Str)", + "delete(timers, tid)") + ); + + + + if (stream.hasNext()) stream.next(); + + save(); + + TypeInference.infer(model); + Simulator simulator = new Simulator(model); + Window window = Window.getInstance(); + window.changeScene(2); + GameEnginePresenter presenter = new GameEnginePresenter(window.getScene(), simulator); + TimerService timerService = new TimerService(simulator); + + SystemState initialState = simulator.init(); + + stream.addLine("startTimer(\"000\", 100)"); + Expression messageStartTimer = parser.parseTerm(stream, model); + Event startTimer = new Event(startTimerChannel, messageStartTimer, timer, initialState.getResource(ResourceIdentifier.createFrom(timer))); + simulator.transition(startTimer); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + private String setMoveDirection(EditorEntity editorEntity){ + for(ComponentView componentView: editorEntity.getEditorComponent(EntityView.class).getConnectedComponentViews()){ + if(componentView.directionText != null) { + if(componentView.direction == Move.Direction.LEFT){ + System.out.println("LEFT"); + return "{\"x\": curPos.x - speed , \"y\": curPos.y, \"z\": curPos.z}"; + } + else if(componentView.direction == Move.Direction.RIGHT){ + System.out.println("RIGHT"); + return "{\"x\": curPos.x + speed , \"y\": curPos.y, \"z\": curPos.z}"; + } + else if(componentView.direction == Move.Direction.UP){ + System.out.println("UP"); + return "{\"x\": curPos.x, \"y\": curPos.y - speed, \"z\": curPos.z}"; + } + else if(componentView.direction == Move.Direction.DOWN){ + System.out.println("DOWN"); + return "{\"x\": curPos.x, \"y\": curPos.y + speed, \"z\": curPos.z}"; + } + } + } + return ""; + } + + private JsonTerm createTransformTerm(Transform transform){ + JsonTerm positionTerm = new JsonTerm(); + positionTerm.addMember("x", new Constant(String.valueOf(transform.position.x))); + positionTerm.addMember("y", new Constant(String.valueOf(transform.position.y))); + positionTerm.addMember("z", new Constant(String.valueOf(transform.position.z))); + + JsonTerm rotationTerm = new JsonTerm(); + rotationTerm.addMember("x", new Constant(String.valueOf(transform.rotation.x))); + rotationTerm.addMember("y", new Constant(String.valueOf(transform.rotation.y))); + rotationTerm.addMember("z", new Constant(String.valueOf(transform.rotation.z))); + + JsonTerm scaleTerm = new JsonTerm(); + scaleTerm.addMember("x", new Constant(String.valueOf(transform.scale.x))); + scaleTerm.addMember("y", new Constant(String.valueOf(transform.scale.y))); + scaleTerm.addMember("z", new Constant(String.valueOf(transform.scale.z))); + + JsonTerm transformTerm = new JsonTerm(); + transformTerm.addMember("position", positionTerm); + transformTerm.addMember("rotation", rotationTerm); + transformTerm.addMember("scale", scaleTerm); + return transformTerm; + } + + public JsonTerm createEnemyJsonTerm(Entity entity){ + + EditorEntity editorEntity = (EditorEntity) entity; + + JsonTerm moveTerm = new JsonTerm(); + JsonTerm meshTerm = new JsonTerm(); + JsonTerm enemyTerm = new JsonTerm(); + + enemyTerm.addMember("transform", createTransformTerm(entity.transform)); + enemyTerm.addMember("eid", new Constant(entity.getId(), DataConstraintModel.typeString)); + + for(EditorEntity.Connectiontype connectionType: editorEntity.ComponentConnections) { + if (connectionType == EditorEntity.Connectiontype.Mesh) { + meshTerm.addMember("type", new Constant("sprite", DataConstraintModel.typeString)); + meshTerm.addMember("sprite", new Constant(Window.RESOURCE_PATH + "enemy1.png", DataConstraintModel.typeString)); + enemyTerm.addMember("mesh", meshTerm); + } + if (connectionType == EditorEntity.Connectiontype.Move) { + moveTerm.addMember("type", new Constant("straight", DataConstraintModel.typeString)); + moveTerm.addMember("speed", new Constant(String.valueOf(5.0))); + enemyTerm.addMember("move", moveTerm); + } + } + return enemyTerm; + } + + + public JsonTerm createPlayerJsonTerm(){ + + JsonTerm meshTerm = new JsonTerm(); + meshTerm.addMember("type", new Constant("sprite", DataConstraintModel.typeString)); + meshTerm.addMember("sprite", new Constant(Window.RESOURCE_PATH + "enemy1.png", DataConstraintModel.typeString)); + + JsonTerm playerTerm = new JsonTerm(); + playerTerm.addMember("transform", createTransformTerm(new Transform())); + playerTerm.addMember("mesh", meshTerm); + + return playerTerm; + } + +// public JsonTerm createSceneTerm(){ +// +// +// } + + private void addInit() { + EditorScene editorScene = (EditorScene) Window.getInstance().getScene(); + + ResourceHierarchy playerHierarchy = new ResourceHierarchy("player", DataConstraintModel.typeJson); + playerHierarchy = model.getOrPutResourceHierarchy(playerHierarchy); + JsonTerm playerRightTerm = createPlayerJsonTerm(); + playerHierarchy.setInitialValue(playerRightTerm); + playerHierarchy.setInitText(playerRightTerm.toString()); + + ResourceHierarchy sceneHierarchy = new ResourceHierarchy("scene", DataConstraintModel.typeJson); + sceneHierarchy = model.getOrPutResourceHierarchy(sceneHierarchy); + + JsonTerm entitiesTerm = new JsonTerm(); + + // 0番はカメラのため + for (int i = 1; i < editorScene.editorEntities.size(); i++) { + EditorEntity editorEntity = (EditorEntity) editorScene.getEditorEntity("" + i); + if(editorEntity.getEditorComponent(EntityView.class) != null) { + ResourceHierarchy enemyHierarchy = new ResourceHierarchy("enemy" + editorScene.getEditorEntity("" + i).getId(), DataConstraintModel.typeJson); + enemyHierarchy = model.getOrPutResourceHierarchy(enemyHierarchy); + JsonTerm enemyTerm = createEnemyJsonTerm(editorScene.getEditorEntity("" + i)); + entitiesTerm.addMember(editorScene.getEditorEntity("" + i).getId() ,enemyTerm); + enemyHierarchy.setInitialValue(enemyTerm); + enemyHierarchy.setInitText(enemyTerm.toString()); + } + } + + JsonTerm sceneTerm = new JsonTerm(); + sceneTerm.addMember("entities", entitiesTerm); + sceneHierarchy.setInitialValue(sceneTerm); + sceneHierarchy.setInitText(sceneTerm.toString()); + + + + } + + public void save() { + String outputFileName = modelFilePath + File.separator + "GameEngineTest.model"; + + try { + File file = new File(outputFileName); + FileWriter fileWriter = new FileWriter(file); + fileWriter.write(model.getSourceText()); + fileWriter.close(); + + } catch (IOException e) { + e.printStackTrace(); + } + } + + public DataTransferChannel addNativeChannel(DataTransferChannel dataTransferChannel, ChannelMemberDefinition... definitions) throws ExpectedRightBracket, WrongJsonExpression, ExpectedColon, ExpectedDoubleQuotation { + dataTransferChannel.setNative(true); + return addChannel(dataTransferChannel, definitions); + } + + public DataTransferChannel addChannel(DataTransferChannel dataTransferChannel, ChannelMemberDefinition... definitions) + throws ExpectedRightBracket, WrongJsonExpression, ExpectedColon, ExpectedDoubleQuotation { + DataTransferChannel channel = dataTransferChannel; + boolean hasInputChannel = false; + + for (ChannelMemberDefinition def : definitions) { + if (def.channelType == ChannelMemberDefinition.ChannelMemberType.Out) { + ChannelMember member = addChannelMember(stream, parser, def.path, def.curStateName, def.messageName, def.nextStateName); + channel.addChannelMemberAsOutput(member); + } else if (def.channelType == ChannelMemberDefinition.ChannelMemberType.In) { + ChannelMember member = addChannelMember(stream, parser, def.path, def.curStateName, def.messageName, def.nextStateName); + channel.addChannelMemberAsInput(member); + hasInputChannel = true; + } else if (def.channelType == ChannelMemberDefinition.ChannelMemberType.Ref){ + ChannelMember member = addChannelMember(stream, parser, def.path, def.curStateName, def.messageName); + channel.addChannelMemberAsReference(member); + } + } + + if(hasInputChannel) model.addChannel(channel); + else model.addInputChannel(channel); + + return channel; + } + + //inが一つでもあればInputChannel + + private ChannelMember addChannelMember(TokenStream stream, Parser parser, ResourcePath path, String curStateName, String messageName, String nextStateName) throws ExpectedRightBracket, WrongJsonExpression, ExpectedColon, ExpectedDoubleQuotation { + ChannelMember member = new ChannelMember(path); + stream.addLine(curStateName); + Expression curState = parser.parseTerm(stream, model); + stream.addLine(messageName); + Expression message = parser.parseTerm(stream, model); + stream.addLine(nextStateName); + Expression nextState = parser.parseTerm(stream, model); + member.getStateTransition().setCurStateExpression(curState); + member.getStateTransition().setMessageExpression(message); + member.getStateTransition().setNextStateExpression(nextState); + return member; + } + + private ChannelMember addChannelMember(TokenStream stream, Parser parser, ResourcePath path, String curStateName, String messageName) throws ExpectedRightBracket, WrongJsonExpression, ExpectedColon, ExpectedDoubleQuotation { + ChannelMember member = new ChannelMember(path); + stream.addLine(curStateName); + Expression curState = parser.parseTerm(stream, model); + stream.addLine(messageName); + Expression message = parser.parseTerm(stream, model); + member.getStateTransition().setCurStateExpression(curState); + member.getStateTransition().setMessageExpression(message); + return member; + } + + + public static class ChannelMemberDefinition { + public enum ChannelMemberType {In, Out, Ref} + public final ChannelMemberType channelType; + public final ResourcePath path; + public final String curStateName; + public final String messageName; + public String nextStateName; + + public ChannelMemberDefinition(ChannelMemberType channelType, ResourcePath path , String curStateName, String messageName, String nextStateName) { + this.path = path; + this.channelType = channelType; + this.curStateName = curStateName; + this.messageName = messageName; + this.nextStateName = nextStateName; + } + + public ChannelMemberDefinition(ChannelMemberType channelType, ResourcePath path , String curStateName, String messageName) { + this.path = path; + this.channelType = channelType; + this.curStateName = curStateName; + this.messageName = messageName; + } + + } + +} diff --git a/GameEngine/src/main/java/gameEngine/ResourceManager.java b/GameEngine/src/main/java/gameEngine/ResourceManager.java new file mode 100644 index 0000000..5825b6a --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/ResourceManager.java @@ -0,0 +1,45 @@ +package gameEngine; + +import java.util.ArrayList; +import java.util.List; + +public class ResourceManager { + private final List paths = new ArrayList<>(); + //0番はEmpty用pathのため、削除、上書きは不可能 + + public List getPathList(){ + return paths; + } + + //追加 + public int addPath(String path) { + paths.add(path); + return paths.size() - 1; + } + + //上書き + public boolean overwritePath(int id, String newPath) { + if (id > 0 && id < paths.size() && newPath != null) { + paths.set(id, newPath); + return true; + } + return false; + } + + //削除 + public boolean removePath(int id) { + if (id > 0 && id < paths.size()) { + paths.set(id, null); + return true; + } + return false; + } + + //取得 + public String getPath(int id) { + if (id >= 0 && id < paths.size()) { + return paths.get(id); + } + return null; + } +} diff --git a/GameEngine/src/main/java/gameEngine/Time.java b/GameEngine/src/main/java/gameEngine/Time.java new file mode 100644 index 0000000..dd026db --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/Time.java @@ -0,0 +1,53 @@ +package gameEngine; + +public class Time { + public static long timeStarted = System.nanoTime(); + private static long lastFrameTime = System.nanoTime(); + public static float deltaTime = 0; + + private static final long targetFrameTimeNanos = (long) (1E9 / 180); // <- フレームタイム + private static long lastFpsCheckTime = System.nanoTime(); + private static int frameCount = 0; + private static float currentFps = 0; + + public static float getTime() { + return (float) ((System.nanoTime() - timeStarted) * 1E-9); + } + + public static void update() { + long currentTime = System.nanoTime(); + deltaTime = (float) ((currentTime - lastFrameTime) * 1E-9); + lastFrameTime = currentTime; + + // FPSの計算 + frameCount++; + if (currentTime - lastFpsCheckTime >= 1E9) { + currentFps = frameCount / ((currentTime - lastFpsCheckTime) * 1E-9f); + frameCount = 0; + lastFpsCheckTime = currentTime; + } + + // フレームレート制限 + long elapsedTime = currentTime - lastFrameTime; + if (elapsedTime < targetFrameTimeNanos) { + try { + Thread.sleep((targetFrameTimeNanos - elapsedTime) / 1_000_000); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + + public static void reset() { + timeStarted = System.nanoTime(); + lastFrameTime = timeStarted; + deltaTime = 0; + lastFpsCheckTime = timeStarted; + frameCount = 0; + currentFps = 0; + } + + public static int getFPS(){ + return (int)currentFps; + } +} diff --git a/GameEngine/src/main/java/gameEngine/entites/Camera.java b/GameEngine/src/main/java/gameEngine/entites/Camera.java new file mode 100644 index 0000000..5172219 --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/entites/Camera.java @@ -0,0 +1,25 @@ +package gameEngine.entites; + +import org.joml.Vector3f; + +public class Camera extends Entity{ + + public enum ProjectionType{ + PERSPECTIVE, //透視投影 + ORTHOGRAPHIC, //平行投影 + } + + private final ProjectionType projection; + + public Camera(String id, ProjectionType projection){ + super(id); + this.projection = projection; + } + public Vector3f getPosition() { + return transform.position; + } + public void move(float x, float y, float z) { + transform.position.add(x, y, z); + } + +} diff --git a/GameEngine/src/main/java/gameEngine/entites/EditorEntity.java b/GameEngine/src/main/java/gameEngine/entites/EditorEntity.java new file mode 100644 index 0000000..76a965b --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/entites/EditorEntity.java @@ -0,0 +1,51 @@ +package gameEngine.entites; + +import gameEngine.entites.editorComponents.EditorComponent; + +import java.util.ArrayList; +import java.util.List; + +public class EditorEntity extends Entity { + public final List editorComponents = new ArrayList<>(); + public enum Connectiontype {Mesh, MoveImage, Move} + public List ComponentConnections = new ArrayList<>(); + + public EditorEntity(String eid) { + super(eid); + } + + public void updateComponents() { + for (EditorComponent component : editorComponents) { + component.update(); + } + } + + public void addEditorComponent(EditorComponent component) { + for (EditorComponent existingComponent : editorComponents) { + if (existingComponent.getClass().equals(component.getClass())) { + System.out.println("Component already exists: " + component.getClass().getSimpleName()); + return; + } + } + this.editorComponents.add(component); + System.out.println("Editor Component added: " + component.getClass().getSimpleName()); + } + + public void removeEditorComponent(EditorComponent component) { + if (editorComponents.remove(component)) { + System.out.println("Editor Component removed: " + component.getClass().getSimpleName()); + } else { + System.out.println("Editor Component not found: " + component.getClass().getSimpleName()); + } + } + + public T getEditorComponent(Class componentClass){ + for (EditorComponent editorComponent : editorComponents) { + if (componentClass.isInstance(editorComponent)) { + return componentClass.cast(editorComponent); + } + } + return null; + } + +} \ No newline at end of file diff --git a/GameEngine/src/main/java/gameEngine/entites/Entity.java b/GameEngine/src/main/java/gameEngine/entites/Entity.java new file mode 100644 index 0000000..4318184 --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/entites/Entity.java @@ -0,0 +1,33 @@ +package gameEngine.entites; + +import gameEngine.entites.gameComponents.GameComponent; +import gameEngine.entites.gameComponents.MoveImage; +import gameEngine.geometry.Transform; + +import java.awt.*; + +public class Entity { + + private String entityId; + public Transform transform = new Transform(); + public Transform screenTransform = new Transform(); + + public boolean active = true; + public String name = "Entity"; + + public Entity(String eid) { + entityId = eid; + } + + public void setActive(boolean active) { + this.active = active; + } + public String getName() { return name; } + public void setName(String name){ + this.name = name; + } + public String getId(){ + return entityId; + } + +} diff --git a/GameEngine/src/main/java/gameEngine/entites/GameObject.java b/GameEngine/src/main/java/gameEngine/entites/GameObject.java new file mode 100644 index 0000000..d06d7b0 --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/entites/GameObject.java @@ -0,0 +1,60 @@ +package gameEngine.entites; + +import gameEngine.entites.gameComponents.GameComponent; +import gameEngine.entites.gameComponents.Mesh; +import gameEngine.entites.gameComponents.TextMesh; +import gameEngine.scenes.GameScene; +import gameEngine.views.Window; + +import java.util.ArrayList; +import java.util.List; + +public class GameObject extends Entity { + public final List gameComponents = new ArrayList<>(); + + public GameObject(String id) { + super(id); + } + + public void addComponent(GameComponent component) { + for (GameComponent existingComponent : gameComponents) { + if (existingComponent.getClass().equals(component.getClass())) { + System.out.println("Component already exists: " + component.getClass().getSimpleName()); + return; + } + } + this.gameComponents.add(component); + System.out.println("Component added: " + component.getClass().getSimpleName()); + } + + public void removeComponent(GameComponent component) { + boolean removed = this.gameComponents.remove(component); + if (removed) { + System.out.println("Component removed: " + component.getClass().getSimpleName()); + } else { + System.out.println("Component not found: " + component.getClass().getSimpleName()); + } + } + + public void initComponents() { + for (GameComponent component : gameComponents) { + component.init(); + } + } + + public void updateComponents() { + for (GameComponent component : gameComponents) { + component.update(); + } + } + + public T getComponent(Class componentClass){ + for (GameComponent gameComponent :gameComponents) { + if (componentClass.isInstance(gameComponent)) { + return componentClass.cast(gameComponent); + } + } + return null; + } + +} \ No newline at end of file diff --git a/GameEngine/src/main/java/gameEngine/entites/editorComponents/CameraView.java b/GameEngine/src/main/java/gameEngine/entites/editorComponents/CameraView.java new file mode 100644 index 0000000..adbbb5d --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/entites/editorComponents/CameraView.java @@ -0,0 +1,65 @@ +package gameEngine.entites.editorComponents; + +import gameEngine.entites.EditorEntity; +import gameEngine.geometry.Transform; +import gameEngine.views.Sprite; +import gameEngine.views.Text; +import gameEngine.views.Window; +import org.joml.Vector3f; + +import java.util.ArrayList; +import java.util.List; + +public class CameraView extends DraggableComponent { + + private PortView portviewA; + public Sprite meshSprite; + + public CameraView(EditorEntity parent) { + this.parent = parent; + meshSprite = new Sprite(Window.RESOURCE_PATH + "CameraFrameView.png"); + this.sprite = new Sprite(Window.RESOURCE_PATH + "CameraView.png"); + this.text = new Text(parent.screenTransform.position.x, parent.screenTransform.position.y, "Camera", 14); + sprite.updateSpriteDimensions(); + portviewA = new PortView(PortView.PortType.IN, parent); + } + + @Override + protected void updatePortView(Vector3f actualPosition, Transform transform) { + Vector3f adjustedPos = new Vector3f(actualPosition.x - 30, actualPosition.y + 8, actualPosition.z); + portviewA.update(adjustedPos, transform.rotation, transform.scale); + portviewA.handleDragging(); + } + + @Override + protected void additionalUpdate(Vector3f actualPosition, Transform transform, Vector3f cameraPosition) { + if (meshSprite != null) { + meshSprite.setPosition(new Vector3f(actualPosition.x, actualPosition.y, actualPosition.z)); + meshSprite.setRotation(transform.rotation); + meshSprite.setScale(transform.scale); + meshSprite.update(); + } + } + @Override + public EditorComponent copy() { + return this; + } + + @Override + public List getConnectedComponentViews() { + List connectedComponentViews = new ArrayList<>(); + + for (PortView connectedPort : portviewA.getConnectedPorts()) { + EditorEntity parentEntity = connectedPort.getParent(); + + if (parentEntity != null) { + for (EditorComponent component : parentEntity.editorComponents) { + if (component instanceof ComponentView) { + connectedComponentViews.add((ComponentView) component); + } + } + } + } + return connectedComponentViews; + } +} \ No newline at end of file diff --git a/GameEngine/src/main/java/gameEngine/entites/editorComponents/ComponentView.java b/GameEngine/src/main/java/gameEngine/entites/editorComponents/ComponentView.java new file mode 100644 index 0000000..6180040 --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/entites/editorComponents/ComponentView.java @@ -0,0 +1,129 @@ +package gameEngine.entites.editorComponents; + +import gameEngine.ResourceManager; +import gameEngine.entites.EditorEntity; +import gameEngine.entites.gameComponents.Move; +import gameEngine.geometry.Transform; +import gameEngine.input.Input; +import gameEngine.input.MouseInput; +import gameEngine.views.Sprite; +import gameEngine.views.Text; +import gameEngine.views.Window; +import org.joml.Vector3f; + +public class ComponentView extends DraggableComponent { + + public EditorEntity.Connectiontype connectionType; + private final PortView portview; + + //Mesh + public int meshId = 0; + public Sprite meshSprite; + ResourceManager resourceManager; + + //Move + public Text directionText; + public Move.Direction direction; + private int directionNum = 0; + + public ComponentView(EditorEntity parent, EditorEntity.Connectiontype connectionType, String text){ + this.parent = parent; + this.sprite = new Sprite(Window.RESOURCE_PATH + "ComponentView.png"); + sprite.updateSpriteDimensions(); + this.text = new Text(parent.screenTransform.position.x, parent.screenTransform.position.y, text, 14); + portview = new PortView(PortView.PortType.OUT, parent); + this.connectionType = connectionType; + + if(connectionType == EditorEntity.Connectiontype.Move) setMoveComponent(); + } + + //Meshの場合 + public ComponentView(EditorEntity parent, EditorEntity.Connectiontype connectionType, ResourceManager resourceManager, String text){ + this(parent, connectionType, text); + this.resourceManager = resourceManager; + meshSprite = new Sprite(resourceManager.getPath(meshId)); + } + + //Moveの場合 + public void setMoveComponent() { + directionText = new Text(parent.transform.position.x, parent.transform.position.y + 30 ,"LEFT",18); + direction = Move.Direction.LEFT; + } + + @Override + protected void updatePortView(Vector3f actualPosition, Transform transform) { + Vector3f adjustedPos = new Vector3f(actualPosition.x + 92, actualPosition.y, actualPosition.z); + portview.update(adjustedPos, transform.rotation, transform.scale); + portview.handleDragging(); + } + + @Override + protected void additionalUpdate(Vector3f actualPosition, Transform transform, Vector3f cameraPosition) { + float mouseX = MouseInput.getX(); + float mouseY = MouseInput.getY(); + if (meshSprite != null) { + meshSprite.setPosition(new Vector3f(actualPosition.x, actualPosition.y + 30, actualPosition.z)); + meshSprite.setRotation(transform.rotation); + meshSprite.setScale(transform.scale); + meshSprite.update(); + + if (Input.GetMouseButtonDown(0) && meshSprite.isMouseOver(mouseX, mouseY)) { + changeMeshSprite(); + } + } + + if(directionText != null){ + directionText.setPosition(new Vector3f(actualPosition.x, actualPosition.y + 30, actualPosition.z)); + directionText.setRotation(transform.rotation); + directionText.setScale(transform.scale); + directionText.update(); + + if (Input.GetMouseButtonDown(0) && directionText.isMouseOver(mouseX, mouseY)) { + changeDirectionType(); + } + } + } + + + private void changeMeshSprite() { + meshId = (meshId + 1) % resourceManager.getPathList().size(); + + // 新しいスプライトを設定 + String newPath = resourceManager.getPath(meshId); + if (newPath != null) { + meshSprite.setTexturePath(newPath); + } + } + + private void changeDirectionType(){ + if(directionNum < 3) directionNum += 1; + else directionNum = 0; + + if(directionNum == 0) { + direction = Move.Direction.LEFT; + directionText.setText("LEFT"); + } + else if(directionNum == 1) { + direction = Move.Direction.RIGHT; + directionText.setText("RIGHT"); + } + else if(directionNum == 2) { + direction = Move.Direction.UP; + directionText.setText("UP"); + } + else if(directionNum == 3) { + direction = Move.Direction.DOWN; + directionText.setText("DOWN"); + } + } + + @Override + public EditorComponent copy() { + return this; + } + + public String getSpritePath(){ + if(meshId == 0) return null; + return resourceManager.getPath(meshId); + } +} diff --git a/GameEngine/src/main/java/gameEngine/entites/editorComponents/Connection.java b/GameEngine/src/main/java/gameEngine/entites/editorComponents/Connection.java new file mode 100644 index 0000000..333af10 --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/entites/editorComponents/Connection.java @@ -0,0 +1,45 @@ +package gameEngine.entites.editorComponents; + +import gameEngine.views.LineRenderer; +import gameEngine.views.Sprite; +import gameEngine.views.Window; +import org.joml.Vector3f; + +public class Connection { + private final PortView portA; + private final PortView portB; + private final LineRenderer lineRenderer; + private final Sprite removeButtonSprite; + + public Connection(PortView inPort, PortView outPort) { + this.portA = inPort; + this.portB = outPort; + this.lineRenderer = new LineRenderer(Window.RESOURCE_PATH + "Line.png"); + this.removeButtonSprite = new Sprite(Window.RESOURCE_PATH + "RemoveButton.png"); + } + + public void render() { + Vector3f posA = portA.getPosition(); + Vector3f posB = portB.getPosition(); + + lineRenderer.updateLine(portA.getPosition(), portB.getPosition(), 3); + lineRenderer.render(); + + Vector3f buttonPosition = lineRenderer.calculatePosition(posA, posB); + removeButtonSprite.setPosition(buttonPosition.x+2, buttonPosition.y+2); + removeButtonSprite.setScale(1,1,1); + removeButtonSprite.update(); + } + + public boolean isRemoveButtonClicked(float mouseX, float mouseY) { + return removeButtonSprite.isMouseOver(mouseX, mouseY); + } + + public PortView getPortA() { + return portA; + } + + public PortView getPortB() { + return portB; + } +} diff --git a/GameEngine/src/main/java/gameEngine/entites/editorComponents/DraggableComponent.java b/GameEngine/src/main/java/gameEngine/entites/editorComponents/DraggableComponent.java new file mode 100644 index 0000000..d02be68 --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/entites/editorComponents/DraggableComponent.java @@ -0,0 +1,171 @@ +package gameEngine.entites.editorComponents; + +import gameEngine.entites.EditorEntity; +import gameEngine.geometry.Transform; +import gameEngine.input.Input; +import gameEngine.input.MouseInput; +import gameEngine.scenes.EditorScene; +import gameEngine.views.Sprite; +import gameEngine.views.Text; +import gameEngine.views.Window; +import org.joml.Vector3f; + +import java.util.ArrayList; +import java.util.List; + +public abstract class DraggableComponent extends EditorComponent { + + protected EditorEntity parent; + protected Sprite sprite; + protected Text text; + protected boolean isDraggable = true; + protected boolean isLeftDragging = false; + protected boolean isRightDragging = false; + protected float dragOffsetX = 0.0f; + protected float dragOffsetY = 0.0f; + + @Override + public void init() { + sprite.update(); + sprite.updateSpriteDimensions(); + } + + @Override + public void update(){ + if (!(Window.getInstance().getScene() instanceof EditorScene)) return; + + Transform transform = parent.transform; + Transform screenTransform = parent.screenTransform; + + Vector3f cameraPosition = Window.getInstance().getScene().getCamera().getPosition(); + Vector3f actualPosition = new Vector3f(screenTransform.position).sub(cameraPosition); + + //サブクラス固有処理 + updatePortView(actualPosition, transform); + additionalUpdate(actualPosition, transform, cameraPosition); + + updateSprite(actualPosition, transform); + updateText(actualPosition, transform); + } + + private void updateSprite(Vector3f actualPosition, Transform transform){ + sprite.setPosition(actualPosition); + sprite.setRotation(transform.rotation); + sprite.setScale(transform.scale); + sprite.update(); + } + + private void updateText(Vector3f actualPosition, Transform transform){ + text.setPosition(new Vector3f(0,-16,0).add(actualPosition)); + text.setRotation(transform.rotation); + text.setScale(transform.scale); + text.update(); + } + + public void setDraggable(boolean draggable) { + this.isDraggable = draggable; + } + + public void handleDragging() { + if (!isDraggable) return; + + float mouseX = MouseInput.getX(); + float mouseY = MouseInput.getY(); + EditorScene scene = (EditorScene) Window.getInstance().getScene(); + + if (scene.getSelectedEntity() == null) { + if (Input.GetMouseButtonDown(0) && isMouseOver(mouseX, mouseY)) { + isLeftDragging = true; + scene.setSelectedEntity(parent); + scene.setClickedEntity(parent); + startDragAction(mouseX, mouseY); + } + + if (Input.GetMouseButtonDown(1) && isMouseOver(mouseX, mouseY)) { + isRightDragging = true; + scene.setSelectedEntity(parent); + scene.setClickedEntity(parent); + startDragAction(mouseX, mouseY); + } + } + + if (isLeftDragging && Input.GetMouseButton(0)) { + draggingAction(mouseX, mouseY, false); + } + if (isRightDragging && Input.GetMouseButton(1)) { + draggingAction(mouseX, mouseY, true); + } + + if (Input.GetMouseButtonUp(0)) { + isLeftDragging = false; + scene.clearSelectedObject(); + } + if (Input.GetMouseButtonUp(1)) { + isRightDragging = false; + scene.clearSelectedObject(); + } + + } + + protected boolean isMouseOver(float mouseX, float mouseY) { + return sprite.isMouseOver(mouseX, mouseY); + } + + protected void startDragAction(float mouseX, float mouseY) { + Vector3f cameraPosition = Window.getInstance().getScene().getCamera().getPosition(); + + float worldMouseX = mouseX + cameraPosition.x; + float worldMouseY = mouseY + cameraPosition.y; + + dragOffsetX = worldMouseX - parent.transform.position.x; + dragOffsetY = worldMouseY - parent.transform.position.y; + } + + protected void draggingAction(float mouseX, float mouseY, boolean isRightDrag) { + Vector3f cameraPosition = Window.getInstance().getScene().getCamera().getPosition(); + + float worldMouseX = mouseX + cameraPosition.x; + float worldMouseY = mouseY + cameraPosition.y; + + float targetX = worldMouseX - dragOffsetX; + float targetY = worldMouseY - dragOffsetY; + + float deltaX = targetX - parent.transform.position.x; + float deltaY = targetY - parent.transform.position.y; + + parent.transform.setPosition(targetX, targetY, parent.transform.position.z); + parent.screenTransform.setPosition( + parent.transform.position.x, + parent.transform.position.y, + parent.transform.position.z + ); + + if (isRightDrag) { + handleConnectedComponents(deltaX, deltaY, cameraPosition); + } + } + + + private void handleConnectedComponents(float deltaX, float deltaY, Vector3f cameraPosition) { + for (ComponentView component : getConnectedComponentViews()) { + EditorEntity componentParent = component.parent; + componentParent.transform.setPosition( + componentParent.transform.position.x + deltaX, + componentParent.transform.position.y + deltaY, + componentParent.transform.position.z + ); + + componentParent.screenTransform.setPosition( + componentParent.transform.position.x, + componentParent.transform.position.y, + componentParent.transform.position.z + ); + } + } + + protected abstract void updatePortView(Vector3f actualPosition, Transform transform); + protected abstract void additionalUpdate(Vector3f actualPosition, Transform transform, Vector3f cameraPosition); + protected List getConnectedComponentViews() { + return new ArrayList<>(); + } +} diff --git a/GameEngine/src/main/java/gameEngine/entites/editorComponents/EditorComponent.java b/GameEngine/src/main/java/gameEngine/entites/editorComponents/EditorComponent.java new file mode 100644 index 0000000..1104b0e --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/entites/editorComponents/EditorComponent.java @@ -0,0 +1,28 @@ +package gameEngine.entites.editorComponents; + +import org.joml.Vector3f; + +public abstract class EditorComponent { + Vector3f localPosition = new Vector3f(0,0,0); + Vector3f localRotation = new Vector3f(0,0,0); + Vector3f localScale = new Vector3f(0,0,0); + public void init() { + // 初期化処理 + } + + public void update() { + // 更新処理 + } + + public abstract EditorComponent copy(); + + public void setLocalPosition(Vector3f pos){ + localPosition.set(pos); + } + public void setLocalRotation(Vector3f rot){ + localRotation.set(rot); + } + public void setLocalScale(Vector3f scale){ + localScale.set(scale); + } +} \ No newline at end of file diff --git a/GameEngine/src/main/java/gameEngine/entites/editorComponents/EntityView.java b/GameEngine/src/main/java/gameEngine/entites/editorComponents/EntityView.java new file mode 100644 index 0000000..5d2f0d6 --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/entites/editorComponents/EntityView.java @@ -0,0 +1,58 @@ +package gameEngine.entites.editorComponents; + +import gameEngine.entites.EditorEntity; +import gameEngine.geometry.Transform; +import gameEngine.views.Sprite; +import gameEngine.views.Text; +import gameEngine.views.Window; +import org.joml.Vector3f; + +import java.util.ArrayList; +import java.util.List; + +public class EntityView extends DraggableComponent { + + private PortView portviewA; + + public EntityView(EditorEntity parent) { + this.parent = parent; + this.sprite = new Sprite(Window.RESOURCE_PATH + "EntityView.png"); + this.text = new Text(parent.screenTransform.position.x, parent.screenTransform.position.y, "Entity", 14); + sprite.updateSpriteDimensions(); + portviewA = new PortView(PortView.PortType.IN, parent); + } + + @Override + protected void updatePortView(Vector3f actualPosition, Transform transform) { + Vector3f adjustedPos = new Vector3f(actualPosition.x - 30, actualPosition.y + 8, actualPosition.z); + portviewA.update(adjustedPos, transform.rotation, transform.scale); + portviewA.handleDragging(); + } + + @Override + protected void additionalUpdate(Vector3f actualPosition, Transform transform, Vector3f cameraPosition) { + } + + @Override + public EditorComponent copy() { + return this; + } + + @Override + public List getConnectedComponentViews() { + List connectedComponentViews = new ArrayList<>(); + + for (PortView connectedPort : portviewA.getConnectedPorts()) { + EditorEntity parentEntity = connectedPort.getParent(); + + if (parentEntity != null) { + for (EditorComponent component : parentEntity.editorComponents) { + if (component instanceof ComponentView) { + connectedComponentViews.add((ComponentView) component); + } + } + } + } + return connectedComponentViews; + } +} \ No newline at end of file diff --git a/GameEngine/src/main/java/gameEngine/entites/editorComponents/PortView.java b/GameEngine/src/main/java/gameEngine/entites/editorComponents/PortView.java new file mode 100644 index 0000000..bdda611 --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/entites/editorComponents/PortView.java @@ -0,0 +1,106 @@ +package gameEngine.entites.editorComponents; + +import gameEngine.entites.EditorEntity; +import gameEngine.scenes.EditorScene; +import gameEngine.views.LineRenderer; +import gameEngine.views.Sprite; +import gameEngine.input.Input; +import gameEngine.input.MouseInput; +import gameEngine.views.Window; +import org.joml.Vector3f; + +import java.util.ArrayList; +import java.util.List; + +public class PortView { + public enum PortType {OUT, IN} + private final PortType portType; + private Sprite sprite; + private Vector3f position = new Vector3f(0,0,0); + private EditorEntity parent; + private List connectedPorts = new ArrayList<>(); + private LineRenderer lineRenderer; + private boolean dragging = false; + + public PortView(PortType portType, EditorEntity parent){ + this.lineRenderer = new LineRenderer(Window.RESOURCE_PATH + "Line.png"); + this.parent = parent; + if(portType == PortType.IN){ + this.sprite = new Sprite(Window.RESOURCE_PATH + "InPort.png"); + } else if(portType == PortType.OUT){ + this.sprite = new Sprite(Window.RESOURCE_PATH + "OutPort.png"); + } + this.portType = portType; + assert sprite != null; + sprite.updateSpriteDimensions(); + } + + public void update(Vector3f pos, Vector3f rot, Vector3f scale){ + sprite.setPosition(pos); + sprite.setRotation(rot); + sprite.setScale(scale); + sprite.update(); + position.set(pos); + } + + public void handleDragging() { + float mouseX = MouseInput.getX(); + float mouseY = MouseInput.getY(); + + if (Input.GetMouseButtonDown(0) && isMouseOver(mouseX, mouseY)) { + EditorScene editorScene = (EditorScene) Window.getInstance().getScene(); + editorScene.connectionManager.handlePortPress(this); + dragging = true; + } + + if (Input.GetMouseButton(0) && dragging) { + lineRenderer.updateLine(sprite.position, new Vector3f(mouseX, mouseY, 0), 3); + } + + if (Input.GetMouseButtonUp(0)) { + dragging = false; + if(isMouseOver(mouseX, mouseY)){ + EditorScene editorScene = (EditorScene) Window.getInstance().getScene(); + editorScene.connectionManager.handlePortRelease(this); + } + } + + } + + protected boolean isMouseOver(float mouseX, float mouseY) { + float width = sprite.getDisplayedWidth(); + float height = sprite.getDisplayedHeight(); + + return mouseX >= position.x && mouseX <= position.x + width && + mouseY >= position.y && mouseY <= position.y + height; + } + + public Vector3f getPosition() { + return position; + } + + public PortType getPortType(){ return portType; } + + public EditorEntity getParent() { + return parent; + } + + public void addConnectedPort(PortView portView) { + if (!connectedPorts.contains(portView)) { + connectedPorts.add(portView); + } + } + + public boolean isConnectedTo(PortView portView) { + return connectedPorts.contains(portView); + } + + public void removeConnectedPort(PortView portView) { + connectedPorts.remove(portView); + } + + public List getConnectedPorts(){ + return connectedPorts; + } + +} \ No newline at end of file diff --git a/GameEngine/src/main/java/gameEngine/entites/gameComponents/ButtonComponent.java b/GameEngine/src/main/java/gameEngine/entites/gameComponents/ButtonComponent.java new file mode 100644 index 0000000..017e670 --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/entites/gameComponents/ButtonComponent.java @@ -0,0 +1,44 @@ +package gameEngine.entites.gameComponents; + +import gameEngine.entites.Entity; +import gameEngine.entites.GameObject; +import gameEngine.views.Button; +import gameEngine.views.Color; + +public class ButtonComponent extends GameComponent { + private Button button; + + public ButtonComponent(GameObject parent) { + this.button = new Button(parent.transform.position.x, parent.transform.position.y, + parent.transform.scale.x, parent.transform.scale.y); + } + + @Override + public GameComponent copy() { + return this; // Deep copy if needed + } + + public void init() { + button.update(); + } + + public void update() { + button.update(); + } + + public void addListener(Runnable listener) { + button.addListener(listener); + } + + public void clearListeners() { + button.clearListeners(); + } + + public void setNormalColor(Color color) { + button.setNormalColor(color); + } + + public void setPressedColor(Color color) { + button.setPressedColor(color); + } +} diff --git a/GameEngine/src/main/java/gameEngine/entites/gameComponents/Collider.java b/GameEngine/src/main/java/gameEngine/entites/gameComponents/Collider.java new file mode 100644 index 0000000..d6b924f --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/entites/gameComponents/Collider.java @@ -0,0 +1,107 @@ +package gameEngine.entites.gameComponents; + +import gameEngine.entites.Entity; +import gameEngine.entites.GameObject; +import gameEngine.views.Window; + +public class Collider extends GameComponent { + private GameObject parent; + private boolean isPassable; // すり抜け可能かどうか + + public Collider(GameObject parent, boolean isPassable) { + this.parent = parent; + this.isPassable = isPassable; + } + + @Override + public GameComponent copy() { + return new Collider(parent, isPassable); + } + + @Override + public void init() { + } + + @Override + public void update() { + for (Entity other : Window.getInstance().getScene().entities.values()) { + GameObject otherObject = (GameObject) other; + if (otherObject == parent) continue; // 自分自身は無視 + + Collider otherCollider = otherObject.getComponent(Collider.class); + if (otherCollider != null && isCollidingWith(otherObject)) { + System.out.println("Collision detected between " + + parent.getName() + " and " + otherObject.getName()); + + if (!isPassable && !otherCollider.isPassable) { + resolveCollision(otherObject); + } + } + } + } + + private boolean isCollidingWith(GameObject other) { + Mesh thisMesh = parent.getComponent(Mesh.class); + Mesh otherMesh = other.getComponent(Mesh.class); + + if (thisMesh == null || otherMesh == null) { + return false; // Meshがない場合は判定できない + } + + float thisLeft = parent.transform.position.x; + float thisRight = thisLeft + thisMesh.getDisplayedWidth(); + float thisTop = parent.transform.position.y; + float thisBottom = thisTop + thisMesh.getDisplayedHeight(); + + float otherLeft = other.transform.position.x; + float otherRight = otherLeft + otherMesh.getDisplayedWidth(); + float otherTop = other.transform.position.y; + float otherBottom = otherTop + otherMesh.getDisplayedHeight(); + + return thisRight > otherLeft && thisLeft < otherRight && + thisBottom > otherTop && thisTop < otherBottom; + } + + private void resolveCollision(GameObject other) { + // 衝突を解消する処理 + Mesh thisMesh = parent.getComponent(Mesh.class); + Mesh otherMesh = other.getComponent(Mesh.class); + + if (thisMesh == null || otherMesh == null) { + return; + } + + float thisLeft = parent.transform.position.x; + float thisRight = thisLeft + thisMesh.getDisplayedWidth(); + float thisTop = parent.transform.position.y; + float thisBottom = thisTop + thisMesh.getDisplayedHeight(); + + float otherLeft = other.transform.position.x; + float otherRight = otherLeft + otherMesh.getDisplayedWidth(); + float otherTop = other.transform.position.y; + float otherBottom = otherTop + otherMesh.getDisplayedHeight(); + + float overlapX = Math.min(thisRight, otherRight) - Math.max(thisLeft, otherLeft); + float overlapY = Math.min(thisBottom, otherBottom) - Math.max(thisTop, otherTop); + + if (overlapX < overlapY) { + // 横方向の重なりを解消 + if (thisLeft < otherLeft) { + parent.transform.position.x -= overlapX; + } else { + parent.transform.position.x += overlapX; + } + } else { + // 縦方向の重なりを解消 + if (thisTop < otherTop) { + parent.transform.position.y -= overlapY; + } else { + parent.transform.position.y += overlapY; + } + } + } + + public boolean isPassable() { + return isPassable; + } +} diff --git a/GameEngine/src/main/java/gameEngine/entites/gameComponents/CopyEntity.java b/GameEngine/src/main/java/gameEngine/entites/gameComponents/CopyEntity.java new file mode 100644 index 0000000..1d8ae16 --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/entites/gameComponents/CopyEntity.java @@ -0,0 +1,28 @@ +package gameEngine.entites.gameComponents; + +import gameEngine.entites.GameObject; +import gameEngine.input.Input; +import gameEngine.views.Window; + +import static org.lwjgl.glfw.GLFW.GLFW_KEY_G; + +public class CopyEntity extends GameComponent{ + + Window window = Window.getInstance(); + private GameObject parent; + public CopyEntity(GameObject parent){ + this.parent = parent; + } + @Override + public GameComponent copy() { + return this; + } + + + public void update() { + if(Input.GetKeyDown(GLFW_KEY_G)){ + window.getScene().Instantiate((GameObject) parent); + System.out.println(parent.getName() + " copied."); + } + } +} diff --git a/GameEngine/src/main/java/gameEngine/entites/gameComponents/GameComponent.java b/GameEngine/src/main/java/gameEngine/entites/gameComponents/GameComponent.java new file mode 100644 index 0000000..c3db312 --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/entites/gameComponents/GameComponent.java @@ -0,0 +1,30 @@ +package gameEngine.entites.gameComponents; + +import org.joml.Vector3f; + +public abstract class GameComponent { + + Vector3f localPosition = new Vector3f(0,0,0); + Vector3f localRotation = new Vector3f(0,0,0); + Vector3f localScale = new Vector3f(0,0,0); + public abstract GameComponent copy(); + + public void init() { + + } + + public void update() { + + } + + public void setLocalPosition(Vector3f pos){ + localPosition.set(pos); + } + public void setLocalRotation(Vector3f rot){ + localRotation.set(rot); + } + public void setLocalScale(Vector3f scale){ + localScale.set(scale); + } + +} diff --git a/GameEngine/src/main/java/gameEngine/entites/gameComponents/Mesh.java b/GameEngine/src/main/java/gameEngine/entites/gameComponents/Mesh.java new file mode 100644 index 0000000..befcd07 --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/entites/gameComponents/Mesh.java @@ -0,0 +1,81 @@ +package gameEngine.entites.gameComponents; + +import gameEngine.entites.Entity; +import gameEngine.entites.GameObject; +import gameEngine.geometry.Transform; +import gameEngine.scenes.GameScene; +import gameEngine.views.Color; +import gameEngine.views.Sprite; +import gameEngine.views.Window; +import org.joml.Vector3f; + +public class Mesh extends GameComponent { + + private GameObject parent; + public enum MeshType { + SPRITE, CUBE + } + + private final MeshType type; + private Sprite sprite; + + public Mesh(GameObject parent, MeshType type, String texturePath) { + this.parent = parent; + this.type = type; + if (type == MeshType.SPRITE) { + this.sprite = new Sprite(texturePath); + sprite.updateSpriteDimensions(); + } + } + public Mesh(Mesh original, GameObject newParent) { + this.type = original.type; + this.sprite = original.sprite; + this.parent = newParent; + sprite.updateSpriteDimensions(); + } + + @Override + public GameComponent copy() { + return this; + } + + @Override + public void init() { + if (type == MeshType.SPRITE) { + sprite.update(); + sprite.updateSpriteDimensions(); + } + } + + @Override + public void update() { + if (type == MeshType.SPRITE) { + Transform transform = parent.transform; + Transform screenTransform = parent.screenTransform; + + Vector3f actualPosition = new Vector3f(screenTransform.position); + + sprite.setPosition(actualPosition); + sprite.setRotation(transform.rotation); + sprite.setScale(transform.scale); + sprite.update(); + } + } + + public void setSprite(String texturePath){ + sprite.setTexturePath(texturePath); + } + + public void setColor(Color color){ + sprite.setColor(color); + sprite.update(); + } + + public float getDisplayedWidth() { + return sprite != null ? sprite.getDisplayedWidth() : 0; + } + + public float getDisplayedHeight() { + return sprite != null ? sprite.getDisplayedHeight() : 0; + } +} \ No newline at end of file diff --git a/GameEngine/src/main/java/gameEngine/entites/gameComponents/Move.java b/GameEngine/src/main/java/gameEngine/entites/gameComponents/Move.java new file mode 100644 index 0000000..c791710 --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/entites/gameComponents/Move.java @@ -0,0 +1,81 @@ +package gameEngine.entites.gameComponents; + +import gameEngine.Time; +import gameEngine.entites.GameObject; +import gameEngine.geometry.Transform; + +public class Move extends GameComponent{ + + private GameObject parent; + private final float moveSpeed = 100; + public enum Direction {UP, DOWN, LEFT, RIGHT} + private Direction direction; + public enum MoveType{ + Straight, Wave + } + private MoveType moveType = MoveType.Straight; + + public Move(GameObject parent, Direction direction){ + this.parent = parent; + this.direction = direction; + } + @Override + public GameComponent copy() { + return this; + } + + public void update() { + if(moveType == MoveType.Straight) moveStraight(); + if(moveType == MoveType.Wave) moveWave(); + } + + private void moveStraight(){ + Transform transform = parent.transform; + if (transform != null) { + float x = transform.position.x; + float y = transform.position.y; + float z = transform.position.z; + + if (direction == Direction.UP) { + transform.setPosition(x, y - moveSpeed * Time.deltaTime, z); + } + if (direction == Direction.LEFT) { + transform.setPosition(x - moveSpeed * Time.deltaTime, y, z); + } + if (direction == Direction.DOWN) { + transform.setPosition(x, y + moveSpeed * Time.deltaTime, z); + } + if (direction == Direction.RIGHT) { + transform.setPosition(x + moveSpeed * Time.deltaTime, y, z); + } + } + } + + private float waveAmplitude = 2f; // 振幅 + private float waveFrequency = 4.0f; // 周波数 + private float waveTime = 0.0f; + + private void moveWave(){ + Transform transform = parent.transform; + if (transform != null) { + float x = transform.position.x; + float y = transform.position.y; + float z = transform.position.z; + + waveTime += Time.deltaTime; + + if (direction == Direction.UP || direction == Direction.DOWN) { + float baseY = direction == Direction.UP ? y - moveSpeed * Time.deltaTime : y + moveSpeed * Time.deltaTime; + float offsetX = (float) Math.sin(waveTime * waveFrequency) * waveAmplitude; + transform.setPosition(x + offsetX, baseY, z); + } + + if (direction == Direction.LEFT || direction == Direction.RIGHT) { + float baseX = direction == Direction.LEFT ? x - moveSpeed * Time.deltaTime : x + moveSpeed * Time.deltaTime; + float offsetY = (float) Math.sin(waveTime * waveFrequency) * waveAmplitude; + transform.setPosition(baseX, y + offsetY, z); + } + } + } + +} diff --git a/GameEngine/src/main/java/gameEngine/entites/gameComponents/MoveImage.java b/GameEngine/src/main/java/gameEngine/entites/gameComponents/MoveImage.java new file mode 100644 index 0000000..a3ed53f --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/entites/gameComponents/MoveImage.java @@ -0,0 +1,90 @@ +package gameEngine.entites.gameComponents; +import gameEngine.Time; +import gameEngine.entites.GameObject; +import gameEngine.geometry.Transform; +import gameEngine.input.*; + +import static org.lwjgl.glfw.GLFW.*; + +public class MoveImage extends GameComponent{ + + private GameObject parent; + private final int moveSpeed = 100; + private final int rotateSpeed = 100; + + public MoveImage(GameObject parent){ + this.parent = parent; + } + @Override + public GameComponent copy() { + return this; + } + + public void update() { + Transform transform = parent.transform; + if (transform != null) { + handlePositionInput(transform); + handleRotationInput(transform); + } + } + + private void handlePositionInput(Transform transform) { + float x = transform.position.x; + float y = transform.position.y; + float z = transform.position.z; + + if (Input.GetKey(GLFW_KEY_W)) { + transform.setPosition(x, y - moveSpeed * Time.deltaTime, z); + } + if (Input.GetKey(GLFW_KEY_A)) { + transform.setPosition(x - moveSpeed * Time.deltaTime, y, z); + } + if (Input.GetKey(GLFW_KEY_S)) { + transform.setPosition(x, y + moveSpeed * Time.deltaTime, z); + } + if (Input.GetKey(GLFW_KEY_D)) { + transform.setPosition(x + moveSpeed * Time.deltaTime, y, z); + } + if (Input.GetKey(GLFW_KEY_Q)) { + transform.setPosition(x, y, z - moveSpeed * Time.deltaTime); + } + if (Input.GetKey(GLFW_KEY_E)) { + transform.setPosition(x, y, z + moveSpeed * Time.deltaTime); + } + } + + private void handleRotationInput(Transform transform) { + float rotX = transform.rotation.x; + float rotY = transform.rotation.y; + float rotZ = transform.rotation.z; + + if (Input.GetKey(GLFW_KEY_Z)) { + transform.setRotation(wrapAngle(rotX - rotateSpeed * Time.deltaTime), rotY, rotZ); // 左回転 + } + if (Input.GetKey(GLFW_KEY_C)) { + transform.setRotation(wrapAngle(rotX + rotateSpeed * Time.deltaTime), rotY, rotZ); // 右回転 + } + if (Input.GetKey(GLFW_KEY_UP)) { + transform.setRotation(rotX, wrapAngle(rotY - rotateSpeed * Time.deltaTime), rotZ); // 上方向 + } + if (Input.GetKey(GLFW_KEY_DOWN)) { + transform.setRotation(rotX, wrapAngle(rotY + rotateSpeed * Time.deltaTime), rotZ); // 下方向 + } + if (Input.GetKey(GLFW_KEY_LEFT)) { + transform.setRotation(rotX, rotY, wrapAngle(rotZ - rotateSpeed * Time.deltaTime)); // 左方向 + } + if (Input.GetKey(GLFW_KEY_RIGHT)) { + transform.setRotation(rotX, rotY, wrapAngle(rotZ + rotateSpeed * Time.deltaTime)); // 右方向 + } + } + + private float wrapAngle(float angle) { + if (angle < 0) { + angle += 360; + } else if (angle >= 360) { + angle -= 360; + } + return angle; + } + +} diff --git a/GameEngine/src/main/java/gameEngine/entites/gameComponents/Physics.java b/GameEngine/src/main/java/gameEngine/entites/gameComponents/Physics.java new file mode 100644 index 0000000..ce375a9 --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/entites/gameComponents/Physics.java @@ -0,0 +1,36 @@ +package gameEngine.entites.gameComponents; + +import gameEngine.Time; +import gameEngine.entites.GameObject; + +public class Physics extends GameComponent{ + + private GameObject parent; + public boolean useGravity = true; + private float gravity = 9.8f; // 地球の重力加速度を使用 + private float velocityY = 0; // Y軸方向の速度 Resetしなければ速度が引き継がれる + + public Physics(GameObject parent){ + this.parent = parent; + } + + @Override + public GameComponent copy() { + return this; + } + + public void update() { + if (useGravity) { + float deltaTime = Time.deltaTime; + + velocityY += gravity * deltaTime; + + float y = parent.transform.position.y; + parent.transform.setPosition(parent.transform.position.x, y + velocityY * deltaTime, parent.transform.position.z); + } + } + + public void reset() { + velocityY = 0; + } +} diff --git a/GameEngine/src/main/java/gameEngine/entites/gameComponents/TextMesh.java b/GameEngine/src/main/java/gameEngine/entites/gameComponents/TextMesh.java new file mode 100644 index 0000000..fb0ac60 --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/entites/gameComponents/TextMesh.java @@ -0,0 +1,69 @@ +package gameEngine.entites.gameComponents; + +import gameEngine.entites.EditorEntity; +import gameEngine.entites.GameObject; +import gameEngine.input.Input; +import gameEngine.input.MouseInput; +import gameEngine.scenes.EditorScene; +import gameEngine.views.*; +import org.joml.Vector3f; + +import static org.lwjgl.glfw.GLFW.*; + +public class TextMesh extends GameComponent{ + + private GameObject parent; + private Text textRenderer; + + public TextMesh(GameObject parent, String text, int textSize) { + this.parent = parent; + this.textRenderer = new Text(parent.transform.position.x, parent.transform.position.y, text, textSize); + } + + public TextMesh(TextMesh original, GameObject newParent) { + this.parent = newParent; + this.textRenderer = new Text( + newParent.transform.position.x, + newParent.transform.position.y, + original.textRenderer.text, + original.textRenderer.textSize + ); + } + + @Override + public GameComponent copy() { + return this; + } + + @Override + public void init() { + textRenderer.update(); + } + + @Override + public void update() { + Vector3f globalPosition = new Vector3f(parent.transform.position).add(localPosition); + textRenderer.setPosition(globalPosition); + Vector3f globalRotation = new Vector3f(parent.transform.rotation).add(localRotation); + textRenderer.setRotation(globalRotation); + Vector3f globalScale = new Vector3f(parent.transform.scale).add(localScale); + textRenderer.setScale(globalScale); + textRenderer.update(); + } + + public void setText(String newText){ + textRenderer.setText(newText); + } + + public void setTextSize(int newTextSize) { + textRenderer.setTextSize(newTextSize); + } + + public void setColor(Color color){ + textRenderer.setColor(color); + } + + public int getTextSize(){ + return textRenderer.textSize; + } +} diff --git a/GameEngine/src/main/java/gameEngine/geometry/Transform.java b/GameEngine/src/main/java/gameEngine/geometry/Transform.java new file mode 100644 index 0000000..7c9dde7 --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/geometry/Transform.java @@ -0,0 +1,49 @@ +package gameEngine.geometry; + +import org.joml.Vector3f; + +public class Transform { + + public Vector3f position; + public Vector3f rotation; + public Vector3f scale; + + public Transform() { + this.position = new Vector3f(0, 0, 0); + this.rotation = new Vector3f(0, 0, 0); + this.scale = new Vector3f(1, 1, 1); + } + + public Transform(Transform other) { + this.position = new Vector3f(other.position); + this.rotation = new Vector3f(other.rotation); + this.scale = new Vector3f(other.scale); + } + + public void setPosition(float x, float y, float z) { + position.x = x; + position.y = y; + position.z = z; + } + public void setPosition(Vector3f newPosition){ + position = newPosition; + } + + public void setRotation(float x, float y, float z) { + rotation.x = x; + rotation.y = y; + rotation.z = z; + } + public void setRotation(Vector3f newRotation){ + rotation = newRotation; + } + + public void setScale(float x, float y, float z) { + scale.x = x; + scale.y = y; + scale.z = z; + } + public void setScale(Vector3f newScale){ + scale = newScale; + } +} diff --git a/GameEngine/src/main/java/gameEngine/input/Input.java b/GameEngine/src/main/java/gameEngine/input/Input.java new file mode 100644 index 0000000..fb50e61 --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/input/Input.java @@ -0,0 +1,40 @@ +package gameEngine.input; + +import java.util.List; + +public class Input { + + // キーが押された瞬間 + public static boolean GetKeyDown(int keyCode) { + return KeyInput.isKeyDown(keyCode); + } + + // キーが押され続けているか + public static boolean GetKey(int keyCode) { + return KeyInput.isKeyPressed(keyCode); + } + + // キーが離された瞬間 + public static boolean GetKeyUp(int keyCode) { + return KeyInput.isKeyUp(keyCode); + } + + // マウスボタンが押された瞬間 + public static boolean GetMouseButtonDown(int mouseButton) { + return MouseInput.isMouseButtonDown(mouseButton); + } + + // マウスボタンが押され続けているか + public static boolean GetMouseButton(int mouseButton) { + return MouseInput.isMouseButtonPressed(mouseButton); + } + + // マウスボタンが離された瞬間 + public static boolean GetMouseButtonUp(int mouseButton) { + return MouseInput.isMouseButtonUp(mouseButton); + } + + public static List getPressedKeys() { + return KeyInput.getPressedKeys(); + } +} diff --git a/GameEngine/src/main/java/gameEngine/input/KeyInput.java b/GameEngine/src/main/java/gameEngine/input/KeyInput.java new file mode 100644 index 0000000..6db6041 --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/input/KeyInput.java @@ -0,0 +1,81 @@ +package gameEngine.input; + +import java.util.ArrayList; +import java.util.List; + +import static org.lwjgl.glfw.GLFW.*; + +public class KeyInput { + private static KeyInput instance; + private boolean keyPressed[] = new boolean[GLFW_KEY_LAST + 1]; // 押されている状態を保持 + private boolean keyDown[] = new boolean[GLFW_KEY_LAST + 1]; // 押された瞬間を検知 + private boolean keyUp[] = new boolean[GLFW_KEY_LAST + 1]; // 離された瞬間を検知 + + private KeyInput() { + } + + public static KeyInput get() { + if (KeyInput.instance == null) { + KeyInput.instance = new KeyInput(); + } + return KeyInput.instance; + } + + public static void keyCallback(long window, int key, int scancode, int action, int mods) { + if (key <= GLFW_KEY_LAST && key >= 0) { + KeyInput input = get(); + if (action == GLFW_PRESS) { + if (!input.keyPressed[key]) { + input.keyDown[key] = true; // 押された瞬間 + } + input.keyPressed[key] = true; + } else if (action == GLFW_RELEASE) { + input.keyPressed[key] = false; + input.keyUp[key] = true; // 離された瞬間 + } + } + } + + // フレームの終わりでリセットするメソッド + public static void endFrame() { + for (int i = 0; i < get().keyDown.length; i++) { + get().keyDown[i] = false; + get().keyUp[i] = false; + } + } + + protected static boolean isKeyDown(int keyCode) { + if (keyCode <= GLFW_KEY_LAST && keyCode >= 0) { + boolean result = get().keyDown[keyCode]; + get().keyDown[keyCode] = false; // 一度だけ検知するため、クリア + return result; + } + return false; + } + + protected static boolean isKeyPressed(int keyCode) { + if (keyCode <= GLFW_KEY_LAST && keyCode >= 0) { + return get().keyPressed[keyCode]; + } + return false; + } + + protected static boolean isKeyUp(int keyCode) { + if (keyCode <= GLFW_KEY_LAST && keyCode >= 0) { + boolean result = get().keyUp[keyCode]; + get().keyUp[keyCode] = false; // 一度だけ検知するため、クリア + return result; + } + return false; + } + + public static List getPressedKeys() { + List pressedKeys = new ArrayList<>(); + for (int i = 0; i < get().keyPressed.length; i++) { + if (get().keyPressed[i]) { + pressedKeys.add(i); // 押されているキーコードを追加 + } + } + return pressedKeys; + } +} \ No newline at end of file diff --git a/GameEngine/src/main/java/gameEngine/input/MouseInput.java b/GameEngine/src/main/java/gameEngine/input/MouseInput.java new file mode 100644 index 0000000..19e1481 --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/input/MouseInput.java @@ -0,0 +1,111 @@ +package gameEngine.input; + +import static org.lwjgl.glfw.GLFW.GLFW_PRESS; +import static org.lwjgl.glfw.GLFW.GLFW_RELEASE; + +public class MouseInput { + private static MouseInput instance; + private double scrollX,scrollY; + private double xPos,yPos,lastX,lastY; + private final boolean[] mouseButtonPressed = new boolean[3]; // 現在押されているか + private final boolean[] mouseButtonDown = new boolean[3]; // 押された瞬間 + private final boolean[] mouseButtonUp = new boolean[3]; // 離された瞬間 + private boolean isDragging; + + private MouseInput(){ + this.scrollX = 0.0; + this.scrollY = 0.0; + this.xPos = 0.0; + this.yPos = 0.0; + this.lastX = 0.0; + this.lastY = 0.0; + } + + public static MouseInput get(){ + if(MouseInput.instance == null){ + MouseInput.instance = new MouseInput(); + } + return MouseInput.instance; + } + + public static void mousePosCallback(long windoow, double xpos, double ypos){ + get().lastX = get().xPos; + get().lastY = get().yPos; + get().xPos = xpos; + get().yPos = ypos; + get().isDragging = get().mouseButtonPressed[0] || get().mouseButtonPressed[1] || get().mouseButtonPressed[2]; + } + + public static void mouseButtonCallback(long window, int button, int action, int mods) { + MouseInput listener = get(); + if (button < listener.mouseButtonPressed.length) { + if (action == GLFW_PRESS) { + if (!listener.mouseButtonPressed[button]) { + listener.mouseButtonDown[button] = true; // 押された瞬間を記録 + } + listener.mouseButtonPressed[button] = true; // 押され続けている状態 + } else if (action == GLFW_RELEASE) { + listener.mouseButtonPressed[button] = false; + listener.mouseButtonUp[button] = true; // 離された瞬間を記録 + listener.isDragging = false; + } + } + } + public static void mouseScrollCallBack(long window, double xOffset, double yOffset){ + get().scrollX = xOffset; + get().scrollY = yOffset; + } + + public static void endFrame() { + get().scrollX = 0; + get().scrollY = 0; + get().lastX = get().xPos; + get().lastY = get().yPos; + + // フレームの終わりでリセット + for (int i = 0; i < get().mouseButtonDown.length; i++) { + get().mouseButtonDown[i] = false; + get().mouseButtonUp[i] = false; + } + } + + public static float getX(){ + return (float)get().xPos; + } + public static float getY(){ + return (float)get().yPos; + } + public static float getDx(){ + return (float)(get().lastX - get().xPos); + } + + public static float getDy(){ + return (float)(get().lastY - get().yPos); + } + + public static float getScrollX(){ + return (float)get().scrollX; + } + public static float getScrollY(){ + return (float)get().scrollY; + } + public static boolean isDragging(){ + return get().isDragging; + } + + protected static boolean isMouseButtonDown(int button) { + return button < get().mouseButtonDown.length && get().mouseButtonDown[button]; + } + + protected static boolean isMouseButtonUp(int button) { + return button < get().mouseButtonUp.length && get().mouseButtonUp[button]; + } + + protected static boolean isMouseButtonPressed(int button) { + if (button < get().mouseButtonPressed.length) { + return get().mouseButtonPressed[button]; + } + return false; + } + +} diff --git a/GameEngine/src/main/java/gameEngine/scenes/EditorScene.java b/GameEngine/src/main/java/gameEngine/scenes/EditorScene.java new file mode 100644 index 0000000..577f041 --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/scenes/EditorScene.java @@ -0,0 +1,220 @@ +package gameEngine.scenes; + +import gameEngine.ConnectionManager; +import gameEngine.GameEditor; +import gameEngine.ResourceManager; +import gameEngine.Time; +import gameEngine.entites.Camera; +import gameEngine.entites.EditorEntity; +import gameEngine.entites.Entity; +import gameEngine.entites.editorComponents.*; +import gameEngine.geometry.Transform; +import gameEngine.input.Input; +import gameEngine.views.Window; +import org.joml.Vector3f; + +import java.util.HashMap; + +import static org.lwjgl.glfw.GLFW.*; +import static org.lwjgl.opengl.GL11.glClearColor; + +public class EditorScene extends Scene { + + private static GameEditor gameEditor; + public HashMap editorEntities = new HashMap<>(); + private EditorEntity selectedEntity = null; + public ConnectionManager connectionManager = new ConnectionManager(); + public ResourceManager resourceManager = new ResourceManager(); + private EditorEntity clickedEntity = null; + private Camera editorCamera; + private Vector3f centerPosition = new Vector3f((float) Window.getInstance().width / 2, (float) Window.getInstance().height / 2, 0); + + public EditorScene(float windowWidth, float windowHeight){ + System.out.println("Active Editor scene"); + gameEditor = new GameEditor(this, windowWidth, windowHeight); + gameEditor.setScene(this); + this.editorCamera = new Camera("001", Camera.ProjectionType.PERSPECTIVE); + this.setCamera(editorCamera); + glClearColor(1, 1, 1, 0); + resourceManager.addPath(Window.RESOURCE_PATH + "empty.png"); + resourceManager.addPath(Window.RESOURCE_PATH + "enemy1.png"); + resourceManager.addPath(Window.RESOURCE_PATH + "enemy2.png"); + System.out.println(resourceManager.getPathList()); + addCamera(); + } + + @Override + public void update(float dt) { + handleEditorCameraMovement(); + + updateDraggable(); + changeScene(1, dt); //Gameシーンへの以降処理 + + connectionManager.update(); + for (EditorEntity editorEntity : editorEntities.values()) { + editorEntity.updateComponents(); + } + gameEditor.update(); + } + + public Entity getEditorEntity(String eid) { + return editorEntities.get(eid); + } + public void addEditorEntity(String eid, EditorEntity entity) { + editorEntities.put(eid, entity); + } + public void removeEditorEntity(String eid) { + editorEntities.remove(eid); + } + + private EditorEntity createEditorEntity(){ + int entitiesLength = editorEntities.size(); + String newId = Integer.toString(entitiesLength); + EditorEntity editorEntity = new EditorEntity(newId); + addEditorEntity(newId, editorEntity); + return editorEntity; + } + + public void addCamera() { + enqueueTask(this::addCameraView); + } + + private void addCameraView(){ + EditorEntity object = createEditorEntity(); + object.transform.setPosition(0 ,0, 0); + object.addEditorComponent(new CameraView(object)); + } + + private EditorEntity createObject(){ + EditorEntity object = createEditorEntity(); + object.transform.setPosition(new Vector3f(centerPosition).add( + new Vector3f(editorCamera.getPosition().x,editorCamera.getPosition().y,editorCamera.getPosition().z))); + return object; + } + + public void addNewEntity() { + enqueueTask(this::addEntityView); + } + + private void addEntityView(){ + EditorEntity object = createObject(); + object.addEditorComponent(new EntityView(object)); + } + + public void addNewMeshComponent() { + enqueueTask(this::addMeshComponentView); + } + + private void addMeshComponentView(){ + EditorEntity object = createObject(); + object.addEditorComponent(new ComponentView(object, EditorEntity.Connectiontype.Mesh, resourceManager,"Mesh")); + } + + public void addNewMoveImageComponent() { + enqueueTask(this::addMoveImageComponentView); + } + + private void addMoveImageComponentView(){ + EditorEntity object = createObject(); + object.addEditorComponent(new ComponentView(object, EditorEntity.Connectiontype.MoveImage, "MoveImage")); + } + + public void addMoveComponent() { + enqueueTask(this::addMoveComponentView); + } + + private void addMoveComponentView(){ + EditorEntity object = createObject(); + object.addEditorComponent(new ComponentView(object, EditorEntity.Connectiontype.Move, "Move")); + } + + ///---------------------------------------------------------------- + /// Editorのカメラ + ///---------------------------------------------------------------- + + private void handleEditorCameraMovement() { + Vector3f velocity = new Vector3f(0, 0, 0); + float speed = 200.0f; + + if (Input.GetKey(GLFW_KEY_UP)) { + velocity.y -= speed * Time.deltaTime; + } + if (Input.GetKey(GLFW_KEY_DOWN)) { + velocity.y += speed * Time.deltaTime; + } + if (Input.GetKey(GLFW_KEY_LEFT)) { + velocity.x -= speed * Time.deltaTime; + } + if (Input.GetKey(GLFW_KEY_RIGHT)) { + velocity.x += speed * Time.deltaTime; + } + + // カメラを移動 + editorCamera.move(velocity.x, velocity.y, 0); + + // 各オブジェクトのscreenTransformをカメラ位置に応じて補正 + Vector3f cameraPosition = editorCamera.getPosition(); + for (EditorEntity editorEntity : editorEntities.values()) { + editorEntity.screenTransform.setPosition( + editorEntity.transform.position.x, + editorEntity.transform.position.y, + editorEntity.transform.position.z + ); + } + } + ///---------------------------------------------------------------- + /// 選択、ドラッグ処理 + ///---------------------------------------------------------------- + + public EditorEntity getSelectedEntity() { + return selectedEntity; + } + + public EditorEntity getClickedEntity(){ + return clickedEntity; + } + + public void setSelectedEntity(EditorEntity editorEntity) { + selectedEntity = editorEntity; + System.out.println("Selected Entity: " + selectedEntity); + } + + public void setClickedEntity(EditorEntity editorEntity){ + clickedEntity = editorEntity; + + if (editorEntity != null) { + Transform transform = editorEntity.transform; + + // 初期値をInspectorに設定 + gameEditor.updateInspectorField(gameEditor.inspectorInputFields[0], transform.position); // Position + gameEditor.updateInspectorField(gameEditor.inspectorInputFields[1], transform.rotation); // Rotation + gameEditor.updateInspectorField(gameEditor.inspectorInputFields[2], transform.scale); // Scale + } + } + + public void clearSelectedObject() { + selectedEntity = null; + } + + public void updateDraggable() { + if (selectedEntity != null) { + if (selectedEntity.getEditorComponent(DraggableComponent.class) != null) { + selectedEntity.getEditorComponent(DraggableComponent.class).handleDragging(); + } + } else { + for (EditorEntity entity : editorEntities.values()) { + if (entity.getEditorComponent(DraggableComponent.class) != null) { + entity.getEditorComponent(DraggableComponent.class).handleDragging(); + } + } + } + } + + //以下テスト用 + private void connectAllComponent(){ + for(EditorEntity editorEntity: editorEntities.values()){ + + } + + } +} diff --git a/GameEngine/src/main/java/gameEngine/scenes/GameScene.java b/GameEngine/src/main/java/gameEngine/scenes/GameScene.java new file mode 100644 index 0000000..dda4e11 --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/scenes/GameScene.java @@ -0,0 +1,107 @@ +package gameEngine.scenes; + +import gameEngine.Time; +import gameEngine.entites.Camera; +import gameEngine.entites.EditorEntity; +import gameEngine.entites.Entity; +import gameEngine.entites.GameObject; +import gameEngine.entites.editorComponents.CameraView; +import gameEngine.entites.editorComponents.ComponentView; +import gameEngine.entites.editorComponents.EntityView; +import gameEngine.entites.gameComponents.*; +import org.joml.Vector3f; + +import java.util.HashMap; +import java.util.Objects; + +import static org.lwjgl.opengl.GL11.glClearColor; + +public class GameScene extends Scene { + + private Camera gameCamera; + + public GameScene(){ + this(null); + } + + public GameScene(HashMap editorEntities) { + gameCamera = new Camera("001", Camera.ProjectionType.PERSPECTIVE); + setCamera(gameCamera); + if(editorEntities != null){ + for(EditorEntity editorEntity : editorEntities.values()){ + if(editorEntity.getEditorComponent(EntityView.class) != null) addNewObject(editorEntity); + if(editorEntity.getEditorComponent(CameraView.class) != null) gameCamera.transform.setPosition(editorEntity.transform.position); + } + } + System.out.println("Active Game scene"); + glClearColor(1, 1, 1, 0); + Time.reset(); + } + + public void addNewObject(EditorEntity editorObject) { + enqueueTask(() -> addNewGameObject(editorObject)); + } + + + private GameObject createGameObject(){ + int entitiesLength = entities.size(); + String newId = Integer.toString(entitiesLength); + GameObject gameObject = new GameObject(newId); + addEntity(newId, gameObject); + return gameObject; + } + + private void addNewGameObject(EditorEntity editorEntity) { + GameObject object = createGameObject(); + object.transform.setPosition(editorEntity.transform.position.x, editorEntity.transform.position.y,0); + + for(EditorEntity.Connectiontype connectionType: editorEntity.ComponentConnections) { + if(connectionType == EditorEntity.Connectiontype.Mesh){ + for(ComponentView componentView: editorEntity.getEditorComponent(EntityView.class).getConnectedComponentViews()){ + if(componentView.meshSprite != null) { + if(Objects.equals(componentView.getSpritePath(), null)) continue; + object.addComponent(new Mesh(object, Mesh.MeshType.SPRITE, componentView.getSpritePath())); + break; + } + + } + object.addComponent(new Collider(object, false)); //コライダー(仮) + } + if(connectionType == EditorEntity.Connectiontype.MoveImage){ + object.addComponent(new MoveImage(object)); + } + if(connectionType == EditorEntity.Connectiontype.Move){ + for(ComponentView componentView: editorEntity.getEditorComponent(EntityView.class).getConnectedComponentViews()){ + if(componentView.directionText != null){ + object.addComponent(new Move(object, componentView.direction)); + } + } + } + } + } + + @Override + public void update(float dt) { + changeScene(0, dt); //Editorシーンへの以降処理 + reflectionCamera(); + for (Entity entity : entities.values()) { + if (entity instanceof GameObject) { + GameObject gameObject = (GameObject) entity; + if(gameObject.getComponent(TextMesh.class) != null) continue; + gameObject.updateComponents(); + } + } + } + + //カメラの反映を行う(座標調整等) + public void reflectionCamera(){ + Vector3f cameraPosition = gameCamera.getPosition(); + for (Entity entity : entities.values()) { + entity.screenTransform.setPosition( + entity.transform.position.x - cameraPosition.x, + entity.transform.position.y - cameraPosition.y, + entity.transform.position.z + ); + } + } +} diff --git a/GameEngine/src/main/java/gameEngine/scenes/Scene.java b/GameEngine/src/main/java/gameEngine/scenes/Scene.java new file mode 100644 index 0000000..9984dab --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/scenes/Scene.java @@ -0,0 +1,125 @@ +package gameEngine.scenes; + +import gameEngine.entites.Camera; +import gameEngine.entites.Entity; +import gameEngine.entites.GameObject; +import gameEngine.entites.gameComponents.*; +import gameEngine.geometry.Transform; +import gameEngine.input.Input; +import gameEngine.views.Color; +import gameEngine.views.Window; + +import java.awt.event.KeyEvent; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Queue; + +import static org.lwjgl.opengl.GL11.glClearColor; + +public abstract class Scene { + + private Camera camera; + public HashMap entities = new HashMap<>(); + + private final Queue taskQueue = new LinkedList<>(); + private boolean changingScene = false; + private float timeToChangeScene = 2.0f; + private Color backColor = new Color(1,1,1,0); + + public Scene(){ + } + + public abstract void update(float dt); + + public Camera getCamera() { + return camera; + } + public void setCamera(Camera camera) { + this.camera = camera; + } + + public Entity getEntity(String eid) { + return entities.get(eid); + } + public void addEntity(String eid, Entity entity) { + entities.put(eid, entity); + } + public void removeEntity(String eid) { + entities.remove(eid); + } + + + public void addComponentToGameObject(GameObject gameObject, GameComponent component) { + enqueueTask(() -> gameObject.addComponent(component)); + } + + public void removeComponentFromGameObject(GameObject gameObject, GameComponent component) { + enqueueTask(() -> gameObject.removeComponent(component)); + } + + synchronized void enqueueTask(Runnable task) { + taskQueue.add(task); + } + + public synchronized void processTasks() { + while (!taskQueue.isEmpty()) { + taskQueue.poll().run(); + } + } + + public void Instantiate(GameObject original) + { + enqueueTask(() -> { + String newId = Integer.toString(entities.size()); + GameObject newGameObject = new GameObject(newId); + + newGameObject.setName(original.getName() + "_copy"); + newGameObject.transform = new Transform(original.transform); + + + if (original.getComponent(Mesh.class) != null) { + newGameObject.addComponent(new Mesh((Mesh)original.getComponent(Mesh.class).copy(), newGameObject)); + } + if (original.getComponent(TextMesh.class) != null) { + newGameObject.addComponent(new TextMesh((TextMesh)original.getComponent(TextMesh.class).copy(), newGameObject)); + } + if (original.getComponent(CopyEntity.class) != null) { + newGameObject.addComponent(new CopyEntity(newGameObject)); + } + if (original.getComponent(MoveImage.class) != null) { + newGameObject.addComponent(new MoveImage(newGameObject)); + } + if (original.getComponent(Physics.class) != null) { + newGameObject.addComponent(new Physics(newGameObject)); + } + + + addEntity(newId, newGameObject); + }); + } + + public void changeSceneStart(){ + if(!changingScene) changingScene = true; + } + + public void changeScene(int scene, float dt){ + if(!changingScene && Input.GetKeyDown(KeyEvent.VK_SPACE)){ + changingScene = true; + } + + if(changingScene) { + if (timeToChangeScene > 0) { + timeToChangeScene -= dt * 1.5f; + backColor.r -= dt * 5.0f; + backColor.g -= dt * 5.0f; + backColor.b -= dt * 5.0f; + } else { + changingScene = false; + timeToChangeScene = 2.0f; + backColor = new Color(1,1,1,0); + Window.changeScene(scene); + } + glClearColor(backColor.r, backColor.g, backColor.b, backColor.a); + } + } +} diff --git a/GameEngine/src/main/java/gameEngine/simulator/interfaces/CameraPositionReceiver.java b/GameEngine/src/main/java/gameEngine/simulator/interfaces/CameraPositionReceiver.java new file mode 100644 index 0000000..3b39b3f --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/simulator/interfaces/CameraPositionReceiver.java @@ -0,0 +1,40 @@ +package gameEngine.simulator.interfaces; + +import gameEngine.entites.Camera; +import models.algebra.Expression; +import models.algebra.Term; +import simulator.Event; +import simulator.SystemState; +import simulator.interfaces.INativeReceiver; + +public class CameraPositionReceiver implements INativeReceiver { + private Camera camera; + + public CameraPositionReceiver(Camera camera) { + this.camera = camera; + } + + @Override + public void onReceiveFromModel(Event event, SystemState nextSystemState) { + Expression message = event.getMessage(); + if (message instanceof Term) { + Expression xExp = ((Term) message).getChild(0); + Expression yExp = ((Term) message).getChild(1); + Expression zExp = ((Term) message).getChild(2); + if (xExp instanceof Term) { + xExp = ((Term) xExp).reduce(); + } + if (yExp instanceof Term) { + yExp = ((Term) yExp).reduce(); + } + if (zExp instanceof Term) { + zExp = ((Term) zExp).reduce(); + } + Float x = Float.parseFloat(xExp.toString()); + Float y = Float.parseFloat(yExp.toString()); + Float z = Float.parseFloat(zExp.toString()); + camera.transform.setPosition(x, y, z); + } + } + +} diff --git a/GameEngine/src/main/java/gameEngine/simulator/interfaces/CameraProjectionReceiver.java b/GameEngine/src/main/java/gameEngine/simulator/interfaces/CameraProjectionReceiver.java new file mode 100644 index 0000000..4d137bc --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/simulator/interfaces/CameraProjectionReceiver.java @@ -0,0 +1,24 @@ +package gameEngine.simulator.interfaces; + +import gameEngine.entites.Camera; +import models.algebra.Expression; +import models.algebra.Term; +import simulator.Event; +import simulator.SystemState; +import simulator.interfaces.INativeReceiver; + +public class CameraProjectionReceiver implements INativeReceiver { + private Camera camera; + + public CameraProjectionReceiver(Camera camera) { + this.camera = camera; + } + + @Override + public void onReceiveFromModel(Event event, SystemState nextSystemState) { + Expression message = event.getMessage(); + if (message instanceof Term) { + } + } + +} diff --git a/GameEngine/src/main/java/gameEngine/simulator/interfaces/CameraRotationReceiver.java b/GameEngine/src/main/java/gameEngine/simulator/interfaces/CameraRotationReceiver.java new file mode 100644 index 0000000..88c048f --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/simulator/interfaces/CameraRotationReceiver.java @@ -0,0 +1,40 @@ +package gameEngine.simulator.interfaces; + +import gameEngine.entites.Camera; +import models.algebra.Expression; +import models.algebra.Term; +import simulator.Event; +import simulator.SystemState; +import simulator.interfaces.INativeReceiver; + +public class CameraRotationReceiver implements INativeReceiver { + private Camera camera; + + public CameraRotationReceiver(Camera camera) { + this.camera = camera; + } + + @Override + public void onReceiveFromModel(Event event, SystemState nextSystemState) { + Expression message = event.getMessage(); + if (message instanceof Term) { + Expression xExp = ((Term) message).getChild(0); + Expression yExp = ((Term) message).getChild(1); + Expression zExp = ((Term) message).getChild(2); + if (xExp instanceof Term) { + xExp = ((Term) xExp).reduce(); + } + if (yExp instanceof Term) { + yExp = ((Term) yExp).reduce(); + } + if (zExp instanceof Term) { + zExp = ((Term) zExp).reduce(); + } + Float x = Float.parseFloat(xExp.toString()); + Float y = Float.parseFloat(yExp.toString()); + Float z = Float.parseFloat(zExp.toString()); + camera.transform.setRotation(x, y, z); + } + } + +} diff --git a/GameEngine/src/main/java/gameEngine/simulator/interfaces/CameraScaleReceiver.java b/GameEngine/src/main/java/gameEngine/simulator/interfaces/CameraScaleReceiver.java new file mode 100644 index 0000000..2f09d09 --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/simulator/interfaces/CameraScaleReceiver.java @@ -0,0 +1,40 @@ +package gameEngine.simulator.interfaces; + +import gameEngine.entites.Camera; +import models.algebra.Expression; +import models.algebra.Term; +import simulator.Event; +import simulator.SystemState; +import simulator.interfaces.INativeReceiver; + +public class CameraScaleReceiver implements INativeReceiver { + private Camera camera; + + public CameraScaleReceiver(Camera camera) { + this.camera = camera; + } + + @Override + public void onReceiveFromModel(Event event, SystemState nextSystemState) { + Expression message = event.getMessage(); + if (message instanceof Term) { + Expression xExp = ((Term) message).getChild(0); + Expression yExp = ((Term) message).getChild(1); + Expression zExp = ((Term) message).getChild(2); + if (xExp instanceof Term) { + xExp = ((Term) xExp).reduce(); + } + if (yExp instanceof Term) { + yExp = ((Term) yExp).reduce(); + } + if (zExp instanceof Term) { + zExp = ((Term) zExp).reduce(); + } + Float x = Float.parseFloat(xExp.toString()); + Float y = Float.parseFloat(yExp.toString()); + Float z = Float.parseFloat(zExp.toString()); + camera.transform.setScale(x, y, z); + } + } + +} diff --git a/GameEngine/src/main/java/gameEngine/simulator/interfaces/EntityPositionReceiver.java b/GameEngine/src/main/java/gameEngine/simulator/interfaces/EntityPositionReceiver.java new file mode 100644 index 0000000..9cf5b05 --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/simulator/interfaces/EntityPositionReceiver.java @@ -0,0 +1,40 @@ +package gameEngine.simulator.interfaces; + +import gameEngine.entites.Entity; +import models.algebra.Expression; +import models.algebra.Term; +import simulator.Event; +import simulator.SystemState; +import simulator.interfaces.INativeReceiver; + +public class EntityPositionReceiver implements INativeReceiver { + private Entity entity; + + public EntityPositionReceiver(Entity entity) { + this.entity = entity; + } + + @Override + public void onReceiveFromModel(Event event, SystemState nextSystemState) { + Expression message = event.getMessage(); + if (message instanceof Term) { + Expression xExp = ((Term) message).getChild(0); + Expression yExp = ((Term) message).getChild(1); + Expression zExp = ((Term) message).getChild(2); + if (xExp instanceof Term) { + xExp = ((Term) xExp).reduce(); + } + if (yExp instanceof Term) { + yExp = ((Term) yExp).reduce(); + } + if (zExp instanceof Term) { + zExp = ((Term) zExp).reduce(); + } + Float x = Float.parseFloat(xExp.toString()); + Float y = Float.parseFloat(yExp.toString()); + Float z = Float.parseFloat(zExp.toString()); + entity.transform.setPosition(x, y, z); + } + } + +} diff --git a/GameEngine/src/main/java/gameEngine/simulator/interfaces/EntityRotationReceiver.java b/GameEngine/src/main/java/gameEngine/simulator/interfaces/EntityRotationReceiver.java new file mode 100644 index 0000000..a1bf209 --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/simulator/interfaces/EntityRotationReceiver.java @@ -0,0 +1,40 @@ +package gameEngine.simulator.interfaces; + +import gameEngine.entites.Entity; +import models.algebra.Expression; +import models.algebra.Term; +import simulator.Event; +import simulator.SystemState; +import simulator.interfaces.INativeReceiver; + +public class EntityRotationReceiver implements INativeReceiver { + private Entity entity; + + public EntityRotationReceiver(Entity entity) { + this.entity = entity; + } + + @Override + public void onReceiveFromModel(Event event, SystemState nextSystemState) { + Expression message = event.getMessage(); + if (message instanceof Term) { + Expression xExp = ((Term) message).getChild(0); + Expression yExp = ((Term) message).getChild(1); + Expression zExp = ((Term) message).getChild(2); + if (xExp instanceof Term) { + xExp = ((Term) xExp).reduce(); + } + if (yExp instanceof Term) { + yExp = ((Term) yExp).reduce(); + } + if (zExp instanceof Term) { + zExp = ((Term) zExp).reduce(); + } + Float x = Float.parseFloat(xExp.toString()); + Float y = Float.parseFloat(yExp.toString()); + Float z = Float.parseFloat(zExp.toString()); + entity.transform.setRotation(x, y, z); + } + } + +} diff --git a/GameEngine/src/main/java/gameEngine/simulator/interfaces/EntityScaleReceiver.java b/GameEngine/src/main/java/gameEngine/simulator/interfaces/EntityScaleReceiver.java new file mode 100644 index 0000000..44f41a7 --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/simulator/interfaces/EntityScaleReceiver.java @@ -0,0 +1,40 @@ +package gameEngine.simulator.interfaces; + +import models.algebra.Expression; +import models.algebra.Term; +import simulator.Event; +import simulator.SystemState; +import simulator.interfaces.INativeReceiver; +import gameEngine.entites.Entity; + +public class EntityScaleReceiver implements INativeReceiver { + private Entity entity; + + public EntityScaleReceiver(Entity entity) { + this.entity = entity; + } + + @Override + public void onReceiveFromModel(Event event, SystemState nextSystemState) { + Expression message = event.getMessage(); + if (message instanceof Term) { + Expression xExp = ((Term) message).getChild(0); + Expression yExp = ((Term) message).getChild(1); + Expression zExp = ((Term) message).getChild(2); + if (xExp instanceof Term) { + xExp = ((Term) xExp).reduce(); + } + if (yExp instanceof Term) { + yExp = ((Term) yExp).reduce(); + } + if (zExp instanceof Term) { + zExp = ((Term) zExp).reduce(); + } + Float x = Float.parseFloat(xExp.toString()); + Float y = Float.parseFloat(yExp.toString()); + Float z = Float.parseFloat(zExp.toString()); + entity.transform.setScale(x, y, z); + } + } + +} diff --git a/GameEngine/src/main/java/gameEngine/simulator/interfaces/GameEnginePresenter.java b/GameEngine/src/main/java/gameEngine/simulator/interfaces/GameEnginePresenter.java new file mode 100644 index 0000000..65f74e4 --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/simulator/interfaces/GameEnginePresenter.java @@ -0,0 +1,287 @@ +package gameEngine.simulator.interfaces; +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 gameEngine.entites.*; +import gameEngine.entites.gameComponents.*; +import gameEngine.scenes.Scene; +import gameEngine.views.Sprite; +import models.algebra.Expression; +import models.algebra.Term; +import models.algebra.Constant; +import models.dataConstraintModel.JsonTerm; +import models.dataConstraintModel.MapTerm; +import models.dataFlowModel.DataTransferChannel; +import simulator.Event; +import simulator.Resource; +import simulator.Simulator; +import simulator.SystemState; +import simulator.interfaces.INativeInitializer; +import simulator.interfaces.INativeReceiver; + +public class GameEnginePresenter implements INativeReceiver, INativeInitializer { + public final String sceneUpdateChannelName = "SceneUpdate"; + public final String cameraPositionUpdateChannelName = "CameraPositionUpdate"; + public final String cameraRotationUpdateChannelName = "CameraRotationUpdate"; + public final String cameraScaleUpdateChannelName = "CameraScaleUpdate"; + public final String cameraProjectionUpdateChannelName = "CameraProjectionUpdate"; + public final String entityPositionUpdateChannelName = "EntityPositionUpdate"; + public final String entityRotationUpdateChannelName = "EntityRotationUpdate"; + public final String entityScaleUpdateChannelName = "EntityScaleUpdate"; + public final String spriteUpdateChannelName = "SpriteUpdate"; + + public final String sceneUpdateEventChannelName = "SceneUpdateEvent"; + public final String keyEventChannelName = "KeyEvent"; + + protected Scene scene; + protected Simulator simulator; + + protected DataTransferChannel sceneUpdateChannel; + protected DataTransferChannel cameraPositionUpdateChannel; + protected DataTransferChannel cameraRotationUpdateChannel; + protected DataTransferChannel cameraScaleUpdateChannel; + protected DataTransferChannel cameraProjectionUpdateChannel; + protected DataTransferChannel entityPositionUpdateChannel; + protected DataTransferChannel entityRotationUpdateChannel; + protected DataTransferChannel entityScaleUpdateChannel; + protected DataTransferChannel spriteUpdateChannel; + + protected DataTransferChannel sceneUpdateEventChannel; + protected DataTransferChannel keyEventChannel; + + protected Map> channelAndResourcesForEntityReceiving = new HashMap<>(); + protected Map channelAndResourcesForCameraReceiving = new HashMap<>(); + + public GameEnginePresenter(Scene scene, Simulator simulator) { + this.scene = scene; + this.simulator = simulator; + + sceneUpdateChannel = (DataTransferChannel) simulator.getModel().getChannel(sceneUpdateChannelName); + cameraPositionUpdateChannel = (DataTransferChannel) simulator.getModel().getChannel(cameraPositionUpdateChannelName); + cameraRotationUpdateChannel = (DataTransferChannel) simulator.getModel().getChannel(cameraRotationUpdateChannelName); + cameraScaleUpdateChannel = (DataTransferChannel) simulator.getModel().getChannel(cameraScaleUpdateChannelName); + cameraProjectionUpdateChannel = (DataTransferChannel) simulator.getModel().getChannel(cameraProjectionUpdateChannelName); + entityPositionUpdateChannel = (DataTransferChannel) simulator.getModel().getChannel(entityPositionUpdateChannelName); + entityRotationUpdateChannel = (DataTransferChannel) simulator.getModel().getChannel(entityRotationUpdateChannelName); + entityScaleUpdateChannel = (DataTransferChannel) simulator.getModel().getChannel(entityScaleUpdateChannelName); + spriteUpdateChannel = (DataTransferChannel) simulator.getModel().getChannel(spriteUpdateChannelName); + + sceneUpdateEventChannel = (DataTransferChannel) simulator.getModel().getInputChannel(sceneUpdateEventChannelName); + keyEventChannel = (DataTransferChannel) simulator.getModel().getInputChannel(keyEventChannelName); + simulator.addNativeReceiver(this, sceneUpdateChannel); + simulator.addSystemInitializer(this); + } + + @Override + public void onInitFromModel(SystemState initialSystemState) { + Resource sceneResource = initialSystemState.getResource("scene"); + if (sceneResource != null) { + Resource entitiesResource = sceneResource.getChildrenMap().get("entities"); + if (entitiesResource != null) { + for (Map.Entry entity: entitiesResource.getChildrenMap().entrySet()) { + String eId = entity.getKey(); + Resource entityResource = entity.getValue(); + Resource transformResource = entityResource.getChildrenMap().get("transform"); + Resource meshResource = entityResource.getChildrenMap().get("mesh"); + Resource positionResource = transformResource.getChildrenMap().get("position"); + Resource rotationResource = transformResource.getChildrenMap().get("rotation"); + Resource scaleResource = transformResource.getChildrenMap().get("scale"); + if (meshResource != null && positionResource != null && rotationResource != null && scaleResource != null) { + // Add a game entity to scene. + String px = positionResource.getChildrenMap().get("x").getState().getValue().toString(); + String py = positionResource.getChildrenMap().get("y").getState().getValue().toString(); + String pz = positionResource.getChildrenMap().get("z").getState().getValue().toString(); + String rx = rotationResource.getChildrenMap().get("x").getState().getValue().toString(); + String ry = rotationResource.getChildrenMap().get("y").getState().getValue().toString(); + String rz = rotationResource.getChildrenMap().get("z").getState().getValue().toString(); + String sx = scaleResource.getChildrenMap().get("x").getState().getValue().toString(); + String sy = scaleResource.getChildrenMap().get("y").getState().getValue().toString(); + String sz = scaleResource.getChildrenMap().get("z").getState().getValue().toString(); + String type = (String) ((Constant) meshResource.getChildrenMap().get("type").getState().getValue()).getValue(); + String texturePath = (String) ((Constant) meshResource.getChildrenMap().get("sprite").getState().getValue()).getValue(); + GameObject gameObj = addGameObjectToScene(eId, meshResource, px, py, pz, rx, ry, rz, sx, sy, sz, type, texturePath); + + // Connect game entity and model. + connectGameEntiotyAndModel(eId, gameObj, positionResource, rotationResource, scaleResource); + } + } + } + } + } + + @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; + Resource sceneResource = nextSystemState.getResource(event.getInputResource().getResourceIdentifier()); + Expression cameraExp = nextSc.get("camera"); + if (cameraExp != null && cameraExp instanceof JsonTerm) { + // Connect camera and model. + JsonTerm camera = (JsonTerm) cameraExp; + Resource cameraResource = sceneResource.getChildrenMap().get("camera"); + Camera cameraObj = scene.getCamera(); + Resource cameraTransformResource = cameraResource.getChildrenMap().get("transform"); + Resource cameraProjectionResource = cameraResource.getChildrenMap().get("projection"); + Resource cameraPositionResource = cameraTransformResource.getChildrenMap().get("position"); + Resource cameraRotationResource = cameraTransformResource.getChildrenMap().get("rotation"); + Resource cameraScaleResource = cameraTransformResource.getChildrenMap().get("scale"); + + if (channelAndResourcesForCameraReceiving.get(cameraPositionUpdateChannel) == null) { + CameraPositionReceiver nativePositionReceiver = new CameraPositionReceiver(cameraObj); + simulator.addNativeReceiver(nativePositionReceiver, cameraPositionUpdateChannel, cameraPositionResource); + channelAndResourcesForCameraReceiving.put(cameraPositionUpdateChannel, cameraPositionResource); + } + + if (channelAndResourcesForCameraReceiving.get(cameraRotationUpdateChannel) == null) { + CameraRotationReceiver nativeRotationReceiver = new CameraRotationReceiver(cameraObj); + simulator.addNativeReceiver(nativeRotationReceiver, cameraRotationUpdateChannel, cameraRotationResource); + channelAndResourcesForCameraReceiving.put(cameraRotationUpdateChannel, cameraRotationResource); + } + + if (channelAndResourcesForCameraReceiving.get(cameraScaleUpdateChannel) == null) { + CameraScaleReceiver nativeScaleReceiver = new CameraScaleReceiver(cameraObj); + simulator.addNativeReceiver(nativeScaleReceiver, cameraScaleUpdateChannel, cameraScaleResource); + channelAndResourcesForCameraReceiving.put(cameraScaleUpdateChannel, cameraScaleResource); + } + + if (channelAndResourcesForCameraReceiving.get(cameraProjectionUpdateChannel) == null) { + CameraProjectionReceiver nativeProjectionReceiver = new CameraProjectionReceiver(cameraObj); + simulator.addNativeReceiver(nativeProjectionReceiver, cameraProjectionUpdateChannel, cameraProjectionResource); + channelAndResourcesForCameraReceiving.put(cameraProjectionUpdateChannel, cameraProjectionResource); + } + } + Expression oldEntities = curSc.get("entities"); + Expression newEntities = nextSc.get("entities"); + Set oldEidSet = new HashSet<>(((MapTerm) oldEntities).keySet()); + Set newEidSet = new HashSet<>(((MapTerm) newEntities).keySet()); + oldEidSet.removeAll(((MapTerm) newEntities).keySet()); + newEidSet.removeAll(((MapTerm) oldEntities).keySet()); + if (!oldEidSet.isEmpty() || !newEidSet.isEmpty()) { + // If the set of scene entities is changed. + + // Remove old entities and their native receivers. + for (String oldEid: oldEidSet) { + scene.removeEntity(oldEid); + } + for (DataTransferChannel channel: channelAndResourcesForEntityReceiving.keySet()) { + Map widToResource = channelAndResourcesForEntityReceiving.get(channel); + for (String oldEid: oldEidSet) { + Resource resource = widToResource.remove(oldEid); + if (resource != null) { + simulator.removeNativeReceiver(channel, resource); + } + } + } + + // Add new entities. + Resource entitiesResource = sceneResource.getChildrenMap().get("entities"); + for (String newEid: newEidSet) { + Expression entityExp = ((MapTerm) newEntities).get(newEid); + if (entityExp instanceof JsonTerm) { + JsonTerm entity = (JsonTerm) entityExp; + Resource entityResource = entitiesResource.getChildrenMap().get(newEid); + Expression transformExp = entity.get("transform"); + Expression meshExp = entity.get("mesh"); + if (transformExp instanceof JsonTerm && meshExp instanceof JsonTerm) { + JsonTerm transform = (JsonTerm) transformExp; + JsonTerm mesh = (JsonTerm) meshExp; + Resource transformResource = entityResource.getChildrenMap().get("transform"); + Resource meshResource = entityResource.getChildrenMap().get("mesh"); + Expression positionExp = transform.get("position"); + Expression rotationExp = transform.get("rotation"); + Expression scaleExp = transform.get("scale"); + Resource positionResource = transformResource.getChildrenMap().get("position"); + Resource rotationResource = transformResource.getChildrenMap().get("rotation"); + Resource scaleResource = transformResource.getChildrenMap().get("scale"); + if (positionExp instanceof JsonTerm + && rotationExp instanceof JsonTerm + && scaleExp instanceof JsonTerm) { + // Add a game entity to scene. + JsonTerm position = (JsonTerm) positionExp; + JsonTerm rotation = (JsonTerm) rotationExp; + JsonTerm scale = (JsonTerm) scaleExp; + String px = position.get("x").toString(); + String py = position.get("y").toString(); + String pz = position.get("z").toString(); + String rx = rotation.get("x").toString(); + String ry = rotation.get("y").toString(); + String rz = rotation.get("z").toString(); + String sx = scale.get("x").toString(); + String sy = scale.get("y").toString(); + String sz = scale.get("z").toString(); + String type = (String) ((Constant) mesh.get("type")).getValue(); + String texturePath = (String) ((Constant) mesh.get("sprite")).getValue(); + GameObject gameObj = addGameObjectToScene(newEid, meshResource, px, py, pz, rx, ry, rz, sx, sy, sz, type, texturePath); + + // Connect game entity and model. + connectGameEntiotyAndModel(newEid, gameObj, positionResource, rotationResource, scaleResource); + } + } + } + } + } + } + } + } + + private GameObject addGameObjectToScene(String eId, Resource meshResource, String px, String py, String pz, + String rx, String ry, String rz, String sx, String sy, String sz, String type, String texturePath) { + GameObject gameObj = new GameObject(eId);//新しいIDが必要あり + gameObj.transform.setPosition(Float.parseFloat(px), Float.parseFloat(py), Float.parseFloat(pz)); + gameObj.transform.setRotation(Float.parseFloat(rx), Float.parseFloat(ry), Float.parseFloat(rz)); + gameObj.transform.setScale(Float.parseFloat(sx), Float.parseFloat(sy), Float.parseFloat(sz)); + if (type.equals("sprite")) { + Mesh meshObj = new Mesh(gameObj, Mesh.MeshType.SPRITE, texturePath); + gameObj.addComponent(meshObj); + SpriteReceiver nativeSpriteReceiver = new SpriteReceiver(meshObj); + simulator.addNativeReceiver(nativeSpriteReceiver, spriteUpdateChannel, meshResource); + Map resources = channelAndResourcesForEntityReceiving.get(spriteUpdateChannel); + if (resources == null) { + resources = new HashMap<>(); + channelAndResourcesForEntityReceiving.put(spriteUpdateChannel, resources); + } + resources.put(eId, meshResource); + } + scene.addEntity(eId, gameObj); + return gameObj; + } + + private void connectGameEntiotyAndModel(String eId, GameObject gameObj, Resource positionResource, + Resource rotationResource, Resource scaleResource) { + EntityPositionReceiver nativePositionReceiver = new EntityPositionReceiver(gameObj); + simulator.addNativeReceiver(nativePositionReceiver, entityPositionUpdateChannel, positionResource); + Map resources = channelAndResourcesForEntityReceiving.get(entityPositionUpdateChannel); + if (resources == null) { + resources = new HashMap<>(); + channelAndResourcesForEntityReceiving.put(entityPositionUpdateChannel, resources); + } + resources.put(eId, positionResource); + + EntityRotationReceiver nativeRotationReceiver = new EntityRotationReceiver(gameObj); + simulator.addNativeReceiver(nativeRotationReceiver, entityRotationUpdateChannel, rotationResource); + resources = channelAndResourcesForEntityReceiving.get(entityRotationUpdateChannel); + if (resources == null) { + resources = new HashMap<>(); + channelAndResourcesForEntityReceiving.put(entityRotationUpdateChannel, resources); + } + resources.put(eId, rotationResource); + + EntityScaleReceiver nativeScaleReceiver = new EntityScaleReceiver(gameObj); + simulator.addNativeReceiver(nativeScaleReceiver, entityScaleUpdateChannel, scaleResource); + resources = channelAndResourcesForEntityReceiving.get(entityScaleUpdateChannel); + if (resources == null) { + resources = new HashMap<>(); + channelAndResourcesForEntityReceiving.put(entityScaleUpdateChannel, resources); + } + resources.put(eId, scaleResource); + } +} diff --git a/GameEngine/src/main/java/gameEngine/simulator/interfaces/SpriteReceiver.java b/GameEngine/src/main/java/gameEngine/simulator/interfaces/SpriteReceiver.java new file mode 100644 index 0000000..d603f5a --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/simulator/interfaces/SpriteReceiver.java @@ -0,0 +1,27 @@ +package gameEngine.simulator.interfaces; + +import gameEngine.entites.gameComponents.Mesh; +import gameEngine.views.Sprite; +import models.algebra.Expression; +import models.algebra.Term; +import simulator.Event; +import simulator.SystemState; +import simulator.interfaces.INativeReceiver; + +public class SpriteReceiver implements INativeReceiver { + private Mesh mesh; + + public SpriteReceiver(Mesh mesh) { + this.mesh = mesh; + } + + @Override + public void onReceiveFromModel(Event event, SystemState nextSystemState) { + Expression message = event.getMessage(); + if (message instanceof Term) { + Expression exp = ((Term) message).getChild(0); + mesh.setSprite(exp.toString()); + } + } + +} diff --git a/GameEngine/src/main/java/gameEngine/views/Button.java b/GameEngine/src/main/java/gameEngine/views/Button.java new file mode 100644 index 0000000..fa90245 --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/views/Button.java @@ -0,0 +1,85 @@ +package gameEngine.views; + +import gameEngine.input.Input; +import gameEngine.input.MouseInput; + +import java.util.ArrayList; +import java.util.List; + +public class Button implements IUpdatable { + private Sprite buttonSprite; + private Text buttonText; + private List onClickListeners = new ArrayList<>(); + private boolean isPressed = false; + + private Color normalColor = new Color(1f, 1f, 1f, 1f); + private Color pressedColor = new Color(0.5f, 0.5f, 0.5f, 1f); + + public Button(float x, float y, Sprite sprite, Text text) { + buttonSprite = sprite; + buttonSprite.setPosition(x, y); + buttonText = text; + } + + public Button(float posX, float posY, float scaleX, float scaleY, String text){ + this(posX, posY, new Sprite(Window.RESOURCE_PATH + "button.png") , new Text(posX + 5.25f, posY- 2.6f, text, 24)); + buttonSprite.setScale(scaleX, scaleY); + } + + public Button(float posX, float posY, float scaleX, float scaleY){ + this(posX, posY, new Sprite(Window.RESOURCE_PATH + "button.png") , new Text(posX + 5.25f, posY - 2.6f, "", 24)); + buttonSprite.setScale(scaleX, scaleY); + } + + public Button(){ + this(0,0, new Sprite(Window.RESOURCE_PATH + "button.png"), new Text(5.25f, -2.6f, "", 24)); + } + + public void update() { + buttonSprite.update(); + buttonText.update(); + float mouseX = MouseInput.getX(); + float mouseY = MouseInput.getY(); + + if (Input.GetMouseButtonDown(0) && buttonSprite.isMouseOver(mouseX, mouseY)) { + isPressed = true; + buttonSprite.setColor(pressedColor); + } + + if (Input.GetMouseButtonUp(0)) { + buttonSprite.setColor(normalColor); + if (isPressed && buttonSprite.isMouseOver(mouseX, mouseY)) { + for (Runnable listener : onClickListeners) { + listener.run(); + } + } + isPressed = false; + } + } + + public void addListener(Runnable listener) { + onClickListeners.add(listener); + } + + public void clearListeners() { + onClickListeners.clear(); + } + + public void setNormalColor(Color normalColor) { + this.normalColor = normalColor; + } + + public void setPressedColor(Color pressedColor) { + this.pressedColor = pressedColor; + } + + public void setPosition(float x, float y) { + buttonSprite.setPosition(x,y); + } + + public void setScale(float x, float y){ + buttonSprite.setScale(x,y); + } + + public void setButtonText(String text) { buttonText.setText(text);} +} \ No newline at end of file diff --git a/GameEngine/src/main/java/gameEngine/views/Color.java b/GameEngine/src/main/java/gameEngine/views/Color.java new file mode 100644 index 0000000..f64b7ee --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/views/Color.java @@ -0,0 +1,27 @@ +package gameEngine.views; + +public class Color { + public float r,g,b,a; + + public Color(float r, float g, float b) { + this.r = r; + this.g = g; + this.b = b; + this.a = 1.0f; + } + public Color(float r, float g, float b, float a) { + this.r = r; + this.g = g; + this.b = b; + this.a = a; + } + public static final Color RED = new Color(1, 0, 0); + public static final Color GREEN = new Color(0, 1, 0); + public static final Color BLUE = new Color(0, 0, 1); + public static final Color WHITE = new Color(1, 1, 1); + public static final Color BLACK = new Color(0, 0, 0); + public static final Color GRAY = new Color(0.75f,0.75f,0.75f); + public static final Color YELLOW = new Color(1, 1, 0); + public static final Color MAGENTA = new Color(1, 0, 1); + public static final Color CYAN = new Color(0, 1, 1); +} diff --git a/GameEngine/src/main/java/gameEngine/views/Font.java b/GameEngine/src/main/java/gameEngine/views/Font.java new file mode 100644 index 0000000..3dac45b --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/views/Font.java @@ -0,0 +1,187 @@ +package gameEngine.views; + +import org.lwjgl.BufferUtils; + +import javax.swing.*; +import java.awt.*; +import java.awt.Color; +import java.awt.geom.AffineTransform; +import java.awt.image.AffineTransformOp; +import java.awt.image.BufferedImage; +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; + +public class Font { + + /** + * Contains the glyphs for each char. + */ + private final Map glyphs; + + private Texture texture; + + private int fontHeight; + + public Map getGlyphs(){ + return glyphs; + } + public Texture getTexture() { + return texture; + } + public int getFontHeight(){ + return fontHeight; + } + + + /** + * Creates a font from an AWT Font. + * + * @param font The AWT Font + * @param antiAlias Wheter the font should be antialiased or not + */ + public Font(java.awt.Font font, boolean antiAlias) { + glyphs = new HashMap<>(); + createFontTexture(font, antiAlias); + } + + private void createFontTexture(java.awt.Font font, boolean antiAlias) { + texture = new Texture(); + /* Loop through the characters to get charWidth and charHeight */ + int imageWidth = 0; + int imageHeight = 0; + + /* Start at char #32, because ASCII 0 to 31 are just control codes */ + for (int i = 32; i < 256; i++) { + if (i == 127) { + /* ASCII 127 is the DEL control code, so we can skip it */ + continue; + } + char c = (char) i; + BufferedImage ch = createCharImage(font, c, antiAlias); + if (ch == null) { + /* If char image is null that font does not contain the char */ + continue; + } + + imageWidth += ch.getWidth(); + imageHeight = Math.max(imageHeight, ch.getHeight()); + } + + fontHeight = imageHeight; + + /* Image for the texture */ + BufferedImage image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_ARGB); + Graphics2D g = image.createGraphics(); + + int x = 0; + + /* Create image for the standard chars, again we omit ASCII 0 to 31 + * because they are just control codes */ + for (int i = 32; i < 256; i++) { + if (i == 127) { + /* ASCII 127 is the DEL control code, so we can skip it */ + continue; + } + char c = (char) i; + BufferedImage charImage = createCharImage(font, c, antiAlias); + if (charImage == null) { + /* If char image is null that font does not contain the char */ + continue; + } + + int charWidth = charImage.getWidth(); + int charHeight = charImage.getHeight(); + + /* Create glyph and draw char on image */ + Glyph ch = new Glyph(charWidth, charHeight, x, image.getHeight() - charHeight, 0f); + g.drawImage(charImage, x, 0, null); + x += ch.width; + glyphs.put(c, ch); + } + + /* Flip image Horizontal to get the origin to bottom left */ + /*AffineTransform transform = AffineTransform.getScaleInstance(1f, -1f); + transform.translate(0, -image.getHeight()); + AffineTransformOp operation = new AffineTransformOp(transform, + AffineTransformOp.TYPE_NEAREST_NEIGHBOR); + image = operation.filter(image, null);*/ + + /* Get charWidth and charHeight of image */ + int width = image.getWidth(); + int height = image.getHeight(); + + /* Get pixel data of image */ + int[] pixelsRaw = new int[width * height]; + image.getRGB(0, 0, width, height, pixelsRaw, 0, width); + + /* Put pixel data into a ByteBuffer */ + ByteBuffer pixels = BufferUtils.createByteBuffer(width * height * 4); + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + /* Pixel as RGBA: 0xAARRGGBB */ + int pixel = pixelsRaw[i * width + j]; + /* Red component 0xAARRGGBB >> 16 = 0x0000AARR */ + pixels.put((byte) ((pixel >> 16) & 0xFF)); + /* Green component 0xAARRGGBB >> 8 = 0x00AARRGG */ + pixels.put((byte) ((pixel >> 8) & 0xFF)); + /* Blue component 0xAARRGGBB >> 0 = 0xAARRGGBB */ + pixels.put((byte) (pixel & 0xFF)); + /* Alpha component 0xAARRGGBB >> 24 = 0x000000AA */ + pixels.put((byte) ((pixel >> 24) & 0xFF)); + } + } + /* Do not forget to flip the buffer! */ + pixels.flip(); + + texture.setTextureData(width, height, pixels); + texture.init(); + } + + /** + * Creates a char image from specified AWT font and char. + * + * @param font The AWT font + * @param c The char + * @param antiAlias Wheter the char should be antialiased or not + * @return Char image + */ + private BufferedImage createCharImage(java.awt.Font font, char c, boolean antiAlias) { + /* Creating temporary image to extract character size */ + BufferedImage image = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); + Graphics2D g = image.createGraphics(); + if (antiAlias) { + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + } + g.setFont(font); + FontMetrics metrics = g.getFontMetrics(); + g.dispose(); + + /* Get char charWidth and charHeight */ + int charWidth = metrics.charWidth(c); + int charHeight = metrics.getHeight(); + + /* Check if charWidth is 0 */ + if (charWidth == 0) { + return null; + } + + /* Create image for holding the char */ + image = new BufferedImage(charWidth, charHeight, BufferedImage.TYPE_INT_ARGB); + g = image.createGraphics(); + if (antiAlias) { + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + } + g.setFont(font); + + //g.setPaint(Color.BLACK); + g.drawString(String.valueOf(c), 0, metrics.getAscent()); + g.dispose(); + return image; + } + + public void delete() { + texture.delete(); + } + +} diff --git a/GameEngine/src/main/java/gameEngine/views/Glyph.java b/GameEngine/src/main/java/gameEngine/views/Glyph.java new file mode 100644 index 0000000..279a172 --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/views/Glyph.java @@ -0,0 +1,33 @@ +package gameEngine.views; + +/** + * This class represents a font glyph. + * + * @author Heiko Brumme + */ +public class Glyph { + + public final int width; + public final int height; + public final int x; + public final int y; + public final float advance; + + /** + * Creates a font Glyph. + * + * @param width Width of the Glyph + * @param height Height of the Glyph + * @param x X coordinate on the font texture + * @param y Y coordinate on the font texture + * @param advance Advance width + */ + public Glyph(int width, int height, int x, int y, float advance) { + this.width = width; + this.height = height; + this.x = x; + this.y = y; + this.advance = advance; + } + +} \ No newline at end of file diff --git a/GameEngine/src/main/java/gameEngine/views/IUpdatable.java b/GameEngine/src/main/java/gameEngine/views/IUpdatable.java new file mode 100644 index 0000000..dd94e01 --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/views/IUpdatable.java @@ -0,0 +1,5 @@ +package gameEngine.views; + +public interface IUpdatable { + void update(); +} diff --git a/GameEngine/src/main/java/gameEngine/views/InputField.java b/GameEngine/src/main/java/gameEngine/views/InputField.java new file mode 100644 index 0000000..b7a78b2 --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/views/InputField.java @@ -0,0 +1,222 @@ +package gameEngine.views; + +import gameEngine.Time; +import gameEngine.input.MouseInput; +import org.joml.Vector3f; +import gameEngine.input.Input; +import static org.lwjgl.glfw.GLFW.*; + +public class InputField implements IUpdatable { + + private Text displayText; + private boolean isFocused; + private Vector3f position; + private float width, height; + private String placeholder; + private String currentText; // 入力中のテキスト + private String displayedText; // 表示するテキスト + private Sprite backgroundSprite; + private int cursorPosition; // カーソル位置 + private boolean showCursor; // カーソルの表示フラグ + private float cursorBlinkTimer; // カーソルの点滅タイマー + private final float CURSOR_BLINK_INTERVAL = 0.5f; // カーソルの点滅間隔(秒) + private Color defaultColor; + private OnChangeListener onChangeListener; + + public interface OnChangeListener { + void onChange(String newValue); + } + + public InputField(float posX, float posY, float width, float height, String placeholder, int textSize) { + this.position = new Vector3f(posX, posY, 0); + this.width = width; + this.height = height; + this.placeholder = placeholder; + this.currentText = ""; + this.displayedText = ""; + this.isFocused = false; + this.cursorPosition = 0; + this.showCursor = true; + this.cursorBlinkTimer = 0.0f; + + this.backgroundSprite = new Sprite(Window.RESOURCE_PATH + "EditorFrame.png", posX, posY, width, height); + this.backgroundSprite.setSize(width, height); + + displayText = new Text(posX + 2, posY, placeholder, textSize); + displayText.setText(displayedText.isEmpty() ? placeholder : displayedText); + displayText.setColor(new Color(1,1,1,1)); // RGB値 (白) + } + + public void setOnChangeListener(OnChangeListener listener) { + this.onChangeListener = listener; + } + + public void update() { + handleInput(); + updateCursorBlink(Time.deltaTime); + render(); + } + + private void handleInput() { + float mouseX = MouseInput.getX(); + float mouseY = MouseInput.getY(); + + if (!isFocused && !currentText.equals(displayedText)) { + currentText = displayedText; + } + + if (Input.GetMouseButtonDown(GLFW_MOUSE_BUTTON_LEFT)) { + boolean wasFocused = isFocused; + isFocused = isMouseOver(mouseX, mouseY); + + if (!isFocused && wasFocused && onChangeListener != null) { + displayedText = currentText; // 最終的な入力内容を更新 + onChangeListener.onChange(currentText); + } + } + + if (isFocused) { + handleTextInput(); + + if (Input.GetKeyDown(GLFW_KEY_ENTER)) { + // Enterキーでフォーカス解除&内容を確定 + isFocused = false; + displayedText = currentText; // 入力内容を確定 + if (onChangeListener != null) { + onChangeListener.onChange(currentText); + } + } + + if (Input.GetKeyDown(GLFW_KEY_BACKSPACE) && cursorPosition > 0) { + currentText = currentText.substring(0, cursorPosition - 1) + currentText.substring(cursorPosition); + cursorPosition--; + } + } + } + + private void handleTextInput() { + boolean isShiftPressed = Input.GetKey(GLFW_KEY_LEFT_SHIFT) || Input.GetKey(GLFW_KEY_RIGHT_SHIFT); + + // 英字入力 + for (int key = GLFW_KEY_A; key <= GLFW_KEY_Z; key++) { + if (Input.GetKeyDown(key)) { + char typedChar = (char) (isShiftPressed ? key : key + 32); // シフトキーで大文字・小文字切り替え + currentText = currentText.substring(0, cursorPosition) + typedChar + currentText.substring(cursorPosition); + cursorPosition++; + } + } + + // 数字入力 (メインの数字キー 0-9) + for (int key = GLFW_KEY_0; key <= GLFW_KEY_9; key++) { + if (Input.GetKeyDown(key)) { + char typedChar = (char) ('0' + (key - GLFW_KEY_0)); + currentText = currentText.substring(0, cursorPosition) + typedChar + currentText.substring(cursorPosition); + cursorPosition++; + displayText.setText(currentText.isEmpty() ? placeholder : currentText); + } + } + + // テンキーの数字入力 (0-9) + for (int key = GLFW_KEY_KP_0; key <= GLFW_KEY_KP_9; key++) { + if (Input.GetKeyDown(key)) { + char typedChar = (char) ('0' + (key - GLFW_KEY_KP_0)); + currentText = currentText.substring(0, cursorPosition) + typedChar + currentText.substring(cursorPosition); + cursorPosition++; + displayText.setText(currentText.isEmpty() ? placeholder : currentText); + } + } + + // 記号入力(上手くいったもののみ) + if (isShiftPressed) { + if (Input.GetKeyDown(GLFW_KEY_MINUS)) appendChar('='); + else if (Input.GetKeyDown(GLFW_KEY_SEMICOLON)) appendChar('+'); + else if (Input.GetKeyDown(GLFW_KEY_COMMA)) appendChar('<'); + else if (Input.GetKeyDown(GLFW_KEY_PERIOD)) appendChar('>'); + else if (Input.GetKeyDown(GLFW_KEY_SLASH)) appendChar('?'); + + } else { + if (Input.GetKeyDown(GLFW_KEY_MINUS)) appendChar('-'); + else if (Input.GetKeyDown(GLFW_KEY_SEMICOLON)) appendChar(';'); + else if (Input.GetKeyDown(GLFW_KEY_COMMA)) appendChar(','); + else if (Input.GetKeyDown(GLFW_KEY_PERIOD)) appendChar('.'); + else if (Input.GetKeyDown(GLFW_KEY_SLASH)) appendChar('/'); + } + + + // Deleteキーでカーソル右側の文字を削除 + if (Input.GetKeyDown(GLFW_KEY_DELETE) && cursorPosition < currentText.length()) { + currentText = currentText.substring(0, cursorPosition) + currentText.substring(cursorPosition + 1); + displayText.setText(currentText.isEmpty() ? placeholder : currentText); + } + + // 左右キーでカーソル移動 + if (Input.GetKeyDown(GLFW_KEY_LEFT) && cursorPosition > 0) { + cursorPosition--; + } + if (Input.GetKeyDown(GLFW_KEY_RIGHT) && cursorPosition < currentText.length()) { + cursorPosition++; + } + } + + private void appendChar(char c) { + currentText = currentText.substring(0, cursorPosition) + c + currentText.substring(cursorPosition); + cursorPosition++; + displayText.setText(currentText.isEmpty() ? placeholder : currentText); + } + + private void updateCursorBlink(float deltaTime) { + cursorBlinkTimer += deltaTime; + if (cursorBlinkTimer > CURSOR_BLINK_INTERVAL) { + cursorBlinkTimer = 0.0f; + showCursor = !showCursor; + } + } + + private void setCursorPositionAtMouse(float mouseX) { + float relativeMouseX = mouseX - position.x; + + if (relativeMouseX <= 0) { + cursorPosition = 0; + } else if (relativeMouseX >= displayText.getDisplayedWidth()) { + cursorPosition = currentText.length(); + } else { + float cumulativeWidth = 0; + for (int i = 0; i < currentText.length(); i++) { + cumulativeWidth += displayText.getWidthAtIndex(i); // 各文字の幅を取得 + if (cumulativeWidth >= relativeMouseX) { + cursorPosition = i; // 最も近い文字位置 + break; + } + } + } + } + + public boolean isMouseOver(float mouseX, float mouseY) { + return mouseX >= position.x && mouseX <= position.x + width && + mouseY >= position.y && mouseY <= position.y + height; + } + + public void render() { + backgroundSprite.update(); + displayText.setText(isFocused ? currentText : (displayedText.isEmpty() ? placeholder : displayedText)); + displayText.update(); + + if (isFocused && showCursor) { + float cursorX = position.x + displayText.getWidthAtIndex(cursorPosition); + Text cursor = new Text(cursorX, position.y, "|", displayText.textSize); + cursor.setColor(new Color(1,1,1,1)); // RGB値 (白) + cursor.update(); + } + } + + public void copyDisplayedTextToCurrent(){ + currentText = displayedText; + } + + public void setText(String text) { + this.displayedText = text; + if (!isFocused) { + displayText.setText(displayedText.isEmpty() ? placeholder : displayedText); + } + } +} diff --git a/GameEngine/src/main/java/gameEngine/views/LineRenderer.java b/GameEngine/src/main/java/gameEngine/views/LineRenderer.java new file mode 100644 index 0000000..a7ab4d2 --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/views/LineRenderer.java @@ -0,0 +1,40 @@ +package gameEngine.views; +import org.joml.Vector3f; + +public class LineRenderer { + private final Sprite lineSprite; + + public LineRenderer(String spritePath) { + this.lineSprite = new Sprite(spritePath); + } + + public void updateLine(Vector3f start, Vector3f end, float thickness) { + Vector3f linePosition = calculatePosition(start, end); + float lineLength = calculateDistance(start, end); + float angle = calculateAngle(start, end); + + linePosition.x -= (float) (lineLength / 2 * Math.cos(Math.toRadians(angle))); + linePosition.y -= (float) (lineLength / 2 * Math.sin(Math.toRadians(angle))); + + lineSprite.setPosition(linePosition.x + 11, linePosition.y + 11); + lineSprite.setRotation(new Vector3f(0, 0, angle)); + lineSprite.setScale(new Vector3f(lineLength, thickness, 1)); + lineSprite.update(); + } + + public void render() { + lineSprite.render(); + } + + public Vector3f calculatePosition(Vector3f posA, Vector3f posB) { + return new Vector3f((posA.x + posB.x) / 2, (posA.y + posB.y) / 2, 0); + } + + private float calculateDistance(Vector3f posA, Vector3f posB) { + return (float) Math.sqrt(Math.pow(posB.x - posA.x, 2) + Math.pow(posB.y - posA.y, 2)); + } + + private float calculateAngle(Vector3f posA, Vector3f posB) { + return (float) Math.toDegrees(Math.atan2(posB.y - posA.y, posB.x - posA.x)); + } +} diff --git a/GameEngine/src/main/java/gameEngine/views/Renderer.java b/GameEngine/src/main/java/gameEngine/views/Renderer.java new file mode 100644 index 0000000..e59dfe0 --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/views/Renderer.java @@ -0,0 +1,80 @@ +package gameEngine.views; + +import org.joml.Vector3f; + +import static org.lwjgl.opengl.GL11.*; + +public abstract class Renderer { + public Vector3f position = new Vector3f(0, 0, 0); + public Vector3f rotation = new Vector3f(0, 0, 0); + public Vector3f scale = new Vector3f(1, 1, 1); + public Color color = new Color(1f, 1f, 1f, 1f); + + public void setPosition(float x, float y){ + this.position.x = x; + this.position.y = y; + } + + public void setPosition(float x, float y, float z){ + this.position = new Vector3f(x,y,z); + } + + public void setPosition(Vector3f pos){ + this.position = pos; + } + + public void setRotation(float x, float y, float z){ + this.rotation = new Vector3f(x,y,z); + } + + public void setRotation(Vector3f rot){ + this.rotation = rot; + } + + public void setScale(float x, float y){ + this.scale.x = x; + this.scale.y = y; + } + + public void setScale(float x, float y, float z){ + this.scale = new Vector3f(x,y,z); + } + + public void setScale(Vector3f scale){ + this.scale = scale; + } + + protected void prepareRendering() { + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(0, Window.getInstance().width, Window.getInstance().height, 0, -1000, 1000); + + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + float zScale = (position.z >= 0) ? 1.0f + (position.z * 0.1f) : 1.0f / (1.0f + Math.abs(position.z) * 0.1f); + glTranslatef(position.x, position.y, position.z); + glRotatef(rotation.x, 1, 0, 0); + glRotatef(rotation.y, 0, 1, 0); + glRotatef(rotation.z, 0, 0, 1); + glScalef(scale.x * zScale, scale.y * zScale, scale.z); + + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColor4f(color.r, color.g, color.b, color.a); + } + + protected void endRendering() { + glDisable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); + + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + } + + public abstract void render(); +} \ No newline at end of file diff --git a/GameEngine/src/main/java/gameEngine/views/Sprite.java b/GameEngine/src/main/java/gameEngine/views/Sprite.java new file mode 100644 index 0000000..cec6a7b --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/views/Sprite.java @@ -0,0 +1,90 @@ +package gameEngine.views; + +import org.joml.Vector3f; + +import static org.lwjgl.opengl.GL11.*; + +public class Sprite extends Renderer implements IUpdatable { + + private Texture texture; + + private int spriteWidth = 1; + private int spriteHeight = 1; + + public Sprite(String texturePath, Vector3f pos, Vector3f scale) { + this.texture = new Texture(texturePath); // テクスチャの読み込み + this.position = pos; + this.rotation = new Vector3f(0, 0, 0); + this.scale = scale; + updateSpriteDimensions(); + } + + public Sprite(String texturePath, float posX, float posY, float scaleX, float scaleY) { + this(texturePath, new Vector3f(posX, posY, 0), new Vector3f(scaleX, scaleY , 1.0f)); + updateSpriteDimensions(); + } + + public Sprite(String texturePath) { + this(texturePath, new Vector3f(0, 0, 0), new Vector3f(1.0f, 1.0f, 1.0f)); + } + + public void update(){ + Integer id = texture.getId(); + if(id == null) { + texture.init(); + } + render(); + } + + public void setColor(Color color) { + this.color = color; + } + + public void setTexturePath(String texturePath) { + this.texture = new Texture(texturePath); + updateSpriteDimensions(); + } + + public void setSize(float width, float height) { + float scaleX = width / spriteWidth; + float scaleY = height / spriteHeight; + setScale(scaleX, scaleY); + } + + public float getDisplayedWidth() { + return spriteWidth * scale.x; + } + + public float getDisplayedHeight() { + return spriteHeight * scale.y; + } + + public void updateSpriteDimensions() { + if (texture != null) { + this.spriteWidth = texture.getWidth(); + this.spriteHeight = texture.getHeight(); + } + } + + public boolean isMouseOver(float mouseX, float mouseY) { + float width = getDisplayedWidth(); + float height = getDisplayedHeight(); + return mouseX >= position.x && mouseX <= position.x + width && + mouseY >= position.y && mouseY <= position.y + height; + } + + @Override + public void render() { + prepareRendering(); + glBindTexture(GL_TEXTURE_2D, texture.getId()); + + glBegin(GL_QUADS); + glTexCoord2f(0, 0); glVertex3f(0, 0, 0); // 左上の頂点 + glTexCoord2f(1, 0); glVertex3f(spriteWidth, 0, 0); // 右上の頂点 + glTexCoord2f(1, 1); glVertex3f(spriteWidth, spriteHeight, 0); // 右下の頂点 + glTexCoord2f(0, 1); glVertex3f(0, spriteHeight, 0); // 左下の頂点 + glEnd(); + + endRendering(); + } +} diff --git a/GameEngine/src/main/java/gameEngine/views/Text.java b/GameEngine/src/main/java/gameEngine/views/Text.java new file mode 100644 index 0000000..3df4d5d --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/views/Text.java @@ -0,0 +1,177 @@ +package gameEngine.views; +import org.joml.Vector3f; + +import java.util.Map; + +import static java.awt.Font.PLAIN; +import static java.awt.Font.SANS_SERIF; +import static org.lwjgl.opengl.GL11.*; + +public class Text extends Renderer implements IUpdatable { + + private Texture fontTexture; + private Map fontGlyphs; + private int fontHeight; + public String text; + public int textSize; + + public Text(float posX, float posY, String text, int textSize) { + this.position = new Vector3f(posX,posY,0); + this.rotation = new Vector3f(0,0,0); + this.scale = new Vector3f(1,1,1); + this.text = text; + this.textSize = textSize; + this.color = new Color(0,0,0,1); + + Font font = new Font(new java.awt.Font(SANS_SERIF, PLAIN, textSize), true); + fontTexture = font.getTexture(); + fontGlyphs = font.getGlyphs(); + fontHeight = font.getFontHeight(); + } + + public void update(){ + Integer id = fontTexture.getId(); + if(id == null) { + fontTexture.init(); + } + render(); + } + + public void setText(String newText) { + this.text = newText; + render(); + } + + public void setTextSize(int newTextSize) { + if (newTextSize < 3 || newTextSize > 127) return; + + this.textSize = newTextSize; + Font font = new Font(new java.awt.Font(SANS_SERIF, PLAIN, textSize), true); + this.fontTexture = font.getTexture(); + this.fontGlyphs = font.getGlyphs(); + this.fontHeight = font.getFontHeight(); + render(); + } + + public void setColor(Color color) { + this.color = color; + render(); + } + + public boolean isMouseOver(float mouseX, float mouseY) { + float width = getDisplayedWidth(); + float height = getDisplayedHeight(); + return mouseX >= position.x && mouseX <= position.x + width && + mouseY >= position.y && mouseY <= position.y + height; + } + + @Override + public void render() { + prepareRendering(); + glBindTexture(GL_TEXTURE_2D, fontTexture.getId()); + + float drawX = 0; // Render relative to the transformed position + float drawY = 0; + + for (int i = 0; i < text.length(); i++) { + char ch = text.charAt(i); + if (ch == '\n') { + drawY += fontHeight; + drawX = 0; + continue; + } + + Glyph g = fontGlyphs.get(ch); + if (g != null) { + glBegin(GL_QUADS); + glTexCoord2f(g.x / (float) fontTexture.getWidth(), g.y / (float) fontTexture.getHeight()); + glVertex3f(drawX, drawY, 0); + + glTexCoord2f((g.x + g.width) / (float) fontTexture.getWidth(), g.y / (float) fontTexture.getHeight()); + glVertex3f(drawX + g.width, drawY, 0); + + glTexCoord2f((g.x + g.width) / (float) fontTexture.getWidth(), (g.y + g.height) / (float) fontTexture.getHeight()); + glVertex3f(drawX + g.width, drawY + g.height, 0); + + glTexCoord2f(g.x / (float) fontTexture.getWidth(), (g.y + g.height) / (float) fontTexture.getHeight()); + glVertex3f(drawX, drawY + g.height, 0); + glEnd(); + + drawX += g.width; // Advance x position by character width + } + } + + endRendering(); + } + + public int getHeight(CharSequence text) { + int height = 0; + int lineHeight = 0; + for (int i = 0; i < text.length(); i++) { + char c = text.charAt(i); + if (c == '\n') { + height += lineHeight; + lineHeight = 0; + continue; + } + if (c == '\r') { + continue; + } + Glyph g = fontGlyphs.get(c); + lineHeight = Math.max(lineHeight, g.height); + } + height += lineHeight; + return height; + } + + public float getDisplayedWidth() { + float width = 0; + float lineWidth = 0; + + for (int i = 0; i < text.length(); i++) { + char ch = text.charAt(i); + if (ch == '\n') { + width = Math.max(width, lineWidth); + lineWidth = 0; // 新しい行に移行するためにリセット + } else { + Glyph g = fontGlyphs.get(ch); + if (g != null) { + lineWidth += g.width; // 文字の幅を加算 + } + } + } + width = Math.max(width, lineWidth); // 最後の行の幅も確認 + return width * scale.x; + } + + public float getWidthAtIndex(int index) { + float width = 0; + float drawX = 0; + + for (int i = 0; i < index; i++) { + char ch = text.charAt(i); + if (ch == '\n') { + drawX = 0; + continue; + } + + Glyph g = fontGlyphs.get(ch); + if (g != null) { + drawX += g.width; // 文字の幅を加算 + } + } + return drawX * scale.x; + } + + public float getDisplayedHeight() { + int numLines = 1; // 初期行は1行 + for (int i = 0; i < text.length(); i++) { + if (text.charAt(i) == '\n') { + numLines++; + } + } + return numLines * fontHeight * scale.y; + } + + +} diff --git a/GameEngine/src/main/java/gameEngine/views/Texture.java b/GameEngine/src/main/java/gameEngine/views/Texture.java new file mode 100644 index 0000000..03426a6 --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/views/Texture.java @@ -0,0 +1,107 @@ +package gameEngine.views; + +import org.lwjgl.BufferUtils; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.nio.ByteBuffer; + +import static org.lwjgl.opengl.GL11.*; + +public class Texture { + + /* + 指定された画像ファイルを読み込み、そのピクセルデータをOpenGLにアップロードして + テクスチャとして利用できるようにする + */ + + private Integer id = null; //OpenGLのテクスチャID + private int width; // 読み込んだ画像の幅 + private int height; // 読み込んだ画像の高さ + private ByteBuffer pixels; + + public Texture() { + } + + public void setTextureData(int width, int height, ByteBuffer buffer) { + this.width = width; + this.height = height; + this.pixels = buffer; + } + + public Texture(String path) { + BufferedImage bi; + try { + //ImageIO.read(new File(path)): 画像ファイルを読み込み、BufferedImageオブジェクトを作成 + //BufferedImageはJava標準ライブラリのクラス + bi = ImageIO.read(new File(path)); + width = bi.getWidth(); + height = bi.getHeight(); + + //画像のピクセルデータをRGB形式で取得 + //pixelsRawは整数配列で、各ピクセルの色データが格納される + int[] pixelsRaw = bi.getRGB(0, 0, width, height, null, 0, width); + + //BufferUtils.createByteBuffer(width * height * 4): OpenGLに送信するためのバッファ(ByteBuffer)を作成 + pixels = BufferUtils.createByteBuffer(width * height * 4); + + // 各ピクセルを取り出し、色成分(R, G, B, A)を抽出してByteBufferに格納 + // (pixel >> 16) & 0xFF: ピクセルデータの上位16ビットを右にシフトし、赤色成分を抽出。 + // 同様に緑(8ビット右シフト)、青(そのまま)、アルファ(24ビット右シフト)も抽出 + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int pixel = pixelsRaw[y * width + x]; + pixels.put((byte) ((pixel >> 16) & 0xFF)); // Red + pixels.put((byte) ((pixel >> 8) & 0xFF)); // Green + pixels.put((byte) (pixel & 0xFF)); // Blue + pixels.put((byte) ((pixel >> 24) & 0xFF)); // Alpha + } + } + + // ByteBufferを読み込み可能な状態に + pixels.flip(); + + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void init() { + // 新しいテクスチャIDを生成 + id = glGenTextures(); + + // 生成したテクスチャIDを現在のテクスチャとしてバインド + // 以降のテクスチャ関連の操作はこのテクスチャに対して行われる + glBindTexture(GL_TEXTURE_2D, id); + + // テクスチャデータをOpenGLに送信 + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + //テクスチャのフィルタリング方法を設定 + //ニアレストフィルタリング:拡大縮小時のぼやけがなくなる + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + // テクスチャのバインディングを解除 + glBindTexture(GL_TEXTURE_2D, 0); + } + + public Integer getId() { + return id; + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public void delete() { + //テクスチャを削除し、OpenGLからリソースを解放します。 + glDeleteTextures(id); + } +} \ No newline at end of file diff --git a/GameEngine/src/main/java/gameEngine/views/Window.java b/GameEngine/src/main/java/gameEngine/views/Window.java new file mode 100644 index 0000000..434a80d --- /dev/null +++ b/GameEngine/src/main/java/gameEngine/views/Window.java @@ -0,0 +1,170 @@ +package gameEngine.views; +import gameEngine.Time; +import gameEngine.input.*; +import gameEngine.scenes.*; + +import org.lwjgl.*; +import org.lwjgl.glfw.*; +import org.lwjgl.opengl.*; +import org.lwjgl.system.*; + +import java.nio.*; +import java.util.HashMap; + +import static org.lwjgl.glfw.Callbacks.*; +import static org.lwjgl.glfw.GLFW.*; +import static org.lwjgl.opengl.GL11.*; +import static org.lwjgl.system.MemoryStack.*; +import static org.lwjgl.system.MemoryUtil.*; + +public class Window { + private static final int DEFAULT_WIDTH = 1200; + private static final int DEFAULT_HEIGHT = 900; + private static final String DEFAULT_TITLE = "PuniPuniGameEngine"; + public static final String RESOURCE_PATH = "GameEngine/resources/"; + + private static Window instance; + private long glfwWindow; + public int width; + public int height; + private String title; + private static Scene currentScene; + private Text fpsText; + private EditorScene editorScene ; + private boolean playDTRAM; + + private Window() { + this.width = DEFAULT_WIDTH; + this.height = DEFAULT_HEIGHT; + this.title = DEFAULT_TITLE; + this.playDTRAM = false; + init(); + System.out.println("JVM Version: " + System.getProperty("java.vm.version")); + } + + public static Window getInstance() { + if (instance == null) { + instance = new Window(); + } + return instance; + } + + public void runFromEditor() { + Window.changeScene(0); + run(); + } + + public void run() { + System.out.println("Hello LWJGL " + Version.getVersion() + "!"); + fpsText = new Text(0,0,"",20); + loop(); + glfwFreeCallbacks(glfwWindow); + glfwDestroyWindow(glfwWindow); + glfwTerminate(); + glfwSetErrorCallback(null).free(); + } + + private void init() { + GLFWErrorCallback.createPrint(System.err).set(); + if ( !glfwInit() ) { + throw new IllegalStateException("Unable to initialize GLFW"); + } + + //Configure GLFW + glfwDefaultWindowHints(); + glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); + glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); + glfwWindowHint(GLFW_MAXIMIZED, GLFW_FALSE); + + glfwWindow = glfwCreateWindow(this.width, this.height, this.title, NULL, NULL); + if ( glfwWindow == NULL ) { + throw new RuntimeException("Failed to create the GLFW window"); + } + + glfwSetCursorPosCallback(glfwWindow, MouseInput::mousePosCallback); + glfwSetMouseButtonCallback(glfwWindow, MouseInput::mouseButtonCallback); + glfwSetScrollCallback(glfwWindow, MouseInput::mouseScrollCallBack); + glfwSetKeyCallback(glfwWindow, KeyInput::keyCallback); + + // Get the thread stack and push a new frame + try ( MemoryStack stack = stackPush() ) { + IntBuffer pWidth = stack.mallocInt(1); // int* + IntBuffer pHeight = stack.mallocInt(1); // int* + + // Get the window size passed to glfwCreateWindow + glfwGetWindowSize(glfwWindow, pWidth, pHeight); + + // Get the resolution of the primary monitor + GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor()); + + // Center the window + glfwSetWindowPos( + glfwWindow, + (vidmode.width() - pWidth.get(0)) / 2, + (vidmode.height() - pHeight.get(0)) / 2 + ); + } + + glfwMakeContextCurrent(glfwWindow); + glfwSwapInterval(1); + glfwShowWindow(glfwWindow); + GL.createCapabilities(); + } + + private void loop() { + while (!glfwWindowShouldClose(glfwWindow)) { + glfwPollEvents(); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + float dt = Time.deltaTime; + if (dt >= 0) { + + if(currentScene instanceof GameScene && !playDTRAM) System.out.println(System.nanoTime()); //開始 + currentScene.update(dt); + currentScene.processTasks(); + if(currentScene instanceof GameScene && !playDTRAM) System.out.println(System.nanoTime()); //終了 + + fpsText.setText("" + Time.getFPS()); + fpsText.setColor(Color.WHITE); + fpsText.update(); + + MouseInput.endFrame(); + KeyInput.endFrame(); + } + + glfwSwapBuffers(glfwWindow); + Time.update(); + } + } + + public static void changeScene(int newScene) { + switch (newScene) { + case 0: // EditorScene + instance.playDTRAM = false; + if (instance.editorScene == null) { + instance.editorScene = new EditorScene(instance.width, instance.height); + } + currentScene = instance.editorScene; + break; + case 1: // GameScene + if(currentScene != null && ((EditorScene) currentScene).editorEntities != null){ + EditorScene editorScene = (EditorScene) currentScene; + currentScene = new GameScene(new HashMap<>(editorScene.editorEntities)); + } + else currentScene = new GameScene(); + break; + case 2: //DTRAM Scene + instance.playDTRAM = true; + currentScene = new GameScene(); + break; + default: + assert false : "Unknown Scene [" + newScene + "]"; + break; + } + } + + public Scene getScene(){ + return currentScene; + } + +} \ No newline at end of file diff --git a/GameEngine/src/main/java/main/Main.java b/GameEngine/src/main/java/main/Main.java new file mode 100644 index 0000000..62d6071 --- /dev/null +++ b/GameEngine/src/main/java/main/Main.java @@ -0,0 +1,10 @@ +package main; + +import gameEngine.views.Window; + +public class Main { + public static void main(String[] args) { + Window window = Window.getInstance(); + window.runFromEditor(); + } +} \ No newline at end of file diff --git a/GameEngine/target/classes/gameEngine/GameEditor.class b/GameEngine/target/classes/gameEngine/GameEditor.class new file mode 100644 index 0000000..d3d421b --- /dev/null +++ b/GameEngine/target/classes/gameEngine/GameEditor.class Binary files differ diff --git a/GameEngine/target/classes/gameEngine/Time.class b/GameEngine/target/classes/gameEngine/Time.class new file mode 100644 index 0000000..5cf6104 --- /dev/null +++ b/GameEngine/target/classes/gameEngine/Time.class Binary files differ diff --git a/GameEngine/target/classes/gameEngine/entites/Camera$ProjectionType.class b/GameEngine/target/classes/gameEngine/entites/Camera$ProjectionType.class new file mode 100644 index 0000000..1aaa774 --- /dev/null +++ b/GameEngine/target/classes/gameEngine/entites/Camera$ProjectionType.class Binary files differ diff --git a/GameEngine/target/classes/gameEngine/entites/Camera.class b/GameEngine/target/classes/gameEngine/entites/Camera.class new file mode 100644 index 0000000..6b024e1 --- /dev/null +++ b/GameEngine/target/classes/gameEngine/entites/Camera.class Binary files differ diff --git a/GameEngine/target/classes/gameEngine/entites/Entity.class b/GameEngine/target/classes/gameEngine/entites/Entity.class new file mode 100644 index 0000000..5dd31c0 --- /dev/null +++ b/GameEngine/target/classes/gameEngine/entites/Entity.class Binary files differ diff --git a/GameEngine/target/classes/gameEngine/entites/GameObject.class b/GameEngine/target/classes/gameEngine/entites/GameObject.class new file mode 100644 index 0000000..9537f7b --- /dev/null +++ b/GameEngine/target/classes/gameEngine/entites/GameObject.class Binary files differ diff --git a/GameEngine/target/classes/gameEngine/entites/gameComponents/CopyEntity.class b/GameEngine/target/classes/gameEngine/entites/gameComponents/CopyEntity.class new file mode 100644 index 0000000..0bd8a12 --- /dev/null +++ b/GameEngine/target/classes/gameEngine/entites/gameComponents/CopyEntity.class Binary files differ diff --git a/GameEngine/target/classes/gameEngine/entites/gameComponents/GameComponent.class b/GameEngine/target/classes/gameEngine/entites/gameComponents/GameComponent.class new file mode 100644 index 0000000..d5c0196 --- /dev/null +++ b/GameEngine/target/classes/gameEngine/entites/gameComponents/GameComponent.class Binary files differ diff --git a/GameEngine/target/classes/gameEngine/entites/gameComponents/Mesh$MeshType.class b/GameEngine/target/classes/gameEngine/entites/gameComponents/Mesh$MeshType.class new file mode 100644 index 0000000..5998bdc --- /dev/null +++ b/GameEngine/target/classes/gameEngine/entites/gameComponents/Mesh$MeshType.class Binary files differ diff --git a/GameEngine/target/classes/gameEngine/entites/gameComponents/Mesh.class b/GameEngine/target/classes/gameEngine/entites/gameComponents/Mesh.class new file mode 100644 index 0000000..e6ecbcf --- /dev/null +++ b/GameEngine/target/classes/gameEngine/entites/gameComponents/Mesh.class Binary files differ diff --git a/GameEngine/target/classes/gameEngine/entites/gameComponents/MoveImage.class b/GameEngine/target/classes/gameEngine/entites/gameComponents/MoveImage.class new file mode 100644 index 0000000..5e5da88 --- /dev/null +++ b/GameEngine/target/classes/gameEngine/entites/gameComponents/MoveImage.class Binary files differ diff --git a/GameEngine/target/classes/gameEngine/entites/gameComponents/Physics.class b/GameEngine/target/classes/gameEngine/entites/gameComponents/Physics.class new file mode 100644 index 0000000..2c95333 --- /dev/null +++ b/GameEngine/target/classes/gameEngine/entites/gameComponents/Physics.class Binary files differ diff --git a/GameEngine/target/classes/gameEngine/geometry/Transform.class b/GameEngine/target/classes/gameEngine/geometry/Transform.class new file mode 100644 index 0000000..c5e3c0a --- /dev/null +++ b/GameEngine/target/classes/gameEngine/geometry/Transform.class Binary files differ diff --git a/GameEngine/target/classes/gameEngine/input/Input.class b/GameEngine/target/classes/gameEngine/input/Input.class new file mode 100644 index 0000000..de8233c --- /dev/null +++ b/GameEngine/target/classes/gameEngine/input/Input.class Binary files differ diff --git a/GameEngine/target/classes/gameEngine/input/KeyInput.class b/GameEngine/target/classes/gameEngine/input/KeyInput.class new file mode 100644 index 0000000..d5d0af4 --- /dev/null +++ b/GameEngine/target/classes/gameEngine/input/KeyInput.class Binary files differ diff --git a/GameEngine/target/classes/gameEngine/input/MouseInput.class b/GameEngine/target/classes/gameEngine/input/MouseInput.class new file mode 100644 index 0000000..01dd9ae --- /dev/null +++ b/GameEngine/target/classes/gameEngine/input/MouseInput.class Binary files differ diff --git a/GameEngine/target/classes/gameEngine/scenes/EditorScene.class b/GameEngine/target/classes/gameEngine/scenes/EditorScene.class new file mode 100644 index 0000000..85384d2 --- /dev/null +++ b/GameEngine/target/classes/gameEngine/scenes/EditorScene.class Binary files differ diff --git a/GameEngine/target/classes/gameEngine/scenes/GameScene.class b/GameEngine/target/classes/gameEngine/scenes/GameScene.class new file mode 100644 index 0000000..79701fa --- /dev/null +++ b/GameEngine/target/classes/gameEngine/scenes/GameScene.class Binary files differ diff --git a/GameEngine/target/classes/gameEngine/scenes/Scene.class b/GameEngine/target/classes/gameEngine/scenes/Scene.class new file mode 100644 index 0000000..0cf5df5 --- /dev/null +++ b/GameEngine/target/classes/gameEngine/scenes/Scene.class Binary files differ diff --git a/GameEngine/target/classes/gameEngine/views/Color.class b/GameEngine/target/classes/gameEngine/views/Color.class new file mode 100644 index 0000000..5fb1170 --- /dev/null +++ b/GameEngine/target/classes/gameEngine/views/Color.class Binary files differ diff --git a/GameEngine/target/classes/gameEngine/views/Texture.class b/GameEngine/target/classes/gameEngine/views/Texture.class new file mode 100644 index 0000000..cb5fcbf --- /dev/null +++ b/GameEngine/target/classes/gameEngine/views/Texture.class Binary files differ diff --git a/GameEngine/target/classes/gameEngine/views/Window.class b/GameEngine/target/classes/gameEngine/views/Window.class new file mode 100644 index 0000000..25d1756 --- /dev/null +++ b/GameEngine/target/classes/gameEngine/views/Window.class Binary files differ