diff --git a/AlgebraicDataflowArchitectureModel/.classpath b/AlgebraicDataflowArchitectureModel/.classpath new file mode 100644 index 0000000..3e99697 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/.classpath @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/AlgebraicDataflowArchitectureModel/.gitignore b/AlgebraicDataflowArchitectureModel/.gitignore new file mode 100644 index 0000000..3e2fcc7 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/.gitignore @@ -0,0 +1 @@ +/bin/ diff --git a/AlgebraicDataflowArchitectureModel/.project b/AlgebraicDataflowArchitectureModel/.project new file mode 100644 index 0000000..15e3364 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/.project @@ -0,0 +1,17 @@ + + + AlgebraicDataflowArchitectureModel + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/AlgebraicDataflowArchitectureModel/.settings/org.eclipse.jdt.core.prefs b/AlgebraicDataflowArchitectureModel/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..263a512 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,15 @@ +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.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=13 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +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 diff --git a/AlgebraicDataflowArchitectureModel/dtram.log b/AlgebraicDataflowArchitectureModel/dtram.log new file mode 100644 index 0000000..8a64fa5 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/dtram.log @@ -0,0 +1,44 @@ +3月 15, 2024 3:10:09 午後 application.ApplicationWindow +情報: launched +3月 15, 2024 3:10:14 午後 application.actions.AbstractSystemAction actionPerformed +情報: 開く +3月 15, 2024 3:10:33 午後 application.views.NavigationWindow stageChanged +情報: application.editor.stages.DataFlowModelingStage@3d463297 +3月 15, 2024 3:10:33 午後 application.views.NavigationWindow stageChanged +情報: application.editor.stages.PushPullSelectionStage@37c62c43 +3月 15, 2024 3:29:48 午後 application.ApplicationWindow +情報: launched +3月 15, 2024 3:29:51 午後 application.actions.AbstractSystemAction actionPerformed +情報: 開く +3月 15, 2024 3:29:55 午後 application.views.NavigationWindow stageChanged +情報: application.editor.stages.DataFlowModelingStage@3f75cd3f +3月 15, 2024 3:29:55 午後 application.views.NavigationWindow stageChanged +情報: application.editor.stages.PushPullSelectionStage@187ec296 +3月 15, 2024 3:29:59 午後 application.editor.stages.PushPullSelectionCellEditor stopEditing +情報: PUSH +3月 15, 2024 3:30:01 午後 application.editor.stages.PushPullSelectionCellEditor stopEditing +情報: PUSH +3月 15, 2024 3:30:03 午後 application.editor.stages.ControlFlowDelegationCellEditor startEditing +情報: Double clicked +3月 15, 2024 3:30:03 午後 application.editor.stages.ControlFlowDelegationCellEditor startEditing +情報: Selecting Control-Flow (src, dst) = (updateGPS, longitude) +3月 15, 2024 3:30:05 午後 application.editor.stages.ControlFlowDelegationStage$2 mouseReleased +情報: Reset selection +3月 15, 2024 3:30:07 午後 application.editor.stages.ControlFlowDelegationCellEditor startEditing +情報: Double clicked +3月 15, 2024 3:30:07 午後 application.editor.stages.ControlFlowDelegationCellEditor startEditing +情報: Selecting Control-Flow (src, dst) = (longitude, mapLongitude) +3月 15, 2024 3:30:07 午後 application.editor.stages.ControlFlowDelegationStage$2 mouseReleased +情報: Reset selection +3月 15, 2024 3:31:24 午後 application.editor.stages.ControlFlowDelegationCellEditor startEditing +情報: Double clicked +3月 15, 2024 3:31:24 午後 application.editor.stages.ControlFlowDelegationCellEditor startEditing +情報: Selecting Control-Flow (src, dst) = (updateGPS, longitude) +3月 15, 2024 3:31:25 午後 application.editor.stages.ControlFlowDelegationStage$2 mouseReleased +情報: Reset selection +3月 15, 2024 3:31:33 午後 application.editor.stages.ControlFlowDelegationCellEditor startEditing +情報: Double clicked +3月 15, 2024 3:31:33 午後 application.editor.stages.ControlFlowDelegationCellEditor startEditing +情報: Selecting Control-Flow (src, dst) = (updateGPS, longitude) +3月 15, 2024 3:31:39 午後 application.editor.stages.ControlFlowDelegationStage$2 mouseReleased +情報: Reset selection diff --git a/AlgebraicDataflowArchitectureModel/lib/jgraphx.jar b/AlgebraicDataflowArchitectureModel/lib/jgraphx.jar new file mode 100644 index 0000000..72b5aae --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/lib/jgraphx.jar Binary files differ diff --git a/AlgebraicDataflowArchitectureModel/models/Algolike.model b/AlgebraicDataflowArchitectureModel/models/Algolike.model new file mode 100644 index 0000000..aa943d3 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/Algolike.model @@ -0,0 +1,113 @@ +init { + deck := nil + handsA := nil + handsB := nil + guessA := 0 + guessB := 0 +} +channel targetAInput{ + out targetA(t:Int, setTargetA(a:Int)) == a +} +channel targetBInput{ + out targetB(t:Int, setTargetB(b:Int)) == b +} +channel attackerAInput{ + out attackerA(t:Int, setAttackerA(a:Int)) == a +} +channel attackerBInput{ + out attackerB(t:Int, setAttackerB(b:Int)) == b +} +channel guessAInput{ + out guessA(t:Int, setGuessA(a:Int)) == a +} +channel guessBInput{ + out guessB(t:Int, setGuessB(b:Int)) == b +} +channel addDeck{ + out deck(d:List, addCard(num:Integer)) == cons(tuple(num, false), d) +} + +channel drawAInput{ + + 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) +} + +channel inputDrawArgsA{ + 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)), + sortByKey(cons(tuple(fst(head(d)), true), outA))) + + 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) +} + +channel selectAInput{ + ref attackerA(a:Int, selectAndAttackA(a, t, g, b)) + 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) +} +channel inputSelectArgA{ + 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), + set(outB, fst(snd(sucTrgAtk)), tuple(fst(get(outB, fst(snd(sucTrgAtk)))), true)), + outB) +} +channel drawBInput{ + 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) +} + +channel inputDrawArgsB{ + 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), + 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), + set(outA, snd(sucTrg), tuple(fst(get(outA, snd(sucTrg))), true)), + outA) + out deck(t:List, drawB(sucTrg, d)) == tail(t) +} + +channel selectBInput{ + ref attackerB(atk:Int, selectAndAttackB(atk, t, g, a)) + 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) +} +channel inputSelectArgB{ + 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), + 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) +} +channel judgeB{ + in handsB(b:List, judge(j)) == j + out loseB(lb:Bool, judge(j)) == eq(length(extractFaceDown(j)), 0) +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/models/Audio.dtram b/AlgebraicDataflowArchitectureModel/models/Audio.dtram new file mode 100644 index 0000000..66862ef --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/Audio.dtram @@ -0,0 +1,33 @@ +model { +channel CIO3 { + out BGMVolume(b: Double, setBGM(b2)) == b2 +} +channel CIO2 { + out soundVolume(s: Double, setSound(s2)) == s2 +} +channel CIO1 { + out audioVolume(a: Double, setAudio(a2)) == a2 +} +channel C1 { + in audioVolume(a, calc(a2, s2)) == a2 + in soundVolume(s, calc(a2, s2)) == s2 + out outputSoundVolume(os, calc(a2, s2)) == a2 * s2 +} +channel C2 { + in audioVolume(a, calc(a2, b2)) == a2 + in BGMVolume(b, calc(a2, b2)) == b2 + out outputBGMVolume(ob, calc(a2, b2)) == a2 * b2 +} +} +geometry { + node r soundVolume:220,140,80,30 + node r audioVolume:220,280,80,30 + node r outputSoundVolume:650,190,80,30 + node r BGMVolume:220,420,80,30 + node r outputBGMVolume:650,340,80,30 + node ioc CIO3:100,420,30,30 + node ioc CIO2:100,140,30,30 + node ioc CIO1:100,280,30,30 + node c C1:530,190,30,30 + node c C2:530,340,30,30 +} diff --git a/AlgebraicDataflowArchitectureModel/models/Base.model b/AlgebraicDataflowArchitectureModel/models/Base.model new file mode 100644 index 0000000..2236c0c --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/Base.model @@ -0,0 +1,13 @@ +channel CIO{ + 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 +} +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 +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/models/Clock.dtram b/AlgebraicDataflowArchitectureModel/models/Clock.dtram new file mode 100644 index 0000000..ae2169e --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/Clock.dtram @@ -0,0 +1,27 @@ +model { + channel CIO1 { + out min(m: Double, tick) == mod(m + 1, 60) + } + channel Clock { + in min(m, update(m2)) == m2 + out hour(h: Double, update(m2)) == if(eq(m2, 0), mod(h + 1, 24), h) + } + channel MinUpdate { + in min(m, update(m2)) == m2 + out min_ang(m_ang: Double, update(m2)) == m2 / 30 * PI + } + channel HourUpdate { + in hour(h, update(h2)) == h2 + out hour_ang(h_ang: Double, update(h2)) == h2 / 6 * PI + } +} +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/CustomerOffice.model b/AlgebraicDataflowArchitectureModel/models/CustomerOffice.model new file mode 100644 index 0000000..f140bff --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/CustomerOffice.model @@ -0,0 +1,33 @@ +channel C_CustomerAOff_In { + out customerA_off(c:String, setOff(x)) == x + out customerA_off(c, e) == c +} + +channel C_CustomerBOff_In { + out customerB_off(c:String, setOff(x)) == x + out customerB_off(c, e) == c +} + +channel C_CompanyC1Add_In { + out companyC1_add(a:String, setAdd(y)) == y + out companyC1_add(a, e) == a +} + +channel C_CompanyC2Add_In { + out companyC2_add(a:String, setAdd(y)) == y + out companyC2_add(a, e) == a +} + +channel CA { + in customerA_off(c, sync(z, u, v)) == z + in companyC1_add(a1, sync(z, u, v)) == u + in companyC2_add(a2, sync(z, u, v)) == v + out customerA_add(a3:String, sync(z, u, v)) == if(eq(z, C1), u, if(eq(z, C2), v, null)) +} + +channel CB { + in customerB_off(c, sync(z, u, v)) == z + in companyC1_add(a1, sync(z, u, v)) == u + in companyC2_add(a2, sync(z, u, v)) == v + out customerB_add(a3:String, sync(z, u, v)) == if(eq(z, C1), u, if(eq(z, C2), v, null)) +} diff --git a/AlgebraicDataflowArchitectureModel/models/Game.model b/AlgebraicDataflowArchitectureModel/models/Game.model new file mode 100644 index 0000000..bc90ba4 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/Game.model @@ -0,0 +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 +} + +channel CIO2 { + 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 +} + +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 +} + +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/JumpGame.model b/AlgebraicDataflowArchitectureModel/models/JumpGame.model new file mode 100644 index 0000000..4c24a56 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/JumpGame.model @@ -0,0 +1,71 @@ +init { + force := pair(0.0, 0.0) + time := 0.0 + movex := 0.0 + movey := 0.0 + mass := 1.0 + ground := true + acceleration := pair(0.0, 0.0) + velocity := pair(0.0, 0.0) + onground := true + position := pair(0.0, 0.0) + clear := false + gameover := false +} +channel CIO { + 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 +} +channel CIO3 { + out movey(y:Double, jump(y2:Double)) == y2 +} +channel CIO4 { + out mass(m:Double, setMass(x:Double)) == x +} +channel CIO5 { + 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)) +} +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)), + 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) +} +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) +} +channel C5 { + 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)), + 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)) +} +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) +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/models/Kinetics.model b/AlgebraicDataflowArchitectureModel/models/Kinetics.model new file mode 100644 index 0000000..5218206 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/Kinetics.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/Map.dtram b/AlgebraicDataflowArchitectureModel/models/Map.dtram new file mode 100644 index 0000000..b1fe577 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/Map.dtram @@ -0,0 +1,23 @@ +model { + channel GPS_In { + out longitude(prev_long: Double, updateGPS(cur_long, cur_lat)) == cur_long + out latitude(prev_lat: Double, updateGPS(cur_long, cur_lat)) == cur_lat + } + channel LongUpdate { + in longitude(prev_long, updateLong(cur_long)) == cur_long + out mapLongitude(prev_mapLong, updateLong(cur_long)) == cur_long + } + channel LatUpdate { + in latitude(prev_lat, updateLat(cur_lat)) == cur_lat + out mapLatitude(prev_mapLat, updateLat(cur_lat)) == cur_lat + } +} +geometry { + node r mapLatitude:580,310,80,30 + node r latitude:190,310,80,30 + node r mapLongitude:580,170,80,30 + node r longitude:190,170,80,30 + node ioc GPS_In:40,60,30,30 + node c LongUpdate:410,170,30,30 + node c LatUpdate:410,310,30,30 +} diff --git a/AlgebraicDataflowArchitectureModel/models/Map.model b/AlgebraicDataflowArchitectureModel/models/Map.model new file mode 100644 index 0000000..2ea85ff --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/Map.model @@ -0,0 +1,14 @@ +channel GPS_In { + out longitude(prev_long: Double, updateGPS(cur_long, cur_lat)) == cur_long + out latitude(prev_lat: Double, updateGPS(cur_long, cur_lat)) == cur_lat +} + +channel LongUpdate { + in longitude(prev_long, updateLong(cur_long)) == cur_long + out mapLongitude(prev_mapLong, updateLong(cur_long)) == cur_long +} + +channel LatUpdate { + in latitude(prev_lat, updateLat(cur_lat)) == cur_lat + out mapLatitude(prev_mapLat, updateLat(cur_lat)) == cur_lat +} diff --git a/AlgebraicDataflowArchitectureModel/models/POS.dtram b/AlgebraicDataflowArchitectureModel/models/POS.dtram new file mode 100644 index 0000000..53eb13d --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/POS.dtram @@ -0,0 +1,27 @@ +model { +channel CIO { + out payment(p:Int, purchase(x:Int)) == x +} +channel C3 { + 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) +} +channel C2 { + in payment(p, update2(z)) == z + out history(h:List, update2(z)) == cons(z, h) +} +} +geometry { + node c C3:850,90,30,30 + node c C1:500,20,30,30 + node c C2:500,90,30,30 + node r total:1000,90,80,30 + node r payment:300,55,80,30 + node r history:650,90,80,30 + node r points:650,20,80,30 + node ioc CIO:150,55,30,30 +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/models/POS.model b/AlgebraicDataflowArchitectureModel/models/POS.model new file mode 100644 index 0000000..fc941ca --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/POS.model @@ -0,0 +1,15 @@ +channel CIO { + out payment(p:Int, purchase(x:Int)) == x +} +channel C3 { + 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) +} +channel C2 { + 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 new file mode 100644 index 0000000..6ae676d --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/POS2.model @@ -0,0 +1,14 @@ +channel CIO { + 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) +} + +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 +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/models/SSDStore.dtram b/AlgebraicDataflowArchitectureModel/models/SSDStore.dtram new file mode 100644 index 0000000..e226ec0 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/SSDStore.dtram @@ -0,0 +1,33 @@ +model { + channel capacity_In { + out capacity(prev_capacity:Int, setCapacity(cur_capacity)) == cur_capacity + } + channel price_In { + out price(prev_price:Int, setPrice(cur_price)) == cur_price + } + channel siteA_Add { + out siteA(prev_products:List, addProductToSiteA(price:Int, capacity:Int)) == cons({"price": price, "capacity": capacity}, prev_products) + } + channel capacity_Update { + in siteA(prev_products:List, updateList(cur_products, cur_capacity)) == cur_products + in capacity(prev_capacity:Int, updateList(cur_products, cur_capacity)) == cur_capacity + out itemsByCapacity(prev_items:List, updateList(cur_products, cur_capacity)) == selectGE(cur_products, "capacity", cur_capacity) + } + channel price_Update { + in siteA(prev_products:List, updateList(cur_products, cur_price)) == cur_products + in price(prev_price:Int, updateList(cur_products, cur_price)) == cur_price + out itemsByPrice(prev_items:List, updateList(cur_products, cur_price)) == selectLE(cur_products, "price", cur_price) + } +} +geometry { + node r price:250,40,80,30 + node r itemsByPrice:650,110,80,30 + node r itemsByCapacity:650,280,80,30 + node r siteA:250,200,80,30 + node r capacity:250,370,80,30 + node ioc capacity_In:100,370,30,30 + node ioc price_In:100,40,30,30 + node ioc siteA_Add:100,200,30,30 + node c capacity_Update:500,280,30,30 + node c price_Update:500,110,30,30 +} diff --git a/AlgebraicDataflowArchitectureModel/models/SSDStore.model b/AlgebraicDataflowArchitectureModel/models/SSDStore.model new file mode 100644 index 0000000..e8696fe --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/SSDStore.model @@ -0,0 +1,23 @@ +channel capacity_In { + out capacity(prev_capacity:Int, setCapacity(cur_capacity)) == cur_capacity +} + +channel price_In { + out price(prev_price:Int, setPrice(cur_price)) == cur_price +} + +channel siteA_Add { + out siteA(prev_products:List, addProductToSiteA(price:Int, capacity:Int)) == cons({"price": price, "capacity": capacity}, prev_products) +} + +channel capacity_Update { + in siteA(prev_products:List, updateList(cur_products, cur_capacity)) == cur_products + in capacity(prev_capacity:Int, updateList(cur_products, cur_capacity)) == cur_capacity + out itemsByCapacity(prev_items:List, updateList(cur_products, cur_capacity)) == selectGE(cur_products, "capacity", cur_capacity) +} + +channel price_Update { + in siteA(prev_products:List, updateList(cur_products, cur_price)) == cur_products + in price(prev_price:Int, updateList(cur_products, cur_price)) == cur_price + out itemsByPrice(prev_items:List, updateList(cur_products, cur_price)) == selectLE(cur_products, "price", cur_price) +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/models/SamplePULL.dtram b/AlgebraicDataflowArchitectureModel/models/SamplePULL.dtram new file mode 100644 index 0000000..0c06ae8 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/SamplePULL.dtram @@ -0,0 +1,21 @@ +model { + channel setAChannel { + out a(aValue:Int, setA(value)) == value + } + channel outBChannel { + in a(aValue:Int, calcB(aValue)) == aValue + out b(bValue:Int ,calcB(aValue)) == aValue * 2 + } + channel outCChannel { + in a(aValue:Int, calcC(aValue)) == aValue + out c(cValue:Int, calcC(aValue)) == aValue + 1 + } +} +geometry { + node r a:250,170,80,30 + node r b:580,80,80,30 + node r c:590,280,80,30 + node ioc setAChannel:90,170,30,30 + node c outBChannel:430,80,30,30 + node c outCChannel:440,280,30,30 +} diff --git a/AlgebraicDataflowArchitectureModel/models/SamplePUSH.dtram b/AlgebraicDataflowArchitectureModel/models/SamplePUSH.dtram new file mode 100644 index 0000000..c07e68b --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/SamplePUSH.dtram @@ -0,0 +1,15 @@ +model { + channel setAChannel { + out a(aValue:Int, setA(value)) == value + } + channel updateBChannel { + in a(aValue:Int, updateB(aValue)) == aValue + out b(bValue:Int, updateB(aValue)) == bValue + aValue + } +} +geometry { + node r a:270,270,80,30 + node r b:660,270,80,30 + node ioc setAChannel:90,240,100,90 + node c updateBChannel:460,240,100,90 +} diff --git a/AlgebraicDataflowArchitectureModel/models/StockManagement.model b/AlgebraicDataflowArchitectureModel/models/StockManagement.model new file mode 100644 index 0000000..b133f44 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/StockManagement.model @@ -0,0 +1,50 @@ +init { + stock := nil + shortage := nil +} + +channel CIO_enter { + 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) +} + +channel C1 { + 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))) +} + +channel C2 { + 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))), + 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))), + 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))) +} + +channel C4 { + 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 + ref stock(st, update5(rq2, st)) + out deriver(dr, update5(rq2, st)) == if(ge(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)), + sh, + insert(sh, fst(rq2), lookup(sh, fst(rq2)) + snd(rq2))) +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/models/TravelDistance.model b/AlgebraicDataflowArchitectureModel/models/TravelDistance.model new file mode 100644 index 0000000..a0c8fe2 --- /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..ebfe6b4 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/Triangle.model @@ -0,0 +1,13 @@ +channel cio1 { + out base(x: Double, setBase(x2)) == x2 +} + +channel cio2 { + out height(y: Double, setHeight(y2)) == y2 +} + +channel triangle { + in base(x, update(x2, y2)) == x2 + in height(y, update(x2, y2)) == y2 + out hypothenuse(z: Double, update(x2, y2)) == sqrt(x2 * x2 + y2 * y2) +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/models/Triangle2.model b/AlgebraicDataflowArchitectureModel/models/Triangle2.model new file mode 100644 index 0000000..db775bd --- /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 new file mode 100644 index 0000000..57de3b3 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/Twitter.model @@ -0,0 +1,47 @@ +channel a_Tweet { + out a_tweets(l:List, a_tweet(t:Str, time:Long)) == cons(tuple(time, t), l) +} + +channel a_Follow { + out a_following(f:List, a_follow(u:Int)) == cons(u, f) +} + +channel a_Home { + in a_tweets(la1, update_a(f2, la2, lb2, lc2)) == la2 + in b_tweets(lb1, update_a(f2, la2, lb2, lc2)) == lb2 + in c_tweets(lc1, update_a(f2, la2, lb2, lc2)) == lc2 + in a_following(f, update_a(f2, la2, lb2, lc2)) == f2 + out a_timeline(t:List, update_a(f2, la2, lb2, lc2)) == merge(la2, if(contains(f2,2), merge(lb2, if(contains(f2,3), lc2, nil)), if(contains(f2,3), lc2, nil))) +} + +channel b_Tweet { + out b_tweets(l:List, b_tweet(t:Str, time:Long)) == cons(tuple(time, t), l) +} + +channel b_Follow { + out b_following(f:List, b_follow(u:Int)) == cons(u, f) +} + +channel b_Home { + in a_tweets(la1, update_b(f2, la2, lb2, lc2)) == la2 + in b_tweets(lb1, update_b(f2, la2, lb2, lc2)) == lb2 + in c_tweets(lc1, update_b(f2, la2, lb2, lc2)) == lc2 + in b_following(f, update_b(f2, la2, lb2, lc2)) == f2 + out b_timeline(t:List, update_b(f2, la2, lb2, lc2)) == merge(lb2, if(contains(f2,1), merge(la2, if(contains(f2,3), lc2, nil)), if(contains(f2,3), lc2, nil))) +} + +channel c_Tweet { + out c_tweets(l:List, c_tweet(t:Str, time:Long)) == cons(tuple(time, t), l) +} + +channel c_Follow { + out c_following(f:List, c_follow(u:Int)) == cons(u, f) +} + +channel c_Home { + in a_tweets(la1, update_c(f2, la2, lb2, lc2)) == la2 + in b_tweets(lb1, update_c(f2, la2, lb2, lc2)) == lb2 + in c_tweets(lc1, update_c(f2, la2, lb2, lc2)) == lc2 + in c_following(f, update_c(f2, la2, lb2, lc2)) == f2 + out c_timeline(t:List, update_c(f2, la2, lb2, lc2)) == merge(lc2, if(contains(f2,1), merge(la2, if(contains(f2,2), lb2, nil)), if(contains(f2,2), lb2, nil))) +} diff --git a/AlgebraicDataflowArchitectureModel/models/WOS.dtram b/AlgebraicDataflowArchitectureModel/models/WOS.dtram new file mode 100644 index 0000000..bbffde1 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/WOS.dtram @@ -0,0 +1,25 @@ +model { +channel CIO2 { + out highest(h:Double, reset(v)) == v +} +channel CIO1 { + 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 +} +channel C2{ + in temp_f(q:Double, update(y)) == y + out highest(h:Double, update(z)) == if(gt(z, h), z, h) +} +} +geometry { + node c C1:500,200,30,30 + node c C2:500,100,30,30 + node r highest:650,100,80,30 + node r temp_c:650,200,80,30 + node r temp_f:250,100,80,30 + node ioc CIO2:100,300,30,30 + node ioc CIO1:100,100,30,30 +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/models/WeatherObservationSystem.model b/AlgebraicDataflowArchitectureModel/models/WeatherObservationSystem.model new file mode 100644 index 0000000..f16d38f --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/WeatherObservationSystem.model @@ -0,0 +1,14 @@ +channel CIO2 { + out highest(h:Double, reset(v)) == v +} +channel CIO1 { + 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 +} +channel C2{ + in temp_f(q:Double, update(y)) == y + out highest(h:Double, update(z)) == if(gt(z, h), z, h) +} diff --git a/AlgebraicDataflowArchitectureModel/resources/locales/en.properties b/AlgebraicDataflowArchitectureModel/resources/locales/en.properties new file mode 100644 index 0000000..ac26d9b --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/resources/locales/en.properties @@ -0,0 +1,49 @@ +# File +file=File +new=New +open=Open +save=Save +saveAs=Save As... +exit=Exit + +# New +newModel=Model +newResources=Resources... +newChannel=Channel... +newEventChannel=Event Channel +newFormulaChannel=Formula Channel + +# Edit +edit=Edit +delete=Delete + +# Layout +layout=Layout +dagLayout=DAG Layout +treeLayout=Tree Layout +circleLayout=Circle Layout + +# View +view=View +zoomIn=Zoom In +zoomOut=Zoom Out + +# Generate +generate=Generate +generateJava=Generate Plain Java Prototype +generateJAX_RS=Generate Plain JAX-RS Prototype + +# Window +window=Window +showNavigation=Show Navigation +showFlowLayer=Show Flow Layer + +# Navigation +dataFlowModeling=Data-Flow Modeling +pushpullSelection = PUSH/PULL Selection +pushpullExtension = PUSH/PULL Extension + +# PopUp Actions +changeCallOrder=Change Call Order +insertMediator=Introduce Mediator +dependsOnMediator=Depends On Mediator diff --git a/AlgebraicDataflowArchitectureModel/resources/locales/ja.properties b/AlgebraicDataflowArchitectureModel/resources/locales/ja.properties new file mode 100644 index 0000000..bc7288a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/resources/locales/ja.properties @@ -0,0 +1,49 @@ +# File +file=\u30D5\u30A1\u30A4\u30EB +new=\u65B0\u898F +open=\u958B\u304F +save=\u4FDD\u5B58 +saveAs=\u4E0A\u66F8\u304D\u4FDD\u5B58 +exit=\u9589\u3058\u308B + +# New +newModel=\u30E2\u30C7\u30EB +newResources=\u30EA\u30BD\u30FC\u30B9 +newChannel=\u30C1\u30E3\u30F3\u30CD\u30EB +newEventChannel=\u30A4\u30D9\u30F3\u30C8\u30C1\u30E3\u30F3\u30CD\u30EB +newFormulaChannel=\u30D5\u30A9\u30FC\u30DF\u30E5\u30E9\u30C1\u30E3\u30F3\u30CD\u30EB + +# Edit +edit=\u7DE8\u96C6 +delete=\u524A\u9664 + +# Layout +layout=\u30EC\u30A4\u30A2\u30A6\u30C8 +dagLayout=DAG \u30EC\u30A4\u30A2\u30A6\u30C8 +treeLayout=\u30C4\u30EA\u30FC\u30EC\u30A4\u30A2\u30A6\u30C8 +circleLayout=\u30B5\u30FC\u30AF\u30EB\u30EC\u30A4\u30A2\u30A6\u30C8 + +# View +view=\u8868\u793A +zoomIn=\u62E1\u5927 +zoomOut=\u7E2E\u5C0F + +# Generate +generate=\u30D7\u30ED\u30C8\u30BF\u30A4\u30D7\u751F\u6210 +generateJava=Java\u30D7\u30ED\u30C8\u30BF\u30A4\u30D7\u306E\u751F\u6210 +generateJAX_RS=JAX-RS\u30D7\u30ED\u30C8\u30BF\u30A4\u30D7\u306E\u751F\u6210 + +# Window +window=\u30A6\u30A3\u30F3\u30C9\u30A6 +showNavigation=\u30CA\u30D3\u30B2\u30FC\u30B7\u30E7\u30F3 +showFlowLayer=\u30D5\u30ED\u30FC\u30EC\u30A4\u30E4\u30FC + +# Navigation +dataFlowModeling=\u30C7\u30FC\u30BF\u30D5\u30ED\u30FC\u30E2\u30C7\u30EA\u30F3\u30B0 +pushpullSelection = PUSH/PULL\u306E\u9078\u629E +pushpullExtension = PUSH/PULL\u306E\u62E1\u5F35 + +# PopUp Actions +changeCallOrder=\u547C\u3073\u51FA\u3057\u9806\u306E\u5909\u66F4 +insertMediator=\u4EF2\u4ECB\u8005\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u306E\u5C0E\u5165 +dependsOnMediator=\u4EF2\u4ECB\u8005\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u3078\u4F9D\u5B58 \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/src/Main.java b/AlgebraicDataflowArchitectureModel/src/Main.java new file mode 100644 index 0000000..54fdaf8 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/Main.java @@ -0,0 +1,12 @@ + + +import application.ApplicationWindow; + +public class Main { + + public static void main(String[] args) { + ApplicationWindow frame = new ApplicationWindow(); + frame.setVisible(true); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/algorithms/DataTransferModelAnalyzer.java b/AlgebraicDataflowArchitectureModel/src/algorithms/DataTransferModelAnalyzer.java new file mode 100644 index 0000000..ff4c528 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/algorithms/DataTransferModelAnalyzer.java @@ -0,0 +1,107 @@ +package algorithms; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; + +import models.*; +import models.algebra.*; +import models.dataConstraintModel.*; +import models.dataFlowModel.*; + +/** + * Algorithms to analyze data transfer model. + * + * @author Nitta + * + */ +public class DataTransferModelAnalyzer { + /** + * Create data flow graph annotated with node attributes that indicate whether each resource state needs to be stored. + * @param model a data transfer model + * @return annotated data flow graph + */ + static public DataFlowGraph createDataFlowGraphWithStateStoringAttribute(DataTransferModel model) { + DataFlowGraph graph = model.getDataFlowGraph(); + Collection channels = new HashSet<>(model.getIOChannels()); + channels.addAll(model.getChannels()); + for (Channel channel: channels) { + for (ChannelMember member: ((DataTransferChannel) channel).getOutputChannelMembers()) { + boolean toBeStored = !member.getStateTransition().isRightUnary(); // The state does not need to be stored if the state transition function is right unary. + for (Node node: graph.getNodes()) { + if (((ResourceNode) node).getResource().equals(member.getResource())) { + setStoreAttribute((ResourceNode) node, toBeStored); + } + } + } + } + for (Node node: graph.getNodes()) { + HashSet inChannels = new HashSet<>(); + for(Edge inEdge: ((ResourceNode) node).getInEdges()) { + inChannels.add(((DataFlowEdge) inEdge).getChannel()); + } + if ((inChannels.size() > 1)) { + // 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((ResourceNode) node, false); + } + } + return graph; + } + + 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() || toBeStored); + } else { + store = new StoreAttribute(); + store.setNeeded(toBeStored); + node.setAttribute(store); + } + } + + /** + * Annotate data flow graph with edge attributes that indicate selectable data transfer methods. + * @param graph a data flow graph + * @return annotated data flow graph + */ + static public DataFlowGraph annotateWithSelectableDataTransferAttiribute(DataFlowGraph graph) { + List nodes = new ArrayList<>(graph.getNodes()); + // set push only attributes + for (Node n: graph.getNodes()) { + if (nodes.contains(n) && ((StoreAttribute) ((ResourceNode) n).getAttribute()).isNeeded()) { + nodes.remove(n); + trackEdges(n, nodes); + } + } + // set push/pull attributes to the remaining edges + for (Edge e : graph.getEdges()) { + if (((DataFlowEdge) e).getAttribute() == null) { + PushPullAttribute ppat = new PushPullAttribute(); + ppat.addOption(PushPullValue.PUSHorPULL); + ppat.addOption(PushPullValue.PUSH); + ppat.addOption(PushPullValue.PULL); + ((DataFlowEdge) e).setAttribute(ppat); + } + } + return graph; + } + + static private void trackEdges(Node n, List nodes) { + // recursively set push only attributes to input side edges + for (Edge e : ((ResourceNode) n).getInEdges()) { + 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); + } + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/algorithms/TypeInference.java b/AlgebraicDataflowArchitectureModel/src/algorithms/TypeInference.java new file mode 100644 index 0000000..9b3ca65 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/algorithms/TypeInference.java @@ -0,0 +1,2076 @@ +package algorithms; + +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.xml.crypto.Data; + +import models.Node; +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Position; +import models.algebra.Symbol; +import models.algebra.Term; +import models.algebra.Type; +import models.algebra.Variable; +import models.dataConstraintModel.Channel; +import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.JsonType; +import models.dataConstraintModel.ResourcePath; +import models.dataConstraintModel.StateTransition; +import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.ResourceNode; + +/** + * Type inference for data transfer model + * + * @author Nitta + * + */ +public class TypeInference { + static private Map listTypes = new HashMap<>(); + static private Map listComponentTypes = new HashMap<>(); + static private Map, Type> tupleTypes = new HashMap<>(); + static private Map> tupleComponentTypes = new HashMap<>(); + static private Map pairTypes = new HashMap<>(); + static private Map pairComponentTypes = new HashMap<>(); + static private Map, Type> mapTypes = new HashMap<>(); + static private Map> mapComponentTypes = new HashMap<>(); + static private Map, Type> jsonTypes = new HashMap<>(); + static private Map> jsonMemberTypes = new HashMap<>(); + + public static Type getListType(Type compType) { + return listTypes.get(compType); + } + + public static Type getListComponentType(Type listType) { + return listComponentTypes.get(listType); + } + + public static Collection getListTypes() { + return listTypes.values(); + } + + public static Type getTupleType(List compTypes) { + return tupleTypes.get(compTypes); + } + + public static List getTupleComponentTypes(Type tupleType) { + return tupleComponentTypes.get(tupleType); + } + + public static Collection getTupleTypes() { + return tupleTypes.values(); + } + + public static Type getPairType(Type compType) { + return pairTypes.get(compType); + } + + public static Type getPairComponentType(Type pairType) { + return pairComponentTypes.get(pairType); + } + + public static Type getMapType(List compTypes) { + return mapTypes.get(compTypes); + } + + public static List getMapComponentTypes(Type mapType) { + return mapComponentTypes.get(mapType); + } + + public static Type getJsonType(Map memberTypes) { + return jsonTypes.get(memberTypes); + } + + public static Map getJsonMemberTypes(Type jsonType) { + return jsonMemberTypes.get(jsonType); + } + + static public void infer(DataTransferModel model) { + Map> resources = new HashMap<>(); + Map 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> 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<>(); + Map> updateFromVariable = new HashMap<>(); + Map> updateFromMessage = new HashMap<>(); + Map> updateFromConsOrSet = new HashMap<>(); + Map> updateFromTuple = new HashMap<>(); + Map> updateFromPair = new HashMap<>(); + Map> updateFromMap = new HashMap<>(); + Map> updateFromJson = new HashMap<>(); + + listComponentTypes.put(DataConstraintModel.typeList, null); + listComponentTypes.put(DataConstraintModel.typeListInt, DataConstraintModel.typeInt); + listComponentTypes.put(DataConstraintModel.typeListStr, DataConstraintModel.typeString); + listTypes.put(DataConstraintModel.typeInt, DataConstraintModel.typeListInt); + listTypes.put(DataConstraintModel.typeString, DataConstraintModel.typeListStr); + pairComponentTypes.put(DataConstraintModel.typePair, null); + pairComponentTypes.put(DataConstraintModel.typePairInt, DataConstraintModel.typeInt); + pairComponentTypes.put(DataConstraintModel.typePairStr, DataConstraintModel.typeString); + pairComponentTypes.put(DataConstraintModel.typePairDouble, DataConstraintModel.typeDouble); + pairTypes.put(DataConstraintModel.typeInt, DataConstraintModel.typePairInt); + pairTypes.put(DataConstraintModel.typeString, DataConstraintModel.typePairStr); + pairTypes.put(DataConstraintModel.typeDouble, DataConstraintModel.typePairDouble); + tupleComponentTypes.put(DataConstraintModel.typeTuple, Arrays.asList(new Type[] { null, null })); + mapComponentTypes.put(DataConstraintModel.typeMap, Arrays.asList(new Type[] { null, null })); + + // 1. Collect type information from the architecture model. + Collection channels = new HashSet<>(model.getIOChannels()); + channels.addAll(model.getChannels()); + for (Channel c : channels) { + for (ChannelMember cm : c.getChannelMembers()) { + StateTransition st = cm.getStateTransition(); + ResourcePath res = cm.getResource(); + + // 1.1 Group expressions by resources. + List sameResource = resources.get(res); + if (sameResource == null) { + sameResource = new ArrayList<>(); + resources.put(res, 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 = res.getResourceStateType(); + Expression exp = st.getCurStateExpression(); + Type expType = getExpTypeIfUpdatable(resType, exp); + if (expType != null) { + res.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); + } + } + } + } 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 = res.getResourceStateType(); + exp = st.getNextStateExpression(); + if (exp != null) { + expType = getExpTypeIfUpdatable(resType, exp); + if (expType != null) { + res.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); + } + } + } + } 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); + } + } + } + } 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 (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(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); + } + } + } + } 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); + } + } + } + } + } + + // 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); // 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 the root symbol of the term is set. + Expression e = t.getChildren().get(2); + consExps.add(e); // list component + 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) 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)); + } + } + } + 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); + } + 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); + termType = newMapType; + Map updateExps = getUpdateSet(updateFromMap, mapExps); + updateExps.put(System.identityHashCode(t), t); + } + map.put(System.identityHashCode(mapExps), termType); + } else if (symbol.equals(DataConstraintModel.addMember)) { + // If the root symbol of the term is addMember (addMember(json, key, value)). + List dotExps = new ArrayList<>(); + Expression jsonArg = t.getChildren().get(0); + Expression keyArg = t.getChildren().get(1); + Expression valueArg = t.getChildren().get(2); + dotExps.add(t); // json + updateExpressionBelonging(expToJson, t, dotExps); + dotExps.add(keyArg); // key + updateExpressionBelonging(expToJson, keyArg, dotExps); + dotExps.add(valueArg); // value + updateExpressionBelonging(expToJson, valueArg, dotExps); + dotExps.add(jsonArg); // json + updateExpressionBelonging(expToJson, jsonArg, dotExps); + Type jsonType = t.getType(); + Type valueType = null; + if (valueArg instanceof Variable) { + valueType = ((Variable) valueArg).getType(); + } else if (valueArg instanceof Term) { + valueType = ((Term) valueArg).getType(); + } + String keyName = null; + if (keyArg instanceof Constant) { + keyName = ((Constant) keyArg).getSymbol().getName(); + } + Type jsonArgType = null; + if (jsonArg instanceof Variable) { + jsonArgType = ((Variable) jsonArg).getType(); + } else if (jsonArg instanceof Term) { + jsonArgType = ((Term) jsonArg).getType(); + } + Type newJsonType = DataConstraintModel.typeJson; + if (jsonType == DataConstraintModel.typeJson && jsonArgType != null && keyName != null) { + Map newMemberTypes = new HashMap<>(((JsonType) jsonArgType).getMemberTypes()); + newMemberTypes.put(keyName, valueType); + newJsonType = jsonTypes.get(newMemberTypes); + if (newJsonType == null) { + // Create new json type; + newJsonType = createNewJsonType(newMemberTypes, DataConstraintModel.typeJson); + } + } + if (jsonType != newJsonType && newJsonType != null && !newJsonType.isAncestorOf(jsonType)) { + // Update the type of the json term and record the updated expression. + t.setType(newJsonType); + jsonType = newJsonType; + Map updateExps = getUpdateSet(updateFromJson, dotExps); + updateExps.put(System.identityHashCode(t), t); + } + json.put(System.identityHashCode(dotExps), jsonType); + } else if (symbol.equals(DataConstraintModel.dot)) { + // If the root symbol of the term is dot (json.property). + List dotExps = new ArrayList<>(); + Expression jsonArg = t.getChildren().get(0); + Expression keyArg = t.getChildren().get(1); + dotExps.add(jsonArg); // json + updateExpressionBelonging(expToJson, jsonArg, dotExps); + dotExps.add(keyArg); // key + updateExpressionBelonging(expToJson, keyArg, dotExps); + dotExps.add(t); // value + updateExpressionBelonging(expToJson, t, dotExps); + dotExps.add(null); // json + Type jsonType = null; + if (jsonArg instanceof Variable) { + jsonType = ((Variable) jsonArg).getType(); + } else if (jsonArg instanceof Term) { + jsonType = ((Term) jsonArg).getType(); + } + String keyName = null; + if (keyArg instanceof Constant) { + keyName = ((Constant) keyArg).getSymbol().getName(); + } + Type newJsonType = DataConstraintModel.typeJson; + if (jsonType == DataConstraintModel.typeJson && t.getType() != null && keyName != null) { + Map newMemberTypes = new HashMap<>(); + newMemberTypes.put(keyName, t.getType()); + newJsonType = jsonTypes.get(newMemberTypes); + if (newJsonType == null) { + // Create new json type; + newJsonType = createNewJsonType(newMemberTypes, DataConstraintModel.typeJson); + } + } + if (jsonType != newJsonType && newJsonType != null && !newJsonType.isAncestorOf(jsonType)) { + // Update the type of the json argument and record the updated expression. + if (jsonArg instanceof Variable) { + ((Variable) jsonArg).setType(newJsonType); + jsonType = newJsonType; + } else if (jsonArg instanceof Term) { + ((Term) jsonArg).setType(newJsonType); + jsonType = newJsonType; + } + Map updateExps = getUpdateSet(updateFromJson, dotExps); + updateExps.put(System.identityHashCode(jsonArg), jsonArg); + } + json.put(System.identityHashCode(dotExps), jsonType); + } else if (symbol.equals(DataConstraintModel.dotParam)) { + // If the root symbol of the term is dot (json.{param}). + List dotExps = new ArrayList<>(); + Expression jsonArg = t.getChildren().get(0); + Expression keyArg = t.getChildren().get(1); + dotExps.add(jsonArg); // json (list/map) + updateExpressionBelonging(expToJson, jsonArg, dotExps); + dotExps.add(null); // key + dotExps.add(t); // value + updateExpressionBelonging(expToJson, t, dotExps); + dotExps.add(null); // json + Type jsonType = null; + if (jsonArg instanceof Variable) { + jsonType = ((Variable) jsonArg).getType(); + } else if (jsonArg instanceof Term) { + jsonType = ((Term) jsonArg).getType(); + } + Type keyType = null; + if (keyArg instanceof Variable) { + keyType = ((Variable) keyArg).getType(); + } else if (keyArg instanceof Term) { + keyType = ((Term) keyArg).getType(); + } + Type newJsonType = null; + if (keyType == DataConstraintModel.typeInt) { + newJsonType = DataConstraintModel.typeList; + } else if (keyType == DataConstraintModel.typeString) { + newJsonType = DataConstraintModel.typeMap; + } + if (t.getType() != null) { + if ((jsonType == DataConstraintModel.typeList)) { + newJsonType = listTypes.get(t.getType()); + if (newJsonType == null) { + // Create new list type; + newJsonType = createNewListType(t.getType(), DataConstraintModel.typeList); + } + } else if (jsonType == DataConstraintModel.typeMap) { + List keyValueTypes = Arrays.asList(new Type[] {DataConstraintModel.typeString, t.getType()}); + newJsonType = mapTypes.get(keyValueTypes); + if (newJsonType == null) { + // Create new map type; + newJsonType = createNewMapType(keyValueTypes, DataConstraintModel.typeMap); + } + } + } + if (jsonType != newJsonType && newJsonType != null && !newJsonType.isAncestorOf(jsonType)) { + // Update the type of the json argument and record the updated expression. + if (jsonArg instanceof Variable) { + ((Variable) jsonArg).setType(newJsonType); + jsonType = newJsonType; + } else if (jsonArg instanceof Term) { + ((Term) jsonArg).setType(newJsonType); + jsonType = newJsonType; + } + Map updateExps = getUpdateSet(updateFromJson, dotExps); + updateExps.put(System.identityHashCode(jsonArg), jsonArg); + } + json.put(System.identityHashCode(dotExps), jsonType); + } else if (symbol.equals(DataConstraintModel.cond)) { + // If the root symbol of the term is if function. + Expression c1 = t.getChild(1); + 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); + } + } + } + } + + // 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 || updateFromJson.size() > 0) { + if (updateFromResource.size() > 0) { + Set resourceKeys = updateFromResource.keySet(); + Integer resourceKey = resourceKeys.iterator().next(); + Map resourceValue = updateFromResource.get(resourceKey); + updateFromResource.remove(resourceKey); + for (int i : resourceValue.keySet()) { + Expression resExp = resourceValue.get(i); + updateVaribleTypes(resExp, variables, expToVariable, updateFromVariable); + updateMessageTypes(resExp, messages, expToMessage, updateFromMessage); + updateConsOrSetTypes(resExp, consOrSet, expToConsOrSet, updateFromConsOrSet); + updateTupleTypes(resExp, tuple, expToTuple, updateFromTuple); + updatePairTypes(resExp, pair, expToPair, updateFromPair); + updateMapTypes(resExp, map, expToMap, updateFromMap); + updateJsonTypes(resExp, json, expToJson, updateFromJson); + } + } + if (updateFromVariable.size() > 0) { + Set variableKeys = updateFromVariable.keySet(); + Integer variableKey = variableKeys.iterator().next(); + Map variableValue = updateFromVariable.get(variableKey); + updateFromVariable.remove(variableKey); + for (int i : variableValue.keySet()) { + Expression var = variableValue.get(i); + updateResourceTypes(var, resources, expToResource, updateFromResource); + 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); + } + } + if (updateFromMessage.size() > 0) { + Set messageKeys = updateFromMessage.keySet(); + Integer messageKey = messageKeys.iterator().next(); + Map messageValue = updateFromMessage.get(messageKey); + updateFromMessage.remove(messageKey); + for (int i : messageValue.keySet()) { + Expression mesExp = messageValue.get(i); + updateResourceTypes(mesExp, resources, expToResource, updateFromResource); + 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); + } + } + if (updateFromConsOrSet.size() > 0) { + Set consKeys = updateFromConsOrSet.keySet(); + Integer consKey = consKeys.iterator().next(); + Map consValue = updateFromConsOrSet.get(consKey); + updateFromConsOrSet.remove(consKey); + for (int i : consValue.keySet()) { + Expression consExp = consValue.get(i); + updateResourceTypes(consExp, resources, expToResource, updateFromResource); + 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) { + Set tupleKeys = updateFromTuple.keySet(); + Integer tupleKey = tupleKeys.iterator().next(); + Map tupleValue = updateFromTuple.get(tupleKey); + updateFromTuple.remove(tupleKey); + for (int i : tupleValue.keySet()) { + Expression tupleExp = tupleValue.get(i); + updateResourceTypes(tupleExp, resources, expToResource, updateFromResource); + 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) { + Set pairKeys = updateFromPair.keySet(); + Integer pairKey = pairKeys.iterator().next(); + Map pairValue = updateFromPair.get(pairKey); + updateFromPair.remove(pairKey); + for (int i : pairValue.keySet()) { + Expression pairExp = pairValue.get(i); + updateResourceTypes(pairExp, resources, expToResource, updateFromResource); + 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) { + Set mapKeys = updateFromMap.keySet(); + Integer mapKey = mapKeys.iterator().next(); + Map mapValue = updateFromMap.get(mapKey); + updateFromMap.remove(mapKey); + for (int i : mapValue.keySet()) { + Expression mapExp = mapValue.get(i); + updateResourceTypes(mapExp, resources, expToResource, updateFromResource); + 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); + 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); + } + } + } + } + + private static void updateExpressionBelonging(Map>> belonging, Expression exp, List group) { + Set> groups = belonging.get(System.identityHashCode(exp)); + if (groups == null) { + groups = new HashSet<>(); + belonging.put(System.identityHashCode(exp), groups); + groups.add(group); + return; + } + if (!groups.contains(group)) { + groups.add(group); + } + } + + private static void updateResourceTypes(Expression exp, Map> resources, + Map> expToResource, Map> updateFromResource) { + List sameResource = expToResource.get(System.identityHashCode(exp)); + if (sameResource == null) return; + for (ResourcePath res : resources.keySet()) { + if (resources.get(res) == sameResource) { + Type resType = res.getResourceStateType(); + Type newResType = getExpTypeIfUpdatable(resType, exp); + if (newResType != null) { + res.setResourceStateType(newResType); + Map updateExps = getUpdateSet(updateFromResource, sameResource); + for (Expression resExp : sameResource) { + if (resExp != exp) { + 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) { + List sameVariable = expToVariable.get(System.identityHashCode(exp)); + if (sameVariable == null) return; + Type varType = variables.get(System.identityHashCode(sameVariable)); + Type newVarType = getExpTypeIfUpdatable(varType, exp); + if (newVarType != null) { + variables.put(System.identityHashCode(sameVariable), newVarType); + Map updateVars = getUpdateSet(updateFromVariable, sameVariable); + for (Expression v : sameVariable) { + if (v != exp) { + if (v instanceof Variable) { + ((Variable) v).setType(newVarType); + updateVars.put(System.identityHashCode(v), v); + } else if (v instanceof Term) { + ((Term) v).setType(newVarType); + updateVars.put(System.identityHashCode(v), v); + } + } + } + } else { + Map updateVars = getUpdateSet(updateFromVariable, sameVariable); + for (Expression v : sameVariable) { + if (v instanceof Variable) { + Type orgVarType = ((Variable) v).getType(); + if (orgVarType != varType && compareTypes(orgVarType, varType)) { + ((Variable) v).setType(varType); + updateVars.put(System.identityHashCode(v), v); + } + } else if (v instanceof Term) { + Type orgVarType = ((Term) v).getType(); + if (orgVarType != varType && compareTypes(orgVarType, varType)) { + ((Term) v).setType(varType); + updateVars.put(System.identityHashCode(v), v); + } + } + } + } + } + + private static void updateMessageTypes(Expression exp, + Map, Type>>> messages, + Map> expToMessage, Map> updateFromMessage) { + List messageExps = expToMessage.get(System.identityHashCode(exp)); + if (messageExps == null) return; + Type msgType = null; + Map.Entry, Type> expsAndType = null; + for (Channel c : messages.keySet()) { + for (int i : messages.get(c).keySet()) { + expsAndType = messages.get(c).get(i); + if (expsAndType.getKey() == messageExps) { + msgType = expsAndType.getValue(); + break; + } + } + if (msgType != null) break; + } + if (msgType == null) return; + Type newMsgType = getExpTypeIfUpdatable(msgType, exp); + if (newMsgType != null) { + expsAndType.setValue(newMsgType); + Map updateExps = getUpdateSet(updateFromMessage, messageExps); + for (Expression e : messageExps) { + if (e != exp) { + if (e instanceof Variable) { + ((Variable) e).setType(newMsgType); + updateExps.put(System.identityHashCode(e), e); + } else if (e instanceof Term) { + ((Term) e).setType(newMsgType); + updateExps.put(System.identityHashCode(e), e); + } + } + } + } + } + + private static void updateConsOrSetTypes(Expression exp, Map consOrSet, + Map>> expToConsOrSet, Map> updateFromConsOrSet) { + Set> consComponentGroups = expToConsOrSet.get(System.identityHashCode(exp)); + if (consComponentGroups == null) return; + for (List consOrSetComponentGroup: consComponentGroups) { + 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); + if (expType != null) { + consOrSet.put(System.identityHashCode(consOrSetComponentGroup), expType); + Map updateExps = getUpdateSet(updateFromConsOrSet, consOrSetComponentGroup); + if (consOrSetComponentGroup.get(2) instanceof Variable) { + ((Variable) consOrSetComponentGroup.get(2)).setType(expType); + updateExps.put(System.identityHashCode(consOrSetComponentGroup.get(2)), consOrSetComponentGroup.get(2)); + } else if (consOrSetComponentGroup.get(2) instanceof Term) { + ((Term) consOrSetComponentGroup.get(2)).setType(expType); + updateExps.put(System.identityHashCode(consOrSetComponentGroup.get(2)), consOrSetComponentGroup.get(2)); + } + Type compType = listComponentTypes.get(expType); + if (consOrSetComponentGroup.get(1) != null && consOrSetComponentGroup.get(1) instanceof Variable) { + ((Variable) consOrSetComponentGroup.get(1)).setType(compType); + updateExps.put(System.identityHashCode(consOrSetComponentGroup.get(1)), consOrSetComponentGroup.get(1)); + } else if (consOrSetComponentGroup.get(1) != null && consOrSetComponentGroup.get(1) instanceof Term) { + ((Term) consOrSetComponentGroup.get(1)).setType(compType); + updateExps.put(System.identityHashCode(consOrSetComponentGroup.get(1)), consOrSetComponentGroup.get(1)); + } + } + break; + case 1: + // exp is a list's component. + listType = consOrSet.get(System.identityHashCode(consOrSetComponentGroup)); + Type compType = listComponentTypes.get(listType); + Type newCompType = getExpTypeIfUpdatable(compType, exp); + if (newCompType != null) { + Type newListType = listTypes.get(newCompType); + if (newListType == null) { + // Create new list type. + newListType = createNewListType(newCompType, listType); + } + consOrSet.put(System.identityHashCode(consOrSetComponentGroup), newListType); + Map updateExps = getUpdateSet(updateFromConsOrSet, consOrSetComponentGroup); + if (consOrSetComponentGroup.get(0) instanceof Term) { + ((Term) consOrSetComponentGroup.get(0)).setType(newListType); + updateExps.put(System.identityHashCode(consOrSetComponentGroup.get(0)), consOrSetComponentGroup.get(0)); + } + if (consOrSetComponentGroup.get(2) instanceof Variable) { + ((Variable) consOrSetComponentGroup.get(2)).setType(newListType); + updateExps.put(System.identityHashCode(consOrSetComponentGroup.get(2)), consOrSetComponentGroup.get(2)); + } else if (consOrSetComponentGroup.get(2) instanceof Term) { + ((Term) consOrSetComponentGroup.get(2)).setType(newListType); + updateExps.put(System.identityHashCode(consOrSetComponentGroup.get(2)), consOrSetComponentGroup.get(2)); + } + } + break; + case 2: + // exp is a list itself. + listType = consOrSet.get(System.identityHashCode(consOrSetComponentGroup)); + expType = getExpTypeIfUpdatable(listType, exp); + if (expType != null) { + consOrSet.put(System.identityHashCode(consOrSetComponentGroup), expType); + Map updateExps = getUpdateSet(updateFromConsOrSet, consOrSetComponentGroup); + if (consOrSetComponentGroup.get(0) instanceof Term) { + ((Term) consOrSetComponentGroup.get(0)).setType(expType); + updateExps.put(System.identityHashCode(consOrSetComponentGroup.get(0)), consOrSetComponentGroup.get(0)); + } + compType = listComponentTypes.get(expType); + if (consOrSetComponentGroup.get(1) != null && consOrSetComponentGroup.get(1) instanceof Variable) { + ((Variable) consOrSetComponentGroup.get(1)).setType(compType); + updateExps.put(System.identityHashCode(consOrSetComponentGroup.get(1)), consOrSetComponentGroup.get(1)); + } else if (consOrSetComponentGroup.get(1) != null && consOrSetComponentGroup.get(1) instanceof Term) { + ((Term) consOrSetComponentGroup.get(1)).setType(compType); + updateExps.put(System.identityHashCode(consOrSetComponentGroup.get(1)), consOrSetComponentGroup.get(1)); + } + } + } + } + } + + private static void updateTupleTypes(Expression exp, Map tuple, + Map>> expToTuple, Map> updateFromTuple) { + Set> tupleComponentGroups = expToTuple.get(System.identityHashCode(exp)); + if (tupleComponentGroups == null) return; + 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) { + // Propagate an update of a tuple's type to its components' types. + tuple.put(System.identityHashCode(tupleComponentGroup), newTupleType); + List componentTypes = tupleComponentTypes.get(newTupleType); + Map updateExps = getUpdateSet(updateFromTuple, tupleComponentGroup); + for (int i = 1; i < tupleComponentGroup.size(); i++) { + Expression compExp = tupleComponentGroup.get(i); + if (compExp instanceof Variable) { + Type compType = ((Variable) compExp).getType(); + if (compType != null && DataConstraintModel.typeTuple.isAncestorOf(compType)) { + // If the type of one component (compExp) is also tuple. + Type newExpType = tupleTypes.get(componentTypes.subList(i - 1, componentTypes.size())); + if (newExpType == null) { + // Create new tuple type; + newExpType = createNewTupleType(componentTypes.subList(i - 1, componentTypes.size()), compType); + } + if (compareTypes(compType, newExpType)) { + ((Variable) compExp).setType(newExpType); + updateExps.put(System.identityHashCode(compExp), compExp); + } + } else { + if (i - 1 < componentTypes.size()) { + if (compareTypes(compType, componentTypes.get(i - 1))) { + ((Variable) compExp).setType(componentTypes.get(i - 1)); + updateExps.put(System.identityHashCode(compExp), compExp); + } + } else { + // for insert + if (compareTypes(compType, newTupleType)) { + ((Variable) compExp).setType(newTupleType); + updateExps.put(System.identityHashCode(compExp), compExp); + } + } + } + } else if (compExp instanceof Term) { + Type compType = ((Term) compExp).getType(); + if (compType != null && DataConstraintModel.typeTuple.isAncestorOf(compType)) { + // If the type of one component (compExp) is also tuple. + Type newExpType = tupleTypes.get(componentTypes.subList(i - 1, componentTypes.size())); + if (newExpType == null) { + // Create new tuple type; + newExpType = createNewTupleType(componentTypes.subList(i - 1, componentTypes.size()), compType); + } + if (compareTypes(compType, newExpType)) { + ((Term) compExp).setType(newExpType); + updateExps.put(System.identityHashCode(compExp), compExp); + } + } else { + if (i - 1 < componentTypes.size()) { + if (compareTypes(compType, componentTypes.get(i - 1))) { + ((Term) compExp).setType(componentTypes.get(i - 1)); + updateExps.put(System.identityHashCode(compExp), compExp); + } + } else { + // for insert + if (compareTypes(compType, newTupleType)) { + ((Term) compExp).setType(newTupleType); + updateExps.put(System.identityHashCode(compExp), compExp); + } + } + } + } + } + } + } else { + // exp is a tuple's component. + Type tupleType = tuple.get(System.identityHashCode(tupleComponentGroup)); + List componentTypes = tupleComponentTypes.get(tupleType); + boolean updated = false; + if (idx == 1) { + Type compType = componentTypes.get(idx - 1); + Type newCompType = getExpTypeIfUpdatable(compType, exp); + if (newCompType != null) { + componentTypes = new ArrayList<>(componentTypes); + componentTypes.set(idx - 1, newCompType); + updated = true; + } + } else { + Type expType = null; + if (exp instanceof Term) { + expType = ((Term) exp).getType(); + } else if (exp instanceof Variable) { + expType = ((Variable) exp).getType(); + } + if (expType != null && DataConstraintModel.typeTuple.isAncestorOf(expType)) { + // If the type of the updated component (exp) is also tuple. + List subCompTypes = tupleComponentTypes.get(expType); + componentTypes = new ArrayList<>(componentTypes); + for (int i = 0; i < subCompTypes.size(); i++) { + if (componentTypes.size() < i + 2) { + componentTypes.add(subCompTypes.get(i)); + updated = true; + } else if (compareTypes(componentTypes.get(i + 1), subCompTypes.get(i))) { + componentTypes.set(i + 1, subCompTypes.get(i)); + updated = true; + } + } + } else { + Type compType = componentTypes.get(idx - 1); + Type newCompType = getExpTypeIfUpdatable(compType, exp); + if (newCompType != null) { + componentTypes = new ArrayList<>(componentTypes); + componentTypes.set(idx - 1, newCompType); + updated = true; + } + } + } + if (updated) { + // Propagate an update of a component's type to its container's (tuple's) type. + Type newTupleType = tupleTypes.get(componentTypes); + if (newTupleType == null) { + // Create new tuple type; + newTupleType = createNewTupleType(componentTypes, tupleType); + } + Map updateExps = getUpdateSet(updateFromTuple, tupleComponentGroup); + Expression tupleExp = tupleComponentGroup.get(0); + if (tupleExp instanceof Variable) { + ((Variable) tupleExp).setType(newTupleType); + updateExps.put(System.identityHashCode(tupleExp), tupleExp); + } else if (tupleExp instanceof Term) { + ((Term) tupleExp).setType(newTupleType); + updateExps.put(System.identityHashCode(tupleExp), tupleExp); + } + tuple.put(System.identityHashCode(tupleComponentGroup), newTupleType); + } + } + } + } + + private static void updatePairTypes(Expression exp, Map pair, + Map>> expToPair, Map> updateFromPair) { + Set> pairComponentGroups = expToPair.get(System.identityHashCode(exp)); + if (pairComponentGroups == null) return; + 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) { + // Propagate an update of a pair's type to its components' types. + pair.put(System.identityHashCode(pairComponentGroup), newPairType); + Type componentType = pairComponentTypes.get(newPairType); + Map updateExps = getUpdateSet(updateFromPair, pairComponentGroup); + for (int i = 1; i < pairComponentGroup.size(); i++) { + Expression compExp = pairComponentGroup.get(i); + if (compExp instanceof Variable) { + if (compareTypes(((Variable) compExp).getType(), componentType)) { + ((Variable) compExp).setType(componentType); + updateExps.put(System.identityHashCode(compExp), compExp); + } + } else if (compExp instanceof Term) { + if (compareTypes(((Term) compExp).getType(), componentType)) { + ((Term) compExp).setType(componentType); + updateExps.put(System.identityHashCode(compExp), compExp); + } + } + } + } + } else { + // exp is a pair's component. + Type pairType = pair.get(System.identityHashCode(pairComponentGroup)); + Type compType = pairComponentTypes.get(pairType); + Type newCompType = getExpTypeIfUpdatable(compType, exp); + if (newCompType != null) { + // Propagate an update of a component's type to its container's (pair's) type. + Type newPairType = pairTypes.get(compType); + if (newPairType != null) { + Map updateExps = getUpdateSet(updateFromPair, pairComponentGroup); + Expression pairExp = pairComponentGroup.get(0); + if (pairExp instanceof Variable) { + ((Variable) pairExp).setType(newPairType); + updateExps.put(System.identityHashCode(pairExp), pairExp); + } else if (pairExp instanceof Term) { + ((Term) pairExp).setType(newPairType); + updateExps.put(System.identityHashCode(pairExp), pairExp); + } + pair.put(System.identityHashCode(pairComponentGroup), newPairType); + } + } + } + } + } + + private static void updateMapTypes(Expression exp, Map map, + Map>> expToMap, Map> updateFromMap) { + Set> mapComponentGroups = expToMap.get(System.identityHashCode(exp)); + if (mapComponentGroups == null) return; + 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) { + // Propagate an update of a map's type to its components' types. + map.put(System.identityHashCode(mapComponentGroup), newMapType); + List componentTypes = mapComponentTypes.get(newMapType); + Map updateExps = getUpdateSet(updateFromMap, mapComponentGroup); + for (int i = 1; i < mapComponentGroup.size() && i < 3; i++) { + Expression compExp = mapComponentGroup.get(i); + if (compExp instanceof Variable) { + if (compareTypes(((Variable) compExp).getType(), componentTypes.get(i - 1))) { + ((Variable) compExp).setType(componentTypes.get(i - 1)); + updateExps.put(System.identityHashCode(compExp), compExp); + } + } else if (compExp instanceof Term) { + if (compareTypes(((Term) compExp).getType(), componentTypes.get(i - 1))) { + ((Term) compExp).setType(componentTypes.get(i - 1)); + updateExps.put(System.identityHashCode(compExp), compExp); + } + } + } + // Propagate an update of a map's type to another map's type. + Expression compExp = null; + if (idx == 0 && mapComponentGroup.size() == 4) { // for insert + compExp = mapComponentGroup.get(3); + } else if (idx == 3) { + compExp = mapComponentGroup.get(0); + } + if (compExp != null) { + if (compExp instanceof Variable) { + if (compareTypes(((Variable) compExp).getType(), newMapType)) { + ((Variable) compExp).setType(newMapType); + updateExps.put(System.identityHashCode(compExp), compExp); + } + } else if (compExp instanceof Term) { + if (compareTypes(((Term) compExp).getType(), newMapType)) { + ((Term) compExp).setType(newMapType); + updateExps.put(System.identityHashCode(compExp), compExp); + } + } + } + } + } 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); + Type newCompType = getExpTypeIfUpdatable(compType, exp); + if (newCompType != null) { + // Propagate an update of a component's type to its container's (map's) type. + componentTypes = new ArrayList<>(componentTypes); + componentTypes.set(idx - 1, newCompType); + Type newMapType = mapTypes.get(componentTypes); + if (newMapType == null) { + // Create new map type; + newMapType = createNewMapType(componentTypes, mapType); + } + Map updateExps = getUpdateSet(updateFromMap, mapComponentGroup); + Expression mapExp = mapComponentGroup.get(0); + if (mapExp instanceof Variable) { + ((Variable) mapExp).setType(newMapType); + updateExps.put(System.identityHashCode(mapExp), mapExp); + } else if (mapExp instanceof Term) { + ((Term) mapExp).setType(newMapType); + updateExps.put(System.identityHashCode(mapExp), mapExp); + } + if (mapComponentGroup.size() == 4) { // for insert + mapExp = mapComponentGroup.get(3); + if (mapExp instanceof Variable) { + ((Variable) mapExp).setType(newMapType); + updateExps.put(System.identityHashCode(mapExp), mapExp); + } else if (mapExp instanceof Term) { + ((Term) mapExp).setType(newMapType); + updateExps.put(System.identityHashCode(mapExp), mapExp); + } + } + map.put(System.identityHashCode(mapComponentGroup), newMapType); + } + } + } + } + + + private static void updateJsonTypes(Expression exp, Map json, + Map>> expToJson, Map> updateFromJson) { + Set> jsonMemberGroups = expToJson.get(System.identityHashCode(exp)); + if (jsonMemberGroups == null) return; + for (List jsonMemberGroup: jsonMemberGroups) { + int idx = jsonMemberGroup.indexOf(exp); + if (idx == 3) { + // exp is a json argument (0:t = addMember(3:json, 1:key, 2:value)). + Type jsonType = json.get(System.identityHashCode(jsonMemberGroup)); + Map memberTypes = new HashMap<>(jsonMemberTypes.get(jsonType)); + Map argMemberTypes = new HashMap<>(memberTypes); + String keyName = null; + Type valueType = null; + if (jsonMemberGroup.get(1) instanceof Constant) { + keyName = ((Constant) jsonMemberGroup.get(1)).getSymbol().getName(); + valueType = ((Constant) jsonMemberGroup.get(1)).getType(); + argMemberTypes.remove(keyName); + } + Type jsonArgType = jsonTypes.get(argMemberTypes); + Type newJsonArgType = getExpTypeIfUpdatable(jsonArgType, exp); + if (newJsonArgType != null && keyName != null) { + // Propagate an update of a json arg's type to its container's (json's) type. + argMemberTypes = ((JsonType) newJsonArgType).getMemberTypes(); + argMemberTypes.put(keyName, valueType); + memberTypes.putAll(argMemberTypes); + Type newJsonType = jsonTypes.get(memberTypes); + if (newJsonType == null) { + // Create new json type. + newJsonType = createNewJsonType(memberTypes, jsonType); + } + // Update the type of the json term and record the updated expression. + Map updateExps = getUpdateSet(updateFromJson, jsonMemberGroup); + Expression jsonExp = jsonMemberGroup.get(0); + if (jsonExp instanceof Variable) { + ((Variable) jsonExp).setType(newJsonType); + updateExps.put(System.identityHashCode(jsonExp), jsonExp); + } else if (jsonExp instanceof Term) { + ((Term) jsonExp).setType(newJsonType); + updateExps.put(System.identityHashCode(jsonExp), jsonExp); + } + json.put(System.identityHashCode(jsonMemberGroup), newJsonType); + } + } else if (idx == 2) { + // exp is a value (0:t = addMember(3:json, 1:key, 2:value) or 2:value = dot(0:list/map, 1:key)). + Type jsonType = json.get(System.identityHashCode(jsonMemberGroup)); + Type newJsonType = null; + if (exp instanceof Term && ((Term) exp).getSymbol().equals(DataConstraintModel.dotParam)) { + if (DataConstraintModel.typeList.isAncestorOf(jsonType)) { + Type elementType = listComponentTypes.get(jsonType); + Type newElementType = getExpTypeIfUpdatable(elementType, exp); + if (newElementType != null) { + // Propagate an update of a member's type to its container's (json's) type. + newJsonType = listTypes.get(newElementType); + if (newJsonType == null) { + // Create new json type. + newJsonType = createNewListType(newElementType, jsonType); + } + } + } else if (DataConstraintModel.typeMap.isAncestorOf(jsonType)) { + List keyValueTypes = mapComponentTypes.get(jsonType); + Type newValueType = getExpTypeIfUpdatable(keyValueTypes.get(1), exp); + if (newValueType != null) { + // Propagate an update of a member's type to its container's (json's) type. + List newKeyValueTypes = Arrays.asList(new Type[] {DataConstraintModel.typeString, newValueType}); + newJsonType = mapTypes.get(newKeyValueTypes); + if (newJsonType == null) { + // Create new json type. + newJsonType = createNewMapType(newKeyValueTypes, jsonType); + } + } + } + } else { + Map memberTypes = jsonMemberTypes.get(jsonType); + String keyName = null; + if (jsonMemberGroup.get(1) instanceof Constant) { + keyName = ((Constant) jsonMemberGroup.get(1)).getSymbol().getName(); + } + if (memberTypes != null) { + Type memberType = memberTypes.get(keyName); + Type newMemberType = getExpTypeIfUpdatable(memberType, exp); + if (newMemberType != null && keyName != null) { + // Propagate an update of a member's type to its container's (json's) type. + Map newMemberTypes = new HashMap<>(memberTypes); + newMemberTypes.put(keyName, newMemberType); + newJsonType = jsonTypes.get(newMemberTypes); + if (newJsonType == null) { + // Create new json type. + newJsonType = createNewJsonType(newMemberTypes, jsonType); + } + } + } + } + if (newJsonType != null) { + // Update the type of the json term and record the updated expression. + Map updateExps = getUpdateSet(updateFromJson, jsonMemberGroup); + Expression jsonExp = jsonMemberGroup.get(0); + if (jsonExp instanceof Variable) { + ((Variable) jsonExp).setType(newJsonType); + updateExps.put(System.identityHashCode(jsonExp), jsonExp); + } else if (jsonExp instanceof Term) { + ((Term) jsonExp).setType(newJsonType); + updateExps.put(System.identityHashCode(jsonExp), jsonExp); + } + json.put(System.identityHashCode(jsonMemberGroup), newJsonType); + } + } + } + } + + private static Type createNewListType(Type compType, Type parentType) { + String compTypeName = getInterfaceTypeName(compType); + List childrenTypes = getChildrenTypes(parentType, listComponentTypes.keySet()); + Type newListType = new Type("List", "ArrayList<>", "List<" + compTypeName + ">", parentType); + listTypes.put(compType, newListType); + listComponentTypes.put(newListType, compType); + for (Type childType : childrenTypes) { + if (compareTypes(childType, newListType)) { + if (newListType.getParentTypes().contains(parentType)) { + newListType.replaceParentType(parentType, childType); + } else { + newListType.addParentType(childType); + } + } else if (compareTypes(newListType, childType)) { + childType.replaceParentType(parentType, newListType); + } + } + return newListType; + } + + private static Type createNewTupleType(List componentTypes, Type parentTupleType) { + 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))); + } + List childrenTypes = getChildrenTypes(parentTupleType, tupleComponentTypes.keySet()); + Type newTupleType = new Type("Tuple", implTypeName, interfaceTypeName, parentTupleType); + tupleTypes.put(componentTypes, newTupleType); + tupleComponentTypes.put(newTupleType, componentTypes); + for (Type childType : childrenTypes) { + if (compareTypes(childType, newTupleType)) { + if (newTupleType.getParentTypes().contains(parentTupleType)) { + newTupleType.replaceParentType(parentTupleType, childType); + } else { + newTupleType.addParentType(childType); + } + } else if (compareTypes(newTupleType, childType)) { + childType.replaceParentType(parentTupleType, newTupleType); + } + } + return newTupleType; + } + + private static Type createNewMapType(List componentTypes, Type parentMapType) { + String implTypeName = "HashMap<>"; + String interfaceTypeName = "Map<$x, $y>"; + if (componentTypes.size() == 2) { + implTypeName = implTypeName.replace("$x", getImplementationTypeName(componentTypes.get(0))); + interfaceTypeName = interfaceTypeName.replace("$x", getInterfaceTypeName(componentTypes.get(0))); + implTypeName = implTypeName.replace("$y", getImplementationTypeName(componentTypes.get(1))); + interfaceTypeName = interfaceTypeName.replace("$y", getInterfaceTypeName(componentTypes.get(1))); + } + List childrenTypes = getChildrenTypes(parentMapType, mapComponentTypes.keySet()); + Type newMapType = new Type("Map", implTypeName, interfaceTypeName, parentMapType); + mapTypes.put(componentTypes, newMapType); + mapComponentTypes.put(newMapType, componentTypes); + for (Type childType : childrenTypes) { + if (compareTypes(childType, newMapType)) { + if (newMapType.getParentTypes().contains(parentMapType)) { + newMapType.replaceParentType(parentMapType, childType); + } else { + newMapType.addParentType(childType); + } + } else if (compareTypes(newMapType, childType)) { + childType.replaceParentType(parentMapType, newMapType); + } + } + return newMapType; + } + + 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 : allTypes) { + if (childType.getParentTypes().contains(parentType)) { + childrenTypes.add(childType); + } + } + return childrenTypes; + } + + private static String getImplementationTypeName(Type type) { + if (type == null) + return "Object"; + String wrapperType = DataConstraintModel.getWrapperType(type); + if (wrapperType != null) + return wrapperType; + return type.getImplementationTypeName(); + } + + private static String getInterfaceTypeName(Type type) { + if (type == null) + return "Object"; + String wrapperType = DataConstraintModel.getWrapperType(type); + if (wrapperType != null) + return wrapperType; + return type.getInterfaceTypeName(); + } + + private static > Map getUpdateSet(Map> updateSets, U keySet) { + Map updatedExps = updateSets.get(System.identityHashCode(keySet)); + if (updatedExps == null) { + updatedExps = new HashMap<>(); + updateSets.put(System.identityHashCode(keySet), updatedExps); + } + return updatedExps; + } + + private static Type getExpTypeIfUpdatable(Type originalType, Expression newExp) { + Type expType = null; + if (newExp instanceof Term) { + expType = ((Term) newExp).getType(); + } else if (newExp instanceof Variable) { + expType = ((Variable) newExp).getType(); + } + if (compareTypes(originalType, expType)) { + return expType; + } + return null; + } + + /** + * Is an given original type an ancestor of a given new type? + * + * @param originalType original type + * @param newType new type (may not have been registered) + * @return true: if the original type equals to the new type or is an ancestor + * of the new type, false: otherwise + */ + private static boolean compareTypes(Type originalType, Type newType) { + if (originalType == null) return true; + if (originalType != newType && newType != null) { + if (originalType.isAncestorOf(newType)) return true; + if (newType.isAncestorOf(originalType)) return false; + if (DataConstraintModel.typeMap.isAncestorOf(originalType) + && DataConstraintModel.typeMap.isAncestorOf(newType)) { + List originalCompTypes = mapComponentTypes.get(originalType); + List newCompTypes = mapComponentTypes.get(newType); + if (originalCompTypes == null) return true; + for (int i = 0; i < originalCompTypes.size(); i++) { + if (originalCompTypes.get(i) != null && + (newCompTypes.get(i) == null || !originalCompTypes.get(i).isAncestorOf(newCompTypes.get(i)))) return false; + } + return true; + } + if (DataConstraintModel.typeTuple.isAncestorOf(originalType) + && DataConstraintModel.typeTuple.isAncestorOf(newType)) { + List originalCompTypes = tupleComponentTypes.get(originalType); + List newCompTypes = tupleComponentTypes.get(newType); + if (originalCompTypes == null) return true; + originalCompTypes = new ArrayList<>(originalCompTypes); + newCompTypes = new ArrayList<>(newCompTypes); + 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); + } + } + 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); + } + } + if (newCompTypes.get(i) == null || !originalCompTypes.get(i).isAncestorOf(newCompTypes.get(i))) return false; + } + } + return true; + } + if (DataConstraintModel.typeList.isAncestorOf(originalType) + && DataConstraintModel.typeList.isAncestorOf(newType)) { + Type originalCompType = listComponentTypes.get(originalType); + Type newCompType = listComponentTypes.get(newType); + if (originalCompType != null && (newCompType == null || !originalCompType.isAncestorOf(newCompType))) return false; + return true; + } + if (DataConstraintModel.typeJson.isAncestorOf(originalType) && DataConstraintModel.typeJson.isAncestorOf(newType)) { + Map originalMemberTypes = jsonMemberTypes.get(originalType); + Map newMemberTypes = jsonMemberTypes.get(newType); + if (originalMemberTypes == null) return true; + if (originalMemberTypes.keySet().size() < newMemberTypes.keySet().size() + && newMemberTypes.keySet().containsAll(originalMemberTypes.keySet())) return true; + if (originalMemberTypes.keySet().size() > newMemberTypes.keySet().size()) return false; + if (!newMemberTypes.keySet().containsAll(originalMemberTypes.keySet())) return false; + for (String key: originalMemberTypes.keySet()) { + if (!originalMemberTypes.get(key).isAncestorOf(newMemberTypes.get(key))) return false; + } + return true; + } + } + return false; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/algorithms/Validation.java b/AlgebraicDataflowArchitectureModel/src/algorithms/Validation.java new file mode 100644 index 0000000..1744af5 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/algorithms/Validation.java @@ -0,0 +1,72 @@ +package algorithms; + +import java.util.*; + +import models.*; +import models.dataFlowModel.*; + +/** + * Validation checker for data transfer model + * + * @author Nitta + * + */ +public class Validation { + private static int index = 0; + private static Deque stack = new ArrayDeque<>(); + private static Set> strong = new HashSet<>(); + private static Map ids = new HashMap<>(); + private static Map lowlink = new HashMap<>(); + private static Map onStack = new HashMap<>(); + + static public boolean checkUpdateConflict(DataTransferModel model) { + init(); + boolean check = true; + for (Node node : model.getDataFlowGraph().getNodes()) { + if (ids.containsKey(node)) { + traverseStronglyConnectedComponents(node); + } + } + return strong.size() == 0 && check; + } + + static private void init() { + index = 0; + stack = new ArrayDeque<>(); + strong = new HashSet<>(); + ids = new HashMap<>(); + lowlink = new HashMap<>(); + onStack = new HashMap<>(); + } + + static private void traverseStronglyConnectedComponents(Node node) { + ids.put(node, index); + lowlink.put(node, index); + index++; + stack.push(node); + onStack.put(node, true); + + for (Node n : node.getSuccessors()) { + if (lowlink.containsKey(n)) { + traverseStronglyConnectedComponents(n); + if (lowlink.get(node) > lowlink.get(n)) { + lowlink.replace(node, lowlink.get(n)); + } + } else if (onStack.get(n)) { + if (lowlink.get(node) > lowlink.get(n)) { + lowlink.replace(node, lowlink.get(n)); + } + } + } + if (lowlink.get(node) == ids.get(node)) { + Set tmp = new HashSet<>(); + Node w; + do { + w = stack.pop(); + onStack.replace(node, false); + tmp.add(w); + } while (node != w); + strong.add(tmp); + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/ApplicationLanguage.java b/AlgebraicDataflowArchitectureModel/src/application/ApplicationLanguage.java new file mode 100644 index 0000000..238f283 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/ApplicationLanguage.java @@ -0,0 +1,84 @@ +package application; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Paths; +import java.util.Locale; +import java.util.ResourceBundle; + +/*********************************************************************** + * アプリケーションの言語を切り替える(シングルトン) + */ +public class ApplicationLanguage { + private Locale locale = null; + private ResourceBundle resBundle = null; + + private static ApplicationLanguage instance = null; + + private final static String RESOURCE_PATH = "resources/locales/"; + + public final static String JP = "ja"; + public final static String EN = "en"; + + /*********************************************************************** + /*********************************************************************** + * [ *constructor ] + /*********************************************************************** + * + * @return + */ + private ApplicationLanguage() { + this.locale = Locale.getDefault(); + setLocaleLanguage(this.locale.getLanguage()); + } + + /*********************************************************************** + /*********************************************************************** + * [ *public static ] + /*********************************************************************** + * + */ + public static ApplicationLanguage getInstance() { + if (instance == null) instance = new ApplicationLanguage(); + return instance; + } + + /*********************************************************************** + /*********************************************************************** + * [ *public ] + /*********************************************************************** + * ローカルの .properties から言語情報を切り替え. + * @param language 変更先の言語 + */ + public void setLocaleLanguage(final String language) { + URL resURL = null; + try { + File resDir = Paths.get(RESOURCE_PATH).toFile(); // for Eclipse project + resURL = resDir.toURI().toURL(); + } catch (MalformedURLException e) { + e.printStackTrace(); + } +// resURL = this.getClass().getClassLoader().getResource(RESOURCE_PATH); // for executable jar + URLClassLoader urlLoader = new URLClassLoader(new URL[] { resURL }); + this.resBundle = ResourceBundle.getBundle(language, this.locale, urlLoader); + +// // Another approach. +// properties = new Properties(); +// try { +// properties.load(Main.class.getClassLoader().getResourceAsStream(RESOURCE_PATH + language + ".properties")); +// } catch (IOException e) { +// return; +// } + } + + /*********************************************************************** + * オプション名に対応した名前を返す. + * @param propName 取得したいプロパティの名前 + */ + public String getOptionByPropName(final String propName) { +// return properties.getProperty(propName); + return this.resBundle.getString(propName); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/ApplicationMenuBar.java b/AlgebraicDataflowArchitectureModel/src/application/ApplicationMenuBar.java new file mode 100644 index 0000000..b63e9f8 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/ApplicationMenuBar.java @@ -0,0 +1,100 @@ +package application; + +import javax.swing.JMenu; +import javax.swing.JMenuBar; + +import application.actions.CircleLayoutAction; +import application.actions.DAGLayoutAction; +import application.actions.DeleteAction; +import application.actions.ExitAction; +import application.actions.JavaPrototypeGenerateAction; +import application.actions.JerseyPrototypeGenerateAction; +import application.actions.NewChannelAction; +import application.actions.NewFormulaChannelAction; +import application.actions.NewIOChannelAction; +import application.actions.NewModelAction; +import application.actions.NewResourceAction; +import application.actions.OpenAction; +import application.actions.SaveAction; +import application.actions.SaveAsAction; +import application.actions.ShowNavigationAction; +import application.actions.ShowFlowLayerWindowAction; +import application.actions.TreeLayoutAction; +import application.actions.ZoomInAction; +import application.actions.ZoomOutAction; +import application.editor.Editor; + +public class ApplicationMenuBar extends JMenuBar { + private static final long serialVersionUID = 4811536194182272888L; + + private ApplicationWindow applicationWindow = null; + + private NewResourceAction newResourceAction = null; + private NewChannelAction newChannelAction = null; + private NewIOChannelAction newIOChannelAction = null; + private NewFormulaChannelAction newFormulaChannelAction = null; + private DeleteAction deleteAction = null; + private JavaPrototypeGenerateAction javaPrototypeGenerateAction = null; + private JerseyPrototypeGenerateAction jerseyPrototypeGenerateAction = null; + private DAGLayoutAction dagLayoutAction = null; + private TreeLayoutAction treeLayoutAction = null; + private CircleLayoutAction circleLayoutAction = null; + + public ApplicationMenuBar(ApplicationWindow applicationWindow) { + this.applicationWindow = applicationWindow; + JMenu newMenu = new JMenu(ApplicationLanguage.getInstance().getOptionByPropName("new")); + + newMenu.add(new NewModelAction(applicationWindow)); + + newMenu.add(newResourceAction = new NewResourceAction(applicationWindow.getEditor())); + newMenu.add(newChannelAction = new NewChannelAction(applicationWindow.getEditor())); + newMenu.add(newIOChannelAction = new NewIOChannelAction(applicationWindow.getEditor())); + newMenu.add(newFormulaChannelAction = new NewFormulaChannelAction(applicationWindow.getEditor())); + + JMenu menu = null; + menu = add(new JMenu(ApplicationLanguage.getInstance().getOptionByPropName("file"))); + menu.add(newMenu); + menu.add(new OpenAction(applicationWindow)); + menu.addSeparator(); + menu.add(new SaveAction(applicationWindow)); + menu.add(new SaveAsAction(applicationWindow)); + menu.addSeparator(); + menu.add(new ExitAction()); + + menu = add(new JMenu(ApplicationLanguage.getInstance().getOptionByPropName("edit"))); + menu.add(deleteAction = new DeleteAction(applicationWindow.getEditor())); + + + menu = add(new JMenu(ApplicationLanguage.getInstance().getOptionByPropName("layout"))); + menu.add(dagLayoutAction = new DAGLayoutAction(applicationWindow.getEditor())); + menu.add(treeLayoutAction = new TreeLayoutAction(applicationWindow.getEditor())); + menu.add(circleLayoutAction = new CircleLayoutAction(applicationWindow.getEditor())); + + menu = add(new JMenu(ApplicationLanguage.getInstance().getOptionByPropName("view"))); + menu.add(new ZoomInAction(applicationWindow.getGraphComponent())); + menu.add(new ZoomOutAction(applicationWindow.getGraphComponent())); + + menu = add(new JMenu(ApplicationLanguage.getInstance().getOptionByPropName("generate"))); + menu.add(javaPrototypeGenerateAction = new JavaPrototypeGenerateAction(applicationWindow.getEditor())); + menu.add(jerseyPrototypeGenerateAction = new JerseyPrototypeGenerateAction(applicationWindow.getEditor())); + + menu = add(new JMenu(ApplicationLanguage.getInstance().getOptionByPropName("window"))); + menu.add(new ShowNavigationAction(applicationWindow)); + menu.add(new ShowFlowLayerWindowAction(applicationWindow)); + } + + public Editor getEditor() { + return applicationWindow.getEditor(); + } + + public void setEditor(Editor editor) { + newResourceAction.setEditor(editor); + newChannelAction.setEditor(editor); + newIOChannelAction.setEditor(editor); + deleteAction.setEditor(editor); + javaPrototypeGenerateAction.setEditor(editor); + jerseyPrototypeGenerateAction.setEditor(editor); + treeLayoutAction.setEditor(editor); + circleLayoutAction.setEditor(editor); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/ApplicationWindow.java b/AlgebraicDataflowArchitectureModel/src/application/ApplicationWindow.java new file mode 100644 index 0000000..d4a58e1 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/ApplicationWindow.java @@ -0,0 +1,117 @@ +package application; + +import java.io.IOException; +import java.util.logging.FileHandler; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.logging.SimpleFormatter; + +import javax.swing.JFrame; + +import com.mxgraph.model.mxGeometry; +import com.mxgraph.swing.mxGraphComponent; +import com.mxgraph.swing.handler.mxRubberband; +import com.mxgraph.view.mxGraph; + +import application.editor.Editor; +import application.views.NavigationWindow; +import application.views.controlFlowDelegation.FlowLayerWindow; + +/** + * Application main window + * + * @author Nitta Lab. + * + */ +public class ApplicationWindow extends JFrame { + private static final long serialVersionUID = -8690140317781055614L; + public static final String title = "Visual Modeling Tool"; + public static final Logger logger = Logger.getLogger("dtram"); + + private Editor editor = null; + private mxGraph graph = null; + private mxGraphComponent graphComponent = null; + + private ApplicationMenuBar menuBar = null; + private NavigationWindow navigationWindow = null; + private FlowLayerWindow showFlowLayerWindow = null; + + public ApplicationWindow() { + setTitle(title); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + try { + Handler fh = new FileHandler("dtram.log", true); + fh.setFormatter(new SimpleFormatter()); + logger.addHandler(fh); + } catch (SecurityException | IOException e) { + e.printStackTrace(); + } + logger.setLevel(Level.INFO); + logger.log(Level.INFO, "launched"); + + // If you want to change the language, change here. + ApplicationLanguage.getInstance().setLocaleLanguage(ApplicationLanguage.JP); + + 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 = new Editor(graphComponent); + + getContentPane().add(graphComponent); + new mxRubberband(graphComponent); + graph.setAllowDanglingEdges(false); + graph.setCellsDisconnectable(true); + graph.setDropEnabled(false); + + menuBar = new ApplicationMenuBar(this); + setJMenuBar(menuBar); + setSize(870, 640); + + navigationWindow = new NavigationWindow(this, editor); + navigationWindow.setVisible(true); + + showFlowLayerWindow = new FlowLayerWindow(this); + showFlowLayerWindow.setVisible(false); + + editor.addStageChangeListener(navigationWindow); + editor.addStageChangeListener(showFlowLayerWindow); + } + + public mxGraph getGraph() { + return graph; + } + + public mxGraphComponent getGraphComponent() { + return graphComponent; + } + + public Editor getEditor() { + return editor; + } + + public void setEditor(Editor editor) { + this.editor = editor; + } + + public void showNavigationWindow() { + navigationWindow.setVisible(true); + } + + public void showSwitchLayerWindow() { + showFlowLayerWindow.setVisible(true); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/AbstractEditorAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/AbstractEditorAction.java new file mode 100644 index 0000000..2db8692 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/AbstractEditorAction.java @@ -0,0 +1,29 @@ +package application.actions; + +import java.awt.event.ActionEvent; +import java.util.logging.Level; + +import javax.swing.AbstractAction; +import javax.swing.Action; + +import application.ApplicationLanguage; +import application.ApplicationWindow; +import application.editor.Editor; + +public abstract class AbstractEditorAction extends AbstractAction { + protected Editor editor; + + public AbstractEditorAction(String propName, Editor editor) { + super(ApplicationLanguage.getInstance().getOptionByPropName(propName)); + this.editor = editor; + } + + public void setEditor(Editor editor) { + this.editor = editor; + } + + @Override + public void actionPerformed(ActionEvent e) { + ApplicationWindow.logger.log(Level.INFO, (String) getValue(Action.NAME)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/AbstractPopupAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/AbstractPopupAction.java new file mode 100644 index 0000000..665fce0 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/AbstractPopupAction.java @@ -0,0 +1,33 @@ +package application.actions; + +import java.awt.event.ActionEvent; +import java.util.logging.Level; + +import javax.swing.AbstractAction; +import javax.swing.Action; + +import com.mxgraph.model.mxCell; +import com.mxgraph.swing.mxGraphComponent; + +import application.ApplicationLanguage; +import application.ApplicationWindow; + +public abstract class AbstractPopupAction extends AbstractAction { + protected mxGraphComponent graphComponent = null; + protected mxCell targetCell = null; + + public AbstractPopupAction(final String propName, final mxCell targetCell, final mxGraphComponent graphComponent) { + super(ApplicationLanguage.getInstance().getOptionByPropName(propName)); + this.graphComponent = graphComponent; + this.targetCell = targetCell; + } + + public void updateTargetCell(final mxCell targetCell) { + this.targetCell = targetCell; + } + + @Override + public void actionPerformed(ActionEvent e) { + ApplicationWindow.logger.log(Level.INFO, (String) getValue(Action.NAME)); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/AbstractSystemAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/AbstractSystemAction.java new file mode 100644 index 0000000..76b644b --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/AbstractSystemAction.java @@ -0,0 +1,30 @@ +package application.actions; + +import java.awt.event.ActionEvent; +import java.util.logging.Level; + +import javax.swing.AbstractAction; +import javax.swing.Action; + +import application.ApplicationLanguage; +import application.ApplicationWindow; +import application.editor.Editor; + +public abstract class AbstractSystemAction extends AbstractAction { + + protected ApplicationWindow frame; + + public AbstractSystemAction(String propName, ApplicationWindow frame) { + super(ApplicationLanguage.getInstance().getOptionByPropName(propName)); + this.frame = frame; + } + + public void setFrame(ApplicationWindow frame) { + this.frame = frame; + } + + @Override + public void actionPerformed(ActionEvent e) { + ApplicationWindow.logger.log(Level.INFO, (String) getValue(Action.NAME)); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/AbstractViewerAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/AbstractViewerAction.java new file mode 100644 index 0000000..f7a283d --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/AbstractViewerAction.java @@ -0,0 +1,27 @@ +package application.actions; + +import java.awt.event.ActionEvent; +import java.util.logging.Level; + +import javax.swing.AbstractAction; +import javax.swing.Action; + +import com.mxgraph.swing.mxGraphComponent; + +import application.ApplicationLanguage; +import application.ApplicationWindow; + +public abstract class AbstractViewerAction extends AbstractAction { + + protected mxGraphComponent graphComponent = null; + + public AbstractViewerAction(String propName, mxGraphComponent graphComponent) { + super(ApplicationLanguage.getInstance().getOptionByPropName(propName)); + this.graphComponent = graphComponent; + } + + @Override + public void actionPerformed(ActionEvent e) { + ApplicationWindow.logger.log(Level.INFO, (String) getValue(Action.NAME)); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/ChangeCallOrderAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/ChangeCallOrderAction.java new file mode 100644 index 0000000..75e4141 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/ChangeCallOrderAction.java @@ -0,0 +1,83 @@ +package application.actions; + +import java.awt.event.ActionEvent; + +import javax.swing.JOptionPane; + +import com.mxgraph.model.mxCell; +import com.mxgraph.swing.mxGraphComponent; + +import models.controlFlowModel.CallEdgeAttribute; + +/************************************************************* + * + */ +public class ChangeCallOrderAction extends AbstractPopupAction { + + /************************************************************* + /************************************************************* + * [ *constructor ] + /************************************************************* + */ + public ChangeCallOrderAction(final mxGraphComponent graphComponent ,final mxCell cell) { + super(/*propName*/"changeCallOrder", cell, graphComponent); + } + + /************************************************************* + /************************************************************* + * [ *public ] + /************************************************************* + * + */ + @Override + public void actionPerformed(ActionEvent e) { + super.actionPerformed(e); + if(targetCell == null) return; + changeCallOrderOfCallEdge(targetCell); + } + + /************************************************************* + /************************************************************* + * [ *private ] + /************************************************************* + * コントロールフローの呼び出し順を変更する + * @param cellObj 選択されたエッジ(コントロールフロー)のセル + */ + private void changeCallOrderOfCallEdge(Object cellObj) { + String input = ""; + int inputOrder = 0; + + CallEdgeAttribute callEdgeAttr = (CallEdgeAttribute)graphComponent.getGraph().getModel().getValue(cellObj); + if (callEdgeAttr == null) return; + + input = JOptionPane.showInputDialog("Call order"); + if ( input == null) return; + + if ( !isNumeric(input) ) { + JOptionPane.showMessageDialog(graphComponent, "Input value must type of number."); + return; + } + + inputOrder = Integer.parseInt(input); + + final int endOfOrderOfSrc = callEdgeAttr.getSourceObjectNode().getOutdegree(); + if (inputOrder <= 0 || endOfOrderOfSrc < inputOrder) { + JOptionPane.showMessageDialog(graphComponent, "Input order must be between 1 and " + endOfOrderOfSrc + "."); + return; + } + + int curOrder = callEdgeAttr.getSourceObjectNode().getOutEdgeCallOrder(callEdgeAttr.getCallEdge()); + callEdgeAttr.getSourceObjectNode().sortOutEdgesByCallOrder(curOrder, inputOrder); + + graphComponent.refresh(); + } + + /************************************************************* + * フォームに入力された文字列が数値か判定 + * @param str フォームに入力された文字列 + */ + private boolean isNumeric(final String str) { + if (str == null) return false; + return str.matches("[0-9.]+"); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/CircleLayoutAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/CircleLayoutAction.java new file mode 100644 index 0000000..d19ff89 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/CircleLayoutAction.java @@ -0,0 +1,19 @@ +package application.actions; + +import java.awt.event.ActionEvent; + +import application.editor.Editor; + +public class CircleLayoutAction extends AbstractEditorAction { + + public CircleLayoutAction(Editor editor) { + super(/*propName*/"circleLayout", editor); + } + + @Override + public void actionPerformed(ActionEvent e) { + super.actionPerformed(e); + editor.setCircleLayout(); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/DAGLayoutAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/DAGLayoutAction.java new file mode 100644 index 0000000..a06c8f5 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/DAGLayoutAction.java @@ -0,0 +1,19 @@ +package application.actions; + +import java.awt.event.ActionEvent; + +import application.editor.Editor; + +public class DAGLayoutAction extends AbstractEditorAction { + + public DAGLayoutAction(Editor editor) { + super(/*propName*/"dagLayout", editor); + } + + @Override + public void actionPerformed(ActionEvent e) { + super.actionPerformed(e); + editor.setDAGLayout(); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/DeleteAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/DeleteAction.java new file mode 100644 index 0000000..7c949f0 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/DeleteAction.java @@ -0,0 +1,25 @@ +package application.actions; + +import java.awt.event.ActionEvent; + +import application.ApplicationLanguage; +import application.editor.Editor; + +public class DeleteAction extends AbstractEditorAction { + + /** + * + */ + private static final long serialVersionUID = -4410145389391154784L; + + public DeleteAction(Editor editor) { + super(/*propName*/ "delete", editor); + } + + @Override + public void actionPerformed(ActionEvent e) { + super.actionPerformed(e); + editor.delete(); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/ExitAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/ExitAction.java new file mode 100644 index 0000000..dcf6245 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/ExitAction.java @@ -0,0 +1,28 @@ +package application.actions; + +import java.awt.event.ActionEvent; +import java.util.logging.Level; + +import javax.swing.AbstractAction; +import javax.swing.Action; + +import application.ApplicationLanguage; +import application.ApplicationWindow; + +public class ExitAction extends AbstractAction { + /** + * + */ + private static final long serialVersionUID = -8733766417378036850L; + + public ExitAction() { + super(ApplicationLanguage.getInstance().getOptionByPropName("exit")); + } + + @Override + public void actionPerformed(ActionEvent e) { + ApplicationWindow.logger.log(Level.INFO, "exit"); + System.exit(0); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/IntroduceMediatorObjectAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/IntroduceMediatorObjectAction.java new file mode 100644 index 0000000..9aba67f --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/IntroduceMediatorObjectAction.java @@ -0,0 +1,101 @@ +package application.actions; + +import java.awt.event.ActionEvent; + +import javax.swing.JOptionPane; + +import com.mxgraph.model.mxCell; +import com.mxgraph.swing.mxGraphComponent; + +import application.editor.stages.ControlFlowDelegationStage; +import application.editor.stages.ControlFlowDelegationStageStatus; +import models.controlFlowModel.ObjectNodeAttribute; + +/************************************************************* + * + */ +public class IntroduceMediatorObjectAction extends AbstractPopupAction{ + + private ControlFlowDelegationStage stage = null; + + /************************************************************ + /************************************************************* + * [ *constructor ] + /************************************************************* + */ + public IntroduceMediatorObjectAction(final ControlFlowDelegationStage stage, final mxGraphComponent graphComponent ,final mxCell cell) { + super(/*propName*/"insertMediator", cell, graphComponent); + this.stage = stage; + } + + /************************************************************ + /************************************************************* + * [ *public ] + /************************************************************* + * + */ + @Override + public void actionPerformed(ActionEvent e) { + super.actionPerformed(e); + insertObjectNode(targetCell); + this.stage.setState( ControlFlowDelegationStageStatus.SELECTING_AN_EDGE); + } + + /************************************************************ + /************************************************************ + * [ *private ] + /************************************************************ + * 仲介者オブジェクトの導入を実行する. + * @param cellObj 選択されたエッジのセル + */ + private void insertObjectNode(final Object cellObj) { + if (cellObj == null) return; + + mxCell edgeCell = null; + if (cellObj instanceof mxCell) edgeCell = (mxCell)cellObj; + else return; + + // Inputing name to the dialog. + String objName = JOptionPane.showInputDialog("Object Name:"); + if (objName == null) return; + + if (objName.isEmpty()) { + JOptionPane.showMessageDialog(graphComponent, "You must input a name. \nIt mustn't be empty."); + return; + } + + if (isDuplicatedName(objName) ) { + JOptionPane.showMessageDialog(graphComponent, "The named object has already existed."); + return; + } + + stage.insertObjectNodeCellInControlFlowLayer(edgeCell, objName); + } + + /************************************************************* + * 同名のノードがすでにあるか判定する + * @param name つけたいノード名 + */ + private boolean isDuplicatedName(final String name) { + mxCell root = (mxCell)graphComponent.getGraph().getDefaultParent(); + + for (int i = 0; i < root.getChildCount(); i++) { + mxCell layerCell = (mxCell)root.getChildAt(i); + + for (int j = 0; j < layerCell.getChildCount(); j++) { + mxCell cell = (mxCell)layerCell.getChildAt(j); + + ObjectNodeAttribute attr = null; + if (cell.getValue() instanceof ObjectNodeAttribute) + attr = (ObjectNodeAttribute)cell.getValue(); + else continue; + + if ( !(attr.getObjectNode().getName().equals(name)) ) continue; + return true; + } + } + return false; + } + + +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/JavaPrototypeGenerateAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/JavaPrototypeGenerateAction.java new file mode 100644 index 0000000..5cf13f9 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/JavaPrototypeGenerateAction.java @@ -0,0 +1,104 @@ +package application.actions; + +import java.awt.event.ActionEvent; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; + +import javax.swing.JFileChooser; + +import algorithms.*; +import application.editor.Editor; +import code.ast.*; +import generators.CodeGenerator; +import generators.CodeGeneratorFromControlFlowGraph; +import generators.CodeGeneratorFromDataFlowGraph; +import generators.DataTransferMethodAnalyzer; +import generators.JavaCodeGenerator; +import generators.JavaMethodBodyGenerator; +import generators.JavaSpecific; +import models.controlFlowModel.ControlFlowGraph; +import models.dataConstraintModel.ResourcePath; +import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.ModelExtension; +import models.dataFlowModel.DataFlowGraph; + +public class JavaPrototypeGenerateAction extends AbstractEditorAction { + /** + * + */ + private static final long serialVersionUID = -3694103632055735068L; + + private String lastDir = null; + + public JavaPrototypeGenerateAction(Editor editor) { + super(/*propName*/"generateJava", editor); + } + + @Override + public void actionPerformed(ActionEvent e) { + super.actionPerformed(e); + DataFlowGraph dataFlowgraph = editor.getDataFlowGraph(); + if (dataFlowgraph == null) { + editor.changeStage(Editor.STAGE_PUSH_PULL_SELECTION); + dataFlowgraph = editor.getDataFlowGraph(); + } + if (dataFlowgraph != null) { + DataTransferModel model = editor.getModel(); + ModelExtension.extendModel(model); + TypeInference.infer(model); + DataTransferMethodAnalyzer.decideToStoreResourceStates(dataFlowgraph); + String fileName = editor.getCurFileName(); + 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); + if (mainTypeName.equals(resourceName)) { + exist = true; + } + } + if (!exist) { + CodeGenerator.setMainTypeName(mainTypeName); // use model's file name as the main type's name. + } else { + CodeGenerator.resetMainTypeName(); // use the default main type's name. + } + ControlFlowGraph controlFlowGraph = editor.getControlFlowGraph(); + if (controlFlowGraph != null) { + editor.setCodes(new CodeGeneratorFromControlFlowGraph().generateCode(model, controlFlowGraph, new JavaSpecific())); + } else { + editor.setCodes(new CodeGeneratorFromDataFlowGraph().generateCode(model, dataFlowgraph, new JavaSpecific())); + } + ModelExtension.recoverModel(model); + for (CompilationUnit file : editor.getCodes()) { + System.out.println(file); + } + + String wd = (lastDir != null) ? lastDir : System.getProperty("user.dir"); + JFileChooser fc = new JFileChooser(wd); + fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); + int rc = fc.showSaveDialog(null); + if (rc == JFileChooser.APPROVE_OPTION) { + lastDir = fc.getSelectedFile().getPath(); + for (CompilationUnit cu : editor.getCodes()) { + save(fc.getSelectedFile(), cu); + } + } + } + } + + private void save(File dir, CompilationUnit cu) { + File javaFile = new File(dir.getPath(), cu.getFileName()); + try { + BufferedWriter writer = new BufferedWriter(new FileWriter(javaFile)); + writer.write(cu.toString()); + writer.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/JerseyPrototypeGenerateAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/JerseyPrototypeGenerateAction.java new file mode 100644 index 0000000..6ad55d2 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/JerseyPrototypeGenerateAction.java @@ -0,0 +1,95 @@ +package application.actions; + +import java.awt.event.ActionEvent; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; + +import javax.swing.JFileChooser; + +import algorithms.*; +import application.editor.Editor; +import code.ast.*; +import generators.DataTransferMethodAnalyzer; +import generators.JerseyCodeGenerator; +import generators.JerseyMethodBodyGenerator; +import models.algebra.Type; +import models.dataConstraintModel.ResourcePath; +import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.ModelExtension; +import models.dataFlowModel.DataFlowGraph; + +public class JerseyPrototypeGenerateAction extends AbstractEditorAction { + /** + * + */ + private static final long serialVersionUID = 1117065654665887436L; + + private String lastDir = null; + + public JerseyPrototypeGenerateAction(Editor editor) { + super(/*propName*/"generateJAX_RS", editor); + } + + @Override + public void actionPerformed(ActionEvent e) { + super.actionPerformed(e); + DataFlowGraph graph = editor.getDataFlowGraph(); + if (graph == null) { + editor.changeStage(Editor.STAGE_PUSH_PULL_SELECTION); + graph = editor.getDataFlowGraph(); + } + if (graph != null) { + DataTransferModel model = editor.getModel(); + ModelExtension.extendModel(model); + TypeInference.infer(model); + DataTransferMethodAnalyzer.decideToStoreResourceStates(graph); + String fileName = editor.getCurFileName(); + 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); + if (mainTypeName.equals(resourceName)) { + exist = true; + } + } + if (!exist) { + JerseyCodeGenerator.setMainTypeName(mainTypeName); // use model's file name as the main type's name. + } else { + JerseyCodeGenerator.resetMainTypeName(); // use the default main type's name. + } + editor.setCodes(JerseyMethodBodyGenerator.doGenerate(graph, model, JerseyCodeGenerator.doGenerate(graph, model))); + ModelExtension.recoverModel(model); + for (CompilationUnit file : editor.getCodes()) { + System.out.println(file); + } + + String wd = (lastDir != null) ? lastDir : System.getProperty("user.dir"); + JFileChooser fc = new JFileChooser(wd); + fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); + int rc = fc.showSaveDialog(null); + if (rc == JFileChooser.APPROVE_OPTION) { + lastDir = fc.getSelectedFile().getPath(); + for (CompilationUnit cu : editor.getCodes()) { + save(fc.getSelectedFile(), cu); + } + } + } + } + + private void save(File dir, CompilationUnit cu) { + File javaFile = new File(dir.getPath(), cu.getFileName()); + try { + BufferedWriter writer = new BufferedWriter(new FileWriter(javaFile)); + writer.write(cu.toString()); + writer.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/NewChannelAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/NewChannelAction.java new file mode 100644 index 0000000..2810dbc --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/NewChannelAction.java @@ -0,0 +1,29 @@ +package application.actions; + +import java.awt.event.ActionEvent; + +import javax.swing.JOptionPane; + +import application.editor.Editor; +import models.dataFlowModel.DataTransferChannel; + +public class NewChannelAction extends AbstractEditorAction { + + /** + * + */ + private static final long serialVersionUID = 5979007029473101802L; + + public NewChannelAction(Editor editor) { + super(/*propName*/"newChannel", editor); + } + + @Override + public void actionPerformed(ActionEvent e) { + super.actionPerformed(e); + String channelName = JOptionPane.showInputDialog("Channel Name:"); + if (channelName == null) return; + editor.addChannel(new DataTransferChannel(channelName)); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/NewFormulaChannelAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/NewFormulaChannelAction.java new file mode 100644 index 0000000..e524a8e --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/NewFormulaChannelAction.java @@ -0,0 +1,63 @@ +package application.actions; + +import java.awt.BorderLayout; +import java.awt.Container; +import java.awt.FlowLayout; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JTextField; + +import application.ApplicationWindow; +import application.editor.Editor; +import models.visualModel.FormulaChannel; + +public class NewFormulaChannelAction extends AbstractEditorAction implements ActionListener { + + /** + * + */ + private static final long serialVersionUID = 5345875219049178252L; + + public NewFormulaChannelAction(Editor editor) { + super(/*propName*/"newFormulaChannel", editor); + } + + @Override + public void actionPerformed(ActionEvent e) { + super.actionPerformed(e); + JPanel panel = new JPanel(); + GridLayout layout = new GridLayout(2,2); + panel.setLayout(layout); + layout.setVgap(5); + layout.setHgap(20); + panel.add(new JLabel("Channel Name:")); + JTextField channelText = new JTextField(); + panel.add(channelText); + panel.add(new JLabel("Symbol:")); + JTextField symbolText = new JTextField(); + panel.add(symbolText); + + int r = JOptionPane.showConfirmDialog( + null, + panel, + "New Formula Channel", + JOptionPane.OK_CANCEL_OPTION, + JOptionPane.QUESTION_MESSAGE); + + String channelName = channelText.getText(); + String symbol = symbolText.getText(); + if(r == JOptionPane.OK_OPTION) { + editor.addFormulaChannel(new FormulaChannel(channelName, editor.getModel().getSymbol(symbol))); + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/NewIOChannelAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/NewIOChannelAction.java new file mode 100644 index 0000000..3d37ba4 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/NewIOChannelAction.java @@ -0,0 +1,29 @@ +package application.actions; + +import java.awt.event.ActionEvent; + +import javax.swing.JOptionPane; + +import application.editor.Editor; +import models.dataFlowModel.DataTransferChannel; + +public class NewIOChannelAction extends AbstractEditorAction { + + /** + * + */ + private static final long serialVersionUID = -1657072017390171313L; + + public NewIOChannelAction(Editor editor) { + super(/*propName*/"newEventChannel", editor); + } + + @Override + public void actionPerformed(ActionEvent e) { + super.actionPerformed(e); + String channelName = JOptionPane.showInputDialog("I/O Channel Name:"); + if (channelName == null) return; + editor.addIOChannel(new DataTransferChannel(channelName)); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/NewModelAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/NewModelAction.java new file mode 100644 index 0000000..77104a8 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/NewModelAction.java @@ -0,0 +1,25 @@ +package application.actions; + +import java.awt.event.ActionEvent; + +import application.ApplicationWindow; +import application.editor.Editor; + +public class NewModelAction extends AbstractSystemAction { + /** + * + */ + private static final long serialVersionUID = 8484493203589724589L; + + public NewModelAction(ApplicationWindow frame) { + super(/*propName*/"newModel", frame); + } + + @Override + public void actionPerformed(ActionEvent e) { + super.actionPerformed(e); + frame.getEditor().clear(); + frame.setTitle(frame.title); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/NewResourceAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/NewResourceAction.java new file mode 100644 index 0000000..623e0b7 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/NewResourceAction.java @@ -0,0 +1,29 @@ +package application.actions; + +import java.awt.event.ActionEvent; + +import javax.swing.JOptionPane; + +import application.editor.Editor; +import models.dataConstraintModel.ResourcePath; + +public class NewResourceAction extends AbstractEditorAction { + + /** + * + */ + private static final long serialVersionUID = -4439207504700741286L; + + public NewResourceAction(Editor editor) { + super(/*propName*/"newResources", editor); + } + + @Override + public void actionPerformed(ActionEvent e) { + super.actionPerformed(e); + String resName = JOptionPane.showInputDialog("Resourece Name:"); + if (resName == null) return; + editor.addResourcePath(new ResourcePath(resName, 0)); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/OpenAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/OpenAction.java new file mode 100644 index 0000000..0502933 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/OpenAction.java @@ -0,0 +1,63 @@ +package application.actions; + +import java.awt.event.ActionEvent; +import java.io.File; + +import javax.swing.JFileChooser; +import javax.swing.filechooser.FileFilter; +import javax.swing.filechooser.FileNameExtensionFilter; + +import application.ApplicationWindow; +import application.editor.Editor; + +public class OpenAction extends AbstractSystemAction { + /** + * + */ + private static final long serialVersionUID = -8290761032629599683L; + + private String lastDir = null; + + public OpenAction(ApplicationWindow frame) { + super(/*propName*/"open", frame); + } + + @Override + public void actionPerformed(ActionEvent e) { + super.actionPerformed(e); + Editor editor = frame.getEditor(); + if (editor != null) { + String wd = (lastDir != null) ? lastDir : System.getProperty("user.dir"); + + JFileChooser fc = new JFileChooser(wd); + + FileFilter model = new FileNameExtensionFilter("model","model"); + FileFilter dtram = new FileNameExtensionFilter("dtram", "dtram"); + + // Adds file filter for supported file format + FileFilter defaultFilter = new FileFilter() { + + public boolean accept(File file) { + String lcase = file.getName().toLowerCase(); + return lcase.endsWith(".model"); + } + + @Override + public String getDescription() { + return null; + } + }; + + fc.addChoosableFileFilter(defaultFilter); + fc.addChoosableFileFilter(model); + fc.addChoosableFileFilter(dtram); + int rc = fc.showDialog(null, "Open Model File"); + if (rc == JFileChooser.APPROVE_OPTION) { + lastDir = fc.getSelectedFile().getParent(); + editor.open(fc.getSelectedFile()); + frame.setTitle(frame.title + " - " + fc.getSelectedFile().getAbsolutePath()); + } + } + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/SaveAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/SaveAction.java new file mode 100644 index 0000000..c19d3bb --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/SaveAction.java @@ -0,0 +1,27 @@ +package application.actions; + +import java.awt.event.ActionEvent; + +import application.ApplicationWindow; +import application.editor.Editor; + +public class SaveAction extends AbstractSystemAction { + /** + * + */ + private static final long serialVersionUID = 5660460585305281982L; + + public SaveAction(ApplicationWindow frame) { + super(/*propName*/"save", frame); + } + + @Override + public void actionPerformed(ActionEvent e) { + super.actionPerformed(e); + Editor editor = frame.getEditor(); + if (editor != null && editor.getCurFileName() != null) { + editor.save(); + } + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/SaveAsAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/SaveAsAction.java new file mode 100644 index 0000000..b527004 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/SaveAsAction.java @@ -0,0 +1,78 @@ +package application.actions; + +import java.awt.event.ActionEvent; +import java.io.File; + +import javax.swing.AbstractAction; +import javax.swing.JFileChooser; +import javax.swing.filechooser.FileFilter; +import javax.swing.filechooser.FileNameExtensionFilter; + +import application.ApplicationWindow; +import application.editor.Editor; + +public class SaveAsAction extends AbstractSystemAction { + /** + * + */ + private static final long serialVersionUID = -2599502783032684084L; + + private String lastDir = null; + + public SaveAsAction(ApplicationWindow frame) { + super(/*propName*/"saveAs", frame); + } + + @Override + public void actionPerformed(ActionEvent e) { + super.actionPerformed(e); + Editor editor = frame.getEditor(); + if (editor != null) { + String wd = (lastDir != null) ? lastDir : System.getProperty("user.dir"); + + JFileChooser fc = new JFileChooser(wd); + FileFilter model = new FileNameExtensionFilter("model","model"); + FileFilter dtram = new FileNameExtensionFilter("dtram", "dtram"); + + // Adds file filter for supported file format + FileFilter defaultFilter = new FileFilter() { + public boolean accept(File file) { + String lcase = file.getName().toLowerCase(); + return lcase.endsWith(".model"); + } + + @Override + public String getDescription() { + return null; + } + }; + + fc.addChoosableFileFilter(defaultFilter); + fc.addChoosableFileFilter(model); + fc.addChoosableFileFilter(dtram); + int rc = fc.showDialog(null, "Save Model File"); + + // choose a file extension from a dialog. + if (rc == JFileChooser.APPROVE_OPTION) { + + // if extension filter is filled, then attaching extension by choosing filter. + // but if it's not filled, then using default extension name. + String extension = ""; + if(fc.getFileFilter() instanceof FileNameExtensionFilter) { + FileNameExtensionFilter selectedFilter = (FileNameExtensionFilter)fc.getFileFilter(); + extension = "." + selectedFilter.getExtensions()[0].toString(); + } + + lastDir = fc.getSelectedFile().getParent(); + + String fileName = fc.getSelectedFile().getAbsolutePath() + extension; + editor.setCurFilePath(fileName); + + // overwriting file + editor.save(); + frame.setTitle(ApplicationWindow.title + " - " + fc.getSelectedFile().getAbsolutePath()); + } + } + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/ShowDependentableMediatorAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/ShowDependentableMediatorAction.java new file mode 100644 index 0000000..8bba754 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/ShowDependentableMediatorAction.java @@ -0,0 +1,52 @@ +package application.actions; + +import java.awt.event.ActionEvent; + +import com.mxgraph.model.mxCell; +import com.mxgraph.swing.mxGraphComponent; + +import application.editor.stages.ControlFlowDelegationStage; +import application.editor.stages.ControlFlowDelegationStageStatus; +import models.controlFlowModel.CallEdgeAttribute; + +public class ShowDependentableMediatorAction extends AbstractPopupAction { + private ControlFlowDelegationStage stage = null; + + /************************************************************* + /************************************************************* + * [ *constructor ] + /************************************************************* + */ + public ShowDependentableMediatorAction(final ControlFlowDelegationStage stage, final mxGraphComponent graphComponent, final mxCell targetCell) { + super(/*propName*/"dependsOnMediator", targetCell, graphComponent); + this.stage = stage; + } + + /************************************************************* + /************************************************************ + * [ *public ] + /************************************************************* + * DoM適用可能範囲の表示まで実行する. + */ + @Override + public void actionPerformed(ActionEvent e) { + super.actionPerformed(e); + showDependentableNodesBySelectedEdge(targetCell); + stage.setState(ControlFlowDelegationStageStatus.SHOWING_DEPENDENTABLE_NODES); + // ⇒ 以降のDoM実行操作は, 【ControlFlowDelgationCellEditor】で実行される. + } + + /************************************************************* + /************************************************************* + * [ *private ] + /************************************************************* + * 選択されたセルの, DoM実行な仲介者オブジェクトを表示する. + */ + private void showDependentableNodesBySelectedEdge(Object cellObj) { + CallEdgeAttribute callEdgeAttr = (CallEdgeAttribute)graphComponent.getGraph().getModel().getValue(cellObj); + if(callEdgeAttr == null) return; + + ControlFlowDelegationStage cfdStage = (ControlFlowDelegationStage)stage; + cfdStage.showDependentableNodes(callEdgeAttr); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/ShowFlowLayerWindowAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/ShowFlowLayerWindowAction.java new file mode 100644 index 0000000..1a9e790 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/ShowFlowLayerWindowAction.java @@ -0,0 +1,17 @@ +package application.actions; + +import java.awt.event.ActionEvent; + +import application.ApplicationWindow; + +public class ShowFlowLayerWindowAction extends AbstractSystemAction { + public ShowFlowLayerWindowAction(ApplicationWindow frame) { + super(/*propName*/"showFlowLayer", frame); + } + + @Override + public void actionPerformed(ActionEvent e) { + super.actionPerformed(e); + frame.showSwitchLayerWindow(); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/ShowNavigationAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/ShowNavigationAction.java new file mode 100644 index 0000000..8d7aacb --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/ShowNavigationAction.java @@ -0,0 +1,19 @@ +package application.actions; + +import java.awt.event.ActionEvent; + +import application.ApplicationWindow; + +public class ShowNavigationAction extends AbstractSystemAction { + + public ShowNavigationAction(ApplicationWindow frame) { + super(/*propName*/"showNavigation", frame); + } + + @Override + public void actionPerformed(ActionEvent e) { + super.actionPerformed(e); + frame.showNavigationWindow(); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/TreeLayoutAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/TreeLayoutAction.java new file mode 100644 index 0000000..a8056eb --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/TreeLayoutAction.java @@ -0,0 +1,19 @@ +package application.actions; + +import java.awt.event.ActionEvent; + +import application.editor.Editor; + +public class TreeLayoutAction extends AbstractEditorAction { + + public TreeLayoutAction(Editor editor) { + super(/*propName*/"treeLayout", editor); + } + + @Override + public void actionPerformed(ActionEvent e) { + super.actionPerformed(e); + editor.setTreeLayout(); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/ZoomInAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/ZoomInAction.java new file mode 100644 index 0000000..e18fb4f --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/ZoomInAction.java @@ -0,0 +1,29 @@ +package application.actions; + +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; +import javax.swing.JOptionPane; + +import com.mxgraph.swing.mxGraphComponent; +import com.mxgraph.util.mxResources; + +import application.editor.Editor; + +public class ZoomInAction extends AbstractViewerAction { + /** + * + */ + private static final long serialVersionUID = 6758532166946195926L; + + public ZoomInAction(mxGraphComponent graphComponent) { + super(/*propName*/"zoomIn", graphComponent); + } + + @Override + public void actionPerformed(ActionEvent e) { + super.actionPerformed(e); + graphComponent.zoomIn(); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/ZoomOutAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/ZoomOutAction.java new file mode 100644 index 0000000..0e306da --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/ZoomOutAction.java @@ -0,0 +1,27 @@ +package application.actions; + +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; + +import com.mxgraph.swing.mxGraphComponent; + +import application.editor.Editor; + +public class ZoomOutAction extends AbstractViewerAction { + /** + * + */ + private static final long serialVersionUID = 8657530769383486605L; + + public ZoomOutAction(mxGraphComponent graphComponent) { + super(/*propName*/"zoomOut", graphComponent); + } + + @Override + public void actionPerformed(ActionEvent e) { + super.actionPerformed(e); + graphComponent.zoomOut(); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/Editor.java b/AlgebraicDataflowArchitectureModel/src/application/editor/Editor.java new file mode 100644 index 0000000..82cb881 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/Editor.java @@ -0,0 +1,529 @@ +package application.editor; + +import java.awt.event.MouseListener; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import com.mxgraph.layout.mxCircleLayout; +import com.mxgraph.layout.mxCompactTreeLayout; +import com.mxgraph.model.mxCell; +import com.mxgraph.swing.mxGraphComponent; +import com.mxgraph.util.mxEvent; +import com.mxgraph.util.mxEventSource.mxIEventListener; +import com.mxgraph.view.mxCellState; +import com.mxgraph.view.mxGraph; +import com.mxgraph.view.mxGraphView; + +import application.editor.stages.ControlFlowDelegationStage; +import application.editor.stages.DataFlowModelingStage; +import application.editor.stages.PushPullSelectionStage; +import application.layouts.*; +import code.ast.CompilationUnit; +import models.EdgeAttribute; +import models.controlFlowModel.ControlFlowGraph; +import models.dataConstraintModel.Channel; +import models.dataConstraintModel.ResourcePath; +import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.DataFlowGraph; +import models.visualModel.FormulaChannel; +import parser.Parser; +import parser.exceptions.ExpectedAssignment; +import parser.exceptions.ExpectedChannel; +import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; +import parser.exceptions.ExpectedEquals; +import parser.exceptions.ExpectedFormulaChannel; +import parser.exceptions.ExpectedGeometry; +import parser.exceptions.ExpectedInOrOutOrRefKeyword; +import parser.exceptions.ExpectedIoChannel; +import parser.exceptions.ExpectedLeftCurlyBracket; +import parser.exceptions.ExpectedModel; +import parser.exceptions.ExpectedNode; +import parser.exceptions.ExpectedRHSExpression; +import parser.exceptions.ExpectedResource; +import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; +import parser.exceptions.WrongLHSExpression; +import parser.exceptions.WrongRHSExpression; +import parser.ParserDTRAM; + +/** + * Main editor for all stages + * + * @author Nitta + * + */ +public class Editor { + public DataTransferModel model = null; + public mxGraph graph = null; + private mxGraphComponent graphComponent = null; + private mxIEventListener curChangeEventListener = null; + private MouseListener curMouseEventListener = null; + + protected Stage curStage = null; + protected List stageQueue = null; + private List stageChangeListeners = null; + + protected String curFileName = null; + protected String curFilePath = null; + protected ArrayList codes = null; + + public static DataFlowModelingStage STAGE_DATA_FLOW_MODELING = null; + public static PushPullSelectionStage STAGE_PUSH_PULL_SELECTION = null; + public static ControlFlowDelegationStage STAGE_CONTROL_FLOW_DELEGATION = null; + + public Editor(mxGraphComponent graphComponent) { + this.graphComponent = graphComponent; + this.graph = graphComponent.getGraph(); + + STAGE_DATA_FLOW_MODELING = new DataFlowModelingStage(graphComponent); + STAGE_PUSH_PULL_SELECTION = new PushPullSelectionStage(graphComponent); + STAGE_CONTROL_FLOW_DELEGATION = new ControlFlowDelegationStage(graphComponent); + + graphComponent.setCellEditor(STAGE_DATA_FLOW_MODELING.createCellEditor(graphComponent)); + + stageQueue = new ArrayList<>(); + stageQueue.add(STAGE_DATA_FLOW_MODELING); + stageQueue.add(STAGE_PUSH_PULL_SELECTION); + stageQueue.add(STAGE_CONTROL_FLOW_DELEGATION); + curStage = STAGE_DATA_FLOW_MODELING; + + stageChangeListeners = new ArrayList<>(); + } + + public mxGraph getGraph() { + return graph; + } + + public mxGraphComponent getGraphComponent() { + return this.graphComponent; + } + + public DataTransferModel getModel() { + model = curStage.getModel(); + return model; + } + + public Stage getCurStage() { + return curStage; + } + + public boolean canChange(Stage nextStage) { + return nextStage.canChangeFrom(curStage); + } + + public boolean nextStage() { + int curStageNo = stageQueue.indexOf(curStage); + if (curStageNo + 1 >= stageQueue.size()) return false; + return changeStage(stageQueue.get(curStageNo + 1)); + } + + public boolean changeStage(Stage nextStage) { + if (!nextStage.canChangeFrom(curStage)) return false; + nextStage.init(curStage); + graphComponent.setCellEditor(nextStage.createCellEditor(graphComponent)); + + // add listeners + // "curChangeEventListener" will be called when updating the mxGraph. + if (curChangeEventListener != null) { + graph.getModel().removeListener(curChangeEventListener); + } + curChangeEventListener = nextStage.createChangeEventListener(this); + if (curChangeEventListener != null) { + graph.getModel().addListener(mxEvent.CHANGE, curChangeEventListener); + } + + // A handler of a mouse event. + if(curMouseEventListener != null) { + graphComponent.getGraphControl().removeMouseListener(curMouseEventListener); + } + curMouseEventListener = nextStage.createMouseEventListener(this); + if(curMouseEventListener != null) { + graphComponent.getGraphControl().addMouseListener(curMouseEventListener); + } + curStage = nextStage; + notifyStageChangeListeners(); + return true; + } + + public void addStageChangeListener(IStageChangeListener stageChangeListener) { + stageChangeListeners.add(stageChangeListener); + } + + private void notifyStageChangeListeners() { + for (IStageChangeListener l: stageChangeListeners) { + l.stageChanged(curStage); + } + } + + public DataFlowGraph getDataFlowGraph() { + if (curStage instanceof PushPullSelectionStage) { + return ((PushPullSelectionStage) curStage).getDataFlowGraph(); + } else if (curStage instanceof ControlFlowDelegationStage) { + return ((ControlFlowDelegationStage) curStage).getControlFlowGraph().getDataFlowGraph(); + } + return null; + } + + public ControlFlowGraph getControlFlowGraph() { + if (curStage instanceof ControlFlowDelegationStage) { + return ((ControlFlowDelegationStage) curStage).getControlFlowGraph(); + } + return null; + } + + public ArrayList getCodes() { + return codes; + } + + public void setCodes(ArrayList codes) { + this.codes = codes; + } + + public String getCurFileName() { + return curFileName; + } + + public String getCurFilePath() { + return curFilePath; + } + + public void setCurFilePath(String curFilePath) { + this.curFilePath = curFilePath; + this.curFileName = new File(curFilePath).getName(); + } + + public void clear() { + // Force to change to the data-flow modeling stage. + boolean stageChanged = changeStage(STAGE_DATA_FLOW_MODELING); + if (!stageChanged) return; + + model = null; + ((DataFlowModelingStage) curStage).clear(); + + curFilePath = null; + curFileName = null; + codes = null; + } + + /** + * Open a given file, parse the file, construct a DataFlowModel and a mxGraph + * @param file given file + * @return a constructed DataFlowModel + */ + public DataTransferModel open(File file) { + // Force to change to the data-flow modeling stage. + boolean stageChanged = changeStage(STAGE_DATA_FLOW_MODELING); + if (!stageChanged) return null; + + try { + String extension =""; + if(file != null && file.exists()) { + // get file's name + String name = file.getName(); + + // get file's extension + extension = name.substring(name.lastIndexOf(".")); + } + if(extension.contains(".model")) { + openModel(file); + } else { + // Parse the .dtram file. + ParserDTRAM parserDTRAM = new ParserDTRAM(new BufferedReader(new FileReader(file))); + try { + model = parserDTRAM.doParseModel(); + if (curStage instanceof DataFlowModelingStage) { + // Update the mxGraph. + ((DataFlowModelingStage) curStage).setModel(model); + } + // Restore the geometry. + parserDTRAM.doParseGeometry(graph); + + // Change to the push/pull selection stage, analyze the data transfer model and construct a data-flow graph. + changeStage(STAGE_PUSH_PULL_SELECTION); + + curFilePath = file.getAbsolutePath(); + curFileName = file.getName(); + return model; + } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedModel | ExpectedGeometry | ExpectedNode | ExpectedResource | ExpectedFormulaChannel | ExpectedIoChannel | WrongJsonExpression | ExpectedColon e) { + e.printStackTrace(); + } + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + + return null; + } + + public DataTransferModel openModel(File file) { + // Force to change to data-flow modeling stage. + boolean stageChanged = changeStage(STAGE_DATA_FLOW_MODELING); + + if (!stageChanged) return null; + + try { + // Parse the .model file. + Parser parser = new Parser(new BufferedReader(new FileReader(file))); + try { + model = parser.doParse(); + if (curStage instanceof DataFlowModelingStage) { + // Update the mxGraph. + ((DataFlowModelingStage) curStage).setModel(model); + } + // Set DAG layout. + setDAGLayout(); + + // Change to the push/pull selection stage, analyze the data transfer model and construct a data-flow graph. + changeStage(STAGE_PUSH_PULL_SELECTION); + + curFilePath = file.getAbsolutePath(); + curFileName = file.getName(); + return model; + } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | WrongJsonExpression | ExpectedColon e) { + e.printStackTrace(); + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + return null; + } + + /************************************************************* + * [save] + /************************************************************* + * + */ + public void save() { + if (curFilePath != null) { + try { + File file = new File(curFilePath); + String extension = ""; + if(file != null && file.exists()) { + // get a file's name + String name = file.getName(); + + // get a file's extension + extension = name.substring(name.lastIndexOf(".")); + } + if(extension.contains(".model")) { + saveModel(file); + } else { + FileWriter filewriter = new FileWriter(file); + filewriter.write(toOutputString()); + filewriter.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + public void saveModel(File file) { + if (curFilePath != null) { + try { + FileWriter filewriter = new FileWriter(file); + filewriter.write(model.getSourceText()); + filewriter.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + /**-------------------------------------------------------------------------------- + * get writing texts "dtram" file information is written. + * + * @return formatted "dtram" info texts. + */ + protected String toOutputString() { + String fileString = ""; + + fileString += "model {\n"; + fileString += this.model.getSourceText(); + fileString += "}\n"; + + fileString += "geometry {\n"; + + mxCell root = (mxCell) graph.getDefaultParent(); + mxCell nodeLayer = (mxCell) root.getChildAt(Stage.NODE_LAYER); + mxCell dataFlowLayer = (mxCell) root.getChildAt(Stage.DATA_FLOW_LAYER); + for (int i = 0; i < graph.getModel().getChildCount(nodeLayer); i++) { + Object cell = graph.getModel().getChildAt(nodeLayer, i); + if (graph.getModel().isVertex(cell)) { + mxGraphView view = graph.getView(); + mxCellState state = view.getState(cell); + int x = (int) state.getX(); + int y = (int) state.getY(); + int w = (int) state.getWidth(); + int h = (int) state.getHeight(); + + for (ResourcePath res: model.getResourcePaths()){ + if(res instanceof ResourcePath && state.getLabel().equals(res.getResourceName())) + fileString += "\tnode r " + state.getLabel() + ":" + x + "," + y + "," + w + "," + h + "\n"; + } + + for (Channel ioC: model.getIOChannels()) { + if(ioC instanceof Channel && state.getLabel().equals(ioC.getChannelName())) { + fileString += "\tnode ioc " + state.getLabel() + ":" + x + "," + y + "," + w + "," + h + "\n"; + } + } + } + } + for (int i = 0; i < graph.getModel().getChildCount(dataFlowLayer); i++) { + Object cell = graph.getModel().getChildAt(dataFlowLayer, i); + if (graph.getModel().isVertex(cell)) { + mxGraphView view = graph.getView(); + mxCellState state = view.getState(cell); + int x = (int) state.getX(); + int y = (int) state.getY(); + int w = (int) state.getWidth(); + int h = (int) state.getHeight(); + + for(Channel ch: model.getChannels()) { + if(ch instanceof FormulaChannel && state.getLabel().equals(ch.getChannelName())) { + fileString += "\tnode fc " + state.getLabel() + ":" + x + "," + y + "," + w + "," + h+"\n"; + } else if(ch instanceof Channel && state.getLabel().equals(ch.getChannelName())) { + fileString +="\tnode c " + state.getLabel() + ":" + x + "," + y + "," + w + "," + h+"\n"; + } + } + } + } + fileString += "}\n"; + + return fileString; + } + + public void setDAGLayout() { + mxCell root = (mxCell) graph.getDefaultParent(); + mxCell dataFlowLayer = (mxCell) root.getChildAt(Stage.DATA_FLOW_LAYER); + graph.getModel().beginUpdate(); + try { + DAGLayout ctl = new DAGLayout(graph); + ctl.execute(dataFlowLayer); +// for(int i = 0; i < root.getChildCount(); i++) { +// ctl.execute(root.getChildAt(i)); +// } + } finally { + graph.getModel().endUpdate(); + } + } + + public void setTreeLayout() { + mxCell root = (mxCell) graph.getDefaultParent(); + graph.getModel().beginUpdate(); + try { + mxCompactTreeLayout ctl = new mxCompactTreeLayout(graph); + ctl.setLevelDistance(100); + // ctl.setHorizontal(false); + ctl.setEdgeRouting(false); + for(int i = 0; i < root.getChildCount(); i++) { + ctl.execute(root.getChildAt(i)); + } + } finally { + graph.getModel().endUpdate(); + } + } + + public void setCircleLayout() { + mxCell root = (mxCell) graph.getDefaultParent(); + graph.getModel().beginUpdate(); + try { + mxCircleLayout ctl = new mxCircleLayout(graph); + for(int i = 0; i < root.getChildCount(); i++) { + ctl.execute(root.getChildAt(i)); + } + } finally { + graph.getModel().endUpdate(); + } + } + + public void addResourcePath(ResourcePath res) { + // Force to change to the data-flow modeling stage. + boolean stageChanged = changeStage(STAGE_DATA_FLOW_MODELING); + if (!stageChanged) return; + + ((DataFlowModelingStage) curStage).addResourcePath(res); + model = ((DataFlowModelingStage) curStage).getModel(); + } + + public void addChannel(DataTransferChannel channel) { + // Force to change to the data-flow modeling stage. + boolean stageChanged = changeStage(STAGE_DATA_FLOW_MODELING); + if (!stageChanged) return; + + ((DataFlowModelingStage) curStage).addChannel(channel); + model = ((DataFlowModelingStage) curStage).getModel(); + + } + + public void addIOChannel(DataTransferChannel ioChannel) { + // Force to change to the data-flow modeling stage. + boolean stageChanged = changeStage(STAGE_DATA_FLOW_MODELING); + if (!stageChanged) return; + + ((DataFlowModelingStage) curStage).addIOChannel(ioChannel); + model = ((DataFlowModelingStage) curStage).getModel(); + } + + public void addFormulaChannel(FormulaChannel formulaChannel) { + // Force to change to the data-flow modeling stage. + boolean stageChanged = changeStage(STAGE_DATA_FLOW_MODELING); + if (!stageChanged) return; + + ((DataFlowModelingStage) curStage).addFormulaChannel(formulaChannel); + model = ((DataFlowModelingStage) curStage).getModel(); + } + + public boolean connectEdge(mxCell edge, mxCell src, mxCell dst) { + // Force to change to the data-flow modeling stage. + boolean stageChanged = changeStage(STAGE_DATA_FLOW_MODELING); + if (!stageChanged) return false; + + boolean isConnected = ((DataFlowModelingStage) curStage).connectEdge(edge, src, dst); + + return isConnected; + } + + public void delete() { + // Force to change to the data-flow modeling stage. + boolean stageChanged = changeStage(STAGE_DATA_FLOW_MODELING); + if (!stageChanged) return; + + ((DataFlowModelingStage) curStage).delete(); + } + + public static class SrcDstAttribute extends EdgeAttribute { + private Object src; + private Object dst; + + public SrcDstAttribute(Object src, Object dst) { + this.src = src; + this.dst = dst; + } + + public Object getSrouce() { + return src; + } + + public Object getDestination() { + return dst; + } + + public String toString() { + return ""; + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/FlowCellEditor.java b/AlgebraicDataflowArchitectureModel/src/application/editor/FlowCellEditor.java new file mode 100644 index 0000000..c44b42d --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/FlowCellEditor.java @@ -0,0 +1,45 @@ +package application.editor; + +import java.util.EventObject; + +import com.mxgraph.swing.mxGraphComponent; +import com.mxgraph.swing.view.mxICellEditor; + +abstract public class FlowCellEditor implements mxICellEditor { + + protected Stage stage = null; + protected mxGraphComponent graphComponent = null; + + protected Object editingCell = null; + + /************************************************************* + * [ *constructor] + /************************************************************* + * + * @param stage + * @param graphComponent + */ + protected FlowCellEditor(Stage stage, mxGraphComponent graphComponent) { + this.stage = stage; + this.graphComponent = graphComponent; + } + + /************************************************************* + * [ *public ] + /************************************************************* + * + */ + public Object getEditingCell() { + return this.editingCell; + } + + /************************************************************* + * + */ + abstract public void startEditing(Object cellObj, EventObject eventObj); + + /************************************************************* + * + */ + abstract public void stopEditing(boolean cancel); +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/IStageChangeListener.java b/AlgebraicDataflowArchitectureModel/src/application/editor/IStageChangeListener.java new file mode 100644 index 0000000..a345fe2 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/IStageChangeListener.java @@ -0,0 +1,5 @@ +package application.editor; + +public interface IStageChangeListener { + public void stageChanged(Stage newStage); +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/InEdgeAttribute.java b/AlgebraicDataflowArchitectureModel/src/application/editor/InEdgeAttribute.java new file mode 100644 index 0000000..5956f6c --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/InEdgeAttribute.java @@ -0,0 +1,19 @@ +package application.editor; + +import models.dataFlowModel.PushPullAttribute; +import models.dataFlowModel.PushPullValue; +import models.dataFlowModel.ResourceNode; + +public class InEdgeAttribute extends PushPullAttribute { + private ResourceNode srcRes; + + public InEdgeAttribute(PushPullAttribute attr, ResourceNode srcRes) { + super(); + super.setOptions(attr.getOptions()); + this.srcRes = srcRes; + } + + public ResourceNode getSrcResource() { + return srcRes; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/OutEdgeAttribute.java b/AlgebraicDataflowArchitectureModel/src/application/editor/OutEdgeAttribute.java new file mode 100644 index 0000000..3d5c286 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/OutEdgeAttribute.java @@ -0,0 +1,18 @@ +package application.editor; + +import application.editor.Editor.SrcDstAttribute; +import models.EdgeAttribute; +import models.dataFlowModel.ResourceNode; + +public class OutEdgeAttribute extends SrcDstAttribute { + private ResourceNode dstRes; + + public OutEdgeAttribute(SrcDstAttribute attr, ResourceNode dstRes) { + super(attr.getSrouce(), attr.getDestination()); + this.dstRes = dstRes; + } + + public ResourceNode getDstResource() { + return dstRes; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/Stage.java b/AlgebraicDataflowArchitectureModel/src/application/editor/Stage.java new file mode 100644 index 0000000..ba5baba --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/Stage.java @@ -0,0 +1,78 @@ +package application.editor; + +import java.awt.event.MouseListener; + +import com.mxgraph.model.mxCell; +import com.mxgraph.swing.mxGraphComponent; +import com.mxgraph.swing.view.mxICellEditor; +import com.mxgraph.util.mxEventSource.mxIEventListener; +import com.mxgraph.view.mxGraph; + +import models.dataFlowModel.DataTransferModel; + +abstract public class Stage { + protected DataTransferModel model = null; + protected mxGraphComponent graphComponent = null; + protected mxGraph graph = null; + public static final int NODE_LAYER = 0; + public static final int DATA_FLOW_LAYER = 0; + public static final int PUSH_FLOW_LAYER = 1; + public static final int PULL_FLOW_LAYER = 2; + + /************************************************************* + * [ *constructor] + /************************************************************* + * + * @param graphComponent + */ + public Stage(mxGraphComponent graphComponent) { + this.graphComponent = graphComponent; + this.graph = graphComponent.getGraph(); + } + + /************************************************************* + * + * @return model + */ + public DataTransferModel getModel() { + return model; + } + + abstract public boolean canChangeFrom(Stage prevStage); + abstract public void init(Stage prevStage); + abstract public mxICellEditor createCellEditor(mxGraphComponent graphComponent); + abstract public mxIEventListener createChangeEventListener(Editor editor); + abstract public MouseListener createMouseEventListener(Editor editor); + + /************************************************************* + * [ *public ] + /************************************************************* + * + */ + public void setEnabledForLayer(final int layerNo, final boolean isEnable) { + mxCell rootCell = (mxCell) graph.getDefaultParent(); + if(rootCell== null) return; + if(rootCell.getChildCount() <= 0) return; + + graph.getModel().setVisible(rootCell.getChildAt(layerNo), isEnable); + graph.refresh(); + } + + /************************************************************* + * Showing layers are specified number of layers. + * @param you want to show numbers of layers. + */ + public void showOnlyLayer(final int... argsOfLayers) { + mxCell rootCell = (mxCell) graph.getDefaultParent(); + if(rootCell== null) return; + if(rootCell.getChildCount() <= 0) return; + + for(int i = 0; i < rootCell.getChildCount(); i++) { + graph.getModel().setVisible(rootCell.getChildAt(i), false); + } + + for(int layerNo : argsOfLayers) { + graph.getModel().setVisible(rootCell.getChildAt(layerNo), true); + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowDelegationCellEditor.java b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowDelegationCellEditor.java new file mode 100644 index 0000000..aee891e --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowDelegationCellEditor.java @@ -0,0 +1,188 @@ +package application.editor.stages; + +import java.util.EventObject; +import java.util.logging.Level; + +import javax.swing.CellEditor; +import javax.swing.JOptionPane; + +import com.mxgraph.model.mxCell; +import com.mxgraph.swing.mxGraphComponent; + +import application.ApplicationWindow; +import application.editor.FlowCellEditor; +import models.controlFlowModel.CallEdgeAttribute; +import models.controlFlowModel.EventChannelObjectNode; +import models.controlFlowModel.ObjectNode; +import models.controlFlowModel.ObjectNodeAttribute; +import models.controlFlowModel.StatefulObjectNode; + +/************************************************************* + * + */ +public class ControlFlowDelegationCellEditor extends FlowCellEditor { + private mxCell targetEdgeCell = null; + + /************************************************************* + * [ *constructor ] + /************************************************************* + * + * @param stage + * @param graphComponent + */ + public ControlFlowDelegationCellEditor(ControlFlowDelegationStage stage, mxGraphComponent graphComponent) { + super(stage, graphComponent); + } + + /************************************************************* + * [ *public ] + /************************************************************* + * + */ + @Override + public void startEditing(Object cellObj, EventObject eventObj) { + if (editingCell != null) stopEditing(true); + ControlFlowDelegationStage cfdStage = (ControlFlowDelegationStage) stage; + + switch (cfdStage.getCurState()) { + case SELECTING_AN_EDGE: + ApplicationWindow.logger.log(Level.INFO, "Double clicked"); + + if (graphComponent.getGraph().getModel().isEdge(cellObj)) { + + targetEdgeCell = (mxCell) cellObj; + + // Logging + CallEdgeAttribute callEdgeAttr = (CallEdgeAttribute) graphComponent.getGraph().getModel().getValue(targetEdgeCell); + if (callEdgeAttr == null) return; + + String pairNodeStr = "(" + callEdgeAttr.getSourceObjectNode().getName() + ", " + callEdgeAttr.getDestinationObjectNode().getName() + ")" ; + ApplicationWindow.logger.log(Level.INFO, "Selecting Control-Flow (src, dst) = " + pairNodeStr); + + showDelegatableNodesBySelectedEdge(cellObj); + cfdStage.setState(ControlFlowDelegationStageStatus.SHOWING_DELEGATABLE_NODES); + } + break; + + case SHOWING_DELEGATABLE_NODES: + if (graphComponent.getGraph().getModel().isVertex(cellObj)) { + mxCell dstCell = null; + if (cellObj instanceof mxCell) dstCell = (mxCell) cellObj; + else throw new ClassCastException(); + + CallEdgeAttribute callEdgeAttr = (CallEdgeAttribute) graphComponent.getGraph().getModel().getValue(targetEdgeCell); + if (callEdgeAttr == null) return; + + ObjectNode dstObjNode = ((ObjectNodeAttribute) dstCell.getValue()).getObjectNode(); + if (dstObjNode == null) throw new ClassCastException(); + + // dstObjNode はCFD適用可能なノードかどうか? + if (!cfdStage.isExecutableDelegation(callEdgeAttr, dstObjNode)) { + JOptionPane.showMessageDialog(graphComponent, "It's impossible for \"" + dstObjNode.getName() + "\" to delegate."); + ApplicationWindow.logger.log(Level.INFO, "\"" + dstObjNode.getName() + "\"" + " wasn't able to apply to CFD"); + return; + } + + // CFD実行 ⇒ 実行後のグラフを表示. + cfdStage.showDelegatedGraph(graphComponent.getGraph(), targetEdgeCell, (mxCell) cellObj); + ApplicationWindow.logger.log(Level.INFO, "Apply CFD to \"" + dstObjNode.getName() + "\""); + + cfdStage.setState(ControlFlowDelegationStageStatus.SELECTING_AN_EDGE); + } else { + // Logging + ApplicationWindow.logger.log(Level.INFO, "CFD was canceled because it was selected a control-flow"); + + cfdStage.resetAllStyleOfCells(); + cfdStage.setState(ControlFlowDelegationStageStatus.SELECTING_AN_EDGE); + } + break; + + case SHOWING_DEPENDENTABLE_NODES: + if (cfdStage.getCachedCell() == null ) return; + this.targetEdgeCell = cfdStage.getCachedCell(); + + if (graphComponent.getGraph().getModel().isVertex(cellObj)) { + mxCell targetObjCell = null; + if (cellObj instanceof mxCell) targetObjCell = (mxCell) cellObj; + else throw new ClassCastException(); + + ObjectNodeAttribute targetObjNodeAttr = (targetObjCell.getValue() instanceof ObjectNodeAttribute) + ?(ObjectNodeAttribute)targetObjCell.getValue() + :null; + if (targetObjNodeAttr == null) return; + + ObjectNode targetObjNode = targetObjNodeAttr.getObjectNode(); + + // 仲介者以外のオブジェクトを選択したときの例外処理 + if (isNotStatelessObject(targetObjNode)) { + JOptionPane.showMessageDialog(graphComponent, "It's impossible for \"" + targetObjNode.getName() + "\" to dependent."); + ApplicationWindow.logger.log(Level.INFO, "\"" + targetObjNode.getName() + "\"" + " wasn't able to apply to Depends on Mediator"); + return; + } + + // DoMを実行 + dependingObjectNode(targetObjCell); + + // Logging + ApplicationWindow.logger.log(Level.INFO, "Apply Depedents on Mediator to " + "\"" + targetObjNode.getName() + "\""); + + cfdStage.resetAllStyleOfCells(); + cfdStage.setState(ControlFlowDelegationStageStatus.SELECTING_AN_EDGE); + } else { + // Logging + ApplicationWindow.logger.log(Level.INFO, "Depends on Mediator was canceled because it was selected a control-flow"); + + cfdStage.resetAllStyleOfCells(); + cfdStage.setState(ControlFlowDelegationStageStatus.SELECTING_AN_EDGE); + } + break; + } + } + + /************************************************************* + * + */ + @Override + public void stopEditing(boolean cancel) { + } + + /************************************************************* + * [ *private ] + /************************************************************* + * view + ************************************************************** + * 選択されたエッジが, CFDによって呼び出し元にできるオブジェクトを表示する. + * @param cellObj コントロールフローのセル + */ + private void showDelegatableNodesBySelectedEdge(Object cellObj) { + CallEdgeAttribute callEdgeAttr = (CallEdgeAttribute) graphComponent.getGraph().getModel().getValue(cellObj); + if (callEdgeAttr == null) + return; + + ControlFlowDelegationStage cfdStage = (ControlFlowDelegationStage) stage; + cfdStage.showDelegatableNodes(callEdgeAttr); + } + + /************************************************************ + * DoMを実行する. + * @param targetMediatorCell 選択された仲介者オブジェクト + */ + private void dependingObjectNode(final mxCell targetMediatorCell) { + if (targetMediatorCell == null) return; + + ControlFlowDelegationStage cfdStage = (ControlFlowDelegationStage) stage; + cfdStage.dependingOnMediatorObject(targetEdgeCell, targetMediatorCell); + } + + /************************************************************ + * あるノードが仲介者(状態を持たない)オブジェクトかどうかを判定する. + * @param targetObjNode 選択されたノード + */ + private boolean isNotStatelessObject(final ObjectNode targetObjNode) { + if (targetObjNode instanceof StatefulObjectNode) return true; + if (targetObjNode instanceof EventChannelObjectNode) return true; + + return false; + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowDelegationStage.java b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowDelegationStage.java new file mode 100644 index 0000000..aa60245 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowDelegationStage.java @@ -0,0 +1,825 @@ +package application.editor.stages; + +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; + +import javax.swing.SwingUtilities; + +import com.mxgraph.model.mxCell; +import com.mxgraph.model.mxGeometry; +import com.mxgraph.model.mxGraphModel; +import com.mxgraph.swing.mxGraphComponent; +import com.mxgraph.swing.util.mxMouseAdapter; +import com.mxgraph.util.mxEventSource.mxIEventListener; +import com.mxgraph.util.mxEventObject; +import com.mxgraph.util.mxPoint; +import com.mxgraph.view.mxGraph; + +import application.ApplicationWindow; +import application.editor.Editor; +import application.editor.FlowCellEditor; +import application.editor.Stage; +import application.views.PopupMenuBase; +import application.views.controlFlowDelegation.ControlFlowDelegationStagePopupMenu; +import models.Edge; +import models.Node; +import models.controlFlowModel.CallEdge; +import models.controlFlowModel.CallEdgeAttribute; +import models.controlFlowModel.CallGraph; +import models.controlFlowModel.CompositeCallEdgeAttribute; +import models.controlFlowModel.ControlFlowDelegator; +import models.controlFlowModel.ControlFlowGraph; +import models.controlFlowModel.EventChannelObjectNode; +import models.controlFlowModel.ObjectNode; +import models.controlFlowModel.ObjectNodeAttribute; +import models.controlFlowModel.StatefulObjectNode; +import models.dataFlowModel.DataFlowGraph; +import models.dataFlowModel.PushPullValue; +import models.dataFlowModel.ResourceNode; +import models.dataFlowModel.ResourceNodeAttribute; + +/************************************************************* + * + */ +public class ControlFlowDelegationStage extends Stage { + public final int PORT_DIAMETER = 8; + public final int PORT_RADIUS = PORT_DIAMETER / 2; + + private ControlFlowDelegationStageStatus curState = null; + + private ControlFlowGraph controlFlowGraph = null; + private PopupMenuBase popupMenu = null; + + private mxCell selectedCellOnAnyEvent = null; + + /************************************************************* + * [ *constructor ] + /************************************************************* + * + */ + public ControlFlowDelegationStage(mxGraphComponent graphComoponent) { + super(graphComoponent); + this.curState = ControlFlowDelegationStageStatus.SELECTING_AN_EDGE; + this.popupMenu = new ControlFlowDelegationStagePopupMenu(this, graphComoponent); + } + + /************************************************************* + * [ *public ] + /************************************************************* + * getters / setters + /************************************************************* + */ + public ControlFlowGraph getControlFlowGraph() { + return controlFlowGraph; + } + + /************************************************************* + * 今のコントロールフローの操作状態がどうなっているかを取得 + */ + public ControlFlowDelegationStageStatus getCurState() { + return curState; + } + + /************************************************************* + * 今のコントロールフローの操作状態をセット. + */ + public void setState(ControlFlowDelegationStageStatus nextState) { + curState = nextState; + } + + + /************************************************************* + * 「Stage」クラス以外の, 他のクラス上で取得されたセルをセット + */ + public void setCellOnAnyEvent(final mxCell selectedCell) { + this.selectedCellOnAnyEvent = selectedCell; + } + + /************************************************************* + * 「Stage」クラス以外の, 他のクラス上で取得されたセルをget + * Action や CellEditor とセルのインスタンス情報を共有するために必要. + * (this.seletedCellOnAnyEvent == null) ならグラフ上のどのセルも選択されていないことになる. + */ + public mxCell getCachedCell() { + return this.selectedCellOnAnyEvent; + } + + /************************************************************* + * + */ + @Override + public boolean canChangeFrom(Stage prevStage) { + if (prevStage instanceof PushPullSelectionStage) return true; + return false; + } + + /************************************************************* + * + */ + @Override + public void init(Stage prevStage) { + if (prevStage instanceof PushPullSelectionStage) { + model = ((PushPullSelectionStage) prevStage).getModel(); + + DataFlowGraph dataFlowGraph = ((PushPullSelectionStage) prevStage).getDataFlowGraph(); + + controlFlowGraph = new ControlFlowGraph(dataFlowGraph, model); + + clearControlFlowGraphCells(graph); + graph = constructGraph(graph, controlFlowGraph); + } + } + + + /************************************************************* + * + */ + @Override + public FlowCellEditor createCellEditor(mxGraphComponent graphComponent) { + return new ControlFlowDelegationCellEditor(this, graphComponent); + } + + /************************************************************* + * ステージ変更時のリスナー作成 + * @param editor + * @return GUI上のイベントリスナー + */ + @Override + public mxIEventListener createChangeEventListener(Editor editor) { + return new mxIEventListener() { + public void invoke(Object sender, mxEventObject evt) { + List terminals = new ArrayList<>(); + mxCell cell = null; + for (Object change: ((List) evt.getProperties().get("changes"))) { + if (change instanceof mxGraphModel.mxTerminalChange) { + mxGraphModel.mxTerminalChange terminalChange = (mxGraphModel.mxTerminalChange) change; + cell = (mxCell) terminalChange.getCell(); + mxCell terminal = (mxCell) terminalChange.getTerminal(); + terminals.add(terminal); + } + } + + if (curState != ControlFlowDelegationStageStatus.SELECTING_AN_EDGE) return; + + if (terminals.size() == 2) { + graph.removeCells(new mxCell[] {cell}); + graph.clearSelection(); + } + } + }; + } + + /************************************************************* + * ステージ変更時のマウスイベントの発行 + * @param editor + */ + @Override + public MouseListener createMouseEventListener(Editor editor) { + MouseListener listener = new mxMouseAdapter() { + @Override + public void mouseReleased(MouseEvent e) { + if (SwingUtilities.isLeftMouseButton(e)) { + if (graphComponent.getCellAt(e.getX(), e.getY()) != null) return; + if (curState.equals(ControlFlowDelegationStageStatus.SELECTING_AN_EDGE)) return; + + ApplicationWindow.logger.log(Level.INFO, "Reset selection"); + resetAllStyleOfCells(); + curState = ControlFlowDelegationStageStatus.SELECTING_AN_EDGE; + + return; + } + else if (SwingUtilities.isRightMouseButton(e)) { + ApplicationWindow.logger.log(Level.INFO, "Open popup menu"); + popupMenu.show(e.getX(), e.getY()); + } + } + }; + return listener; + } + + /************************************************************* + * コントロールフローグラフの操作 + /************************************************************* + * CFDが適用できるノードの表示 + * @param callEdgeAttr 選択された呼び出しエッジの情報 + */ + public void showDelegatableNodes(final CallEdgeAttribute callEdgeAttr){ + mxCell root = (mxCell)graph.getDefaultParent(); + graph.getModel().beginUpdate(); + try { + ObjectNode delegatingNode = callEdgeAttr.getDestinationObjectNode(); + for (int layerNo = Stage.PUSH_FLOW_LAYER; layerNo <= PULL_FLOW_LAYER; layerNo++) { + + mxCell layerCell = (mxCell)root.getChildAt(layerNo); + for (Object node : graph.getChildVertices(layerCell)) { + if ( !(node instanceof mxCell) ) continue; + mxCell cell = (mxCell)node; + + ObjectNodeAttribute objNodeAttr = (ObjectNodeAttribute)cell.getValue(); + if (objNodeAttr == null) return; + + ObjectNode objNode = objNodeAttr.getObjectNode(); + ControlFlowDelegator delegator = new ControlFlowDelegator(controlFlowGraph); + List delegatableNodes = delegator.searchDelegatableNodes(callEdgeAttr.getCallEdge()); + + if (delegatableNodes.contains(objNode)) { + graph.getModel().setStyle(cell, objNodeAttr.getEnableStyle()); + } + else { + graph.getModel().setStyle(cell, objNodeAttr.getDisableStyle()); + } + + if (delegatingNode.equals(objNodeAttr.getObjectNode())) + /* base-Node*/graph.getModel().setStyle(cell, objNodeAttr.getDelegatingStyle()); + } + } + } + finally { + graph.getModel().endUpdate(); + graph.refresh(); + } + } + + /************************************************************* + * DoMを実行可能なノードの表示 + * @param callEdgeAttr 選択された呼び出しエッジの情報 + */ + public void showDependentableNodes(final CallEdgeAttribute callEdgeAttr) { + mxCell root = (mxCell)graph.getDefaultParent(); + mxCell layerCell = (mxCell)root.getChildAt(PULL_FLOW_LAYER); + graph.getModel().beginUpdate(); + try { + ObjectNode delegatingNode = callEdgeAttr.getDestinationObjectNode(); + + for(Object node : graph.getChildVertices(layerCell)) { + if ( !(node instanceof mxCell) ) continue; + mxCell cell = (mxCell)node; + + ObjectNodeAttribute objNodeAttr = (ObjectNodeAttribute)cell.getValue(); + if (objNodeAttr == null) return; + + ObjectNode objNode = objNodeAttr.getObjectNode(); + ControlFlowDelegator delegator = new ControlFlowDelegator(controlFlowGraph); + List delegatableNodes = delegator.searchDependentableMediatorNodes(callEdgeAttr.getCallEdge()); + + if (delegatableNodes.contains(objNode)) { + graph.getModel().setStyle(cell, objNodeAttr.getEnableStyle()); + } + else { + graph.getModel().setStyle(cell, objNodeAttr.getDisableStyle()); + } + + if (delegatingNode.equals(objNodeAttr.getObjectNode())) + /* base-Node*/graph.getModel().setStyle(cell, objNodeAttr.getDelegatingStyle()); + } + } + finally { + graph.getModel().endUpdate(); + graph.refresh(); + } + } + + /************************************************************* + * CFD実行後のグラフを表示 + * @param graph 被操作対象のグラフ + * @param targetEdgeCell CFD実行時に選択された呼び出しエッジ + * @param dstObjNodeCell CFDによって呼び出し元となるセル + */ + public void showDelegatedGraph(mxGraph graph, mxCell targetEdgeCell, final mxCell dstObjNodeCell) { + ObjectNode dstObjNode = ((ObjectNodeAttribute)dstObjNodeCell.getValue()).getObjectNode(); + if (dstObjNode == null) throw new ClassCastException(); + CallEdgeAttribute targetEdgeAttr = (CallEdgeAttribute)targetEdgeCell.getValue(); + if (targetEdgeAttr == null) throw new ClassCastException(); + + ControlFlowDelegator delegator = new ControlFlowDelegator(controlFlowGraph); + delegator.delegateCallEdge(targetEdgeAttr.getCallEdge(), dstObjNode); + + mxCell root = (mxCell) graph.getDefaultParent(); + mxCell layerCell = null; + switch (targetEdgeAttr.getSelectedOption()) { + case PUSH: + layerCell = (mxCell) root.getChildAt (Stage.PUSH_FLOW_LAYER); + break; + + case PULL: + case PUSHorPULL: + layerCell = (mxCell) root.getChildAt (Stage.PULL_FLOW_LAYER); + break; + } + + try { + mxCell dstNodeCell = targetEdgeAttr.getDestinationCell(); + + // 対象となるコントロールフローをグラフ上から削除. + if (graph.getModel().getValue(targetEdgeCell) != null) { + graph.getModel().remove(targetEdgeCell); + } + + CallEdgeAttribute newAttr = new CallEdgeAttribute(targetEdgeAttr.getCallEdge(), targetEdgeAttr.getOriginalSourceObjectNode(),dstObjNodeCell, dstNodeCell); + graph.insertEdge(layerCell, "", newAttr, dstObjNodeCell, dstNodeCell, "movable=false;"); + + resetAllStyleOfCells(); + } + finally { + graph.getModel().endUpdate(); + } + } + + /************************************************************* + * 仲介者オブジェクトへの依存(DoM)を実行し, mxGraphを変更する. + * @param targetEdgeCell DoMによって変更されるコントロールフローのセル + * @param targetMediatorObjNodeCell DoMの対象となる仲介者オブジェクトのセル + */ + public void dependingOnMediatorObject(mxCell targetEdgeCell, mxCell targetMediatorObjNodeCell) { + CallEdgeAttribute targetCallEdgeAttr = (CallEdgeAttribute)targetEdgeCell.getValue(); + if (targetCallEdgeAttr == null) return; + + mxCell root = (mxCell)graph.getDefaultParent(); + mxCell pullFlowLayerCell = (mxCell)root.getChildAt(Stage.PULL_FLOW_LAYER); + + graph.getModel().beginUpdate(); + + try { + // 各ノードの情報を取得. + ObjectNode srcObjNode = targetCallEdgeAttr.getSourceObjectNode(); + ObjectNode dstObjNode = targetCallEdgeAttr.getDestinationObjectNode(); + if (srcObjNode == null || dstObjNode == null) throw new NullPointerException(); + if ( !(srcObjNode instanceof ObjectNode && dstObjNode instanceof ObjectNode) ) throw new ClassCastException(); + + ObjectNodeAttribute targetObjNodeAttr = (ObjectNodeAttribute)targetMediatorObjNodeCell.getValue(); + if (targetObjNodeAttr == null) throw new NullPointerException(); + + ObjectNode targetMediatorObjNode = targetObjNodeAttr.getObjectNode(); + if (targetMediatorObjNode == null) throw new NullPointerException(); + if (targetMediatorObjNode instanceof StatefulObjectNode || targetMediatorObjNode instanceof EventChannelObjectNode) return; + + // 新たに接続するエッジ情報を生成. + CallEdge srcToMediatorEdge = new CallEdge(targetCallEdgeAttr.getSourceObjectNode(), targetMediatorObjNode, targetCallEdgeAttr.getSelectedOption()); + CallEdge mediatorToDstEdge = new CallEdge(targetMediatorObjNode, dstObjNode, targetCallEdgeAttr.getSelectedOption()); + if (srcToMediatorEdge == null || mediatorToDstEdge == null) throw new NullPointerException(); + + // ノード同士の接続情報を更新 + srcObjNode.addOutEdge(srcToMediatorEdge); + srcObjNode.removeOutEdge(targetCallEdgeAttr.getCallEdge()); + + dstObjNode.removeInEdge(targetCallEdgeAttr.getCallEdge()); + + targetMediatorObjNode.addInEdge(srcToMediatorEdge); + + // 既に仲介者に接続されているエッジの情報を CompositeCallEdgeAttr に保持させる. + CompositeCallEdgeAttribute compositeEdgeAttr = null; + + for (int i = 0; i < pullFlowLayerCell.getChildCount(); i++) { + mxCell edgeCell = (mxCell)pullFlowLayerCell.getChildAt(i); + if ( !edgeCell.isEdge() ) continue; + if ( !(edgeCell.getValue() instanceof CallEdgeAttribute) ) continue; + + CallEdgeAttribute edgeAttr = (CallEdgeAttribute)edgeCell.getValue(); + + if ( !(edgeAttr.getSourceObjectNode().equals(targetMediatorObjNode)) )continue; + if ( !(edgeAttr.getDestinationObjectNode().equals(dstObjNode)) )continue; + + compositeEdgeAttr = new CompositeCallEdgeAttribute(edgeAttr); + + break; + } + + if (compositeEdgeAttr != null) + compositeEdgeAttr.mergeCallEdgeAttribute(targetCallEdgeAttr); + + // mxGraph へ接続状態を反映する. + for (int i = 0; i < pullFlowLayerCell.getChildCount(); i++) { + mxCell nodeCell = (mxCell)pullFlowLayerCell.getChildAt(i); + if ( !nodeCell.isVertex() ) continue; + + ObjectNodeAttribute cellObjNodeAttr = (ObjectNodeAttribute)nodeCell.getValue(); + if (cellObjNodeAttr == null) throw new ClassCastException("dosen't have the value of "); + + // nodeCell が呼び出し元のノードか? + if (nodeCell.equals(targetCallEdgeAttr.getSourceCell())){ + mxCell srcNodeCell = targetCallEdgeAttr.getSourceCell(); + + // mxGraph に変更対象のコントロールフローが存在するなら削除する. + if (graph.getModel().getValue(targetEdgeCell) != null) + graph.getModel().remove(targetEdgeCell); + + graph.insertEdge(pullFlowLayerCell, null, compositeEdgeAttr, srcNodeCell, targetMediatorObjNodeCell, "movable=false;"); + continue; + } + + } + } + finally { + graph.getModel().endUpdate(); + } + } + + /************************************************************* + * グラフの選択状態をクリアする. + */ + public void resetAllStyleOfCells() { + mxCell root = (mxCell)graph.getDefaultParent(); + + graph.getModel().beginUpdate(); + try { + for (int layerNo = Stage.PUSH_FLOW_LAYER; layerNo <= Stage.PULL_FLOW_LAYER; layerNo++) { + + mxCell layerCell = (mxCell)root.getChildAt(layerNo); + for (Object node : graph.getChildVertices(layerCell)) { + mxCell cell = null; + if (node instanceof mxCell) cell = (mxCell)node; + else continue; + + ObjectNodeAttribute objNodeAttr = (ObjectNodeAttribute)(cell.getValue()); + if (objNodeAttr == null) throw new NullPointerException(""); + + graph.getModel().setStyle(cell, objNodeAttr.getDefaultStyle()); + } + } + } + finally { + graph.getModel().endUpdate(); + graph.refresh(); + } + } + + /************************************************************* + * コントロールフローに対して, 状態を持たないノードを挿入する. + * @param targetEdge CFDによって変更されるコントロールフロー. + * @param insertObjName 挿入するノードの名前 + */ + public void insertObjectNodeCellInControlFlowLayer(mxCell targetEdge, final String insertObjName) { + CallEdgeAttribute callEdgeAttr = (CallEdgeAttribute)targetEdge.getValue(); + if (callEdgeAttr == null) throw new NullPointerException(); + + mxCell root = (mxCell)graph.getDefaultParent(); + mxCell layerCell = null; + switch(callEdgeAttr.getSelectedOption()) { + case PUSH: + layerCell = (mxCell)root.getChildAt(Stage.PUSH_FLOW_LAYER); + break; + + case PULL: + case PUSHorPULL: + layerCell = (mxCell)root.getChildAt(Stage.PULL_FLOW_LAYER); + break; + } + + graph.getModel().beginUpdate(); + + try { + // オブジェクトは ObjectNode として挿入する. + ObjectNode insertObjNode = new ObjectNode(insertObjName); + ObjectNodeAttribute objNodeAttr = new ObjectNodeAttribute(insertObjNode); + + mxPoint srcPoint = new mxPoint(callEdgeAttr.getSourceCell().getGeometry().getX(), callEdgeAttr.getSourceCell().getGeometry().getY()); + mxPoint dstPoint = new mxPoint(callEdgeAttr.getDestinationCell().getGeometry().getX(), callEdgeAttr.getDestinationCell().getGeometry().getY()); + mxPoint insertPoint = new mxPoint( + (srcPoint.getX() + dstPoint.getX())/2, + (srcPoint.getY() + dstPoint.getY())/2); + + mxCell insertObjNodeCell = + (mxCell)graph.insertVertex(layerCell, null, objNodeAttr, + /* 座標 */ insertPoint.getX(), insertPoint.getY(), + /* スケール*/ 40, 40, + objNodeAttr.getDefaultStyle()); + insertObjNodeCell.setValue(objNodeAttr); + + addObjectNodeToCallGraph(insertObjNode, callEdgeAttr.getSelectedOption()); + + ObjectNode srcObjNode = callEdgeAttr.getSourceObjectNode(); + ObjectNode dstObjNode = callEdgeAttr.getDestinationObjectNode(); + if (srcObjNode == null || dstObjNode == null) throw new NullPointerException(); + if ( !(srcObjNode instanceof ObjectNode && dstObjNode instanceof ObjectNode) ) throw new ClassCastException(); + + CallEdge srcToInsertEdge = callEdgeAttr.getCallEdge(); + CallEdge insertToDstEdge = new CallEdge(insertObjNode, dstObjNode, callEdgeAttr.getSelectedOption()); + if (srcToInsertEdge == null || insertToDstEdge == null) throw new NullPointerException(); + + // ノードとエッジを再接続する + dstObjNode.removeInEdge(srcToInsertEdge); + dstObjNode.addInEdge(insertToDstEdge); + + srcToInsertEdge.setDestination(insertObjNode); // 呼び出し先オブジェクトを変更する. + + insertObjNode.addInEdge(srcToInsertEdge); + insertObjNode.addOutEdge(insertToDstEdge); + + // mxGraphを更新する. + for(int i =0; i < layerCell.getChildCount(); i++) { + mxCell nodeCell = (mxCell)layerCell.getChildAt(i); + if (!nodeCell.isVertex()) continue; + + ObjectNodeAttribute cellObjNodeAttr = (ObjectNodeAttribute)nodeCell.getValue(); + if (cellObjNodeAttr == null) throw new ClassCastException("dosen't have the value of "); + + // nodeCell が呼び出し元のオブジェクトか? + if (nodeCell.equals(callEdgeAttr.getSourceCell())){ + mxCell srcNodeCell = callEdgeAttr.getSourceCell(); + CallEdgeAttribute newInEdgeAttr = new CallEdgeAttribute(srcToInsertEdge, srcNodeCell, insertObjNodeCell); + + // mxGraph上にtargetEdgeが残っているなら削除する. + if (graph.getModel().getValue(targetEdge) != null) + graph.getModel().remove(targetEdge); + + mxCell outPortCell = (mxCell)srcNodeCell.getChildAt(0); + if + (outPortCell != null) { + graph.insertEdge(layerCell, null, newInEdgeAttr, outPortCell, insertObjNodeCell, "movable=false;"); + } + else { + graph.insertEdge(layerCell, null, newInEdgeAttr, srcNodeCell, insertObjNodeCell, "movable=false;"); + } + continue; + } + // nodeCell は呼び出し先オブジェクトか? + else if (nodeCell.equals(callEdgeAttr.getDestinationCell())) { + mxCell dstNodeCell = callEdgeAttr.getDestinationCell(); + CallEdgeAttribute newOutEdgeAttr = new CallEdgeAttribute(insertToDstEdge, insertObjNodeCell, dstNodeCell); + + // mxGraph上にtargetEdgeが残っているなら削除する. + if (graph.getModel().getValue(targetEdge) != null) + graph.getModel().remove(targetEdge); + + graph.insertEdge(layerCell, null, newOutEdgeAttr, insertObjNodeCell, dstNodeCell, "movable=false;"); + + continue; + } + } + } + finally { + graph.getModel().endUpdate(); + } + } + + /************************************************************* + * CFD適用可能かどうかを判定する + * @param targetEdgeAttr CFDによって呼び出し元が変更されるコントロールフロー + * @param dstObjNode CFDによって呼び出し元となるオブジェクト + */ + public boolean isExecutableDelegation(final CallEdgeAttribute targetEdgeAttr, final ObjectNode dstObjNode) { + ControlFlowDelegator delegator = new ControlFlowDelegator(controlFlowGraph); + List delegatableNodes = delegator.searchDelegatableNodes(targetEdgeAttr.getCallEdge()); + + return delegatableNodes.contains(dstObjNode); + } + + /************************************************************* + /************************************************************* + * [ *private ] + /************************************************************* + * GUI上のグラフを生成する + * @param graph 初期化対象のグラフ + * @param controlFlowGraph + */ + private mxGraph constructGraph(mxGraph graph, ControlFlowGraph controlFlowGraph) { + showOnlyLayer(PUSH_FLOW_LAYER, PULL_FLOW_LAYER); + + graph.getModel().beginUpdate(); + try { + // PUSH/PULLごとに ResourceNode, mxCellのインスタンスを対応させる. + Map pushResNodeCells = createCellsOfResourceMap(PUSH_FLOW_LAYER); + Map pullResNodeCells = createCellsOfResourceMap(PULL_FLOW_LAYER); + + Map pushFlowEventNodeCells = createEventChannelCells(PUSH_FLOW_LAYER); + + // PUSH/PULLのmxGraph上の頂点をエッジでつなぐ. + graph = insertControlFlowEdges(PUSH_FLOW_LAYER, pushResNodeCells, pushFlowEventNodeCells); + graph = insertControlFlowEdges(PULL_FLOW_LAYER, pullResNodeCells, null); + + } finally { + graph.getModel().endUpdate(); + } + return graph; + } + + + /************************************************************* + * データフローモデリングステージへ戻るとき, GUI上のコントローフローグラフを削除する. + * @param graph 変更対象のグラフ + */ + private void clearControlFlowGraphCells(mxGraph graph) { + mxCell root = (mxCell) graph.getDefaultParent(); + + graph.getModel().beginUpdate(); + try { + // Rootのレイヤー以下のPUSH/PULLレイヤーを削除. + root.remove (root.getChildAt(PULL_FLOW_LAYER)); + root.remove (root.getChildAt(PUSH_FLOW_LAYER)); + + root.insert (new mxCell()); + root.insert (new mxCell()); + } finally { + graph.getModel().endUpdate(); + graph.refresh(); + } + } + + + /************************************************************* + * mxGraphの指定したレイヤー上にリソースのノードを追加する. + * また, mxGraph上のセルにリソースの情報をマッピングする. + * @param layerNumber mxCellを追加する, PUSH/PULLのレイヤー番号 + * @return ノード(リソース)の情報と, 対応するセルのインスタンスのマップ + */ + private Map createCellsOfResourceMap(final int layerNumber) { + Map resNodeCells = new HashMap<>(); + + mxCell root = (mxCell)graph.getDefaultParent(); + mxCell nodeLayerCell = (mxCell)root.getChildAt(NODE_LAYER); + mxCell layerCell = (mxCell)root.getChildAt(layerNumber); + + // データフローグラフ上に存在するリソースの情報をコントロールフローグラフに引き継ぐ. + for (ResourceNode resNode : controlFlowGraph.getDataFlowGraph().getResouceNodes()) { + + // 「リソース」に対応するノードのインスタンスを生成. + ObjectNode objNode = null; + switch (layerNumber) { + case PUSH_FLOW_LAYER: + if (controlFlowGraph.getPushCallGraph().getStatefulObjectNode(resNode) != null) + objNode = controlFlowGraph.getPushCallGraph().getStatefulObjectNode(resNode); + break; + + case PULL_FLOW_LAYER: + if (controlFlowGraph.getPullCallGraph().getStatefulObjectNode(resNode) != null) + objNode = controlFlowGraph.getPullCallGraph().getStatefulObjectNode(resNode); + break; + } + + if (objNode == null) continue; + + for(int i =0; i < nodeLayerCell.getChildCount(); i++) { + mxCell nodeCell = (mxCell)nodeLayerCell.getChildAt(i); + if( nodeCell.getValue() instanceof ResourceNodeAttribute ) { + nodeCell = (mxCell)nodeLayerCell.getChildAt(i); + } + else continue; + + // !不要かもしれない! + // ResourceNodeがちゃんとAttributeを持っているかをチェックして, 不正値検出しようとしてるみたい. + ResourceNodeAttribute resNodeAttr = (ResourceNodeAttribute) nodeCell.getValue(); + if (!resNodeAttr.getResourceNode().equals(resNode) ) continue; + + // ノードの情報をmxCellにマッピング. + ObjectNodeAttribute objNodeAttr = new ObjectNodeAttribute(objNode); + + mxCell resNodeObjCell = (mxCell)graph.insertVertex (layerCell, null, objNodeAttr, + /* サイズ */nodeCell.getGeometry().getX(), nodeCell.getGeometry().getY(), + /* 座標 */nodeCell.getGeometry().getWidth(), nodeCell.getGeometry().getHeight(), + objNodeAttr.getDefaultStyle()); + + resNodeCells.put(resNode, resNodeObjCell); + } + } + + return resNodeCells; + } + + + /************************************************************* + * mxGraphの指定したレイヤー上にイベントチャンネルのノードを追加する. + * また, mxGraph上のセルにイベントチャンネルの情報をマッピングする. + * @param layerNumber 指定したレイヤー(現状PULLは例外扱いしている) + * @return イベントチャンネルの情報と, 対応するセルのインスタンスのマップ + */ + private Map createEventChannelCells(final int layerNumber){ + if (layerNumber == PULL_FLOW_LAYER) return null; + + mxCell root = (mxCell)graph.getDefaultParent(); + mxCell layerCell = (mxCell)root.getChildAt(layerNumber); + + Map eventChannelCells = new HashMap<>(); + + graph.getModel().beginUpdate(); + try { + mxGeometry outPortGeometry = new mxGeometry (1.0, 0.5, PORT_DIAMETER, PORT_DIAMETER); + outPortGeometry.setOffset (new mxPoint(-PORT_RADIUS, -PORT_RADIUS)); + outPortGeometry.setRelative (true); + + CallGraph callGraph = controlFlowGraph.getPushCallGraph(); + + // イベントチャンネルのセルを追加 + for (Node node : callGraph.getNodes()) { + EventChannelObjectNode entryPointObjNode = null; + if (node instanceof EventChannelObjectNode) + entryPointObjNode = (EventChannelObjectNode) node; + else continue; + + ObjectNodeAttribute entryObjAttr = new ObjectNodeAttribute(entryPointObjNode); + + // イベントチャンネルの名前と座標値をデータフローグラフから取得. + mxCell dataFlowLayerCell = (mxCell)root.getChildAt(Stage.DATA_FLOW_LAYER); + for (int i = 0; i < dataFlowLayerCell.getChildCount(); i++) { + mxCell channelCell =(mxCell)dataFlowLayerCell.getChildAt(i); + + String eventChObjNodeName = entryPointObjNode.getIOChannel().getChannelName(); + String channelCellName = ""; + if (channelCell.getValue() instanceof String) channelCellName = (String) channelCell.getValue(); + else continue; + + if (!eventChObjNodeName.equals(channelCellName)) continue; + + mxCell entryPointCelll = (mxCell) graph.insertVertex (layerCell, null, entryObjAttr, + /* スケール */ channelCell.getGeometry().getX(), channelCell.getGeometry().getY(), + /* 座標 */ channelCell.getGeometry().getWidth(), channelCell.getGeometry().getHeight()); + mxCell port_out = new mxCell (null, outPortGeometry, "shape=ellipse;perimter=ellipsePerimeter"); + port_out.setVertex (true); + + graph.addCell (port_out, entryPointCelll); + eventChannelCells.put (entryPointObjNode, entryPointCelll); + } + } + } + finally { + graph.getModel().endUpdate(); + } + + return eventChannelCells; + } + + + /************************************************************* + * コントロールフローグラフの各頂点セルの間に, エッジのセルを追加する. + * @param layerNumber エッジを追加したいmxGraphのレイヤー + * @param resNodeCells リソースと, その頂点セルのインスタンスのマップ + * @param eventChNodeCells イベントチャンネルと, その頂点セルのインスタンスのマップ + */ + private mxGraph insertControlFlowEdges(final int layerNumber, + final Map resNodeCells, final Map eventChNodeCells) { + mxCell root = (mxCell)graph.getDefaultParent(); + mxCell layerCell = (mxCell)root.getChildAt(layerNumber); + + CallGraph callGraph = (layerNumber == PUSH_FLOW_LAYER) + ? controlFlowGraph.getPushCallGraph() + : controlFlowGraph.getPullCallGraph(); + + for (Edge callGraphEdge : callGraph.getEdges()) { + if ( !(callGraphEdge instanceof CallEdge))continue; + CallEdge callEdge = (CallEdge) callGraphEdge; + + // エッジがノードと接続しているか? + if (callEdge.getSource() == null || callEdge.getDestination() == null) continue; + + Node srcResNode = null; + ResourceNode dstResNode = ((StatefulObjectNode)callEdge.getDestination()).getResource(); + + mxCell srcNodeCell =null; + mxCell srcOutPortCell = null; + mxCell dstNodeCell = resNodeCells.get(dstResNode); + + if (callEdge.getSource() instanceof StatefulObjectNode) { + srcResNode = ((StatefulObjectNode)callEdge.getSource()).getResource(); + srcNodeCell =resNodeCells.get(srcResNode); + } + else if (callEdge.getSource() instanceof EventChannelObjectNode) { + srcResNode = (EventChannelObjectNode)callEdge.getSource(); + srcNodeCell = eventChNodeCells.get(srcResNode); + srcOutPortCell = (mxCell)srcNodeCell.getChildAt(0); + } + else continue; + + if(srcNodeCell == null || dstNodeCell == null) continue; + + CallEdgeAttribute callEdgeAttr = new CallEdgeAttribute(callEdge, (ObjectNode)callEdge.getSource(), srcNodeCell, dstNodeCell); + + if (srcResNode instanceof ResourceNode) { + graph.insertEdge(layerCell, null, callEdgeAttr, srcNodeCell, dstNodeCell, "movable=false;"); + } + // イベントチャンネルはoutPortのセルの座標を参照. + else if (srcResNode instanceof EventChannelObjectNode) { + graph.insertEdge(layerCell, null, callEdgeAttr, srcOutPortCell, dstNodeCell, "movable=false;"); + } + + } + return graph; + } + + + /************************************************************* + * 状態を持たないオブジェクトをコントロールフローグラフに追加する. + * @param insertObjNode 追加したいオブジェクトの情報 + * @param selectedOption コントロールフローのデータ転送方式 + */ + private void addObjectNodeToCallGraph(final ObjectNode insertObjNode, final PushPullValue selectedOption) { + switch (selectedOption) { + case PUSH: + if (controlFlowGraph.getPushCallGraph().getNodes().contains(insertObjNode)) return; + controlFlowGraph.getPushCallGraph().addNode(insertObjNode); + + break; + + case PULL: + case PUSHorPULL: + if (controlFlowGraph.getPullCallGraph().getNodes().contains(insertObjNode)) return; + controlFlowGraph.getPullCallGraph().addNode(insertObjNode); + + break; + } + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowDelegationStageStatus.java b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowDelegationStageStatus.java new file mode 100644 index 0000000..9e7ddb2 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/ControlFlowDelegationStageStatus.java @@ -0,0 +1,10 @@ +package application.editor.stages; + +/************************************************************* + * + */ +public enum ControlFlowDelegationStageStatus { + SELECTING_AN_EDGE, // いずれかのエッジを選ぼうとしている状態(Idle状態) + SHOWING_DELEGATABLE_NODES, // CFD適用可能範囲を表示している状態 + SHOWING_DEPENDENTABLE_NODES // DoM適用可能範囲を表示している状態 +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DataFlowCellEditor.java b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DataFlowCellEditor.java new file mode 100644 index 0000000..a893a85 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DataFlowCellEditor.java @@ -0,0 +1,152 @@ +package application.editor.stages; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Rectangle; +import java.util.EventObject; +import java.util.List; + +import javax.swing.BorderFactory; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JTextArea; +import javax.swing.JTextField; + +import com.mxgraph.model.mxCell; +import com.mxgraph.model.mxIGraphModel; +import com.mxgraph.swing.mxGraphComponent; +import com.mxgraph.swing.view.mxICellEditor; +import com.mxgraph.util.mxConstants; +import com.mxgraph.util.mxUtils; +import com.mxgraph.view.mxCellState; + +import application.editor.Editor; +import application.editor.FlowCellEditor; +import models.algebra.Expression; +import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.PushPullAttribute; +import models.dataFlowModel.PushPullValue; +import models.visualModel.FormulaChannel; +import parser.Parser; +import parser.Parser.TokenStream; +import parser.exceptions.ExpectedColon; +import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.WrongJsonExpression; + +/************************************************************* + * + */ +public class DataFlowCellEditor extends FlowCellEditor { + + /************************************************************* + * [ *constructor ] + /************************************************************* + * + * @param stage + * @param graphComponent + */ + public DataFlowCellEditor(DataFlowModelingStage stage, mxGraphComponent graphComponent) { + super(stage, graphComponent); + } + + /************************************************************* + * + * @param cellObj + * @param eventObj + */ + @Override + public void startEditing(Object cellObj, EventObject eventObj) { + if (editingCell != null) { + stopEditing(true); + } + + if (!graphComponent.getGraph().getModel().isEdge(cellObj)) { + DataTransferChannel ch = (DataTransferChannel) stage.getModel().getChannel((String) ((mxCell) cellObj).getValue()); + if (ch == null) { + ch = (DataTransferChannel) stage.getModel().getIOChannel((String) ((mxCell) cellObj).getValue()); + if(ch == null) { + //resource + return; + } + } + + if (ch instanceof FormulaChannel) { + + JPanel panel = new JPanel(); + JLabel label1 = new JLabel("Formula: "); + JLabel label2 = new JLabel("Source: "); + GridBagLayout layout = new GridBagLayout(); + panel.setLayout(layout); + GridBagConstraints gbc = new GridBagConstraints(); + + gbc.gridx = 0; + gbc.gridy = 0; + layout.setConstraints(label1, gbc); + panel.add(label1); + + gbc.gridx = 1; + gbc.gridy = 0; + JTextField formulaText = new JTextField(((FormulaChannel) ch).getFormula(),15); + layout.setConstraints(formulaText, gbc); + panel.add(formulaText); + + gbc.gridx = 0; + gbc.gridy = 1; + layout.setConstraints(label2, gbc); + panel.add(label2); + + gbc.gridx = 1; + gbc.gridy = 1; + JTextArea textArea = new JTextArea(ch.getSourceText(),7,15); + textArea.setEditable(false); + layout.setConstraints(textArea, gbc); + panel.add(textArea); + + int r = JOptionPane.showConfirmDialog( + null, // owner window + panel, // message + "Edit Formula Channel", // window's title + JOptionPane.OK_CANCEL_OPTION, // option (button types) + JOptionPane.QUESTION_MESSAGE); // message type (icon types) + if (r == JOptionPane.OK_OPTION) { + TokenStream stream = new Parser.TokenStream(); + Parser parser = new Parser(stream); + + String formula = formulaText.getText(); + stream.addLine(formula.split(Parser.EQUALS)[1]); + + + try { + Expression exp = parser.parseTerm(stream, stage.getModel()); + ((FormulaChannel) ch).setFormula(formula); + ((FormulaChannel) ch).setFormulaTerm(exp); + } catch (ExpectedRightBracket | WrongJsonExpression | ExpectedColon e) { + e.printStackTrace(); + } + } + } else { + JPanel panel = new JPanel(); + JTextArea textArea = new JTextArea(ch.getSourceText(), 10, 20); + panel.add(textArea); + // JEditorPane panel = new JEditorPane("text/plain", ch.toString()); + // panel.setEditable(true); + int ret = JOptionPane.showConfirmDialog(null, panel, "Channel Code", JOptionPane.OK_CANCEL_OPTION); + if (ret == JOptionPane.OK_OPTION) { + ((DataFlowModelingStage)stage).setChannelCode(ch, textArea.getText()); + } + } + return; + } + } + + /************************************************************* + * + * @param cancel + */ + @Override + public void stopEditing(boolean cancel) { + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DataFlowModelingStage.java b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DataFlowModelingStage.java new file mode 100644 index 0000000..8db3522 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DataFlowModelingStage.java @@ -0,0 +1,467 @@ +package application.editor.stages; + +import java.awt.event.MouseListener; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.mxgraph.model.mxCell; +import com.mxgraph.model.mxGeometry; +import com.mxgraph.model.mxGraphModel; +import com.mxgraph.swing.mxGraphComponent; +import com.mxgraph.util.mxEventSource.mxIEventListener; +import com.mxgraph.util.mxEventObject; +import com.mxgraph.util.mxPoint; +import com.mxgraph.view.mxGraph; + +import algorithms.Validation; +import application.editor.Editor; +import application.editor.Editor.SrcDstAttribute; +import application.editor.FlowCellEditor; +import application.editor.Stage; +import models.dataConstraintModel.Channel; +import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.ResourcePath; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.ResourceNodeAttribute; +import models.visualModel.FormulaChannel; +import parser.Parser; +import parser.Parser.TokenStream; +import parser.exceptions.ExpectedAssignment; +import parser.exceptions.ExpectedChannel; +import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; +import parser.exceptions.ExpectedEquals; +import parser.exceptions.ExpectedInOrOutOrRefKeyword; +import parser.exceptions.ExpectedLeftCurlyBracket; +import parser.exceptions.ExpectedRHSExpression; +import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; +import parser.exceptions.WrongLHSExpression; +import parser.exceptions.WrongRHSExpression; + +/************************************************************* + * + * Issue : リソースからチャンネルに対して, 新たにエッジを引こうとするとキャンセルされる. + */ +public class DataFlowModelingStage extends Stage { + public int PORT_DIAMETER = 8; + public int PORT_RADIUS = PORT_DIAMETER / 2; + + private boolean bReflectingArchitectureModel = false; + + /************************************************************* + * [ *constructor] + /************************************************************* + * + */ + public DataFlowModelingStage(mxGraphComponent graphComponent) { + super(graphComponent); + } + + /************************************************************* + * [ *public ] + /************************************************************* + * + */ + @Override + public boolean canChangeFrom(Stage prevStage) { + return true; + } + + /************************************************************* + * + */ + @Override + public void init(Stage prevStage) { + showOnlyLayer(DATA_FLOW_LAYER); + } + + /************************************************************* + * + */ + @Override + public FlowCellEditor createCellEditor(mxGraphComponent graphComponent) { + return new DataFlowCellEditor(this, graphComponent); + } + + @Override + public mxIEventListener createChangeEventListener(Editor editor) { + return new mxIEventListener() { + public void invoke(Object sender, mxEventObject evt) { + List terminals = new ArrayList<>(); + mxCell cell = null; + for (Object change: ((List) evt.getProperties().get("changes"))) { + if (change instanceof mxGraphModel.mxTerminalChange) { + mxGraphModel.mxTerminalChange terminalChange = (mxGraphModel.mxTerminalChange) change; + cell = (mxCell) terminalChange.getCell(); + mxCell terminal = (mxCell) terminalChange.getTerminal(); + terminals.add(terminal); + } + } + if (terminals.size() == 2) { + if (!editor.connectEdge(cell, terminals.get(0), terminals.get(1))) { + graph.removeCells(new mxCell[] {cell}); + graph.clearSelection(); + } + } + } + }; + } + + @Override + public MouseListener createMouseEventListener(Editor editor) { + return null; + } + + /************************************************************* + * mxGraphのレイヤーを全てクリアする. + */ + public void clear() { + model = null; + ((mxGraphModel) graph.getModel()).clear(); + + // 空のレイヤーを新規作成 + mxCell root = (mxCell) graph.getDefaultParent(); + graph.getModel().beginUpdate(); + try { + root.insert(new mxCell()); // NODE_LAYER, DATA_FLOW_LAYER + root.insert(new mxCell()); // PUSH_FLOW_LAYER + root.insert(new mxCell()); // PULL_FLOW_LAYER + + showOnlyLayer(NODE_LAYER, DATA_FLOW_LAYER); + } + finally { + graph.getModel().endUpdate(); + } + graph.refresh(); + } + + /************************************************************* + * + */ + public DataTransferModel getModel() { + if (model == null) { + setModel(new DataTransferModel()); + } + return model; + } + + /************************************************************* + * + */ + public void setModel(DataTransferModel model) { + clear(); + // Set the model. + this.model = model; + + // Update the mxGraph. + graph = constructGraph(graph, model); + } + + /************************************************************* + * + */ + public boolean isValid() { + if (model == null) return false; + if (!Validation.checkUpdateConflict(model)) return false; + return true; + } + + /************************************************************* + * Construct a mxGraph from DataFlowModel + * @param model + * @param dataFlowGraph + * @return constructed mxGraph + */ + public mxGraph constructGraph(mxGraph graph, DataTransferModel model) { + bReflectingArchitectureModel = true; + mxCell root = (mxCell) graph.getDefaultParent(); + mxCell nodeLayer = (mxCell) root.getChildAt(NODE_LAYER); + mxCell dataFlowLayer = (mxCell) root.getChildAt(DATA_FLOW_LAYER); + + graph.getModel().beginUpdate(); + try { + mxGeometry geo1 = new mxGeometry(0, 0.5, PORT_DIAMETER, PORT_DIAMETER); + geo1.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS)); + geo1.setRelative(true); + + mxGeometry geo2 = new mxGeometry(1.0, 0.5, PORT_DIAMETER, PORT_DIAMETER); + geo2.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS)); + geo2.setRelative(true); + + Map channelsIn = new HashMap<>(); + Map channelsOut = new HashMap<>(); + Map resources = new HashMap<>(); + + // create channel vertices + for (Channel c: model.getChannels()) { + DataTransferChannel channelGen = (DataTransferChannel) c; + if (channelsIn.get(channelGen) == null || channelsOut.get(channelGen) == null) { + Object channel = graph.insertVertex(dataFlowLayer, null, channelGen.getChannelName(), 150, 20, 30, 30); // insert a channel as a vertex + mxCell port_in = new mxCell(null, geo1, "shape=ellipse;perimter=ellipsePerimeter"); + port_in.setVertex(true); + graph.addCell(port_in, channel); // insert the input port of a channel + mxCell port_out = new mxCell(null, geo2, "shape=ellipse;perimter=ellipsePerimeter"); + port_out.setVertex(true); + graph.addCell(port_out, channel); // insert the output port of a channel + channelsIn.put(channelGen, port_in); + channelsOut.put(channelGen, port_out); + } + } + + // create resource vertices + for (ResourcePath res: model.getResourcePaths()) { + // insert a resource as a vertex + ResourceNodeAttribute resNodeAttr = new ResourceNodeAttribute(model.getDataFlowGraph().getResouceNode(res)); + Object resource = graph.insertVertex( + dataFlowLayer, null, resNodeAttr, + /*coordinate*/20, 20, + /* scale */80, 30, + resNodeAttr.getDefaultStyle()); + resources.put(res, resource); + } + + // add input, output and reference edges + for (Channel ch: model.getChannels()) { + DataTransferChannel channel = (DataTransferChannel) ch; + // input edge + for (ResourcePath srcRes: channel.getInputResources()) { + graph.insertEdge(dataFlowLayer, null, new SrcDstAttribute(srcRes, channel), resources.get(srcRes), channelsIn.get(channel), "movable=false;strokeColor=#FF0000"); + } + // output edge + for (ResourcePath dstRes: channel.getOutputResources()) { + graph.insertEdge(dataFlowLayer, null, new SrcDstAttribute(channel, dstRes), channelsOut.get(channel), resources.get(dstRes), "movable=false;strokeColor=#FF0000"); + } + // reference edges + for (ResourcePath refRes: channel.getReferenceResources()) { + graph.insertEdge(dataFlowLayer, null, null, resources.get(refRes), channelsIn.get(channel), "dashed=true;movable=false;strokeColor=#FF0000"); + } + } + + for (Channel ioChannel: model.getIOChannels()) { + if (channelsOut.get(ioChannel) == null) { + Object channel = graph.insertVertex(nodeLayer, null, ioChannel.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) ioChannel, port_out); + + for (ResourcePath outRes: ((DataTransferChannel) ioChannel).getOutputResources()) { + graph.insertEdge(dataFlowLayer, null, null, port_out, resources.get(outRes), "movable=false;strokeColor=#FF0000"); + } + + } + } + } finally { + graph.getModel().endUpdate(); + } + + bReflectingArchitectureModel = false; + return graph; + } + + /************************************************************* + * + */ + public void addResourcePath(ResourcePath res) { + getModel().addResourcePath(res); + graph.getModel().beginUpdate(); + mxCell root = (mxCell) graph.getDefaultParent(); + mxCell layer = (mxCell) root.getChildAt(NODE_LAYER); + try { + graph.insertVertex(layer, null, res.getResourceName(), 20, 20, 80, 30, + "shape=ellipse;perimeter=ellipsePerimeter"); // insert a resource as a vertex + } finally { + graph.getModel().endUpdate(); + } + } + + /************************************************************* + * + */ + public void addChannel(DataTransferChannel channelGen) { + getModel().addChannel(channelGen); + graph.getModel().beginUpdate(); + mxCell root = (mxCell) graph.getDefaultParent(); + mxCell layer = (mxCell) root.getChildAt(DATA_FLOW_LAYER); + try { + mxGeometry geo1 = new mxGeometry(0, 0.5, PORT_DIAMETER, PORT_DIAMETER); + geo1.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS)); + geo1.setRelative(true); + + mxGeometry geo2 = new mxGeometry(1.0, 0.5, PORT_DIAMETER, PORT_DIAMETER); + geo2.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS)); + geo2.setRelative(true); + + Object channel = graph.insertVertex(layer, null, channelGen.getChannelName(), 150, 20, 30, 30); // insert a channel as a vertex + mxCell port_in = new mxCell(null, geo1, "shape=ellipse;perimter=ellipsePerimeter"); + port_in.setVertex(true); + graph.addCell(port_in, channel); // insert the input port of a channel + mxCell port_out = new mxCell(null, geo2, "shape=ellipse;perimter=ellipsePerimeter"); + port_out.setVertex(true); + graph.addCell(port_out, channel); // insert the output port of a channel + } finally { + graph.getModel().endUpdate(); + } + } + + /************************************************************* + * + */ + public void addIOChannel(DataTransferChannel ioChannelGen) { + getModel().addIOChannel(ioChannelGen); + graph.getModel().beginUpdate(); + mxCell root = (mxCell) graph.getDefaultParent(); + mxCell layer = (mxCell) root.getChildAt(NODE_LAYER); + try { + mxGeometry geo2 = new mxGeometry(1.0, 0.5, PORT_DIAMETER, PORT_DIAMETER); + geo2.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS)); + geo2.setRelative(true); + + Object channel = graph.insertVertex(layer, null, ioChannelGen.getChannelName(), 150, 20, 30, 30); // insert an I/O channel as a vertex + mxCell port_out = new mxCell(null, geo2, "shape=ellipse;perimter=ellipsePerimeter"); + port_out.setVertex(true); + graph.addCell(port_out, channel); // insert the output port of a channel + } finally { + graph.getModel().endUpdate(); + } + } + + public void addFormulaChannel(FormulaChannel formulaChannelGen) { + getModel().addChannel(formulaChannelGen); + graph.getModel().beginUpdate(); + mxCell root = (mxCell) graph.getDefaultParent(); + mxCell layer = (mxCell) root.getChildAt(DATA_FLOW_LAYER); + try { + mxGeometry geo1 = new mxGeometry(0, 0.5, PORT_DIAMETER, PORT_DIAMETER); + geo1.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS)); + geo1.setRelative(true); + + mxGeometry geo2 = new mxGeometry(1.0, 0.5, PORT_DIAMETER, PORT_DIAMETER); + geo2.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS)); + geo2.setRelative(true); + + Object channel = graph.insertVertex(layer, null, formulaChannelGen.getChannelName(), 150, 20, 30, 30); // insert a channel as a vertex + mxCell port_in = new mxCell(null, geo1, "shape=ellipse;perimter=ellipsePerimeter"); + port_in.setVertex(true); + graph.addCell(port_in, channel); // insert the input port of a channel + mxCell port_out = new mxCell(null, geo2, "shape=ellipse;perimter=ellipsePerimeter"); + port_out.setVertex(true); + graph.addCell(port_out, channel); // insert the output port of a channel + } finally { + graph.getModel().endUpdate(); + } + } + + public boolean connectEdge(mxCell edge, mxCell src, mxCell dst) { + if (bReflectingArchitectureModel) return false; + DataTransferModel model = getModel(); + + if (!(src.getValue() instanceof String)) return false; + + Channel srcCh = model.getChannel((String) src.getValue()); + if (srcCh == null) { + srcCh = model.getIOChannel((String) src.getValue()); + if (srcCh == null) { + ResourcePath srcRes = model.getResourcePath((String) src.getValue()); + Channel dstCh = model.getChannel((String) dst.getValue()); + if (srcRes == null || dstCh == null) return false; + // resource to channel edge + ChannelMember srcCm = new ChannelMember(srcRes); + ((DataTransferChannel ) dstCh).addChannelMemberAsInput(srcCm); + edge.setValue(new SrcDstAttribute(srcRes, dstCh)); + return true; + } + } + ResourcePath dstRes = model.getResourcePath((String) dst.getValue()); + if (dstRes == null) return false; + // channel to resource edge + ChannelMember dstCm = new ChannelMember(dstRes); + ((DataTransferChannel) srcCh).addChannelMemberAsOutput(dstCm); + edge.setValue(new SrcDstAttribute(srcCh, dstRes)); + return true; + } + + /************************************************************* + * + */ + public void delete() { + for (Object obj: graph.getSelectionCells()) { + mxCell cell = (mxCell) obj; + if (cell.isEdge()) { + String srcName = (String) cell.getSource().getValue(); + String dstName = (String) cell.getTarget().getValue(); + if (model.getResourcePath(srcName) != null) { + // resource to channel edge + Channel ch = model.getChannel(dstName); + ch.removeChannelMember(model.getResourcePath(srcName)); + } else if (model.getResourcePath(dstName) != null) { + // channel to resource edge + Channel ch = model.getChannel(srcName); + if (ch == null) { + ch = model.getIOChannel(srcName); + } + ch.removeChannelMember(model.getResourcePath(dstName)); + } + } else if (cell.isVertex()) { + String name = (String) cell.getValue(); + if (model.getChannel(name) != null) { + model.removeChannel(name); + } else if (model.getIOChannel(name) != null) { + model.removeIOChannel(name); + } else if (model.getResourcePath(name) != null) { + model.removeResourcePath(name); + } + } + } + graph.removeCells(graph.getSelectionCells()); + } + + /************************************************************* + * + */ + public void setChannelCode(DataTransferChannel ch, String code) { + ch.setSourceText(code); + TokenStream stream = new TokenStream(); + Parser parser = new Parser(stream); + + for (String line: code.split("\n")) { + stream.addLine(line); + } + try { + DataTransferChannel ch2 = parser.parseChannel(getModel()); + for (ChannelMember chm2: ch2.getInputChannelMembers()) { + for (ChannelMember chm: ch.getInputChannelMembers()) { + if (chm2.getResource() == chm.getResource()) { + chm.setStateTransition(chm2.getStateTransition()); + break; + } + } + } + for (ChannelMember chm2: ch2.getOutputChannelMembers()) { + for (ChannelMember chm: ch.getOutputChannelMembers()) { + if (chm2.getResource() == chm.getResource()) { + chm.setStateTransition(chm2.getStateTransition()); + break; + } + } + } + for (ChannelMember chm2: ch2.getReferenceChannelMembers()) { + for (ChannelMember chm: ch.getReferenceChannelMembers()) { + if (chm2.getResource() == chm.getResource()) { + chm.setStateTransition(chm2.getStateTransition()); + break; + } + } + } + } catch (ExpectedRightBracket | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket + | ExpectedInOrOutOrRefKeyword | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression + | WrongLHSExpression | WrongRHSExpression | ExpectedAssignment | WrongJsonExpression | ExpectedColon e) { + e.printStackTrace(); + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/PushPullSelectionCellEditor.java b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/PushPullSelectionCellEditor.java new file mode 100644 index 0000000..67611b4 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/PushPullSelectionCellEditor.java @@ -0,0 +1,168 @@ +package application.editor.stages; + +import java.awt.Rectangle; +import java.util.EventObject; +import java.util.List; +import java.util.logging.Level; + +import javax.swing.BorderFactory; +import javax.swing.JComboBox; + +import com.mxgraph.model.mxIGraphModel; +import com.mxgraph.swing.mxGraphComponent; +import com.mxgraph.util.mxConstants; +import com.mxgraph.util.mxUtils; +import com.mxgraph.view.mxCellState; + +import application.ApplicationWindow; +import application.editor.FlowCellEditor; +import models.dataFlowModel.PushPullAttribute; +import models.dataFlowModel.PushPullValue; + +/************************************************************* + * + */ +public class PushPullSelectionCellEditor extends FlowCellEditor { + public int DEFAULT_MIN_WIDTH = 70; + public int DEFAULT_MIN_HEIGHT = 30; + public double DEFAULT_MINIMUM_EDITOR_SCALE = 1; + + protected double minimumEditorScale = DEFAULT_MINIMUM_EDITOR_SCALE; + protected int minimumWidth = DEFAULT_MIN_WIDTH; + protected int minimumHeight = DEFAULT_MIN_HEIGHT; + + private EventObject trigger; + private JComboBox comboBox; + + /************************************************************* + * [ *constructor ] + /************************************************************* + * @param stage + * @param graphComponent + */ + public PushPullSelectionCellEditor(PushPullSelectionStage stage, mxGraphComponent graphComponent) { + super(stage, graphComponent); + } + + /************************************************************* + * + * @param cellObj + * @param eventObj + */ + @Override + public void startEditing(Object cellObj, EventObject eventObj) { + if (editingCell != null) { + stopEditing(true); + } + + if (graphComponent.getGraph().getModel().isEdge(cellObj)) { + mxCellState state = graphComponent.getGraph().getView().getState(cellObj); + if (state != null && state.getLabel() != null && !state.getLabel().equals("")) { + editingCell = cellObj; + trigger = eventObj; + + double scale = Math.max(minimumEditorScale, graphComponent.getGraph().getView().getScale()); + Object value = graphComponent.getGraph().getModel().getValue(cellObj); + 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(); + } + } + } + } + + /************************************************************* + * + * @param cancel + */ + @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); + ApplicationWindow.logger.log(Level.INFO, selected.name()); // log output + } + graphComponent.labelChanged(cell, attr, trig); + } + } else { + mxCellState state = graphComponent.getGraph().getView().getState(cell); + graphComponent.redraw(state); + } + + if (comboBox.getParent() != null) { + comboBox.setVisible(false); + comboBox.getParent().remove(comboBox); + } + + graphComponent.requestFocusInWindow(); + } + } + + public String getCurrentValue() { + return (String) comboBox.getSelectedItem(); + } + + /** + * Returns the bounds to be used for the editor. + */ + public Rectangle getEditorBounds(mxCellState state, double scale) { + mxIGraphModel model = state.getView().getGraph().getModel(); + Rectangle bounds = null; + + bounds = state.getLabelBounds().getRectangle(); + bounds.height += 10; + + // Applies the horizontal and vertical label positions + if (model.isVertex(state.getCell())) { + String horizontal = mxUtils.getString(state.getStyle(), mxConstants.STYLE_LABEL_POSITION, mxConstants.ALIGN_CENTER); + + if (horizontal.equals(mxConstants.ALIGN_LEFT)) { + bounds.x -= state.getWidth(); + } else if (horizontal.equals(mxConstants.ALIGN_RIGHT)) { + bounds.x += state.getWidth(); + } + + String vertical = mxUtils.getString(state.getStyle(), + mxConstants.STYLE_VERTICAL_LABEL_POSITION, + mxConstants.ALIGN_MIDDLE); + + if (vertical.equals(mxConstants.ALIGN_TOP)) { + bounds.y -= state.getHeight(); + } else if (vertical.equals(mxConstants.ALIGN_BOTTOM)) { + bounds.y += state.getHeight(); + } + } + + bounds.setSize( + (int) Math.max(bounds.getWidth(), + Math.round(minimumWidth * scale)), + (int) Math.max(bounds.getHeight(), + Math.round(minimumHeight * scale))); + + return bounds; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/PushPullSelectionStage.java b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/PushPullSelectionStage.java new file mode 100644 index 0000000..f77ced3 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/PushPullSelectionStage.java @@ -0,0 +1,139 @@ +package application.editor.stages; + +import java.awt.event.MouseListener; +import java.util.ArrayList; +import java.util.List; + +import com.mxgraph.model.mxCell; +import com.mxgraph.model.mxGraphModel; +import com.mxgraph.swing.mxGraphComponent; +import com.mxgraph.util.mxEventObject; +import com.mxgraph.util.mxEventSource.mxIEventListener; +import com.mxgraph.view.mxGraph; + +import algorithms.DataTransferModelAnalyzer; +import application.editor.Editor.SrcDstAttribute; +import application.editor.Editor; +import application.editor.FlowCellEditor; +import application.editor.InEdgeAttribute; +import application.editor.OutEdgeAttribute; +import application.editor.Stage; +import models.Edge; +import models.dataFlowModel.DataFlowEdge; +import models.dataFlowModel.DataFlowGraph; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.PushPullAttribute; +import models.dataFlowModel.ResourceNode; + +public class PushPullSelectionStage extends Stage { + protected DataFlowGraph dataFlowGraph = null; + + public PushPullSelectionStage(mxGraphComponent graphComponent) { + super(graphComponent); + } + + @Override + public boolean canChangeFrom(Stage prevStage) { + if (prevStage instanceof DataFlowModelingStage) { + if (!((DataFlowModelingStage) prevStage).isValid()) return false; + return true; + } + if (prevStage instanceof ControlFlowDelegationStage) return true; + return false; + } + + @Override + public void init(Stage prevStage) { + if (prevStage instanceof DataFlowModelingStage) { + model = ((DataFlowModelingStage) prevStage).getModel(); + dataFlowGraph = analyzeDataTransferModel(graph, model); + showOnlyLayer(DATA_FLOW_LAYER); + } + + if(prevStage instanceof ControlFlowDelegationStage) { + showOnlyLayer(DATA_FLOW_LAYER); + } + } + + @Override + public FlowCellEditor createCellEditor(mxGraphComponent graphComponent) { + return new PushPullSelectionCellEditor(this, graphComponent); + } + + @Override + public mxIEventListener createChangeEventListener(Editor editor) { + return new mxIEventListener() { + public void invoke(Object sender, mxEventObject evt) { + List terminals = new ArrayList<>(); + mxCell cell = null; + for (Object change: ((List) evt.getProperties().get("changes"))) { + if (change instanceof mxGraphModel.mxTerminalChange) { + mxGraphModel.mxTerminalChange terminalChange = (mxGraphModel.mxTerminalChange) change; + cell = (mxCell) terminalChange.getCell(); + mxCell terminal = (mxCell) terminalChange.getTerminal(); + terminals.add(terminal); + } + } + if (terminals.size() == 2) { + // cancel connect + graph.removeCells(new mxCell[] {cell}); + graph.clearSelection(); + } + } + }; + } + + @Override + public MouseListener createMouseEventListener(Editor editor) { + return null; + } + + + public DataFlowGraph getDataFlowGraph() { + return dataFlowGraph; + } + + public DataFlowGraph analyzeDataTransferModel(mxGraph graph, DataTransferModel model) { + DataFlowGraph flowGraph = DataTransferModelAnalyzer.createDataFlowGraphWithStateStoringAttribute(model); + DataFlowGraph dataFlowGraph = DataTransferModelAnalyzer.annotateWithSelectableDataTransferAttiribute(flowGraph); + updateEdgeAttiributes(graph, dataFlowGraph); + return dataFlowGraph; + } + + private void updateEdgeAttiributes(mxGraph graph, DataFlowGraph dataFlowGraph) { + mxCell root = (mxCell) graph.getDefaultParent(); + mxCell layer = (mxCell) root.getChildAt(DATA_FLOW_LAYER); + + graph.getModel().beginUpdate(); + try { + // update attributes of input and output edges + for (Edge e : dataFlowGraph.getEdges()) { + if (e instanceof DataFlowEdge) { + DataFlowEdge dataFlow = (DataFlowEdge) e; + DataTransferChannel channel = dataFlow.getChannel(); + ResourceNode srcRes = (ResourceNode) dataFlow.getSource(); + ResourceNode dstRes = (ResourceNode) dataFlow.getDestination(); + for (Object edge: graph.getChildEdges(layer)) { + mxCell edgeCell = (mxCell) edge; + if (edgeCell.getValue() instanceof SrcDstAttribute) { + SrcDstAttribute edgeAttr = (SrcDstAttribute) edgeCell.getValue(); + if (edgeAttr.getSrouce() == srcRes.getResource() && edgeAttr.getDestination() == channel) { + // input edge + edgeCell.setValue(new InEdgeAttribute((PushPullAttribute) dataFlow.getAttribute(), srcRes)); + break; + } else if (edgeAttr.getSrouce() == channel && edgeAttr.getDestination() == dstRes.getResource()) { + // output edge + edgeCell.setValue(new OutEdgeAttribute(edgeAttr, dstRes)); + break; + } + } + } + } + } + } finally { + graph.getModel().endUpdate(); + } + graph.refresh(); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/layouts/DAGLayout.java b/AlgebraicDataflowArchitectureModel/src/application/layouts/DAGLayout.java new file mode 100644 index 0000000..ed9181a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/layouts/DAGLayout.java @@ -0,0 +1,154 @@ +package application.layouts; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import com.mxgraph.layout.mxGraphLayout; +import com.mxgraph.model.mxCell; +import com.mxgraph.model.mxGeometry; +import com.mxgraph.model.mxICell; +import com.mxgraph.model.mxIGraphModel; +import com.mxgraph.util.mxRectangle; +import com.mxgraph.view.mxCellState; +import com.mxgraph.view.mxGraph; +import com.mxgraph.view.mxGraphView; + +import models.controlFlowModel.ObjectNodeAttribute; +import models.dataConstraintModel.Channel; +import models.dataConstraintModel.ResourcePath; +import models.dataFlowModel.ResourceNodeAttribute; + +public class DAGLayout extends mxGraphLayout { + + public DAGLayout(mxGraph graph) { + super(graph); + } + + @Override + public void execute(Object parent) { + mxIGraphModel model = graph.getModel(); + + model.beginUpdate(); + try { + List> map = new ArrayList>(); + List moved = new ArrayList<>(); + + for (int i = 0; i < model.getChildCount(parent); i++) { + + mxCell cell = (mxCell) model.getChildAt(parent, i); + + if (model.isVertex(cell)) { + mxGraphView view = graph.getView(); + mxCellState state = view.getState(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); + } + } + } + + 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++; + } + } + if (count >= map.get(i).size())skip++; + } + + } finally { + model.endUpdate(); + } + } + + + 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); + 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()); + + } else { + lines(mapping, (mxCell) edge.getTarget()); + } + } + } + } + + public boolean checkmoved(List list, mxCell cell) { + for (int i = 0; i < list.size(); i++) { + if (list.get(i).equals(cell.getId()))return false; + } + 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; + } + } + } else { + for (int i = n; i < map.size(); i++) { + if (map.get(i).size() > msize) { + mnum = i; + } + } + } + if (mnum >= 0) { + Collections.swap(map, n, mnum); + sort(map, n+1, true); + } else if(n < map.size()) { + sort(map, n+1, false); + } + } + + +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/src/application/views/NavigationWindow.java b/AlgebraicDataflowArchitectureModel/src/application/views/NavigationWindow.java new file mode 100644 index 0000000..9a7d700 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/views/NavigationWindow.java @@ -0,0 +1,160 @@ +package application.views; + +import java.awt.Container; +import java.awt.Point; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.logging.Level; + +import javax.swing.ButtonGroup; +import javax.swing.JDialog; +import javax.swing.JToggleButton; + +import application.ApplicationLanguage; +import application.ApplicationWindow; +import application.editor.Editor; +import application.editor.IStageChangeListener; +import application.editor.Stage; +import application.editor.stages.ControlFlowDelegationStage; +import application.editor.stages.DataFlowModelingStage; +import application.editor.stages.PushPullSelectionStage; + +public class NavigationWindow extends JDialog implements IStageChangeListener { + private String title = "Navigation"; + private Editor editor; + private JToggleButton dataFlowModelingButton; + private JToggleButton pushPullSelectionButton; + private JToggleButton controlFlowDelegationButton; + private boolean forbidReentry = false; + + public NavigationWindow(ApplicationWindow owner, Editor editor) { + super(owner); + setTitle(title); + setDefaultCloseOperation(HIDE_ON_CLOSE); + this.editor = editor; + Container panel = getContentPane(); + panel.setLayout(new java.awt.GridLayout(3, 1)); + dataFlowModelingButton = new JToggleButton(ApplicationLanguage.getInstance().getOptionByPropName("dataFlowModeling")); + pushPullSelectionButton = new JToggleButton(ApplicationLanguage.getInstance().getOptionByPropName("pushpullSelection")); + controlFlowDelegationButton = new JToggleButton(ApplicationLanguage.getInstance().getOptionByPropName("pushpullExtension")); + dataFlowModelingButton.addActionListener(new DataFlowModelingButtonListener()); + pushPullSelectionButton.addActionListener(new PushPullSelectionButtonListener()); + controlFlowDelegationButton.addActionListener(new ControlFlowDelegationButtonListener()); + ButtonGroup group = new ButtonGroup(); + group.add(dataFlowModelingButton); + group.add(pushPullSelectionButton); + group.add(controlFlowDelegationButton); + panel.add(dataFlowModelingButton); + panel.add(pushPullSelectionButton); + panel.add(controlFlowDelegationButton); + controlFlowDelegationButton.setEnabled(false); + pushPullSelectionButton.setEnabled(false); + dataFlowModelingButton.setSelected(true); + pack(); + + Point location = new Point(owner.getX() + owner.getWidth() - this.getWidth(), owner.getY() + owner.getHeight() - this.getHeight()); + setLocation(location); + + setResizable(false); + } + + @Override + public void stageChanged(Stage newStage) { + if (forbidReentry) return; + ApplicationWindow.logger.log(Level.INFO, newStage.toString()); + if (newStage instanceof DataFlowModelingStage) { + dataFlowModelingButton.setSelected(true); + if (editor.canChange(Editor.STAGE_PUSH_PULL_SELECTION)) { + pushPullSelectionButton.setEnabled(true); + } else { + pushPullSelectionButton.setEnabled(false); + } + if (editor.canChange(Editor.STAGE_CONTROL_FLOW_DELEGATION)) { + controlFlowDelegationButton.setEnabled(true); + } else { + controlFlowDelegationButton.setEnabled(false); + } + } else if (newStage instanceof PushPullSelectionStage) { + pushPullSelectionButton.setSelected(true); + if (editor.canChange(Editor.STAGE_DATA_FLOW_MODELING)) { + dataFlowModelingButton.setEnabled(true); + } else { + dataFlowModelingButton.setEnabled(false); + } + if (editor.canChange(Editor.STAGE_CONTROL_FLOW_DELEGATION)) { + controlFlowDelegationButton.setEnabled(true); + } else { + controlFlowDelegationButton.setEnabled(false); + } + } else if (newStage instanceof ControlFlowDelegationStage) { + controlFlowDelegationButton.setSelected(true); + if (editor.canChange(Editor.STAGE_DATA_FLOW_MODELING)) { + dataFlowModelingButton.setEnabled(true); + } else { + dataFlowModelingButton.setEnabled(false); + } + if (editor.canChange(Editor.STAGE_PUSH_PULL_SELECTION)) { + pushPullSelectionButton.setEnabled(true); + } else { + pushPullSelectionButton.setEnabled(false); + } + } + } + + private class DataFlowModelingButtonListener implements ActionListener { + @Override + public void actionPerformed(ActionEvent e) { + forbidReentry = true; + editor.changeStage(Editor.STAGE_DATA_FLOW_MODELING); + forbidReentry = false; + if (editor.canChange(Editor.STAGE_PUSH_PULL_SELECTION)) { + pushPullSelectionButton.setEnabled(true); + } else { + pushPullSelectionButton.setEnabled(false); + } + if (editor.canChange(Editor.STAGE_CONTROL_FLOW_DELEGATION)) { + controlFlowDelegationButton.setEnabled(true); + } else { + controlFlowDelegationButton.setEnabled(false); + } + } + } + + private class PushPullSelectionButtonListener implements ActionListener { + @Override + public void actionPerformed(ActionEvent e) { + forbidReentry = true; + editor.changeStage(Editor.STAGE_PUSH_PULL_SELECTION); + forbidReentry = false; + if (editor.canChange(Editor.STAGE_DATA_FLOW_MODELING)) { + dataFlowModelingButton.setEnabled(true); + } else { + dataFlowModelingButton.setEnabled(false); + } + if (editor.canChange(Editor.STAGE_CONTROL_FLOW_DELEGATION)) { + controlFlowDelegationButton.setEnabled(true); + } else { + controlFlowDelegationButton.setEnabled(false); + } + } + } + + private class ControlFlowDelegationButtonListener implements ActionListener { + @Override + public void actionPerformed(ActionEvent e) { + forbidReentry = true; + editor.changeStage(Editor.STAGE_CONTROL_FLOW_DELEGATION); + forbidReentry = false; + if (editor.canChange(Editor.STAGE_DATA_FLOW_MODELING)) { + dataFlowModelingButton.setEnabled(true); + } else { + dataFlowModelingButton.setEnabled(false); + } + if (editor.canChange(Editor.STAGE_PUSH_PULL_SELECTION)) { + pushPullSelectionButton.setEnabled(true); + } else { + pushPullSelectionButton.setEnabled(false); + } + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/views/PopupMenuBase.java b/AlgebraicDataflowArchitectureModel/src/application/views/PopupMenuBase.java new file mode 100644 index 0000000..6fd1628 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/views/PopupMenuBase.java @@ -0,0 +1,48 @@ +package application.views; + +import java.awt.Component; + +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; + +import com.mxgraph.model.mxCell; +import com.mxgraph.swing.mxGraphComponent; + +import application.actions.AbstractPopupAction; + +/************************************************************* + * + */ +public abstract class PopupMenuBase { + protected JPopupMenu popupMenu = null; + protected mxGraphComponent graphComponent = null; + + /************************************************************* + * [ *constructor ] + /************************************************************* + */ + public PopupMenuBase(final mxGraphComponent graphComponent) { + this.graphComponent = graphComponent; + this.popupMenu = new JPopupMenu(); + } + + /************************************************************* + * [ *public ] + /************************************************************* + * ポップアップを開く. + * @param x, y ポップアップを開いた座標 + */ + public void show(int x, int y) { + popupMenu.show(graphComponent, x, y); + } + + /************************************************************* + * [ *protected ] + /************************************************************* + * + */ + protected void addMenuItem(JMenuItem menuItem) { + popupMenu.add(menuItem); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/views/controlFlowDelegation/ControlFlowDelegationStagePopupMenu.java b/AlgebraicDataflowArchitectureModel/src/application/views/controlFlowDelegation/ControlFlowDelegationStagePopupMenu.java new file mode 100644 index 0000000..2ee9ee3 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/views/controlFlowDelegation/ControlFlowDelegationStagePopupMenu.java @@ -0,0 +1,126 @@ +package application.views.controlFlowDelegation; + +import java.awt.Component; + +import javax.swing.JMenuItem; + +import com.mxgraph.model.mxCell; +import com.mxgraph.swing.mxGraphComponent; + +import application.actions.AbstractPopupAction; +import application.actions.ChangeCallOrderAction; +import application.actions.ShowDependentableMediatorAction; +import application.actions.IntroduceMediatorObjectAction; +import application.editor.stages.ControlFlowDelegationStage; +import application.views.PopupMenuBase; +import models.controlFlowModel.CallEdgeAttribute; +import models.dataFlowModel.PushPullValue; + +/************************************************************* + * + */ +public class ControlFlowDelegationStagePopupMenu extends PopupMenuBase { + private ControlFlowDelegationStage stage = null; + private mxCell selectedCell = null; + + /************************************************************* + /************************************************************* + * [ *constructor ] + /************************************************************* + */ + public ControlFlowDelegationStagePopupMenu(final ControlFlowDelegationStage stage, mxGraphComponent graphComponent) { + super(graphComponent); + this.stage = stage; + + addMenuItem(new JMenuItem(new IntroduceMediatorObjectAction(stage, graphComponent, selectedCell))); + addMenuItem(new JMenuItem(new ChangeCallOrderAction(graphComponent, selectedCell))); + addMenuItem(new JMenuItem(new ShowDependentableMediatorAction(stage, graphComponent, selectedCell))); + } + + /************************************************************* + /************************************************************* + * [ *public ] + /************************************************************* + * + * + */ + @Override + public void show(int x, int y) { + if (graphComponent.getCellAt(x, y) instanceof mxCell ) { + selectedCell =(mxCell) graphComponent.getCellAt(x, y); + } + else { + selectedCell = null; + } + + if (this.selectedCell == null) return; + + notifyCellCached(selectedCell); + stage.setCellOnAnyEvent(selectedCell); + + boolean isEnable = (graphComponent.getCellAt(x, y) != null) + ? true + : false; + + setEnableMenuItems(isEnable); + + super.show(x, y); + } + + /************************************************************* + /************************************************************* + * [ *private ] + /************************************************************* + * ポップアップ表示時に選択したセルを, Stageに保存させる. + * @param cell 選択したセル + */ + private void notifyCellCached(final mxCell cell) { + if (cell == null) return; + + for (Component component : popupMenu.getComponents()) { + JMenuItem menuItem = null; + if (component instanceof JMenuItem) menuItem = (JMenuItem)component; + else return; + + AbstractPopupAction action = null; + if (menuItem.getAction() instanceof AbstractPopupAction) { + action = (AbstractPopupAction)menuItem.getAction(); + } + else return; + + action.updateTargetCell(cell); // 選択したセルをキャッシュする. + } + } + + /************************************************************* + * + */ + private void setEnableMenuItems(final boolean isEnable) { + if (this.selectedCell == null) return; + + for (Component component : popupMenu.getComponents()) { + component.setEnabled(isEnable); + + // pull only + if (!isSelectedPullCallEdge(selectedCell)) { + JMenuItem menuItem = null; + if (component instanceof JMenuItem) menuItem = (JMenuItem)component; + + if (menuItem.getAction() instanceof ShowDependentableMediatorAction) { + component.setEnabled(false); + continue; + } + } + + } + } + + /************************************************************* + * + */ + private boolean isSelectedPullCallEdge(final mxCell selectedCell) { + CallEdgeAttribute callEdgeAttr = (CallEdgeAttribute)selectedCell.getValue(); + return callEdgeAttr.getSelectedOption().equals(PushPullValue.PULL); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/views/controlFlowDelegation/FlowLayerValue.java b/AlgebraicDataflowArchitectureModel/src/application/views/controlFlowDelegation/FlowLayerValue.java new file mode 100644 index 0000000..0c9317c --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/views/controlFlowDelegation/FlowLayerValue.java @@ -0,0 +1,10 @@ +package application.views.controlFlowDelegation; + +/************************************************************* + * + */ +public enum FlowLayerValue { + DATA_FLOW, + PUSH_FLOW, + PULL_FLOW +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/views/controlFlowDelegation/FlowLayerWindow.java b/AlgebraicDataflowArchitectureModel/src/application/views/controlFlowDelegation/FlowLayerWindow.java new file mode 100644 index 0000000..6b49ab8 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/views/controlFlowDelegation/FlowLayerWindow.java @@ -0,0 +1,109 @@ +package application.views.controlFlowDelegation; + +import java.awt.Container; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JCheckBox; +import javax.swing.JDialog; + +import application.ApplicationWindow; +import application.editor.Editor; +import application.editor.IStageChangeListener; +import application.editor.Stage; +import application.editor.stages.ControlFlowDelegationStage; + +/************************************************************* + * レイヤー表示 + */ +public class FlowLayerWindow extends JDialog implements IStageChangeListener { + private String title = "Flow Layer"; + private JCheckBox dataFlowCheckBox = null; + private JCheckBox pushFlowCheckBox = null; + private JCheckBox pullFlowCheckBox = null; + + private ControlFlowDelegationStage stage = null; + + /************************************************************* + /************************************************************* + * [ *constructor ] + /************************************************************* + * + */ + public FlowLayerWindow(final ApplicationWindow owner) { + super(owner); + + setTitle(title); + setDefaultCloseOperation(HIDE_ON_CLOSE); + + stage = Editor.STAGE_CONTROL_FLOW_DELEGATION; + + // ボタンの追加. + + dataFlowCheckBox = new JCheckBox("Data-Flow", false); + pushFlowCheckBox = new JCheckBox("Push-Flow", true); + pullFlowCheckBox = new JCheckBox("Pull-Flow", true); + + // 各Viewにイベントハンドラを追加 + dataFlowCheckBox.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + stage.setEnabledForLayer(Stage.DATA_FLOW_LAYER, dataFlowCheckBox.isSelected()); + }}); + + pushFlowCheckBox.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + stage.setEnabledForLayer(Stage.PUSH_FLOW_LAYER, pushFlowCheckBox.isSelected()); + }}); + + pullFlowCheckBox.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + stage.setEnabledForLayer(Stage.PULL_FLOW_LAYER, pullFlowCheckBox.isSelected()); + }}); + + dataFlowCheckBox.setEnabled(false); + pushFlowCheckBox.setEnabled(false); + pullFlowCheckBox.setEnabled(false); + + // レイヤーのパネルレイアウトの初期化. + Container panel = getContentPane(); + panel.setLayout(new GridLayout(/*low*/3, /*col*/1)); + panel.add(dataFlowCheckBox); + panel.add(pushFlowCheckBox); + panel.add(pullFlowCheckBox); + + pack(); + setResizable(false); + } + + /************************************************************* + /************************************************************* + * [ *public ] + /************************************************************* + * ステージが変わったら, レイヤー表示のボタンのアクティブ状態を切り替える. + * @param stage 現在のステージ + */ + @Override + public void stageChanged(Stage newStage) { + if (newStage instanceof ControlFlowDelegationStage) { + dataFlowCheckBox.setEnabled(true); + pushFlowCheckBox.setEnabled(true); + pullFlowCheckBox.setEnabled(true); + + newStage.setEnabledForLayer(Stage.PUSH_FLOW_LAYER, pushFlowCheckBox.isSelected()); + newStage.setEnabledForLayer(Stage.PULL_FLOW_LAYER, pullFlowCheckBox.isSelected()); + } + else { + dataFlowCheckBox.setEnabled(false); + pushFlowCheckBox.setEnabled(false); + pullFlowCheckBox.setEnabled(false); + + newStage.setEnabledForLayer(Stage.PUSH_FLOW_LAYER, false); + newStage.setEnabledForLayer(Stage.PULL_FLOW_LAYER, false); + } + } +} + diff --git a/AlgebraicDataflowArchitectureModel/src/code/ast/ASTNode.java b/AlgebraicDataflowArchitectureModel/src/code/ast/ASTNode.java new file mode 100644 index 0000000..202e700 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/code/ast/ASTNode.java @@ -0,0 +1,15 @@ +package code.ast; + +import java.io.Serializable; + +public abstract class ASTNode implements Serializable{ + private ASTNode parent; + + public ASTNode getParent() { + return parent; + } + + public void setParent(ASTNode parent) { + this.parent = parent; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/code/ast/AbstractTypeDeclaration.java b/AlgebraicDataflowArchitectureModel/src/code/ast/AbstractTypeDeclaration.java new file mode 100644 index 0000000..383962c --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/code/ast/AbstractTypeDeclaration.java @@ -0,0 +1,13 @@ +package code.ast; + +public class AbstractTypeDeclaration extends BodyDeclaration { + protected String typeName = null; + + public String getTypeName() { + return typeName; + } + + public void setTypeName(String typeName) { + this.typeName = typeName; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/code/ast/Annotation.java b/AlgebraicDataflowArchitectureModel/src/code/ast/Annotation.java new file mode 100644 index 0000000..c6235e1 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/code/ast/Annotation.java @@ -0,0 +1,57 @@ +package code.ast; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class Annotation extends ASTNode { + private String name = null; + private Map keyValueMap = null; + + public Annotation(String name) { + this.name = name; + keyValueMap = new HashMap<>(); + } + + public Annotation(String name, String value) { + this.name = name; + keyValueMap = new HashMap<>(); + keyValueMap.put("value", value); + } + + public String getElementName() { + return name; + } + + public Map getParams() { + return keyValueMap; + } + + public Object getValue(String key) { + return keyValueMap.get(key); + } + + public void addParam(String key, Object value) { + keyValueMap.put(key, value); + } + + public String toString() { + String code = "@" + name; + Set keySet = keyValueMap.keySet(); + if (keySet.size() == 0) { + return code; + } else if (keySet.size() == 1 && keySet.iterator().next().equals("value")) { + code += "(" + keyValueMap.get("value").toString() + ")"; + } else { + code += "("; + String delimitar = ""; + for (String key: keySet) { + Object value = keyValueMap.get(key); + code += delimitar + key + " = \"" + value.toString() + "\""; + delimitar = ", "; + } + code += ")"; + } + return code; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/code/ast/Block.java b/AlgebraicDataflowArchitectureModel/src/code/ast/Block.java new file mode 100644 index 0000000..2b4f654 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/code/ast/Block.java @@ -0,0 +1,32 @@ +package code.ast; + +import java.util.List; +import java.util.ArrayList; + +public class Block extends ASTNode { + private List statements = new ArrayList(); + + public List getStatements() { + return statements; + } + + public void setStatements(List statements) { + this.statements = statements; + } + + public void addFirstStatement(String statement) { + statements.add(0, statement); + } + + public void addStatement(String statement) { + statements.add(statement); + } + + public String toString() { + String code = ""; + for (String statement: statements) { + code += (statement + "\n"); + } + return code; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/code/ast/BodyDeclaration.java b/AlgebraicDataflowArchitectureModel/src/code/ast/BodyDeclaration.java new file mode 100644 index 0000000..812f0d7 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/code/ast/BodyDeclaration.java @@ -0,0 +1,13 @@ +package code.ast; + +public abstract class BodyDeclaration extends ASTNode { + private int modifiers = 0; + + public int getModifiers() { + return modifiers; + } + + public void setModifiers(int modifiers) { + this.modifiers = modifiers; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/code/ast/CodeUtil.java b/AlgebraicDataflowArchitectureModel/src/code/ast/CodeUtil.java new file mode 100644 index 0000000..99f1561 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/code/ast/CodeUtil.java @@ -0,0 +1,43 @@ +package code.ast; + +public class CodeUtil { + + public static String insertTab(String code) { + String newString = ""; + String[] lines = code.split("\n"); + for (String line: lines) { + newString = newString + "\t" + line + "\n"; + } + return newString; + } + + public static String getToStringExp(String typeName, String rawExp) { + if (typeName.equals("int")) { + return "Integer.toString(" + rawExp + ")"; + } else if (typeName.equals("float")) { + return "Float.toString(" + rawExp + ")"; + } else if (typeName.equals("double")) { + return "Double.toString(" + rawExp + ")"; + } else if (typeName.equals("boolean")) { + return "Boolean.toString(" + rawExp + ")"; + } else { + return rawExp + ".toString()"; + } + } + + public static String getToValueExp(String typeName, String strExp) { + if (typeName.equals("int")) { + return "Integer.parseInt(" + strExp + ")"; + } else if (typeName.equals("float")) { + return "Float.parseFloat(" + strExp + ")"; + } else if (typeName.equals("double")) { + return "Double.parseDouble(" + strExp + ")"; + } else if (typeName.equals("boolean")) { + return "Boolean.parseBoolean(" + strExp + ")"; + } else if (typeName.startsWith("ArrayList") || typeName.startsWith("List")) { + return "Arrays.asList(" + strExp + ".replace(\"[\",\"\").replace(\"]\",\"\").split(\",\",0))"; + } else { + return strExp; + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/code/ast/CompilationUnit.java b/AlgebraicDataflowArchitectureModel/src/code/ast/CompilationUnit.java new file mode 100644 index 0000000..9a5a58a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/code/ast/CompilationUnit.java @@ -0,0 +1,50 @@ +package code.ast; + +import java.util.ArrayList; +import java.util.List; + +public class CompilationUnit extends ASTNode { + private String fileName = null; + private List imports = new ArrayList<>(); + private List types = new ArrayList<>(); + + public CompilationUnit(TypeDeclaration type) { + types.add(type); + if(type.getTypeName().contains("<")) + fileName = type.getTypeName().split("<")[0] + ".java"; + else + fileName = type.getTypeName() + ".java"; + } + + public List imports() { + return imports; + } + + public List types() { + return types; + } + + public void addImport(ImportDeclaration imp) { + imports.add(imp); + } + + public void addType(TypeDeclaration type) { + types.add(type); + } + + public String getFileName() { + return fileName; + } + + public String toString() { + String result = ""; + for (ImportDeclaration imp: imports) { + result += imp.toString(); + } + result +="\n"; + for (TypeDeclaration type: types) { + result += type.toString(); + } + return result; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/code/ast/FieldDeclaration.java b/AlgebraicDataflowArchitectureModel/src/code/ast/FieldDeclaration.java new file mode 100644 index 0000000..0993b92 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/code/ast/FieldDeclaration.java @@ -0,0 +1,77 @@ +package code.ast; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import models.algebra.Type; + +public class FieldDeclaration extends BodyDeclaration implements IAnnotatable { + private Type type; + private String fieldName; + private String initializer; + private Map annotations = new HashMap<>(); + + public FieldDeclaration(Type type, String fieldName) { + this.type = type; + this.fieldName = fieldName; + } + + public FieldDeclaration(Type type, String fieldName, String initializer) { + this.type = type; + this.fieldName = fieldName; + this.initializer = initializer; + } + + public Type getType() { + return type; + } + + public void setType(Type type) { + this.type = type; + } + + public String getName() { + return fieldName; + } + + public void setName(String fieldName) { + this.fieldName = fieldName; + } + + public String getInitializer() { + return initializer; + } + + public void setInitializer(String initializer) { + this.initializer = initializer; + } + + @Override + public Annotation getAnnotation(String name) { + return annotations.get(name); + } + + @Override + public Collection getAnnotations() { + return annotations.values(); + } + + @Override + public void addAnnotation(Annotation annotation) { + annotations.put(annotation.getElementName(), annotation); + } + + public String toString() { + String code = ""; + for (Annotation annotation: getAnnotations()) { + code += annotation.toString() + "\n"; + } + if (initializer == null) { + code += "private " + type.getInterfaceTypeName() + " " + fieldName + ";\n"; + } else { + code += "private " + type.getInterfaceTypeName() + " " + fieldName + " = " + initializer + ";\n"; + } + return code; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/code/ast/IAnnotatable.java b/AlgebraicDataflowArchitectureModel/src/code/ast/IAnnotatable.java new file mode 100644 index 0000000..db02277 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/code/ast/IAnnotatable.java @@ -0,0 +1,9 @@ +package code.ast; + +import java.util.Collection; + +public interface IAnnotatable { + Annotation getAnnotation(String name); + Collection getAnnotations(); + void addAnnotation(Annotation annotation); +} diff --git a/AlgebraicDataflowArchitectureModel/src/code/ast/ImportDeclaration.java b/AlgebraicDataflowArchitectureModel/src/code/ast/ImportDeclaration.java new file mode 100644 index 0000000..fdb6309 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/code/ast/ImportDeclaration.java @@ -0,0 +1,23 @@ +package code.ast; + +import java.io.Serializable; + +public class ImportDeclaration implements Serializable { + private String name; + + public ImportDeclaration(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String toString() { + return "import " + name + ";\n"; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/code/ast/MethodDeclaration.java b/AlgebraicDataflowArchitectureModel/src/code/ast/MethodDeclaration.java new file mode 100644 index 0000000..fad7629 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/code/ast/MethodDeclaration.java @@ -0,0 +1,158 @@ +package code.ast; + +import java.util.List; +import java.util.Map; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; + +import models.algebra.Type; + +public class MethodDeclaration extends BodyDeclaration implements IAnnotatable { + private String name = null; + private boolean isConstructor = false; + private Type returnType = null; + private List parameters = null; + private Block body = null; + private Map annotations = new HashMap<>(); + private Throws thrws = null; + + public MethodDeclaration(String methodName) { + this(methodName, false); + } + + public MethodDeclaration(String methodName, Type returnType) { + this(methodName, false); + this.returnType = returnType; + } + + public MethodDeclaration(String methodName, boolean isConstructor) { + this.name = methodName; + this.isConstructor = isConstructor; + } + + public MethodDeclaration(String methodName, boolean isConstructor, Type returnType, List parameters) { + this(methodName, isConstructor, returnType, parameters, null); + } + + public MethodDeclaration(String methodName, boolean isConstructor, Type returnType, List parameters, Block body) { + this(methodName, isConstructor); + this.returnType = returnType; + this.parameters = parameters; + this.body = body; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public boolean isConstructor() { + return isConstructor; + } + + public void setConstructor(boolean isConstructor) { + this.isConstructor = isConstructor; + } + + public Type getReturnType() { + return returnType; + } + + public void setReturnType(Type returnType) { + this.returnType = returnType; + } + + public List getParameters() { + return parameters; + } + + public void setParameters(List parameters) { + this.parameters = parameters; + } + + public void addParameter(VariableDeclaration parameter) { + if (parameters == null) { + parameters = new ArrayList<>(); + } + parameters.add(parameter); + } + + public Block getBody() { + return body; + } + + public void setBody(Block body) { + this.body = body; + } + + public void addStatement(String statement) { + if (body == null) { + body = new Block(); + } + body.addStatement(statement); + } + + public void addFirstStatement(String statement) { + if (body == null) { + body = new Block(); + } + body.addFirstStatement(statement); + } + + public void addThrow(String exception) { + if (thrws == null) { + thrws = new Throws(); + } + thrws.addException(exception); + } + + @Override + public Annotation getAnnotation(String name) { + return annotations.get(name); + } + + @Override + public Collection getAnnotations() { + return annotations.values(); + } + + @Override + public void addAnnotation(Annotation annotation) { + annotations.put(annotation.getElementName(), annotation); + } + + public String toString() { + String code = ""; + for (Annotation annotation: getAnnotations()) { + code += annotation.toString() + "\n"; + } + code += "public "; + if (returnType == null) { + if(!isConstructor) code += "void "; + }else { + code += returnType.getInterfaceTypeName() + " "; + } + code += (name + "("); + if (parameters != null) { + String delimitar = ""; + for (VariableDeclaration parameter: parameters) { + code = code + delimitar + parameter.toString(); + delimitar = ", "; + } + } + code += ") "; + if (thrws != null) { + code += thrws.toString() + " "; + } + code += "{\n"; + if (body != null) { + code += CodeUtil.insertTab(body.toString()); + } + code += "}"; + return code; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/code/ast/Modifier.java b/AlgebraicDataflowArchitectureModel/src/code/ast/Modifier.java new file mode 100644 index 0000000..7d1645a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/code/ast/Modifier.java @@ -0,0 +1,29 @@ +package code.ast; + +public class Modifier extends ASTNode { + public static final int ABSTRACT = 0x0400; + public static final int PRIVATE = 0x0002; + public static final int PROTECTED = 0x0004; + public static final int PUBLIC = 0x0001; + public static final int STATIC = 0x0008; + + public static boolean isAbstract(int flags) { + return (flags & ABSTRACT) != 0; + } + + public static boolean isPrivate(int flags) { + return (flags & PRIVATE) != 0; + } + + public static boolean isProtected(int flags) { + return (flags & PROTECTED) != 0; + } + + public static boolean isPublic(int flags) { + return (flags & PUBLIC) != 0; + } + + public static boolean isStatic(int flags) { + return (flags & STATIC) != 0; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/code/ast/Throws.java b/AlgebraicDataflowArchitectureModel/src/code/ast/Throws.java new file mode 100644 index 0000000..ee9ddb2 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/code/ast/Throws.java @@ -0,0 +1,29 @@ +package code.ast; + +import java.util.Set; +import java.util.HashSet; + +public class Throws extends ASTNode { + private Set exceptions = new HashSet<>(); + + public Throws() { + } + + public void addException(String exception) { + exceptions.add(exception); + } + + public Set getExceptions() { + return exceptions; + } + + public String toString() { + String code = "throws "; + String delimiter = ""; + for (String exception: exceptions) { + code += delimiter + exception; + delimiter = ", "; + } + return code; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/code/ast/TypeDeclaration.java b/AlgebraicDataflowArchitectureModel/src/code/ast/TypeDeclaration.java new file mode 100644 index 0000000..0406d59 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/code/ast/TypeDeclaration.java @@ -0,0 +1,85 @@ +package code.ast; + +import java.util.List; +import java.util.Map; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; + +public class TypeDeclaration extends AbstractTypeDeclaration implements IAnnotatable { + private List fields = new ArrayList<>(); + private List methods = new ArrayList<>(); + private Map annotations = new HashMap<>(); + + public TypeDeclaration(String typeName) { + this.typeName = typeName; + } + + public TypeDeclaration(String typeName, List fields) { + this.typeName = typeName; + this.fields = fields; + } + + public TypeDeclaration(String typeName, List fields, List methods) { + this.typeName = typeName; + this.fields = fields; + this.methods = methods; + } + + public void addField(FieldDeclaration field) { + fields.add(field); + } + + public void addMethod(MethodDeclaration method) { + methods.add(method); + } + + public void removeMethod(MethodDeclaration method) { + methods.remove(method); + } + + public List getFields() { + return fields; + } + + public List getMethods() { + return methods; + } + + public MethodDeclaration createConstructor() { + MethodDeclaration constructor = new MethodDeclaration(typeName, true); + addMethod(constructor); + return constructor; + } + + @Override + public Annotation getAnnotation(String name) { + return annotations.get(name); + } + + @Override + public Collection getAnnotations() { + return annotations.values(); + } + + @Override + public void addAnnotation(Annotation annotation) { + annotations.put(annotation.getElementName(), annotation); + } + + public String toString() { + String code = ""; + for (Annotation annotation: getAnnotations()) { + code += annotation.toString() + "\n"; + } + code += "public class " + typeName + " {\n"; + for (FieldDeclaration f: fields) { + code += "\t" + f.toString(); + } + for (MethodDeclaration m: methods) { + code += CodeUtil.insertTab(m.toString()); + } + code += "}"; + return code; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/code/ast/VariableDeclaration.java b/AlgebraicDataflowArchitectureModel/src/code/ast/VariableDeclaration.java new file mode 100644 index 0000000..d4f5c86 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/code/ast/VariableDeclaration.java @@ -0,0 +1,58 @@ +package code.ast; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import models.algebra.Type; + +public class VariableDeclaration extends ASTNode implements IAnnotatable{ + private Type type; + private String variableName; + private Map annotations = new HashMap<>(); + + public VariableDeclaration(Type type, String variableName) { + this.type = type; + this.variableName = variableName; + } + + public Type getType() { + return type; + } + + public void setType(Type type) { + this.type = type; + } + + public String getName() { + return variableName; + } + + public void setName(String variableName) { + this.variableName = variableName; + } + + @Override + public Annotation getAnnotation(String name) { + return annotations.get(name); + } + + @Override + public Collection getAnnotations() { + return annotations.values(); + } + + @Override + public void addAnnotation(Annotation annotation) { + annotations.put(annotation.getElementName(), annotation); + } + + public String toString() { + String code = ""; + for (Annotation annotation: getAnnotations()) { + code += annotation.toString() + " "; + } + code += type.getInterfaceTypeName() + " " + variableName; + return code; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/generators/CodeGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/CodeGenerator.java new file mode 100644 index 0000000..8f05eb4 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/generators/CodeGenerator.java @@ -0,0 +1,394 @@ +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 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.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.controlFlowModel.EventChannelObjectNode; +import models.controlFlowModel.ObjectNode; +import models.controlFlowModel.StatefulObjectNode; +import models.dataConstraintModel.Channel; +import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.ResourcePath; +import models.dataFlowModel.DataFlowEdge; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.IFlowGraph; +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 getterOfResourceState = "getValue"; + public static final String updateMethodName = "update"; + private static String mainTypeName = null; + + public static String getMainTypeName() { + return mainTypeName; + } + + public static void setMainTypeName(String mainTypeName) { + CodeGenerator.mainTypeName = mainTypeName; + } + + public static void resetMainTypeName() { + CodeGenerator.mainTypeName = null; + } + + /** + * Generate source codes in specified language from data-flow/control-flow graph. + * + * @param model architecture model + * @param flowGraph data-flow or control-flow graph + * @param langSpec specified language + * @return source codes + */ + public ArrayList generateCode(DataTransferModel model, IFlowGraph flowGraph, ILanguageSpecific langSpec) { + ArrayList codes = new ArrayList<>(); + + // Sort the all components. + ArrayList> components = determineComponentOrder(flowGraph); + + // 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, mainComponent, mainConstructor, codes, langSpec); + + return codes; + } + + public abstract void generateCodeFromFlowGraph(DataTransferModel model, IFlowGraph flowGraph, ArrayList> components, + TypeDeclaration mainComponent, MethodDeclaration mainConstructor, ArrayList codes, ILanguageSpecific langSpec); + + private static ArrayList> determineComponentOrder(IFlowGraph graph) { + ArrayList> objects = new ArrayList<>(); + Set> visited = new HashSet<>(); + Map> allNodeSets = graph.getAllNodes(); + for (Set nodeSet: allNodeSets.values()) { + if (!(nodeSet.iterator().next() instanceof EventChannelObjectNode)) { + topologicalSort(allNodeSets, nodeSet, visited, objects); + } + } + return objects; + } + + private static void topologicalSort(Map> allNodeSets, Set curNodeSet, Set> visited, List> orderedList) { + if (visited.contains(curNodeSet)) return; + visited.add(curNodeSet); + // a caller is before the callee + for (Node curNode: curNodeSet) { + for (Edge e: curNode.getInEdges()) { + if (!(e.getSource() instanceof EventChannelObjectNode)) { + if (!(e instanceof DataFlowEdge) || ((PushPullAttribute)((DataFlowEdge) e).getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { + // for a call edge or PUSH data-flow edge + topologicalSort(allNodeSets, allNodeSets.get(e.getSource()), visited, orderedList); + } + } + } + } +// if (curNode instanceof StatefulObjectNode) { +// ResourceNode resNode = ((StatefulObjectNode) curNode).getResource(); +// for (Node n: allNodes) { +// if (n != curNode && n instanceof StatefulObjectNode && !visited.contains(n)) { +// if (resNode.equals(((StatefulObjectNode) n).getResource())) { +// // n and curNode are identical (one in PUSH call graph and another in PULL call graph). +// visited.add(n); +// for (Edge e: n.getInEdges()) { +// if (!(e.getSource() instanceof EntryPointObjectNode)) { +// if (!(e instanceof DataFlowEdge)) { +// // for a call edge +// topologicalSort(allNodes, e.getSource(), visited, orderedList); +// } +// } +// } +// } +// } +// } +// } + if (curNodeSet.iterator().next() instanceof ResourceNode) { + for (Node curNode: curNodeSet) { + for (Edge e: curNode.getOutEdges()) { + DataFlowEdge de = (DataFlowEdge) e; + if (((PushPullAttribute) de.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { + // for a PULL data-flow edge + topologicalSort(allNodeSets, allNodeSets.get(e.getDestination()), visited, orderedList); + } + } + } + } + // For reference resources. + ResourceNode cn = null; + Node curNode = curNodeSet.iterator().next(); + if (curNode instanceof ResourceNode) { + cn = (ResourceNode) curNode; + } else if (curNode instanceof StatefulObjectNode) { + cn = ((StatefulObjectNode) curNode).getResource(); + } + if (cn != null) { + for (Node n: allNodeSets.keySet()) { + ResourceNode rn = null; + if (n instanceof ResourceNode) { + rn = (ResourceNode) n; + } else if (n instanceof StatefulObjectNode) { + rn = ((StatefulObjectNode) n).getResource(); + } + if (rn != null) { + for (Edge e: rn.getOutEdges()) { + DataTransferChannel ch = ((DataFlowEdge) e).getChannel(); + for (ChannelMember m: ch.getReferenceChannelMembers()) { + if (m.getResource() == cn.getResource()) { + topologicalSort(allNodeSets, allNodeSets.get(n), visited, orderedList); + } + } + } + } + } + } + orderedList.add(0, curNodeSet); + } + + protected void updateMainComponent(TypeDeclaration mainType, MethodDeclaration mainConstructor, Node componentNode, + MethodDeclaration constructor, 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.getResource().getResourceName(); + } else if (componentNode instanceof ObjectNode) { + nodeName = ((ObjectNode) componentNode).getName(); + if (componentNode instanceof StatefulObjectNode) { + resNode = ((StatefulObjectNode) componentNode).getResource(); + } + } + 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<>(); + 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 void addReference(TypeDeclaration component, MethodDeclaration constructor, Node dstNode, ILanguageSpecific langSpec) { + String dstNodeName = null; + if (dstNode instanceof ResourceNode) { + dstNodeName = ((ResourceNode) dstNode).getResource().getResourceName(); + } else if (dstNode instanceof ObjectNode) { + dstNodeName = ((ObjectNode) dstNode).getName(); + } + String dstComponentName = langSpec.toComponentName(dstNodeName); + 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()); + } + + protected void fillGetterMethodToReturnStateField(MethodDeclaration getter, Type resStateType, ILanguageSpecific langSpec) { + // returns the state field when all incoming data-flow edges are PUSH-style. + if (langSpec.isValueType(resStateType)) { + getter.addStatement(langSpec.getReturnStatement(langSpec.getFieldAccessor(fieldOfResourceState)) + langSpec.getStatementDelimiter()); // return value; + } else { + // copy the current state to be returned as a 'value' + String implTypeName = resStateType.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; +// } + List parameters = new ArrayList<>(); + parameters.add(langSpec.getFieldAccessor(fieldOfResourceState)); + getter.addStatement(langSpec.getReturnStatement(langSpec.getConstructorInvocation(implTypeName, parameters)) + langSpec.getStatementDelimiter()); // return new Resource(value); + } + } + + protected void declareAccessorInMainComponent(TypeDeclaration mainComponent, ResourcePath accessRes, ILanguageSpecific langSpec) { + MethodDeclaration getter = langSpec.newMethodDeclaration("get" + langSpec.toComponentName(accessRes.getResourceName()), accessRes.getResourceStateType()); + Block block = new Block(); + block.addStatement(langSpec.getReturnStatement(langSpec.getMethodInvocation(accessRes.getResourceName(), getterOfResourceState)) + langSpec.getStatementDelimiter()); + getter.setBody(block); + mainComponent.addMethod(getter); + } + + protected void declareFieldsToReferenceResources(DataTransferModel model, ResourceNode resourceNode, TypeDeclaration component, MethodDeclaration constructor, + final List depends, ILanguageSpecific langSpec) { + Set refs = new HashSet<>(); + for (Channel ch : model.getChannels()) { + DataTransferChannel c = (DataTransferChannel) ch; + if (c.getInputResources().contains(resourceNode.getResource())) { + for (ResourcePath id: c.getReferenceResources()) { + if (!refs.contains(id) && !depends.contains(id)) { + refs.add(id); + String refResName = langSpec.toComponentName(id.getResourceName()); + component.addField(langSpec.newFieldDeclaration(new Type(refResName, refResName), id.getResourceName())); + constructor.addParameter(langSpec.newVariableDeclaration(new Type(refResName, refResName), id.getResourceName())); + constructor.getBody().addStatement(langSpec.getFieldAccessor(id.getResourceName()) + langSpec.getAssignment() + id.getResourceName() + langSpec.getStatementDelimiter()); + } + } + } + } + } + + protected MethodDeclaration getUpdateMethod(Edge inEdge, TypeDeclaration component, + Map>> dataFlowInform, ILanguageSpecific langSpec) { + List passedResoueces = dataFlowInform.get(inEdge).get(PushPullValue.PUSH); + String methodName = updateMethodName; + for (ResourceNode rn: passedResoueces) { + ResourcePath rId = rn.getResource(); + methodName += langSpec.toComponentName(rId.getResourceName()); + } + return getMethod(component, methodName); + } + + protected MethodDeclaration getInputMethod(ResourceNode resourceNode, DataTransferChannel ch, TypeDeclaration component) { + MethodDeclaration input = null; + for (ChannelMember out : ch.getOutputChannelMembers()) { + if (out.getResource().equals(resourceNode.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(ResourcePath target, ResourcePath from) { + if (target.equals(from)) { + return new Field(fieldOfResourceState, + target.getResourceStateType() != null ? target.getResourceStateType() + : DataConstraintModel.typeInt); + } + // use the cached value as the current state + return new Field(target.getResourceName(), + target.getResourceStateType() != null ? target.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { + return new Parameter(target.getResourceName(), + target.getResourceStateType() != null ? target.getResourceStateType() + : DataConstraintModel.typeInt); + } + }; + } + + protected IResourceStateAccessor getPullAccessor() { + return new IResourceStateAccessor() { + @Override + public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) { + if (target.equals(from)) { + return new Field(fieldOfResourceState, + target.getResourceStateType() != null ? target.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + Term getter = new Term(new Symbol(getterOfResourceState, 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(getterOfResourceState, 1, Symbol.Type.METHOD)); + getter.addChild(new Field(target.getResourceName(), target.getResourceStateType())); + return getter; + } + }; + } + + protected IResourceStateAccessor getRefAccessor() { + return new IResourceStateAccessor() { + @Override + public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) { + if (target.equals(from)) { + return new Field(fieldOfResourceState, + target.getResourceStateType() != null ? target.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + return new Parameter(target.getResourceName(), + target.getResourceStateType() != null ? target.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { + return new Parameter(target.getResourceName(), + target.getResourceStateType() != null ? target.getResourceStateType() + : DataConstraintModel.typeInt); + } + }; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromControlFlowGraph.java b/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromControlFlowGraph.java new file mode 100644 index 0000000..aded588 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromControlFlowGraph.java @@ -0,0 +1,925 @@ +package generators; + +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.Set; + +import code.ast.Block; +import code.ast.CompilationUnit; +import code.ast.FieldDeclaration; +import code.ast.MethodDeclaration; +import code.ast.TypeDeclaration; +import code.ast.VariableDeclaration; +import models.Edge; +import models.Node; +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Field; +import models.algebra.InvalidMessage; +import models.algebra.ParameterizedIdentifierIsFutureWork; +import models.algebra.Symbol; +import models.algebra.Term; +import models.algebra.Type; +import models.algebra.UnificationFailed; +import models.algebra.ValueUndefined; +import models.algebra.Variable; +import models.controlFlowModel.ControlFlowGraph; +import models.controlFlowModel.EventChannelObjectNode; +import models.controlFlowModel.ObjectNode; +import models.controlFlowModel.StatefulObjectNode; +import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.ResourcePath; +import models.dataFlowModel.DataFlowEdge; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.IFlowGraph; +import models.dataFlowModel.PushPullAttribute; +import models.dataFlowModel.PushPullValue; +import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork; +import models.dataFlowModel.ResourceNode; +import models.dataFlowModel.StoreAttribute; +import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor; + +public class CodeGeneratorFromControlFlowGraph extends CodeGenerator { + + @Override + public void generateCodeFromFlowGraph(DataTransferModel model, IFlowGraph flowGraph, ArrayList> components, + TypeDeclaration mainComponent, MethodDeclaration mainConstructor, ArrayList codes, ILanguageSpecific langSpec) { + // Reconstruct data-flow information. + Map>> dataFlowInform = new HashMap<>(); + ControlFlowGraph controlFlowGraph = (ControlFlowGraph) flowGraph; + for (Node root: controlFlowGraph.getPushCallGraph().getRootNodes()) { + Set treeResources = traverseCallTree(root, new HashSet<>()); + annotateDataFlowAttributes(root, dataFlowInform, treeResources, new ArrayList<>()); + removeRedundantAttributes(root, dataFlowInform); + } + for (Node root: controlFlowGraph.getPullCallGraph().getRootNodes()) { + Set treeResources = traverseCallTree(root, new HashSet<>()); + annotateDataFlowAttributes(root, dataFlowInform, treeResources, new ArrayList<>()); + removeRedundantAttributes(root, dataFlowInform); + } + + // For each component other than the main component. + Map componentMap = new HashMap<>(); + for (Set componentNodeSet: components) { + // Declare this component. + Node componentNode = componentNodeSet.iterator().next(); + String componentName = langSpec.toComponentName(((ObjectNode) componentNode).getName()); + TypeDeclaration component = langSpec.newTypeDeclaration(componentName); + for (Node compNode: componentNodeSet) { + componentMap.put(compNode, component); + } + + // Declare the constructor and the fields to refer to the callee components. + List depends = new ArrayList<>(); + MethodDeclaration constructor = declareConstructorAndFieldsToCalleeComponents(componentNodeSet, component, depends, langSpec); + + if (componentNode instanceof StatefulObjectNode) { + // For this resource. + ResourceNode resourceNode = ((StatefulObjectNode) componentNode).getResource(); + ResourcePath res = resourceNode.getResource(); + Type resStateType = res.getResourceStateType(); + + // Declare the field in this resource to store the state. + if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { + FieldDeclaration stateField = langSpec.newFieldDeclaration(resStateType, fieldOfResourceState, langSpec.getFieldInitializer(resStateType, res.getInitialValue())); + component.addField(stateField); + } + + // Declare the accessor method in the main component to call the getter method. + declareAccessorInMainComponent(mainComponent, res, langSpec); + + // Declare the fields to refer to reference resources. + declareFieldsToReferenceResources(model, resourceNode, component, constructor, depends, langSpec); + } + + // Update the main component for this component. + updateMainComponent(mainComponent, mainConstructor, componentNode, constructor, langSpec); + if (constructor.getParameters() == null) { + component.removeMethod(constructor); + } + + // Add compilation unit for this component. + CompilationUnit cu = langSpec.newCompilationUnit(component); + codes.add(cu); + } + + // Declare and Fill the getter method to return the resource state. + for (Node node: controlFlowGraph.getPushCallGraph().getNodes()) { + TypeDeclaration component = componentMap.get(node); + if (node instanceof StatefulObjectNode) { + ResourceNode resourceNode = ((StatefulObjectNode) node).getResource(); + Type resStateType = resourceNode.getResource().getResourceStateType(); + if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { + // Declare the getter method in this resource to obtain the state. + MethodDeclaration getter = langSpec.newMethodDeclaration(getterOfResourceState, resStateType); + component.addMethod(getter); + fillGetterMethodToReturnStateField(getter, resourceNode.getResource().getResourceStateType(), langSpec); // return this.value; + } + } + } + + for (Node node: controlFlowGraph.getPullCallGraph().getNodes()) { + String nodeName = ((ObjectNode) node).getName(); + if (componentMap.get(node) == null) { + for (Node node2: componentMap.keySet()) { + if (((ObjectNode) node2).getName().equals(nodeName)) { + componentMap.put(node, componentMap.get(node2)); // Since nodes shared by PUSH and PULL call graphs are duplicated. + break; + } + } + } + } + + // Declare other getter methods. + for (Node root: controlFlowGraph.getPullCallGraph().getRootNodes()) { + MethodDeclaration getter = declareAndFillGetterMethods(root, null, dataFlowInform, componentMap, langSpec); + } + + // Declare update and input methods. + for (Node root: controlFlowGraph.getPushCallGraph().getRootNodes()) { + MethodDeclaration input = declareAndFillUpdateAndInputMethods(root, null, null, dataFlowInform, componentMap, langSpec); + mainComponent.addMethod(input); + } + } + + private Set traverseCallTree(Node node, Set visited) { + if (node instanceof StatefulObjectNode) { + ResourceNode resNode = ((StatefulObjectNode) node).getResource(); + visited.add(resNode); + } + // Traverse the call tree. + for (Edge e: node.getOutEdges()) { + visited = traverseCallTree(e.getDestination(), visited); + } + return visited; + } + + private void annotateDataFlowAttributes(Node node, Map>> dataFlowInform, Set resourceNodes, List path) { + if (node instanceof StatefulObjectNode) { + // Add data-flow attributes to the path to node. + ResourceNode resNode = ((StatefulObjectNode) node).getResource(); + for (Edge outE: resNode.getOutEdges()) { + // If resNode is the source of data-flow. + ResourceNode dstOfDataFlowNode = (ResourceNode) outE.getDestination(); + if (resourceNodes.contains(dstOfDataFlowNode)) { + // If the data transfer is closed within this call tree. + for (Edge e: path) { + // Add pull attributes to the path to resNode. + Map> edgeAttributes = dataFlowInform.get(e); + if (edgeAttributes == null) { + edgeAttributes = new HashMap<>(); + dataFlowInform.put(e, edgeAttributes); + } + List pullSrcs = edgeAttributes.get(PushPullValue.PULL); + if (pullSrcs == null) { + pullSrcs = new ArrayList<>(); + edgeAttributes.put(PushPullValue.PULL, pullSrcs); + } + pullSrcs.add(resNode); + } + } + } + for (Edge inE: resNode.getInEdges()) { + // If resNode is a destination of data-flow. + ResourceNode srcOfDataFlowNode = (ResourceNode) inE.getSource(); + if (resourceNodes.contains(srcOfDataFlowNode)) { + // If the data transfer is closed done within this call tree. + for (Edge e: path) { + // Add push attributes to the path to resNode. + Map> edgeAttributes = dataFlowInform.get(e); + if (edgeAttributes == null) { + edgeAttributes = new HashMap<>(); + dataFlowInform.put(e, edgeAttributes); + } + List pushSrcs = edgeAttributes.get(PushPullValue.PUSH); + if (pushSrcs == null) { + pushSrcs = new ArrayList<>(); + edgeAttributes.put(PushPullValue.PUSH, pushSrcs); + } + pushSrcs.add(srcOfDataFlowNode); + } + } + } + } + // Traverse the call tree. + for (Edge e: node.getOutEdges()) { + path.add(e); + annotateDataFlowAttributes(e.getDestination(), dataFlowInform, resourceNodes, path); + path.remove(e); + } + } + + private void removeRedundantAttributes(Node node, Map>> dataFlowInform) { + // Traverse the call tree. + for (Edge e: node.getOutEdges()) { + // Remove attributes that are common to PUSH and PULL. + if (dataFlowInform.get(e) == null) { + dataFlowInform.put(e, new HashMap<>()); + } + List pushFlows = dataFlowInform.get(e).get(PushPullValue.PUSH); + List pullFlows = dataFlowInform.get(e).get(PushPullValue.PULL); + if (pushFlows == null) { + pushFlows = new ArrayList<>(); + } + if (pullFlows == null) { + pullFlows = new ArrayList<>(); + } + List pushFlowsOrg = new ArrayList<>(pushFlows); + for (ResourceNode r: pullFlows) { + pushFlows.remove(r); + } + for (ResourceNode r: pushFlowsOrg) { + pullFlows.remove(r); + } + pushFlows = new ArrayList<>(new HashSet<>(pushFlows)); + pullFlows = new ArrayList<>(new HashSet<>(pullFlows)); + dataFlowInform.get(e).put(PushPullValue.PUSH, pushFlows); + dataFlowInform.get(e).put(PushPullValue.PULL, pullFlows); + removeRedundantAttributes(e.getDestination(), dataFlowInform); + } + } + + private MethodDeclaration declareConstructorAndFieldsToCalleeComponents(Set componentNodeSet, TypeDeclaration component, + List depends, ILanguageSpecific langSpec) { + // Declare a constructor in each component. + MethodDeclaration constructor = component.createConstructor(); + Block block = new Block(); + constructor.setBody(block); + + // Declare fields in each component. (for control-flow graph) + for (Node componentNode: componentNodeSet) { + for (Edge e: componentNode.getOutEdges()) { + ObjectNode dstNode = (ObjectNode) e.getDestination(); + addReference(component, constructor, dstNode, langSpec); + if (dstNode instanceof StatefulObjectNode) { + ResourcePath dstRes = ((StatefulObjectNode) dstNode).getResource().getResource(); + if (!depends.contains(dstRes)) depends.add(dstRes); + } + } + } + return constructor; + } + + + private MethodDeclaration declareAndFillGetterMethods(Node node, Edge inEdge, + Map>> dataFlowInform, Map componentMap, + ILanguageSpecific langSpec) { + TypeDeclaration component = componentMap.get(node); + List resourcesToReturn = null; + if (inEdge != null) { + resourcesToReturn = dataFlowInform.get(inEdge).get(PushPullValue.PULL); + } + if (node instanceof StatefulObjectNode) { + ResourceNode resourceNode = ((StatefulObjectNode) node).getResource(); + Type resStateType = resourceNode.getResource().getResourceStateType(); + MethodDeclaration getter = langSpec.newMethodDeclaration(getterOfResourceState, resStateType); + MethodDeclaration getter2 = getMethod(component, getter.getName()); + if (getter2 == null) { + component.addMethod(getter); + } else { + getter = getter2; + } + if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { + if (getter2 == null) { + // Declare the getter method in this resource to obtain the state. + fillGetterMethodToReturnStateField(getter, resourceNode.getResource().getResourceStateType(), langSpec); // return this.value; + } + } else { + // Invocations to other getter methods when at least one incoming data-flow edges is PULL-style. + boolean isContainedPush = false; + DataTransferChannel ch = null; + HashMap inputResourceToStateAccessor = new HashMap<>(); + for (Edge eIn: resourceNode.getInEdges()) { + DataFlowEdge dataFlowInEdge = (DataFlowEdge) eIn; + if (((PushPullAttribute) dataFlowInEdge.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { + // PUSH data transfer + isContainedPush = true; + inputResourceToStateAccessor.put(((ResourceNode) dataFlowInEdge.getSource()).getResource(), getPushAccessor()); + } else { + // PULL data transfer + for (Edge callEdge: node.getOutEdges()) { + // For each call edge. + ObjectNode calledNode = (ObjectNode) callEdge.getDestination(); + List returnedResources = dataFlowInform.get(callEdge).get(PushPullValue.PULL); + if (returnedResources.contains((ResourceNode) dataFlowInEdge.getSource())) { + if (returnedResources.size() == 1) { + MethodDeclaration nextGetter = declareAndFillGetterMethods(calledNode, callEdge, dataFlowInform, componentMap, langSpec); + inputResourceToStateAccessor.put(((ResourceNode) dataFlowInEdge.getSource()).getResource(), getPullAccessor(calledNode.getName(), nextGetter.getName())); + break; + } else { + MethodDeclaration nextGetter = declareAndFillGetterMethods(calledNode, callEdge, dataFlowInform, componentMap, langSpec); + int idx = returnedResources.indexOf((ResourceNode) dataFlowInEdge.getSource()); + int len = returnedResources.size(); + inputResourceToStateAccessor.put(((ResourceNode) dataFlowInEdge.getSource()).getResource(), + getPullAccessor(langSpec.getTupleGet(langSpec.getMethodInvocation(langSpec.getFieldAccessor(calledNode.getName()), nextGetter.getName()), idx, len))); + break; + } + } + } + ch = dataFlowInEdge.getChannel(); // Always unique. + } + } + // For reference channel members. + for (ChannelMember c: ch.getReferenceChannelMembers()) { + inputResourceToStateAccessor.put(c.getResource(), getPullAccessor()); // by pull data transfer + } + + // Add a return statement. + try { + for (ChannelMember out: ch.getOutputChannelMembers()) { + if (out.getResource().equals(resourceNode.getResource())) { + String[] sideEffects = new String[] {""}; + // The following process is common to the cases of 1) and 2). + // 1) All incoming edges are in PULL-style. + // 2) At least one incoming edge is in PUSH-style. + String curState = ch.deriveUpdateExpressionOf(out, getPullAccessor(), inputResourceToStateAccessor).toImplementation(sideEffects); + getter.addStatement(sideEffects[0] + langSpec.getReturnStatement(curState) + langSpec.getStatementDelimiter()); + break; + } + } + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork + | InvalidMessage | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + } + if (resourcesToReturn == null || resourcesToReturn.size() == 1) return getter; + } else if (resourcesToReturn == null || resourcesToReturn.size() == 1) { + // Declare a mediate getter method to return a single value. + String getterMethodName = "get"; + ResourceNode returnedRes = null; + if (resourcesToReturn != null) { + returnedRes = resourcesToReturn.get(0); + } else { + // Unexpected. + } + getterMethodName += langSpec.toComponentName(returnedRes.getResource().getResourceName()) + "Value"; + MethodDeclaration mediateGetter = getMethod(component, getterMethodName); + if (mediateGetter != null) return mediateGetter; + mediateGetter = langSpec.newMethodDeclaration(getterMethodName, returnedRes.getResource().getResourceStateType()); + component.addMethod(mediateGetter); + + // Add a return statement. + if (node.getOutdegree() == 1) { + Edge callEdge = node.getOutEdges().iterator().next(); + ObjectNode calledNode = (ObjectNode) callEdge.getDestination(); + MethodDeclaration nextGetter = declareAndFillGetterMethods(calledNode, callEdge, dataFlowInform, componentMap, langSpec); + mediateGetter.addStatement( + langSpec.getReturnStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(calledNode.getName()), nextGetter.getName())) + + langSpec.getStatementDelimiter()); + } else { + // Unexpected. + } + return mediateGetter; + } + // Declare a mediate getter method to return multiple values. + String getterMethodName = "get"; + for (ResourceNode rn: resourcesToReturn) { + getterMethodName += langSpec.toComponentName(rn.getResource().getResourceName()); + } + getterMethodName += "Values"; + MethodDeclaration mediateGetter = getMethod(component, getterMethodName); + if (mediateGetter != null) return mediateGetter; + Type returnType = createReturnType(resourcesToReturn, langSpec); + mediateGetter = langSpec.newMethodDeclaration(getterMethodName, returnType); + component.addMethod(mediateGetter); + + // Add a return statement. + if (node.getOutdegree() == 1 && resourcesToReturn != null + && resourcesToReturn.equals(dataFlowInform.get(node.getOutEdges().iterator().next()).get(PushPullValue.PULL))) { + // Directly returns the returned value. + Edge outEdge = node.getOutEdges().iterator().next(); + ObjectNode dstNode = (ObjectNode) outEdge.getDestination(); + MethodDeclaration nextGetter = declareAndFillGetterMethods(dstNode, outEdge, dataFlowInform, componentMap, langSpec); + String getterInvocation = langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstNode.getName()), nextGetter.getName()); + mediateGetter.addStatement(langSpec.getReturnStatement(getterInvocation) + langSpec.getStatementDelimiter()); + } else { + List params = new ArrayList<>(); + for (ResourceNode rn: resourcesToReturn) { + ResourcePath rId = rn.getResource(); + if (rId.getResourceName().equals(((ObjectNode) node).getName())) { + params.add(langSpec.getMethodInvocation(getterOfResourceState)); + } else { + for (Edge outEdge: node.getOutEdges()) { + ObjectNode dstNode = (ObjectNode) outEdge.getDestination(); + List returnedResources = dataFlowInform.get(outEdge).get(PushPullValue.PULL); + if (returnedResources.contains(rn)) { + if (returnedResources.size() == 1) { + MethodDeclaration nextGetter = declareAndFillGetterMethods(dstNode, outEdge, dataFlowInform, componentMap, langSpec); + params.add(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstNode.getName()), nextGetter.getName())); + } else { + MethodDeclaration nextGetter = declareAndFillGetterMethods(dstNode, outEdge, dataFlowInform, componentMap, langSpec); + int idx = returnedResources.indexOf(rn); + int len = returnedResources.size(); + params.add(langSpec.getTupleGet(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstNode.getName()), nextGetter.getName()), idx, len)); + } + break; + } + } + } + } + mediateGetter.addStatement( + langSpec.getReturnStatement(langSpec.getConstructorInvocation(returnType.getImplementationTypeName(), params)) + + langSpec.getStatementDelimiter()); + } + return mediateGetter; + } + + private MethodDeclaration declareAndFillUpdateAndInputMethods(Node node, Edge inEdge, Node prevResNode, + Map>> dataFlowInform, Map componentMap, ILanguageSpecific langSpec) { + TypeDeclaration component = componentMap.get(node); + List resourcesToReturn = null; + List resourcesToReceive = null; + if (dataFlowInform.get(inEdge) != null) { + resourcesToReturn = dataFlowInform.get(inEdge).get(PushPullValue.PULL); + resourcesToReceive = dataFlowInform.get(inEdge).get(PushPullValue.PUSH); + } + if (node instanceof StatefulObjectNode) { + // Declare update or input method in the resource component. + ResourceNode resourceNode = ((StatefulObjectNode) node).getResource(); + MethodDeclaration updateOrInput = null; + if (!(prevResNode instanceof EventChannelObjectNode) + || (resourcesToReceive != null && resourcesToReceive.size() > 0)) { + updateOrInput = getUpdateMethod(inEdge, component, dataFlowInform, langSpec); + if (updateOrInput != null) return updateOrInput; + // Declare an update method. + updateOrInput = declareUpdateMethod(node, inEdge, component, dataFlowInform, langSpec); + } else { + DataTransferChannel ch = ((EventChannelObjectNode) prevResNode).getIOChannel(); + updateOrInput = getInputMethod(resourceNode, ch, component); + if (updateOrInput != null) return updateOrInput; + // Declare an input method. + updateOrInput = declareInputMethod(resourceNode, ch, langSpec); + } + component.addMethod(updateOrInput); + + Map resToVar = new HashMap<>(); + Map> varToRes = new HashMap<>(); + for (Edge outEdge: node.getOutEdges()) { + Node dstNode = outEdge.getDestination(); + MethodDeclaration calleeMethod = declareAndFillUpdateAndInputMethods(dstNode, outEdge, node, dataFlowInform, componentMap, langSpec); + // Add a statement to call the destination method. + List returnedResources = dataFlowInform.get(outEdge).get(PushPullValue.PULL); + String varName = addInvocationInResourceUpdate(node, updateOrInput, calleeMethod, ((ObjectNode) dstNode).getName(), returnedResources, langSpec); + if (varName != null && returnedResources != null) { + for (ResourceNode rn: returnedResources) { + String resName = rn.getResource().getResourceName(); + resToVar.put(rn, resName); + varToRes.put(resName, Arrays.asList(new ResourceNode[] {rn})); + } +// // Alternative implementation. +// varToRes.put(varName, returnedResources); +// for (ResourceNode rn: returnedResources) { +// resToVar.put(rn, varName); +// } + } + } + + if (resourcesToReturn != null && resourcesToReturn.size() > 0) { + // Set the return type and add a return statement. + Type returnType = createReturnType(resourcesToReturn, langSpec); + updateOrInput.setReturnType(returnType); + String returnValue = createReturnValue(resourcesToReturn, node, returnType, resToVar, varToRes, langSpec); + updateOrInput.addStatement(langSpec.getReturnStatement(returnValue) + langSpec.getStatementDelimiter()); + } + return updateOrInput; + } else if (node instanceof EventChannelObjectNode) { + // Declare an input method. + MethodDeclaration input = null; + for (Edge outEdge: node.getOutEdges()) { + Node dstNode = outEdge.getDestination(); + MethodDeclaration calleeMethod = declareAndFillUpdateAndInputMethods(dstNode, outEdge, node, dataFlowInform, componentMap, langSpec); + if (input == null) { + // Declare an input method. + if (calleeMethod.getParameters() != null) { + input = langSpec.newMethodDeclaration(calleeMethod.getName(), false, null, new ArrayList<>(calleeMethod.getParameters())); + } else { + input = langSpec.newMethodDeclaration(calleeMethod.getName(), null); + } + } + // Add a statement to call the destination method. + String varName = addInvocationInMediatorUpdate(input, calleeMethod, ((ObjectNode) dstNode).getName(), dataFlowInform.get(outEdge).get(PushPullValue.PULL), langSpec); + } + return input; + } else { + // Declare update or input method in the mediate component. + List updateMethods = getUpdateMethods(component); + if (updateMethods.size() > 0) return updateMethods.get(0); + MethodDeclaration updateOrInput = null; + if (!(prevResNode instanceof EventChannelObjectNode) + || (resourcesToReceive != null && resourcesToReceive.size() > 0)) { + // Declare an update method. + updateOrInput = declareUpdateMethod(node, inEdge, component, dataFlowInform, langSpec); + component.addMethod(updateOrInput); + } + + if (node.getOutdegree() == 1 && resourcesToReturn != null && resourcesToReturn.size() > 0 + && resourcesToReturn.equals(dataFlowInform.get(node.getOutEdges().iterator().next()).get(PushPullValue.PULL))) { + // Directly returns the returned value. + Edge outEdge = node.getOutEdges().iterator().next(); + ObjectNode dstNode = (ObjectNode) outEdge.getDestination(); + MethodDeclaration calleeMethod = declareAndFillUpdateAndInputMethods(dstNode, outEdge, prevResNode, dataFlowInform, componentMap, langSpec); + if (updateOrInput == null && prevResNode instanceof EventChannelObjectNode) { + // Declare an input method. + if (calleeMethod.getParameters() != null) { + updateOrInput = langSpec.newMethodDeclaration(calleeMethod.getName(), false, null, new ArrayList<>(calleeMethod.getParameters())); + } else { + updateOrInput = langSpec.newMethodDeclaration(calleeMethod.getName(), null); + } + component.addMethod(updateOrInput); + } + // Set the return type and add a return statement. + updateOrInput.setReturnType(calleeMethod.getReturnType()); + String updateInvocation = langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstNode.getName()), calleeMethod.getName()); + updateOrInput.addStatement(langSpec.getReturnStatement(updateInvocation) + langSpec.getStatementDelimiter()); + } else { + Map resToVar = new HashMap<>(); + Map> varToRes = new HashMap<>(); + for (Edge outEdge: node.getOutEdges()) { + Node dstNode = outEdge.getDestination(); + MethodDeclaration calleeMethod = declareAndFillUpdateAndInputMethods(dstNode, outEdge, prevResNode, dataFlowInform, componentMap, langSpec); + if (updateOrInput == null && prevResNode instanceof EventChannelObjectNode) { + // Declare an input method. + if (calleeMethod.getParameters() != null) { + updateOrInput = langSpec.newMethodDeclaration(calleeMethod.getName(), false, null, new ArrayList<>(calleeMethod.getParameters())); + } else { + updateOrInput = langSpec.newMethodDeclaration(calleeMethod.getName(), null); + } + component.addMethod(updateOrInput); + } + // Add a statement to call the destination method. + List returnedResources = dataFlowInform.get(outEdge).get(PushPullValue.PULL); + String varName = addInvocationInMediatorUpdate(updateOrInput, calleeMethod, ((ObjectNode) dstNode).getName(), returnedResources, langSpec); + if (varName != null && returnedResources != null) { + for (ResourceNode rn: returnedResources) { + String resName = rn.getResource().getResourceName(); + resToVar.put(rn, resName); + varToRes.put(resName, Arrays.asList(new ResourceNode[] {rn})); + } +// // Alternative implementation. +// varToRes.put(varName, returnedResources); +// for (ResourceNode rn: returnedResources) { +// resToVar.put(rn, varName); +// } + } + } + if (resourcesToReturn != null && resourcesToReturn.size() > 0) { + // Set the return type and add a return statement. + Type returnType = createReturnType(resourcesToReturn, langSpec); + updateOrInput.setReturnType(returnType); + String returnValue = createReturnValue(resourcesToReturn, node, returnType, resToVar, varToRes, langSpec); + updateOrInput.addStatement(langSpec.getReturnStatement(returnValue) + langSpec.getStatementDelimiter()); + } + } + return updateOrInput; + } + } + + private MethodDeclaration declareUpdateMethod(Node node, Edge inEdge, TypeDeclaration component, + Map>> dataFlowInform, ILanguageSpecific langSpec) { + // Declare an update method in the component. + ArrayList vars = new ArrayList<>(); + List passedResoueces = dataFlowInform.get(inEdge).get(PushPullValue.PUSH); + Set passedResPaths = new HashSet<>(); + String methodName = updateMethodName; + for (ResourceNode rn: passedResoueces) { + ResourcePath resPath = rn.getResource(); + passedResPaths.add(resPath); + methodName += langSpec.toComponentName(resPath.getResourceName()); + vars.add(langSpec.newVariableDeclaration(resPath.getResourceStateType(), resPath.getResourceName())); + } + MethodDeclaration update = langSpec.newMethodDeclaration(methodName, false, null, vars); + + if (node instanceof StatefulObjectNode) { + // Add a statement to update the state field + ResourceNode resourceNode = ((StatefulObjectNode) node).getResource(); + ChannelMember inMember = null; + if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { + try { + boolean stateUpdateAdded = false; + for (Edge e: resourceNode.getInEdges()) { + DataFlowEdge re = (DataFlowEdge) e; + for (ChannelMember in: re.getChannel().getInputChannelMembers()) { + if (passedResPaths.contains(in.getResource())) { + for (ChannelMember out: re.getChannel().getOutputChannelMembers()) { + if (out.getResource().equals(resourceNode.getResource())) { + Expression updateExp = null; + if (re.getChannel().getReferenceChannelMembers().size() == 0) { + updateExp = re.getChannel().deriveUpdateExpressionOf(out, getPushAccessor()); + } else { + // if there exists one or more reference channel member. + HashMap inputResourceToStateAccessor = new HashMap<>(); + for (Edge eIn: resourceNode.getInEdges()) { + DataFlowEdge dIn = (DataFlowEdge) eIn; + inputResourceToStateAccessor.put(((ResourceNode) dIn.getSource()).getResource(), getPushAccessor()); + } + for (ChannelMember c: re.getChannel().getReferenceChannelMembers()) { + inputResourceToStateAccessor.put(c.getResource(), getRefAccessor()); + } + updateExp = re.getChannel().deriveUpdateExpressionOf(out, getPushAccessor(), 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] + langSpec.getFieldAccessor(fieldOfResourceState) + langSpec.getAssignment() + curState + langSpec.getStatementDelimiter(); // this.value = ... + } + update.addFirstStatement(updateStatement); + stateUpdateAdded = true; + inMember = in; + break; + } + } + } + if (stateUpdateAdded) break; + } + if (stateUpdateAdded) 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 (node.getIndegree() > 1 || + (node.getIndegree() == 1 && inMember != null && inMember.getStateTransition().isRightPartial())) { + // If incoming edges are multiple, or the current state of an input member is needed. + for (ResourcePath srcRes: passedResPaths) { + String srcResName = srcRes.getResourceName(); + if (langSpec.declareField()) { + // Declare the cache field. + FieldDeclaration cacheField = langSpec.newFieldDeclaration( + srcRes.getResourceStateType(), + srcResName, + langSpec.getFieldInitializer(srcRes.getResourceStateType(), srcRes.getInitialValue())); + component.addField(cacheField); + + } + // Update the cache field. + String cacheStatement = langSpec.getFieldAccessor(srcResName) + langSpec.getAssignment() + srcResName + langSpec.getStatementDelimiter(); + update.addStatement(cacheStatement); + } + } + } + return update; + } + + private MethodDeclaration declareInputMethod(ResourceNode resourceNode, DataTransferChannel ch, ILanguageSpecific langSpec) { + MethodDeclaration input = null; + for (ChannelMember out : ch.getOutputChannelMembers()) { + if (out.getResource().equals(resourceNode.getResource())) { + Expression message = out.getStateTransition().getMessageExpression(); + if (message instanceof Term) { + // Declare an input method in this component. + ArrayList params = new ArrayList<>(); + for (Variable var: message.getVariables().values()) { + params.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + } + input = langSpec.newMethodDeclaration(((Term) message).getSymbol().getImplName(), false, null, params); + } else if (message instanceof Variable) { + // Declare an input method in this component. + input = langSpec.newMethodDeclaration(((Variable) message).getName(), null); + } + + if (input != null) { + // Add a statement to update the state field to the input method. + try { + String[] sideEffects = new String[] {""}; + Expression updateExp; + updateExp = ch.deriveUpdateExpressionOf(out, getPullAccessor()); + String newState = updateExp.toImplementation(sideEffects); + String updateStatement; + if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { + updateStatement = sideEffects[0]; + } else { + updateStatement = sideEffects[0] + langSpec.getFieldAccessor(fieldOfResourceState) + langSpec.getAssignment() + newState + langSpec.getStatementDelimiter(); + } + input.addFirstStatement(updateStatement); + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork + | InvalidMessage | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + } + break; + } + } + return input; + } + + private String createReturnValue(List resourcesToReturn, Node node, Type returnType, Map resToVar, Map> varToRes, ILanguageSpecific langSpec) { + List params = new ArrayList<>(); + for (ResourceNode rn: resourcesToReturn) { + ResourcePath rId = rn.getResource(); + if (rId.getResourceName().equals(((ObjectNode) node).getName())) { + params.add(langSpec.getFieldAccessor(fieldOfResourceState)); + } else { + String varName = resToVar.get(rn); + if (varToRes.get(varName).size() == 1) { + params.add(varName); + } else { + params.add(langSpec.getTupleGet(varName, varToRes.get(varName).indexOf(rn), varToRes.get(varName).size())); + } + } + } + if (params.size() == 1) { + return params.iterator().next(); + } else { + return langSpec.getConstructorInvocation(returnType.getImplementationTypeName(), params); + } + } + + private Type createReturnType(List resourcesToReturn, ILanguageSpecific langSpec) { + if (resourcesToReturn.size() == 1) { + return resourcesToReturn.iterator().next().getResource().getResourceStateType(); + } + List compTypes = new ArrayList<>(); + for (ResourceNode rn: resourcesToReturn) { + ResourcePath rId = rn.getResource(); + compTypes.add(rId.getResourceStateType()); + } + Type returnType = langSpec.newTupleType(compTypes); + return returnType; + } + + + private String addInvocationInResourceUpdate(Node node, MethodDeclaration resourceUpdateMethod, MethodDeclaration calleeMethod, String dstNodeName, List returnResources, ILanguageSpecific langSpec) { + List params = new ArrayList<>(); + params.add(langSpec.getFieldAccessor(fieldOfResourceState)); + if (calleeMethod.getParameters() != null) { + for (VariableDeclaration v: calleeMethod.getParameters()) { + if (!((ObjectNode) node).getName().equals(v.getName())) { + params.add(v.getName()); + } + } + } +// for (ChannelMember rc: re.getChannelGenerator().getReferenceChannelMembers()) { +// // to get the value of reference member. +// IdentifierTemplate ref = rc.getIdentifierTemplate(); +// if (referredSet == null) { +// referredSet = new HashSet<>(); +// referredResources.put(update, referredSet); +// } +// if (ref != resourceNode.getIdentifierTemplate()) { +// String refVarName = ref.getResourceName(); +// if (!referredSet.contains(ref)) { +// referredSet.add(ref); +// Expression refGetter = langSpec.getPullAccessor().getCurrentStateAccessorFor(ref, ((ResourceNode) dOut.getSource()).getIdentifierTemplate()); +// String[] sideEffects = new String[] {""}; +// String refExp = refGetter.toImplementation(sideEffects); +// String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); +// resourceUpdateMethod.addFirstStatement(sideEffects[0] + langSpec.getVariableDeclaration(refTypeName, refVarName) + langSpec.getAssignment() + refExp + langSpec.getStatementDelimiter()); +// } +// params.add(refVarName); +// } +// } + if (calleeMethod.getReturnType() == null || langSpec.isVoidType(calleeMethod.getReturnType()) || returnResources == null) { + resourceUpdateMethod.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstNodeName), + calleeMethod.getName(), + params) + langSpec.getStatementDelimiter()); // this.dst.updateSrc(value, refParams); + return null; + } else { + String targetVarName = null; + if (returnResources.size() == 1) { + ResourceNode targetNode = returnResources.get(0); + targetVarName = targetNode.getResource().getResourceName(); + resourceUpdateMethod.addStatement( + langSpec.getVariableDeclaration(calleeMethod.getReturnType().getInterfaceTypeName(), targetVarName) + + langSpec.getAssignment() + + langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstNodeName), + calleeMethod.getName(), + params) + langSpec.getStatementDelimiter()); // ResType res = this.dst.updateSrc(value, refParams); + } else { + targetVarName = getMultipleResourcesVarName(returnResources, langSpec); + VariableDeclaration targetVar = langSpec.newVariableDeclaration(calleeMethod.getReturnType(), targetVarName); + List vars = new ArrayList<>(); + for (ResourceNode rn: returnResources) { + ResourcePath rId = rn.getResource(); + vars.add(langSpec.newVariableDeclaration(rId.getResourceStateType(), rId.getResourceName())); + } + resourceUpdateMethod.addStatement( + langSpec.getDecomposedTuple( + langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstNodeName), calleeMethod.getName(), params), + targetVar, // ResType res = this.dst.updateSrc(value, refParams); + vars)); // Type1 res1 = res.getKey(); Type2 res2 = res.getValue(); + } + return targetVarName; + } + } + + private String addInvocationInMediatorUpdate(MethodDeclaration resourceUpdateMethod, MethodDeclaration calleeMethod, String dstNodeName, List returnResources, ILanguageSpecific langSpec) { + List params = new ArrayList<>(); + if (calleeMethod.getParameters() != null) { + for (VariableDeclaration v: calleeMethod.getParameters()) { + params.add(v.getName()); + } + } + if (calleeMethod.getReturnType() == null || langSpec.isVoidType(calleeMethod.getReturnType()) || returnResources == null ) { + resourceUpdateMethod.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstNodeName), + calleeMethod.getName(), + params) + langSpec.getStatementDelimiter()); // this.dst.updateSrc(value, refParams); + return null; + } else { + String targetVarName = null; + if (returnResources.size() == 1) { + ResourceNode targetNode = returnResources.get(0); + targetVarName = targetNode.getResource().getResourceName(); + resourceUpdateMethod.addStatement( + langSpec.getVariableDeclaration(calleeMethod.getReturnType().getInterfaceTypeName(), targetVarName) + + langSpec.getAssignment() + + langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstNodeName), + calleeMethod.getName(), + params) + langSpec.getStatementDelimiter()); // ResType res = this.dst.updateSrc(value, refParams); + } else { + targetVarName = getMultipleResourcesVarName(returnResources, langSpec); + VariableDeclaration targetVar = langSpec.newVariableDeclaration(calleeMethod.getReturnType(), targetVarName); + List vars = new ArrayList<>(); + for (ResourceNode rn: returnResources) { + ResourcePath rId = rn.getResource(); + vars.add(langSpec.newVariableDeclaration(rId.getResourceStateType(), rId.getResourceName())); + } + resourceUpdateMethod.addStatement( + langSpec.getDecomposedTuple( + langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstNodeName), calleeMethod.getName(), params), + targetVar, // ResType res = this.dst.updateSrc(value, refParams); + vars)); // Type1 res1 = res.getKey(); Type2 res2 = res.getValue(); + } + return targetVarName; + } + } + + private String getMultipleResourcesVarName(List resources, ILanguageSpecific langSpec) { + String varName = null; + for (ResourceNode rn: resources) { + if (varName == null) { + varName = rn.getResource().getResourceName(); + } else { + varName += langSpec.toComponentName(rn.getResource().getResourceName()); + } + } + return varName; + } + + private List getUpdateMethods(TypeDeclaration component) { + List updates = new ArrayList<>(); + for (MethodDeclaration m: component.getMethods()) { + if (m.getName().startsWith(updateMethodName)) { + updates.add(m); + } + } + return updates; + } + + protected IResourceStateAccessor getPullAccessor(final String receiverName, final String getterOfResourceState) { + return new IResourceStateAccessor() { + @Override + public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) { + if (target.equals(from)) { + return new Field(fieldOfResourceState, + target.getResourceStateType() != null ? target.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + Term getter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); + getter.addChild(new Field(receiverName, target.getResourceStateType())); + return getter; + } + + @Override + public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { + Term getter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); + getter.addChild(new Field(receiverName, target.getResourceStateType())); + return getter; + } + }; + } + + protected IResourceStateAccessor getPullAccessor(final String resourceAccessor) { + return new IResourceStateAccessor() { + @Override + public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) { + if (target.equals(from)) { + return new Field(fieldOfResourceState, + target.getResourceStateType() != null ? target.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + Term getter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); + getter.addChild(new Field(target.getResourceName(), target.getResourceStateType())); + return getter; + } + + @Override + public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { + return new Constant(resourceAccessor); + } + }; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java b/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java new file mode 100644 index 0000000..67ea910 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java @@ -0,0 +1,457 @@ +package generators; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import code.ast.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.Expression; +import models.algebra.InvalidMessage; +import models.algebra.ParameterizedIdentifierIsFutureWork; +import models.algebra.Position; +import models.algebra.Term; +import models.algebra.Type; +import models.algebra.UnificationFailed; +import models.algebra.ValueUndefined; +import models.algebra.Variable; +import models.dataConstraintModel.Channel; +import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.ResourcePath; +import models.dataFlowModel.DataFlowEdge; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.IFlowGraph; +import models.dataFlowModel.PushPullAttribute; +import models.dataFlowModel.PushPullValue; +import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork; +import models.dataFlowModel.ResourceNode; +import models.dataFlowModel.StoreAttribute; +import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor; + +public class CodeGeneratorFromDataFlowGraph extends CodeGenerator { + + public void generateCodeFromFlowGraph(DataTransferModel model, IFlowGraph flowGraph, ArrayList> components, + TypeDeclaration mainComponent, MethodDeclaration mainConstructor, ArrayList codes, ILanguageSpecific langSpec) { + // For each of other components. + for (Set componentNodeSet: components) { + // Declare this resource. + Node componentNode = componentNodeSet.iterator().next(); + ResourceNode resourceNode = (ResourceNode) componentNode; + String resourceName = langSpec.toComponentName(resourceNode.getResource().getResourceName()); + TypeDeclaration component = langSpec.newTypeDeclaration(resourceName); + + // Declare the constructor and the fields to refer to other resources. + List depends = new ArrayList<>(); + MethodDeclaration constructor = declareConstructorAndFieldsToReferToResources(resourceNode, component, depends, langSpec); + + // Update the main component for this component. + updateMainComponent(mainComponent, mainConstructor, componentNode, constructor, langSpec); + + ResourcePath res = resourceNode.getResource(); + Type resStateType = res.getResourceStateType(); + + // Declare the field in this resource to store the state. + if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { + FieldDeclaration stateField = langSpec.newFieldDeclaration(resStateType, fieldOfResourceState, langSpec.getFieldInitializer(resStateType, res.getInitialValue())); + component.addField(stateField); + } + + // Declare the getter method in this resource to obtain the state. + MethodDeclaration getter = declareGetterMethod(resourceNode, component, resStateType, langSpec); + + // Declare the accessor method in the main component to call the getter method. + declareAccessorInMainComponent(mainComponent, res, langSpec); + + // Declare the fields to refer to reference resources. + declareFieldsToReferenceResources(model, resourceNode, component, constructor, depends, langSpec); + + // Declare cache fields and update methods in this resource. + List updates = declareCacheFieldsAndUpdateMethods(resourceNode, component, langSpec); + + // Declare input methods in this component and the main component. + List inputs = declareInputMethodsInThisAndMainComponents(resourceNode, component, mainComponent, model, langSpec); + + if (constructor.getParameters() == null) { + component.removeMethod(constructor); + } + + // Add compilation unit for this component. + CompilationUnit cu = langSpec.newCompilationUnit(component); + codes.add(cu); + } + } + + private MethodDeclaration declareConstructorAndFieldsToReferToResources(ResourceNode resourceNode, TypeDeclaration component, + List depends, ILanguageSpecific langSpec) { + // Declare a constructor in each component. + MethodDeclaration constructor = component.createConstructor(); + Block block = new Block(); + constructor.setBody(block); + + // Declare fields in each component. (for data-flow graph) + for (Edge e: resourceNode.getOutEdges()) { + if (((PushPullAttribute) ((DataFlowEdge) e).getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { + // for PUSH transfer + addReference(component, constructor, e.getDestination(), langSpec); + ResourcePath dstId = ((ResourceNode) e.getDestination()).getResource(); + if (!depends.contains(dstId)) depends.add(dstId); + } + } + for (Edge e: resourceNode.getInEdges()) { + if (((PushPullAttribute) ((DataFlowEdge) e).getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { + // for PULL transfer + addReference(component, constructor, e.getSource(), langSpec); + ResourcePath srcId = ((ResourceNode) e.getSource()).getResource(); + if (!depends.contains(srcId)) depends.add(srcId); + } + } + return constructor; + } + + private MethodDeclaration declareGetterMethod(ResourceNode resourceNode, TypeDeclaration component, Type resStateType, ILanguageSpecific langSpec) { + // Declare the getter method of the resource state. + MethodDeclaration getter = langSpec.newMethodDeclaration(getterOfResourceState, resStateType); + component.addMethod(getter); + + if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { + fillGetterMethodToReturnStateField(getter, resStateType, langSpec); + } else { + // invocations to other getter methods when at least one incoming data-flow edges is PULL-style. + boolean isContainedPush = false; + DataTransferChannel ch = null; + HashMap inputResourceToStateAccessor = new HashMap<>(); + for (Edge eIn: resourceNode.getInEdges()) { + DataFlowEdge dIn = (DataFlowEdge) eIn; + if (((PushPullAttribute) dIn.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { + // PUSH transfer + isContainedPush = true; + inputResourceToStateAccessor.put(((ResourceNode) dIn.getSource()).getResource(), getPushAccessor()); + } else { + // PULL transfer + inputResourceToStateAccessor.put(((ResourceNode) dIn.getSource()).getResource(), getPullAccessor()); + ch = dIn.getChannel(); + } + } + // for reference channel members. + for (ChannelMember c: ch.getReferenceChannelMembers()) { + inputResourceToStateAccessor.put(c.getResource(), getPullAccessor()); // by pull data transfer + } + + // generate a return statement. + try { + for (ChannelMember out: ch.getOutputChannelMembers()) { + if (out.getResource().equals(resourceNode.getResource())) { + String[] sideEffects = new String[] {""}; + if (!isContainedPush) { + // All incoming edges are in PULL-style. + String curState = ch.deriveUpdateExpressionOf(out, getPullAccessor()).toImplementation(sideEffects); + getter.addStatement(sideEffects[0] + langSpec.getReturnStatement(curState) + langSpec.getStatementDelimiter()); + } else { + // At least one incoming edge is in PUSH-style. + String curState = ch.deriveUpdateExpressionOf(out, getPullAccessor(), inputResourceToStateAccessor).toImplementation(sideEffects); + getter.addStatement(sideEffects[0] + langSpec.getReturnStatement(curState) + langSpec.getStatementDelimiter()); + } + break; + } + } + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork + | InvalidMessage | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + } + + return getter; + } + + private List declareCacheFieldsAndUpdateMethods(ResourceNode resourceNode, TypeDeclaration component, ILanguageSpecific langSpec) { + // Declare cash fields and update methods in the component. + String resComponentName = langSpec.toComponentName(resourceNode.getResource().getResourceName()); + List updateMethods = new ArrayList<>(); + for (Edge e: resourceNode.getInEdges()) { + DataFlowEdge re = (DataFlowEdge) e; + ResourcePath srcRes = ((ResourceNode) re.getSource()).getResource(); + String srcResName = srcRes.getResourceName(); + String srcResComponentName = langSpec.toComponentName(srcResName); + if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { + // for push data transfer + + // Declare an update method in the type of the destination resource. + ArrayList vars = new ArrayList<>(); + vars.add(langSpec.newVariableDeclaration(srcRes.getResourceStateType(), srcRes.getResourceName())); + // For the refs. + DataTransferChannel ch = (DataTransferChannel) re.getChannel(); + for (ResourcePath ref: ch.getReferenceResources()) { + if (!ref.equals(resourceNode.getResource())) { + vars.add(langSpec.newVariableDeclaration(ref.getResourceStateType(), ref.getResourceName())); + } + } + MethodDeclaration update = langSpec.newMethodDeclaration(updateMethodName + srcResComponentName, false, null, vars); + component.addMethod(update); + updateMethods.add(update); + + // Add a statement to update the state field + if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { + try { + for (ChannelMember out: ch.getOutputChannelMembers()) { + if (out.getResource().equals(resourceNode.getResource())) { + Expression updateExp = null; + if (ch.getReferenceChannelMembers().size() == 0) { + updateExp = ch.deriveUpdateExpressionOf(out, getPushAccessor()); + } else { + // if there exists one or more reference channel member. + HashMap inputResourceToStateAccessor = new HashMap<>(); + for (Edge eIn: resourceNode.getInEdges()) { + DataFlowEdge dIn = (DataFlowEdge) eIn; + inputResourceToStateAccessor.put(((ResourceNode) dIn.getSource()).getResource(), getPushAccessor()); + } + for (ChannelMember c: ch.getReferenceChannelMembers()) { + inputResourceToStateAccessor.put(c.getResource(), getRefAccessor()); + } + updateExp = ch.deriveUpdateExpressionOf(out, getPushAccessor(), 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] + langSpec.getFieldAccessor(fieldOfResourceState) + langSpec.getAssignment() + curState + langSpec.getStatementDelimiter(); // this.value = ... + } + if (update.getBody() == null || !update.getBody().getStatements().contains(updateStatement)) { + update.addFirstStatement(updateStatement); + } + break; + } + } + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork + | InvalidMessage | UnificationFailed | ValueUndefined e1) { + e1.printStackTrace(); + } + } + + // Declare the field to cache the state of the source resource in the type of the destination resource. + if (resourceNode.getIndegree() > 1 + || (resourceNode.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( + srcRes.getResourceStateType(), + srcRes.getResourceName(), + langSpec.getFieldInitializer(srcRes.getResourceStateType(), srcRes.getInitialValue())); + component.addField(cacheField); + + } + // Update the cache field. + String cacheStatement = langSpec.getFieldAccessor(srcResName) + langSpec.getAssignment() + srcResName + langSpec.getStatementDelimiter(); + if (update.getBody() == null || !update.getBody().getStatements().contains(cacheStatement)) { + update.addStatement(cacheStatement); + } + } + + // Add an invocation to another update method (for a chain of update method invocations). + for (Edge eOut: resourceNode.getOutEdges()) { + DataFlowEdge dOut = (DataFlowEdge) eOut; + if (((PushPullAttribute) dOut.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { + // PUSH transfer + Map> referredResources = new HashMap<>(); + List params = new ArrayList<>(); + params.add(langSpec.getFieldAccessor(fieldOfResourceState)); + Set referredSet = referredResources.get(update); + for (ChannelMember rc: dOut.getChannel().getReferenceChannelMembers()) { + // to get the value of reference member. + ResourcePath ref = rc.getResource(); + if (referredSet == null) { + referredSet = new HashSet<>(); + referredResources.put(update, referredSet); + } + if (!ref.equals(resourceNode.getResource())) { + String refVarName = ref.getResourceName(); + if (!referredSet.contains(ref)) { + referredSet.add(ref); + Expression refGetter = getPullAccessor().getCurrentStateAccessorFor(ref, ((ResourceNode) dOut.getSource()).getResource()); + 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); + } + } + update.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(((ResourceNode) dOut.getDestination()).getResource().getResourceName()), + updateMethodName + resComponentName, + params) + langSpec.getStatementDelimiter()); // this.dst.updateSrc(value, refParams); + } + } + } + } + return updateMethods; + } + + private List declareInputMethodsInThisAndMainComponents(ResourceNode resourceNode, TypeDeclaration component, + TypeDeclaration mainComponent, DataTransferModel model, ILanguageSpecific langSpec) { + // Declare input methods. + String resName = resourceNode.getResource().getResourceName(); + String resComponentName = langSpec.toComponentName(resName); + List inputMethods = new ArrayList<>(); + for (Channel ch : model.getIOChannels()) { + for (ChannelMember out : ((DataTransferChannel) ch).getOutputChannelMembers()) { + if (out.getResource().equals(resourceNode.getResource())) { + Expression message = out.getStateTransition().getMessageExpression(); + MethodDeclaration input = null; + MethodDeclaration mainInput = null; + if (message instanceof Term) { + // Declare an input method in this component. + ArrayList params = new ArrayList<>(); + 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().getResourceName(); + break; + } + } + } + if (refVarName != null) { + // var has come from a reference resource. + params.add(langSpec.newVariableDeclaration(var.getType(), refVarName)); + } else { + // var has not come from reference resource. + params.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); + } + } + input = langSpec.newMethodDeclaration(((Term) message).getSymbol().getImplName(), false, null, params); + component.addMethod(input); + inputMethods.add(input); + + // Declare the accessor in the main component to call the input method. + String str = ((Term) message).getSymbol().getImplName(); + mainInput = getMethod(mainComponent, str); + if (mainInput == null) { + mainInput = langSpec.newMethodDeclaration(str, false, null, params); + mainComponent.addMethod(mainInput); + } else { + // Add type to a parameter without type. + if (mainInput.getParameters() != null) { + for (VariableDeclaration param: mainInput.getParameters()) { + if (param.getType() == null) { + for (VariableDeclaration p: params) { + 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. + input = langSpec.newMethodDeclaration(((Variable) message).getName(), null); + component.addMethod(input); + inputMethods.add(input); + String str = ((Variable) message).getName(); + + // Declare the accessor in the main component to call the input method. + mainInput = getMethod(mainComponent, str); + if (mainInput == null) { + mainInput = langSpec.newMethodDeclaration(str, null); + mainComponent.addMethod(mainInput); + } + } + + // Add an invocation to the accessor method. + if (mainInput != null) { + List args = new ArrayList<>(); + 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().getResourceName(); + break; + } + } + if (refVarName != null) { + args.add(refVarName); + } else { + args.add(varEnt.getValue().getName()); + } + } + } + mainInput.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(resName), input.getName(), args) + langSpec.getStatementDelimiter()); + } + + if (input != null) { + // Add a statement to update the state field to the input method. + try { + String[] sideEffects = new String[] {""}; + Expression updateExp; + updateExp = ((DataTransferChannel) ch).deriveUpdateExpressionOf(out, getRefAccessor()); + String newState = updateExp.toImplementation(sideEffects); + String updateStatement; + if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { + updateStatement = sideEffects[0]; + } else { + updateStatement = sideEffects[0] + langSpec.getFieldAccessor(fieldOfResourceState) + langSpec.getAssignment() + newState + langSpec.getStatementDelimiter(); + } + 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 eOut: resourceNode.getOutEdges()) { + DataFlowEdge dOut = (DataFlowEdge) eOut; + if (((PushPullAttribute) dOut.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { + // PUSH transfer + Map> referredResources = new HashMap<>(); + List params = new ArrayList<>(); + params.add(langSpec.getFieldAccessor(fieldOfResourceState)); + Set referredSet = referredResources.get(input); + for (ChannelMember rc: dOut.getChannel().getReferenceChannelMembers()) { + // to get the value of reference member. + ResourcePath ref = rc.getResource(); + if (referredSet == null) { + referredSet = new HashSet<>(); + referredResources.put(input, referredSet); + } + if (!ref.equals(resourceNode.getResource())) { + String refVarName = ref.getResourceName(); + if (!referredSet.contains(ref)) { + referredSet.add(ref); + Expression refGetter = getPullAccessor().getCurrentStateAccessorFor(ref, ((ResourceNode) dOut.getSource()).getResource()); + 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); + } + } + input.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(((ResourceNode) dOut.getDestination()).getResource().getResourceName()), + updateMethodName + resComponentName, + params) + langSpec.getStatementDelimiter()); // this.dst.updateSrc(value, refParams); + } + } + } + } + } + } + return inputMethods; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/generators/DataTransferMethodAnalyzer.java b/AlgebraicDataflowArchitectureModel/src/generators/DataTransferMethodAnalyzer.java new file mode 100644 index 0000000..8e68103 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/generators/DataTransferMethodAnalyzer.java @@ -0,0 +1,47 @@ +package generators; + +import java.util.HashSet; + +import models.*; +import models.algebra.*; +import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.DataConstraintModel; +import models.dataFlowModel.*; + +/** + * Algorithm to analyze data transfer methods selected by a user + * + * @author Nitta + * + */ +public class DataTransferMethodAnalyzer { + static private HashSet reachableNodes = new HashSet<>(); + + /** + * Determine whether each resource state is stored or not depending on selected data transfer methods. + * + * @param graph a data flow graph (in/out) + */ + static public void decideToStoreResourceStates(DataFlowGraph graph) { + reachableNodes.clear(); + for (Node n : graph.getNodes()) { + ResourceNode resource = (ResourceNode) n; + trackNode(resource); + } + } + + static private void trackNode(ResourceNode resource) { + if (reachableNodes.contains(resource)) + return; + reachableNodes.add(resource); + boolean flag = true; + for (Edge e : resource.getInEdges()) { + if (((PushPullAttribute) e.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { + // Traverse pull edges only. + trackNode((ResourceNode) e.getSource()); + flag = false; + } + } + ((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..f223c0b --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/generators/ILanguageSpecific.java @@ -0,0 +1,45 @@ +package generators; + +import java.util.ArrayList; +import java.util.List; + +import code.ast.CompilationUnit; +import code.ast.FieldDeclaration; +import code.ast.MethodDeclaration; +import code.ast.TypeDeclaration; +import code.ast.VariableDeclaration; +import models.algebra.Expression; +import models.algebra.Type; +import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor; + +public interface ILanguageSpecific { + CompilationUnit newCompilationUnit(TypeDeclaration component); + TypeDeclaration newTypeDeclaration(String typeName); + VariableDeclaration newVariableDeclaration(Type type, String varName); + MethodDeclaration newMethodDeclaration(String methodName, Type returnType); + MethodDeclaration newMethodDeclaration(String methodName, boolean isConstructor, Type returnType, List parameters); + FieldDeclaration newFieldDeclaration(Type fieldType, String fieldName); + FieldDeclaration newFieldDeclaration(Type fieldType, String fieldName, String fieldInitializer); + Type newListType(String compTypeName); + Type newMapType(Type keyType, String compTypeName); + Type newTupleType(List compTypes); + String getVariableDeclaration(String typeName, String varName); + String getFieldInitializer(Type type, Expression initialValue); + boolean declareField(); + String getFieldAccessor(String fieldName); + String getMethodInvocation(String methodName); + String getMethodInvocation(String receivertName, String methodName); + String getMethodInvocation(String receivertName, String methodName, List parameters); + String getConstructorInvocation(String componentName, List parameters); + String getReturnStatement(String returnValue); + String toComponentName(String name); + String toVariableName(String name); + String getMainComponentName(); + String getTupleGet(String tupleExp, int idx, int length); + String getDecomposedTuple(String tupleExp, VariableDeclaration tupleVar, List vars); + String getAssignment(); + String getStatementDelimiter(); + String getStringDelimiter(); + boolean isValueType(Type type); + boolean isVoidType(Type type); +} diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JavaCodeGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/JavaCodeGenerator.java new file mode 100644 index 0000000..350ea16 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/generators/JavaCodeGenerator.java @@ -0,0 +1,516 @@ +package generators; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + + +import code.ast.Block; +import code.ast.CompilationUnit; +import code.ast.FieldDeclaration; +import code.ast.ImportDeclaration; +import code.ast.MethodDeclaration; +import code.ast.TypeDeclaration; +import code.ast.VariableDeclaration; +import models.Edge; +import models.Node; +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; +import models.algebra.Variable; +import models.dataConstraintModel.Channel; +import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.ResourcePath; +import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor; +import models.dataFlowModel.PushPullAttribute; +import models.dataFlowModel.PushPullValue; +import models.dataFlowModel.DataFlowEdge; +import models.dataFlowModel.DataFlowGraph; +import models.dataFlowModel.ResourceNode; +import models.dataFlowModel.StoreAttribute; + +/** + * Generator for plain Java prototypes + * + * @author Nitta + * + */ +public class JavaCodeGenerator { + public static final Type typeVoid = new Type("Void", "void"); + private static String defaultMainTypeName = "Main"; + static String mainTypeName = defaultMainTypeName; + + public static String getMainTypeName() { + return mainTypeName; + } + + public static void setMainTypeName(String mainTypeName) { + JavaCodeGenerator.mainTypeName = mainTypeName; + } + + public static void resetMainTypeName() { + JavaCodeGenerator.mainTypeName = defaultMainTypeName; + } + + static public ArrayList doGenerate(DataFlowGraph graph, DataTransferModel model) { + ArrayList codes = new ArrayList<>(); + ArrayList resources = determineResourceOrder(graph); + + TypeDeclaration mainType = new TypeDeclaration(mainTypeName); + CompilationUnit mainCU = new CompilationUnit(mainType); + 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); + + // 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 + || (rn.getIndegree() == 1 && re.getChannel().getInputChannelMembers().iterator().next().getStateTransition().isRightPartial())) { + // Declare a field to cache 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))); + } + } + } + Set refs = new HashSet<>(); + for (Channel ch : model.getChannels()) { + DataTransferChannel c = (DataTransferChannel) ch; + if (c.getInputResources().contains(rn.getResource())) { + for (ResourcePath res: c.getReferenceResources()) { + if (!refs.contains(res) && !depends.contains(res)) { + refs.add(res); + String refResName = res.getResourceName(); + fieldInitializer += refResName.toLowerCase() + ","; + f = true; + } + } + } + } + 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 mainConstructorBody = mainConstructor.getBody(); + if (mainConstructorBody == null) { + mainConstructorBody = new Block(); + mainConstructor.setBody(mainConstructorBody); + } + mainConstructorBody.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.equals(rn.getResource())) { + vars.add(new VariableDeclaration(ref.getResourceStateType(), ref.getResourceName())); + } + } + type.addMethod(new MethodDeclaration("update" + srcResName, false, typeVoid, vars)); + } + } + // Declare a field to refer to the reference resource. + refs = new HashSet<>(); + for (Channel ch : model.getChannels()) { + DataTransferChannel c = (DataTransferChannel) ch; + if (c.getInputResources().contains(rn.getResource())) { + for (ResourcePath res: c.getReferenceResources()) { + if (!refs.contains(res) && !depends.contains(res)) { + refs.add(res); + String refResName = res.getResourceName(); + refResName = refResName.substring(0, 1).toUpperCase() + refResName.substring(1); + type.addField(new FieldDeclaration(new Type(refResName, refResName), res.getResourceName())); + constructor.addParameter(new VariableDeclaration(new Type(refResName, refResName), res.getResourceName())); + block.addStatement("this." + res.getResourceName() + " = " + res.getResourceName() + ";"); + } + } + } + } + constructor.setBody(block); + if (constructor.getParameters() != null) + type.addMethod(constructor); + + // Declare input methods in resources and the main type. + for (Channel ch : model.getIOChannels()) { + for (ChannelMember cm : ((DataTransferChannel) ch).getOutputChannelMembers()) { + if (cm.getResource().equals(rn.getResource())) { + Expression message = cm.getStateTransition().getMessageExpression(); + if (message instanceof Term) { + ArrayList params = new ArrayList<>(); + 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().getResourceName(); + break; + } + } + } + if (refVarName != null) { + // var has come from a reference resource. + params.add(new VariableDeclaration(var.getType(), refVarName)); + } else { + // var has not come from reference resource. + params.add(new VariableDeclaration(var.getType(), var.getName())); + } + } + 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 (input == null) { + input = new MethodDeclaration(str, false, typeVoid, params); + mainType.addMethod(input); + } else { + // Add type to a parameter without type. + for (VariableDeclaration param: input.getParameters()) { + if (param.getType() == null) { + for (VariableDeclaration p: params) { + if (param.getName().equals(p.getName()) && p.getType() != null) { + param.setType(p.getType()); + } + } + } + } + } + } else if (message instanceof Variable) { + 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); + if (input == null) { + input = new MethodDeclaration(str, false, typeVoid, null); + mainType.addMethod(input); + } + } + } + } + } + + // Declare the field to store the state in the type of each resource. + if (((StoreAttribute) rn.getAttribute()).isStored()) { + ResourcePath res = rn.getResource(); + type.addField(new FieldDeclaration(res.getResourceStateType(), "value", getInitializer(res))); + } + + // 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); + } + + // Declare the Pair class. + boolean isCreatedPair = false; + for(ResourceNode rn : resources) { + if(isCreatedPair) continue; + if(model.getType("Pair").isAncestorOf(rn.getResource().getResourceStateType())) { + TypeDeclaration type = new TypeDeclaration("Pair"); + type.addField(new FieldDeclaration(new Type("Double", "T"), "left")); + type.addField(new FieldDeclaration(new Type("Double", "T"), "right")); + + MethodDeclaration constructor = new MethodDeclaration("Pair", true); + constructor.addParameter(new VariableDeclaration(new Type("Double", "T"), "left")); + constructor.addParameter(new VariableDeclaration(new Type("Double", "T"), "right")); + Block block = new Block(); + block.addStatement("this.left = left;"); + block.addStatement("this.right = right;"); + constructor.setBody(block); + type.addMethod(constructor); + + for(FieldDeclaration field : type.getFields()) { + MethodDeclaration getter = new MethodDeclaration( + "get" + field.getName().substring(0,1).toUpperCase() + field.getName().substring(1), + new Type("Double","T")); + getter.setBody(new Block()); + getter.getBody().addStatement("return " + field.getName() + ";"); + type.addMethod(getter); + } + + CompilationUnit cu = new CompilationUnit(type); + cu.addImport(new ImportDeclaration("java.util.*")); + codes.add(cu); + + isCreatedPair = true; + } + } + + // Declare getter methods in the main type. + for (Node n : graph.getNodes()) { + ResourceNode rn = (ResourceNode) n; + MethodDeclaration getter = new MethodDeclaration( + "get" + rn.getResource().getResourceName().substring(0, 1).toUpperCase() + + rn.getResource().getResourceName().substring(1), + rn.getResource().getResourceStateType()); + getter.setBody(new Block()); + getter.getBody().addStatement( + "return " + rn.getResource().getResourceName() + ".getValue();"); + mainType.addMethod(getter); + } + + + HashSet tmps = new HashSet<>(); + HashSet cont = new HashSet<>(); + for (MethodDeclaration method : mainType.getMethods()) { + if (!tmps.contains(method.getName())) + tmps.add(method.getName()); + else + cont.add(method.getName()); + } + for (MethodDeclaration method : mainType.getMethods()) { + if (cont.contains(method.getName())) { + method.setName(method.getName() + method.getParameters().get(0).getName().substring(0, 1).toUpperCase() + + method.getParameters().get(0).getName().substring(1)); + } + } + 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() + "()"; + } + } + return initializer; + } + + static public ArrayList getCodes(ArrayList codeTree) { + ArrayList codes = new ArrayList<>(); + for (TypeDeclaration type : codeTree) { + codes.add("public class " + type.getTypeName() + "{"); + for (FieldDeclaration field : type.getFields()) { + if (type.getTypeName() != mainTypeName) { + String cons = "\t" + "private " + field.getType().getInterfaceTypeName() + " " + + field.getName(); + if (DataConstraintModel.isListType(field.getType())) + cons += " = new ArrayList<>()"; + cons += ";"; + codes.add(cons); + } else { + String cons = "\t" + "private " + field.getType().getInterfaceTypeName() + " " + + field.getName() + " = new " + field.getType().getTypeName() + "("; + cons += ");"; + codes.add(cons); + } + } + codes.add(""); + for (MethodDeclaration method : type.getMethods()) { + String varstr = "\t" + "public " + method.getReturnType().getInterfaceTypeName() + " " + + method.getName() + "("; + if (method.getParameters() != null) { + for (VariableDeclaration var : method.getParameters()) { + varstr += var.getType().getInterfaceTypeName() + " " + var.getName() + ","; + } + if (!method.getParameters().isEmpty()) + varstr = varstr.substring(0, varstr.length() - 1); + } + if (method.getBody() != null) { + for (String str : method.getBody().getStatements()) { + codes.add("\t\t" + str + ";"); + } + } + codes.add(varstr + ")" + "{"); + codes.add("\t" + "}"); + codes.add(""); + } + codes.add("}"); + codes.add(""); + } + 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().equals(curNode.getResource())) { + topologicalSort(graph, rn, visited, orderedList); + } + } + } + } + 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)) { + return new Field("value", + target.getResourceStateType() != null ? target.getResourceStateType() + : DataConstraintModel.typeInt); + } + // use the cached value as the current state + return new Field(target.getResourceName(), + target.getResourceStateType() != null ? target.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { + return new Parameter(target.getResourceName(), + target.getResourceStateType() != null ? target.getResourceStateType() + : DataConstraintModel.typeInt); + } + }; + static public IResourceStateAccessor pullAccessor = new IResourceStateAccessor() { + @Override + public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) { + if (target.equals(from)) { + return new Field("value", + target.getResourceStateType() != null ? target.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + 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; + } + }; + static public IResourceStateAccessor refAccessor = new IResourceStateAccessor() { + @Override + public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) { + if (target.equals(from)) { + return new Field("value", + target.getResourceStateType() != null ? target.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + return new Parameter(target.getResourceName(), + target.getResourceStateType() != null ? target.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { + return new Parameter(target.getResourceName(), + target.getResourceStateType() != null ? target.getResourceStateType() + : DataConstraintModel.typeInt); + } + }; +} diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java new file mode 100644 index 0000000..9c61208 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java @@ -0,0 +1,378 @@ +package generators; + +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import code.ast.CompilationUnit; +import code.ast.MethodDeclaration; +import code.ast.TypeDeclaration; +import models.Edge; +import models.Node; +import models.algebra.Expression; +import models.algebra.InvalidMessage; +import models.algebra.ParameterizedIdentifierIsFutureWork; +import models.algebra.Position; +import models.algebra.Term; +import models.algebra.Type; +import models.algebra.UnificationFailed; +import models.algebra.ValueUndefined; +import models.algebra.Variable; +import models.dataConstraintModel.Channel; +import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.ResourcePath; +import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor; +import models.dataFlowModel.PushPullAttribute; +import models.dataFlowModel.PushPullValue; +import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork; +import models.dataFlowModel.DataFlowEdge; +import models.dataFlowModel.DataFlowGraph; +import models.dataFlowModel.ResourceNode; +import models.dataFlowModel.StoreAttribute; + +public class JavaMethodBodyGenerator { + public static ArrayList doGenerate(DataFlowGraph graph, DataTransferModel model, ArrayList codes) { + // Create a map from type names (lower case) to their types. + Map typeMap = new HashMap<>(); + for (CompilationUnit code: codes) { + for (TypeDeclaration type: code.types()) { + typeMap.put(type.getTypeName().substring(0,1).toLowerCase() + type.getTypeName().substring(1), type); + } + } + + // Generate the body of each update or getter method. + try { + Map> referredResources = new HashMap<>(); + for (Edge e: graph.getEdges()) { + DataFlowEdge d = (DataFlowEdge) e; + PushPullAttribute pushPull = (PushPullAttribute) d.getAttribute(); + ResourceNode src = (ResourceNode) d.getSource(); + ResourceNode dst = (ResourceNode) d.getDestination(); + String srcResourceName = src.getResource().getResourceName(); + String dstResourceName = dst.getResource().getResourceName(); + TypeDeclaration srcType = typeMap.get(srcResourceName); + TypeDeclaration dstType = typeMap.get(dstResourceName); + for (ChannelMember out: d.getChannel().getOutputChannelMembers()) { + if (out.getResource().equals(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, JavaCodeGenerator.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(), JavaCodeGenerator.pushAccessor); + } + for (ChannelMember c: d.getChannel().getReferenceChannelMembers()) { + inputResourceToStateAccessor.put(c.getResource(), JavaCodeGenerator.refAccessor); + } + updateExp = d.getChannel().deriveUpdateExpressionOf(out, JavaCodeGenerator.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)) { + update.addFirstStatement(updateStatement); + } + } + if (dst.getIndegree() > 1 + || (dst.getIndegree() == 1 && d.getChannel().getInputChannelMembers().iterator().next().getStateTransition().isRightPartial())) { + // update a cache of src side resource (when incoming edges are multiple) + String cacheStatement = "this." + srcResourceName + " = " + srcResourceName + ";"; + if (update.getBody() == null || !update.getBody().getStatements().contains(cacheStatement)) { + update.addStatement(cacheStatement); + } + } + 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;"); + } 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);"); + } + } + } + // 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.equals(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 + ";"); + } + 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.equals(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 + ";"); + } + refParams += ", " + refVarName; + } + } + 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); + } else { + inputResourceToStateAccessor.put(((ResourceNode) dIn.getSource()).getResource(), JavaCodeGenerator.pullAccessor); + } + } + // 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);"); + } + } + // 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.refAccessor); + String newState = updateExp.toImplementation(sideEffects); + String updateStatement; + if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { + updateStatement = sideEffects[0]; + } else { + updateStatement = sideEffects[0] + "this.value = " + newState + ";"; + } + if (input.getBody() == null || !input.getBody().getStatements().contains(updateStatement)) { + input.addFirstStatement(updateStatement); + } + if (mainType != null) { + MethodDeclaration mainInput = getMethod(mainType, input.getName()); + if (mainInput != null) { + String args = ""; + String delimitar = ""; + if (out.getStateTransition().getMessageExpression() instanceof Term) { + Term message = (Term) out.getStateTransition().getMessageExpression(); + for (Map.Entry varEnt: message.getVariables().entrySet()) { + String refVarName = null; + for (ChannelMember rc: entry.getKey().getReferenceChannelMembers()) { + Expression varExp = rc.getStateTransition().getMessageExpression().getSubTerm(varEnt.getKey()); + if (varExp != null && rc.getStateTransition().getCurStateExpression().contains(varExp)) { + refVarName = rc.getResource().getResourceName(); + break; + } + } + if (refVarName != null) { + args += delimitar + refVarName; + } else { + args += delimitar + varEnt.getValue().getName(); + } + delimitar = ", "; + } + } + mainInput.addStatement("this." + resourceName + "." + input.getName() + "(" + args + ");"); + } + } + } + } + } + } + } + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork + | InvalidMessage | UnificationFailed | ValueUndefined e1) { + e1.printStackTrace(); + } + return codes; + } + + private static MethodDeclaration getUpdateMethod(TypeDeclaration type, TypeDeclaration from) { + for (MethodDeclaration m: type.getMethods()) { + if (m.getName().equals("update" + from.getTypeName())) return m; + } + return null; + } + + private static List getUpdateMethods(TypeDeclaration type) { + List updates = new ArrayList<>(); + for (MethodDeclaration m: type.getMethods()) { + if (m.getName().startsWith("update")) { + updates.add(m); + } + } + return updates; + } + + private static MethodDeclaration getGetterMethod(TypeDeclaration type) { + for (MethodDeclaration m: type.getMethods()) { + if (m.getName().startsWith("get")) return m; + } + return null; + } + + private static Map> getIOChannelsAndMembers(ResourceNode resource, DataTransferModel model) { + Map> ioChannelsAndMembers = new HashMap<>(); + for (Channel c: model.getIOChannels()) { + DataTransferChannel ch = (DataTransferChannel) c; + // I/O channel + for (ChannelMember out: ch.getOutputChannelMembers()) { + if (out.getResource().equals(resource.getResource())) { + if (out.getStateTransition().getMessageExpression() instanceof Term || out.getStateTransition().getMessageExpression() instanceof Variable) { + Set channelMembers = ioChannelsAndMembers.get(ch); + if (channelMembers == null) { + channelMembers = new HashSet<>(); + ioChannelsAndMembers.put(ch, channelMembers); + } + channelMembers.add(out); + } + } + } + } + return ioChannelsAndMembers; + } + + private static List getInputMethods(TypeDeclaration type, ResourceNode resource, DataTransferModel model) { + List inputs = new ArrayList<>(); + for (Channel c: model.getIOChannels()) { + DataTransferChannel channel = (DataTransferChannel) c; + // I/O channel + for (ChannelMember out: channel.getOutputChannelMembers()) { + if (out.getResource().equals(resource.getResource())) { + MethodDeclaration input = getInputMethod(type, out); + inputs.add(input); + } + } + } + return inputs; + } + + private static MethodDeclaration getInputMethod(TypeDeclaration type, ChannelMember out) { + MethodDeclaration input = null; + if (out.getStateTransition().getMessageExpression() instanceof Term) { + Term message = (Term) out.getStateTransition().getMessageExpression(); + input = getMethod(type, message.getSymbol().getImplName()); + } else if (out.getStateTransition().getMessageExpression() instanceof Variable) { + Variable message = (Variable) out.getStateTransition().getMessageExpression(); + input = getMethod(type, message.getName()); + } + return input; + } + + private static MethodDeclaration getMethod(TypeDeclaration type, String methodName) { + for (MethodDeclaration m: type.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..b7c56ac --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/generators/JavaSpecific.java @@ -0,0 +1,275 @@ +package generators; + +import java.util.ArrayList; +import java.util.List; + +import code.ast.CompilationUnit; +import code.ast.FieldDeclaration; +import code.ast.ImportDeclaration; +import code.ast.MethodDeclaration; +import code.ast.TypeDeclaration; +import code.ast.VariableDeclaration; +import models.algebra.Expression; +import models.algebra.Parameter; +import models.algebra.Term; +import models.algebra.Type; +import models.algebra.Variable; +import models.dataConstraintModel.DataConstraintModel; + +public class JavaSpecific implements ILanguageSpecific { + public static final Type typeVoid = new Type("Void", "void"); + public static final String self = "this"; + + @Override + public CompilationUnit newCompilationUnit(TypeDeclaration component) { + CompilationUnit cu = new CompilationUnit(component); + cu.addImport(new ImportDeclaration("java.util.*")); + return cu; + } + + @Override + public TypeDeclaration newTypeDeclaration(String typeName) { + return new TypeDeclaration(typeName); + } + + @Override + public VariableDeclaration newVariableDeclaration(Type type, String varName) { + return new VariableDeclaration(type, varName); + } + + @Override + public MethodDeclaration newMethodDeclaration(String methodName, Type returnType) { + if (returnType == null) { + returnType = typeVoid; + } + return new MethodDeclaration(methodName, returnType); + } + + @Override + public MethodDeclaration newMethodDeclaration(String methodName, boolean isConstructor, Type returnType, List parameters) { + if (returnType == null) { + returnType = typeVoid; + } + return new MethodDeclaration(methodName, isConstructor, returnType, parameters); + } + + @Override + public FieldDeclaration newFieldDeclaration(Type fieldType, String fieldName) { + return new FieldDeclaration(fieldType, fieldName); + } + + @Override + public FieldDeclaration newFieldDeclaration(Type fieldType, String fieldName, String fieldInitializer) { + return new FieldDeclaration(fieldType, fieldName, fieldInitializer); + } + + @Override + public Type newListType(String compTypeName) { + return new Type("List", "ArrayList<>", "List<" + compTypeName + ">", DataConstraintModel.typeList); + } + + @Override + public Type newMapType(Type keyType, String valueTypeName) { + return new Type("Map", "HashMap<>", "Map<" + keyType.getImplementationTypeName() + ", " + valueTypeName + ">", DataConstraintModel.typeMap); + } + + @Override + public Type newTupleType(List componentTypes) { + String implTypeName = "AbstractMap.SimpleEntry<>"; + String interfaceTypeName = "Map.Entry<$x>"; + if (componentTypes.size() >= 2) { + implTypeName = implTypeName.replace("$x", getImplementationTypeName(componentTypes.get(0)) + "$x"); + interfaceTypeName = interfaceTypeName.replace("$x", getInterfaceTypeName(componentTypes.get(0)) + "$x"); + for (Type argType : componentTypes.subList(1, componentTypes.size() - 1)) { + implTypeName = implTypeName.replace("$x", + ", AbstractMap.SimpleEntry<" + getImplementationTypeName(argType) + "$x>"); + interfaceTypeName = interfaceTypeName.replace("$x", + ", Map.Entry<" + getInterfaceTypeName(argType) + "$x>"); + } + implTypeName = implTypeName.replace("$x", + ", " + getImplementationTypeName(componentTypes.get(componentTypes.size() - 1))); + interfaceTypeName = interfaceTypeName.replace("$x", + ", " + getInterfaceTypeName(componentTypes.get(componentTypes.size() - 1))); + } + Type newTupleType = new Type("Tuple", implTypeName, interfaceTypeName, DataConstraintModel.typeTuple); + return newTupleType; + } + + @Override + public String getVariableDeclaration(String typeName, String varName) { + return typeName + " " + varName; + } + + @Override + public String getFieldInitializer(Type type, Expression initialValue) { + String initializer = null; + if (initialValue != null) { + initializer = initialValue.toImplementation(new String[] {""}); + } else { + if (DataConstraintModel.typeList.isAncestorOf(type)) { + initializer = "new " + type.getImplementationTypeName() + "()"; + } else if (DataConstraintModel.typeMap.isAncestorOf(type)) { + initializer = "new " + type.getImplementationTypeName() + "()"; + } + } + return initializer; + } + + @Override + public boolean declareField() { + return true; + } + + @Override + public String getFieldAccessor(String fieldName) { + return self + "." + fieldName; + } + + @Override + public String getMethodInvocation(String methodName) { + return self + "." + methodName + "()"; + } + + @Override + public String getMethodInvocation(String receiverName, String methodName) { + return receiverName + "." + methodName + "()"; + } + + @Override + public String getMethodInvocation(String receiverName, String methodName, List parameters) { + if (parameters == null) return getMethodInvocation(receiverName, methodName); + String invocation = receiverName + "." + methodName + "("; + if (parameters.size() > 0) { + for (int i = 0; i < parameters.size(); i++) { + if (i < parameters.size() - 1) { + invocation += parameters.get(i) + ", "; + } else { + invocation += parameters.get(i); + } + } + } + invocation += ")"; + return invocation; + } + + @Override + public String getConstructorInvocation(String componentName, List parameters) { + String invocation = "new " + componentName + "("; + if (parameters.size() > 0) { + for (int i = 0; i < parameters.size(); i++) { + if (i < parameters.size() - 1) { + invocation += parameters.get(i) + ", "; + } else { + invocation += parameters.get(i); + } + } + } + invocation += ")"; + return invocation; + } + + @Override + public String getReturnStatement(String returnValue) { + return "return " + returnValue; + } + + @Override + public String toComponentName(String name) { + return name.substring(0, 1).toUpperCase() + name.substring(1); + } + + @Override + public String toVariableName(String name) { + return name.substring(0, 1).toLowerCase() + name.substring(1); + } + + @Override + public String getMainComponentName() { + return "Main"; + } + + @Override + public String getAssignment() { + return " = "; + } + + @Override + public String getStatementDelimiter() { + return ";"; + } + + @Override + public String getStringDelimiter() { + return "\""; + } + + @Override + public String getTupleGet(String tupleExp, int idx, int length) { + Expression t = new Variable(tupleExp, DataConstraintModel.typeTuple); + for (int i = 0; i < idx; i++) { + Term next = new Term(DataConstraintModel.snd); + next.addChild(t); + t = next; + } + if (idx < length - 1) { + Term last = new Term(DataConstraintModel.fst); + last.addChild(t); + t = last; + } + return t.toImplementation(new String[]{}); + } + + @Override + public String getDecomposedTuple(String tupleExp, VariableDeclaration tupleVar, List vars) { + String statements = ""; + statements += getVariableDeclaration(tupleVar.getType().getInterfaceTypeName(), tupleVar.getName()) + + getAssignment() + tupleExp + getStatementDelimiter(); + for (int i = 0; i < vars.size(); i++) { + VariableDeclaration var = vars.get(i); + statements += "\n" + getVariableDeclaration(var.getType().getInterfaceTypeName(), var.getName()) + + getAssignment() + + getTupleGet(tupleVar.getName(), i, vars.size()) + + getStatementDelimiter(); + } + return statements; + } + + @Override + public boolean isValueType(Type type) { + if (type == DataConstraintModel.typeInt + || type == DataConstraintModel.typeLong + || type == DataConstraintModel.typeFloat + || type == DataConstraintModel.typeDouble + || type == DataConstraintModel.typeBoolean) { + return true; + } + return false; + } + + + @Override + public boolean isVoidType(Type type) { + if (type == typeVoid) { + return true; + } + return false; + } + + private String getImplementationTypeName(Type type) { + if (type == null) + return "Object"; + String wrapperType = DataConstraintModel.getWrapperType(type); + if (wrapperType != null) + return wrapperType; + return type.getImplementationTypeName(); + } + + private String getInterfaceTypeName(Type type) { + if (type == null) + return "Object"; + String wrapperType = DataConstraintModel.getWrapperType(type); + if (wrapperType != null) + return wrapperType; + return type.getInterfaceTypeName(); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JerseyCodeGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/JerseyCodeGenerator.java new file mode 100644 index 0000000..b084c66 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/generators/JerseyCodeGenerator.java @@ -0,0 +1,404 @@ +package generators; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import code.ast.Annotation; +import code.ast.Block; +import code.ast.CompilationUnit; +import code.ast.FieldDeclaration; +import code.ast.ImportDeclaration; +import code.ast.MethodDeclaration; +import code.ast.TypeDeclaration; +import code.ast.VariableDeclaration; +import models.Edge; +import models.Node; +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; +import models.algebra.Variable; +import models.dataConstraintModel.Channel; +import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.ResourcePath; +import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor; +import models.dataFlowModel.PushPullAttribute; +import models.dataFlowModel.PushPullValue; +import models.dataFlowModel.DataFlowEdge; +import models.dataFlowModel.DataFlowGraph; +import models.dataFlowModel.ResourceNode; +import models.dataFlowModel.StoreAttribute; + +/** + * Generator for Jersey prototypes + * + * @author Nitta + * + */ +public class JerseyCodeGenerator { + public static final Type typeVoid = new Type("Void", "void"); + public static final Type typeClient = new Type("Client", "Client"); + private static String defaultMainTypeName = "Main"; + static String mainTypeName = defaultMainTypeName; + + public static String getMainTypeName() { + return mainTypeName; + } + + public static void setMainTypeName(String mainTypeName) { + JerseyCodeGenerator.mainTypeName = mainTypeName; + } + + public static void resetMainTypeName() { + JerseyCodeGenerator.mainTypeName = defaultMainTypeName; + } + + static public ArrayList doGenerate(DataFlowGraph graph, DataTransferModel model) { + ArrayList codes = new ArrayList<>(); +// ArrayList resources = StoreResourceCheck(graph); + Set resources = graph.getNodes(); + + 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; + } + } 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.equals(rn.getResource())) { + param = new VariableDeclaration(refRes.getResourceStateType(), refRes.getResourceName()); + param.addAnnotation(new Annotation("FormParam", "\"" + refRes.getResourceName() + "\"")); + vars.add(param); + } + } + MethodDeclaration update = new MethodDeclaration("update" + srcResName, false, typeVoid, vars); + for (ChannelMember cm: re.getChannel().getOutputChannelMembers()) { + if (cm.getResource().equals(rn.getResource())) { + if (cm.getStateTransition().isRightUnary()) { + update.addAnnotation(new Annotation("PUT")); + } else { + update.addAnnotation(new Annotation("POST")); + } + } + } + if (rn.getInEdges().size() > 1 + || (rn.getIndegree() == 1 && re.getChannel().getInputChannelMembers().iterator().next().getStateTransition().isRightPartial())) { + // Declare a field to cache the state of the source resource in the type of the destination resource. + ResourcePath cacheRes = ((ResourceNode) re.getSource()).getResource(); + type.addField(new FieldDeclaration(cacheRes.getResourceStateType(), srcName, getInitializer(cacheRes))); + if (rn.getIndegree() > 1) { + // For each source resource, a child resource is defined in the destination resource so that its state can be updated separately. + update.addAnnotation(new Annotation("Path", "\"/" + srcName + "\"")); + } + } + 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 ch : model.getIOChannels()) { + for (ChannelMember cm : ((DataTransferChannel) ch).getOutputChannelMembers()) { + if (cm.getResource().equals(rn.getResource())) { + Expression message = cm.getStateTransition().getMessageExpression(); + if (message instanceof Term) { + ArrayList params = new ArrayList<>(); + 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().getResourceName(); + break; + } + } + } + if (refVarName != null) { + // var has come from a reference resource. + VariableDeclaration param = new VariableDeclaration(var.getType(), refVarName); + param.addAnnotation(new Annotation("FormParam", "\"" + refVarName + "\"")); + params.add(param); + } else { + // var has not come from reference resource. + String paramName = var.getName(); + VariableDeclaration param = new VariableDeclaration(var.getType(), paramName); + param.addAnnotation(new Annotation("FormParam", "\"" + paramName + "\"")); + params.add(param); + } + } + MethodDeclaration input = new MethodDeclaration( + ((Term) message).getSymbol().getImplName(), + false, typeVoid, params); + if (cm.getStateTransition().isRightUnary()) { + input.addAnnotation(new Annotation("PUT")); + } else { + input.addAnnotation(new Annotation("POST")); + } + type.addMethod(input); + } else if (message instanceof Variable) { + MethodDeclaration input = new MethodDeclaration( + ((Variable) message).getName(), + false, typeVoid, null); + if (cm.getStateTransition().isRightUnary()) { + input.addAnnotation(new Annotation("PUT")); + } else { + input.addAnnotation(new Annotation("POST")); + } + type.addMethod(input); + } + } + } + } + + // Declare the field to store the state in the type of each resource. + if (((StoreAttribute) rn.getAttribute()).isStored()) { + ResourcePath res = rn.getResource(); + type.addField(new FieldDeclaration(res.getResourceStateType(), "value", getInitializer(res))); + } + + // Declare the getter method to obtain the state in the type of each resource. + MethodDeclaration getter = new MethodDeclaration("getValue", rn.getResource().getResourceStateType()); + getter.addAnnotation(new Annotation("Produces", "MediaType.APPLICATION_JSON")); + getter.addAnnotation(new Annotation("GET")); + type.addMethod(getter); + + // Add compilation unit for each resource. + CompilationUnit cu = new CompilationUnit(type); + cu.addImport(new ImportDeclaration("java.util.*")); + cu.addImport(new ImportDeclaration("javax.ws.rs.*")); + cu.addImport(new ImportDeclaration("javax.ws.rs.client.*")); + cu.addImport(new ImportDeclaration("javax.ws.rs.core.*")); + cu.addImport(new ImportDeclaration("org.springframework.stereotype.Component")); + cu.addImport(new ImportDeclaration("com.fasterxml.jackson.databind.ObjectMapper")); + cu.addImport(new ImportDeclaration("com.fasterxml.jackson.core.JsonProcessingException")); + codes.add(cu); + } + + // Declare the Pair class. + boolean isCreatedPair = false; + for(Node n : resources) { + ResourceNode rn = (ResourceNode) n; + if(isCreatedPair) continue; + if(model.getType("Pair").isAncestorOf(rn.getResource().getResourceStateType())) { + TypeDeclaration type = new TypeDeclaration("Pair"); + type.addField(new FieldDeclaration(new Type("Double", "T"), "left")); + type.addField(new FieldDeclaration(new Type("Double", "T"), "right")); + + MethodDeclaration constructor = new MethodDeclaration("Pair", true); + constructor.addParameter(new VariableDeclaration(new Type("Double", "T"), "left")); + constructor.addParameter(new VariableDeclaration(new Type("Double", "T"), "right")); + Block block = new Block(); + block.addStatement("this.left = left;"); + block.addStatement("this.right = right;"); + constructor.setBody(block); + type.addMethod(constructor); + + for(FieldDeclaration field : type.getFields()) { + MethodDeclaration getter = new MethodDeclaration( + "get" + field.getName().substring(0,1).toUpperCase() + field.getName().substring(1), + new Type("Double","T")); + getter.setBody(new Block()); + getter.getBody().addStatement("return " + field.getName() + ";"); + type.addMethod(getter); + } + +// MethodDeclaration toStr = new MethodDeclaration("toString", false, DataConstraintModel.typeString, null); +// block = new Block(); +// block.addStatement("return \"{\\\"\" + left + \"\\\":\\\"\" + right + \"\\\"}\";"); +// toStr.setBody(block); +// type.addMethod(toStr); + + CompilationUnit cu = new CompilationUnit(type); + cu.addImport(new ImportDeclaration("java.util.*")); + codes.add(cu); + + isCreatedPair = true; + } + } + + return codes; + } + + private static String getInitializer(ResourcePath resId) { + Type stateType = resId.getResourceStateType(); + String initializer = null; + if (resId.getInitialValue() != null) { + initializer = resId.getInitialValue().toImplementation(new String[] {""}); + } else { + if (DataConstraintModel.typeList.isAncestorOf(stateType)) { + initializer = "new " + resId.getResourceStateType().getImplementationTypeName() + "()"; + } else if (DataConstraintModel.typeMap.isAncestorOf(stateType)) { + initializer = "new " + resId.getResourceStateType().getImplementationTypeName() + "()"; + } + } + return initializer; + } + + static public ArrayList getCodes(ArrayList codeTree) { + ArrayList codes = new ArrayList<>(); + for (TypeDeclaration type : codeTree) { + codes.add("public class " + type.getTypeName() + "{"); + for (FieldDeclaration field : type.getFields()) { + if (type.getTypeName() != mainTypeName) { + String cons = "\t" + "private " + field.getType().getInterfaceTypeName() + " " + + field.getName(); + if (DataConstraintModel.isListType(field.getType())) + cons += " = new " + field.getType().getImplementationTypeName() + "()"; + cons += ";"; + codes.add(cons); + } else { + String cons = "\t" + "private " + field.getType().getInterfaceTypeName() + " " + + field.getName() + " = new " + field.getType().getTypeName() + "("; + cons += ");"; + codes.add(cons); + } + } + codes.add(""); + for (MethodDeclaration method : type.getMethods()) { + String varstr = "\t" + "public " + method.getReturnType().getInterfaceTypeName() + " " + + method.getName() + "("; + if (method.getParameters() != null) { + for (VariableDeclaration var : method.getParameters()) { + varstr += var.getType().getInterfaceTypeName() + " " + var.getName() + ","; + } + if (!method.getParameters().isEmpty()) + varstr = varstr.substring(0, varstr.length() - 1); + } + if (method.getBody() != null) { + for (String str : method.getBody().getStatements()) { + codes.add("\t\t" + str + ";"); + } + } + codes.add(varstr + ")" + "{"); + codes.add("\t" + "}"); + codes.add(""); + } + codes.add("}"); + codes.add(""); + } + return codes; + } + + static public IResourceStateAccessor pushAccessor = 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); + } + // use the cached value as the current state + return new Field(target.getResourceName(), + target.getResourceStateType() != null ? target.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { + return new Parameter(target.getResourceName(), + target.getResourceStateType() != null ? target.getResourceStateType() + : DataConstraintModel.typeInt); + } + }; + static public IResourceStateAccessor pullAccessor = new IResourceStateAccessor() { + @Override + public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) { + if (target.equals(from)) { + return new Field("value", + target.getResourceStateType() != null ? target.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + return new Parameter(target.getResourceName(), + target.getResourceStateType() != null ? target.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { + return new Parameter(target.getResourceName(), + target.getResourceStateType() != null ? target.getResourceStateType() + : DataConstraintModel.typeInt); + } + }; + static public IResourceStateAccessor refAccessor = new IResourceStateAccessor() { + @Override + public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) { + if (target.equals(from)) { + return new Field("value", + target.getResourceStateType() != null ? target.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + return new Parameter(target.getResourceName(), + target.getResourceStateType() != null ? target.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { + return new Parameter(target.getResourceName(), + target.getResourceStateType() != null ? target.getResourceStateType() + : DataConstraintModel.typeInt); + } + }; +} diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java new file mode 100644 index 0000000..ddc3ba7 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java @@ -0,0 +1,598 @@ +package generators; + +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import algorithms.TypeInference; +import code.ast.CodeUtil; +import code.ast.CompilationUnit; +import code.ast.MethodDeclaration; +import code.ast.TypeDeclaration; +import code.ast.VariableDeclaration; +import models.Edge; +import models.Node; +import models.algebra.Expression; +import models.algebra.InvalidMessage; +import models.algebra.ParameterizedIdentifierIsFutureWork; +import models.algebra.Term; +import models.algebra.Type; +import models.algebra.UnificationFailed; +import models.algebra.ValueUndefined; +import models.algebra.Variable; +import models.dataConstraintModel.Channel; +import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.ResourcePath; +import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.PushPullAttribute; +import models.dataFlowModel.PushPullValue; +import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork; +import models.dataFlowModel.DataFlowEdge; +import models.dataFlowModel.DataFlowGraph; +import models.dataFlowModel.ResourceNode; +import models.dataFlowModel.StoreAttribute; +import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor; + +public class JerseyMethodBodyGenerator { + private static String baseURL = "http://localhost:8080"; + + public static ArrayList doGenerate(DataFlowGraph graph, DataTransferModel model, ArrayList codes) { + // Create a map from type names (lower case) to their types. + Map typeMap = new HashMap<>(); + for (CompilationUnit code: codes) { + for (TypeDeclaration type: code.types()) { + typeMap.put(type.getTypeName().substring(0,1).toLowerCase() + type.getTypeName().substring(1), type); + } + } + + // Generate the body of each update or getter method. + try { + 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().equals(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); + } + for (ChannelMember c: d.getChannel().getReferenceChannelMembers()) { + inputResourceToStateAccessor.put(c.getResource(), JerseyCodeGenerator.refAccessor); + } + 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 + || (dst.getIndegree() == 1 && d.getChannel().getInputChannelMembers().iterator().next().getStateTransition().isRightPartial())) { + // 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.addStatement(cashStatement); + } + } + // to convert a json param to a tuple, pair or map object. + for (VariableDeclaration param: update.getParameters()) { + Type paramType = param.getType(); + String paramName = param.getName(); + String paramConverter = ""; + if (DataConstraintModel.typeList.isAncestorOf(paramType) && paramType != DataConstraintModel.typeList) { + Type compType = TypeInference.getListComponentType(paramType); + if (DataConstraintModel.typeTuple.isAncestorOf(compType)) { + param.setType(DataConstraintModel.typeListStr); + param.setName(paramName + "_json"); + paramConverter += paramType.getInterfaceTypeName() + " " + paramName + " = new " + paramType.getImplementationTypeName() + "();\n"; + paramConverter += "for (String str: " + param.getName() + ") {\n"; + String mapTypeName = convertFromEntryToMapType(compType); + paramConverter += "\t" + mapTypeName + " i = new ObjectMapper().readValue(str, HashMap.class);\n"; + paramConverter += "\t" + paramName + ".add(" + getCodeForConversionFromMapToTuple(compType, "i") + ");\n"; + paramConverter += "}"; + update.addThrow("JsonProcessingException"); + } else if (DataConstraintModel.typePair.isAncestorOf(compType)) { + param.setType(DataConstraintModel.typeListStr); + param.setName(paramName + "_json"); + paramConverter += paramType.getInterfaceTypeName() + " " + paramName + " = new " + paramType.getImplementationTypeName() + "();\n"; + paramConverter += "for (String str: " + param.getName() + ") {\n"; + String mapTypeName = convertFromEntryToMapType(compType); + paramConverter += "\t" + mapTypeName + " i = new ObjectMapper().readValue(str, HashMap.class);\n"; + paramConverter += "\t" + paramName + ".add(" + getCodeForConversionFromMapToPair(compType, "i") + ");\n"; + paramConverter += "}"; + update.addThrow("JsonProcessingException"); + } else if (DataConstraintModel.typeMap.isAncestorOf(compType)) { + param.setType(DataConstraintModel.typeListStr); + // To do. + } + } else if (DataConstraintModel.typeTuple.isAncestorOf(paramType)) { + param.setType(DataConstraintModel.typeString); + param.setName(paramName + "_json"); + paramConverter += paramType.getInterfaceTypeName() + " " + paramName + ";\n"; + paramConverter += "{\n"; + String mapTypeName = convertFromEntryToMapType(paramType); + paramConverter += "\t" + mapTypeName + " i = new ObjectMapper().readValue(" + paramName + "_json" + ", HashMap.class);\n"; + paramConverter += "\t" + paramName + " = " + getCodeForConversionFromMapToTuple(paramType, "i") + ";\n"; + paramConverter += "}"; + update.addThrow("JsonProcessingException"); + } else if (DataConstraintModel.typePair.isAncestorOf(paramType)) { + param.setType(DataConstraintModel.typeString); + param.setName(paramName + "_json"); + paramConverter += paramType.getInterfaceTypeName() + " " + paramName + ";\n"; + paramConverter += "{\n"; + String mapTypeName = convertFromEntryToMapType(paramType); + paramConverter += "\t" + mapTypeName + " i = new ObjectMapper().readValue(" + paramName + "_json" + ", HashMap.class);\n"; + paramConverter += "\t" + paramName + " = " + getCodeForConversionFromMapToPair(paramType, "i") + ";\n"; + paramConverter += "}"; + update.addThrow("JsonProcessingException"); + } else if (DataConstraintModel.typeMap.isAncestorOf(paramType)) { + param.setType(DataConstraintModel.typeString); + param.setName(paramName + "_json"); + paramConverter += paramType.getInterfaceTypeName() + " " + paramName + " = " + "new " + paramType.getImplementationTypeName() + "();\n"; + paramConverter += "{\n"; + String mapTypeName = convertFromEntryToMapType(paramType); + paramConverter += "\t" + mapTypeName + " i = new ObjectMapper().readValue(" + paramName + "_json" + ", HashMap.class);\n"; + paramConverter += "\t" + getCodeForConversionFromMapToMap(paramType, "i", paramName) + "\n"; + paramConverter += "}"; + update.addThrow("JsonProcessingException"); + } + if (paramConverter.length() > 0) update.addFirstStatement(paramConverter); + } + 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()) { + // 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 (!ref.equals(dst.getResource())) { + String refResourceName = ref.getResourceName(); + Type refResourceType = ref.getResourceStateType(); + if (!referredSet.contains(ref)) { + referredSet.add(ref); + generatePullDataTransfer(srcUpdate, refResourceName, refResourceType); + } + // Value of a reference side resource. + params.add(new AbstractMap.SimpleEntry<>(refResourceType, new AbstractMap.SimpleEntry<>(refResourceName, refResourceName))); + } + } + } + String srcResName = null; + if (dst.getIndegree() > 1) { + srcResName = srcResourceName; + } + if (!chainedCalls.contains(srcUpdate)) { + // The first call to an update method in this method + // Value of the source side (input side) resource. + params.add(0, new AbstractMap.SimpleEntry<>(src.getResource().getResourceStateType(), new AbstractMap.SimpleEntry<>(srcResourceName, "this.value"))); + srcUpdate.addStatement(getHttpMethodParamsStatement(srcType.getTypeName(), params, true)); + srcUpdate.addStatement("String result = " + getHttpMethodCallStatement(baseURL, dstResourceName, srcResName, httpMethod)); + chainedCalls.add(srcUpdate); + } else { + // After the second time of call to update methods in this method + // Value of the source side (input side) resource. + params.add(0, new AbstractMap.SimpleEntry<>(src.getResource().getResourceStateType(), new AbstractMap.SimpleEntry<>(srcResourceName, "this.value"))); + srcUpdate.addStatement(getHttpMethodParamsStatement(srcType.getTypeName(), params, false)); + srcUpdate.addStatement("result = " + getHttpMethodCallStatement(baseURL, dstResourceName, srcResName, httpMethod)); + } + srcUpdate.addThrow("JsonProcessingException"); + } + } + for (MethodDeclaration srcInput: getInputMethods(srcType, src, model)) { + List>> params = new ArrayList<>(); + Set referredSet = referredResources.get(srcInput); + for (ChannelMember rc: d.getChannel().getReferenceChannelMembers()) { + // For each reference channel member, get the current state of the reference side resource by pull data transfer. + ResourcePath ref = rc.getResource(); + if (referredSet == null) { + referredSet = new HashSet<>(); + referredResources.put(srcInput, referredSet); + } + if (!ref.equals(dst.getResource())) { + String refResourceName = ref.getResourceName(); + Type refResourceType = ref.getResourceStateType(); + if (!referredSet.contains(ref)) { + referredSet.add(ref); + generatePullDataTransfer(srcInput, refResourceName, refResourceType); + } + // Value of a reference side resource. + params.add(new AbstractMap.SimpleEntry<>(refResourceType, new AbstractMap.SimpleEntry<>(refResourceName, refResourceName))); + } + } + String srcResName = null; + if (dst.getIndegree() > 1) { + srcResName = srcResourceName; + } + if (!chainedCalls.contains(srcInput)) { + // First call to an update method in this method + // Value of the source side (input side) resource. + params.add(0, new AbstractMap.SimpleEntry<>(src.getResource().getResourceStateType(), new AbstractMap.SimpleEntry<>(srcResourceName, "this.value"))); + srcInput.addStatement(getHttpMethodParamsStatement(srcType.getTypeName(), params, true)); + srcInput.addStatement("String result = " + getHttpMethodCallStatement(baseURL, dstResourceName, srcResName, httpMethod)); + chainedCalls.add(srcInput); + } else { + // After the second time of call to update methods in this method + // Value of the source side (input side) resource. + params.add(0, new AbstractMap.SimpleEntry<>(src.getResource().getResourceStateType(), new AbstractMap.SimpleEntry<>(srcResourceName, "this.value"))); + srcInput.addStatement(getHttpMethodParamsStatement(srcType.getTypeName(), params, false)); + srcInput.addStatement("result = " + getHttpMethodCallStatement(baseURL, dstResourceName, srcResName, httpMethod)); + } + srcInput.addThrow("JsonProcessingException"); + } + } else { + // for pull (or push/pull) data transfer + MethodDeclaration getter = getGetterMethod(dstType); + if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { + // generate a return statement. + String[] sideEffects = new String[] {""}; + String curState = d.getChannel().deriveUpdateExpressionOf(out, JerseyCodeGenerator.pullAccessor).toImplementation(sideEffects); // no pull data transfer is included. + getter.addStatement(sideEffects[0] + "return " + curState + ";"); + // For each reference channel member, get the current state of the reference side resource by pull data transfer. + for (ChannelMember c: d.getChannel().getReferenceChannelMembers()) { + String refResourceName = c.getResource().getResourceName(); + Type refResourceType = c.getResource().getResourceStateType(); + generatePullDataTransfer(getter, refResourceName, refResourceType); + } + } + // get src side resource state by pull data transfer. + Type srcResourceType = src.getResource().getResourceStateType(); + generatePullDataTransfer(getter, srcResourceName, srcResourceType); + } + } + } + } + // for source nodes + for (Node n: graph.getNodes()) { + ResourceNode resource = (ResourceNode) n; + String resourceName = resource.getResource().getResourceName(); + TypeDeclaration type = typeMap.get(resourceName); + if (type != null) { + // getter method + MethodDeclaration getter = getGetterMethod(type); + if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { + getter.addStatement("return value;"); + } + // 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.refAccessor); + String[] sideEffects = new String[] {""}; + String newState = updateExp.toImplementation(sideEffects); + String updateStatement; + if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { + updateStatement = sideEffects[0]; + } else { + updateStatement = sideEffects[0] + "this.value = " + newState + ";"; + } + if (input.getBody() == null || !input.getBody().getStatements().contains(updateStatement)) { + input.addFirstStatement(updateStatement); + } + } + } + } + } + } + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork + | InvalidMessage | UnificationFailed | ValueUndefined e1) { + e1.printStackTrace(); + } + return codes; + } + + private static void generatePullDataTransfer(MethodDeclaration methodBody, String fromResourceName, Type fromResourceType) { + String varName = new String(fromResourceName); + String respTypeName = fromResourceType.getInterfaceTypeName(); + String respImplTypeName = fromResourceType.getImplementationTypeName(); + String respConverter = ""; + if (DataConstraintModel.typeList.isAncestorOf(fromResourceType) && fromResourceType != DataConstraintModel.typeList) { + Type compType = TypeInference.getListComponentType(fromResourceType); + if (DataConstraintModel.typeTuple.isAncestorOf(compType)) { + varName += "_json"; + String mapTypeName = convertFromEntryToMapType(compType); + respTypeName = "List<" + mapTypeName + ">"; + respConverter += fromResourceType.getInterfaceTypeName() + " " + fromResourceName + " = new " + fromResourceType.getImplementationTypeName() + "();\n"; + respConverter += "for (" + mapTypeName + " i: " + varName + ") {\n"; + respConverter += "\t" + fromResourceName + ".add(" + getCodeForConversionFromMapToTuple(compType, "i") + ");\n"; + respConverter += "}"; + methodBody.addThrow("JsonProcessingException"); + } else if (DataConstraintModel.typeMap.isAncestorOf(compType)) { + // To do. + } + } else if (DataConstraintModel.typeTuple.isAncestorOf(fromResourceType)) { + varName += "_json"; + respTypeName = convertFromEntryToMapType(fromResourceType); + respConverter += fromResourceType.getInterfaceTypeName() + " " + fromResourceName + " = " + getCodeForConversionFromMapToTuple(fromResourceType, varName) + ";"; + respImplTypeName = "HashMap"; + } else if (DataConstraintModel.typePair.isAncestorOf(fromResourceType)) { + varName += "_json"; + respTypeName = convertFromEntryToMapType(fromResourceType); + respConverter += fromResourceType.getInterfaceTypeName() + " " + fromResourceName + " = " + getCodeForConversionFromMapToPair(fromResourceType, varName) + ";"; + respImplTypeName = "HashMap"; + } else if (DataConstraintModel.typeMap.isAncestorOf(fromResourceType)) { + varName += "_json"; + respTypeName = convertFromEntryToMapType(fromResourceType); + respConverter += fromResourceType.getInterfaceTypeName() + " " + fromResourceName + " = new " + fromResourceType.getImplementationTypeName() + "();\n"; + respConverter += getCodeForConversionFromMapToMap(fromResourceType, varName, fromResourceName); + respImplTypeName = "HashMap"; + } + if (respConverter.length() > 0) { + methodBody.addFirstStatement(respConverter); + } + methodBody.addFirstStatement(respTypeName + " " + varName + " = " + getHttpMethodCallStatementWithResponse(baseURL, fromResourceName, "get", respImplTypeName)); + } + + private static String convertFromEntryToMapType(Type type) { + String mapTypeName = null; + if (DataConstraintModel.typePair.isAncestorOf(type)) { + Type compType = TypeInference.getPairComponentType(type); + String wrapperType = DataConstraintModel.getWrapperType(compType); + if (wrapperType != null) { + mapTypeName = "Map"; + } else { + mapTypeName = "Map"; + } + } else if (DataConstraintModel.typeMap.isAncestorOf(type)) { + List compTypes = TypeInference.getMapComponentTypes(type); + String wrapperType = DataConstraintModel.getWrapperType(compTypes.get(1)); + if (wrapperType != null) { + mapTypeName = "Map"; + } else { + mapTypeName = "Map"; + } + } else { + mapTypeName = type.getInterfaceTypeName(); + mapTypeName = mapTypeName.replace("Map.Entry", "Map"); + for (int idx = mapTypeName.indexOf("<", 0); idx >= 0; idx = mapTypeName.indexOf("<", idx + 1)) { + int to = mapTypeName.indexOf(",", idx); + if (to > idx) { + mapTypeName = mapTypeName.substring(0, idx + 1) + "String" + mapTypeName.substring(to); // All elements except for the last one have the string type. + } + } + } + return mapTypeName; + } + + private static String getCodeForConversionFromMapToTuple(Type tupleType, String mapVar) { + String decoded = "$x"; + List elementsTypes = TypeInference.getTupleComponentTypes(tupleType); + String elementBase = mapVar; + for (Type elmType: elementsTypes.subList(0, elementsTypes.size() - 1)) { + elementBase += ".entrySet().iterator().next()"; + if (elmType == DataConstraintModel.typeBoolean + || elmType == DataConstraintModel.typeInt + || elmType == DataConstraintModel.typeLong + || elmType == DataConstraintModel.typeFloat + || elmType == DataConstraintModel.typeDouble) { + String elmVal = CodeUtil.getToValueExp(elmType.getImplementationTypeName(), elementBase + ".getKey()"); + decoded = decoded.replace("$x", "new AbstractMap.SimpleEntry<>(" + elmVal + ", $x)"); + } else if (elmType == DataConstraintModel.typeString) { + decoded = decoded.replace("$x", "new AbstractMap.SimpleEntry<>(" + elementBase + ".getKey(), $x)"); + } else { + // To do. + } + elementBase += ".getValue()"; + } + decoded = decoded.replace("$x", elementBase); + return decoded; + } + + private static String getCodeForConversionFromMapToPair(Type pairType, String mapVar) { + String decoded = "$x"; + decoded = decoded.replace("$x", "new Pair<>(" + mapVar + ".get(\"left\"), $x)"); + decoded = decoded.replace("$x", mapVar + ".get(\"right\")"); + return decoded; + } + + private static String getCodeForConversionFromMapToMap(Type mapType, String mapVal, String mapVar) { + List elementsTypes = TypeInference.getMapComponentTypes(mapType); + Type keyType = elementsTypes.get(0); + Type valType = elementsTypes.get(1); + String keyVal = null; + String decoded = ""; + if (keyType == DataConstraintModel.typeBoolean + || keyType == DataConstraintModel.typeInt + || keyType == DataConstraintModel.typeLong + || keyType == DataConstraintModel.typeFloat + || keyType == DataConstraintModel.typeDouble) { + decoded += "for (String k: " + mapVal + ".keySet()) {\n"; + decoded += "\t" + mapVar + ".put("; + keyVal = CodeUtil.getToValueExp(keyType.getImplementationTypeName(), "k"); + decoded += keyVal + ", " + mapVal + ".get(" + keyVal + ")" + ");\n"; + decoded += "}"; + } else if (keyType == DataConstraintModel.typeString) { + decoded += mapVar + " = " + mapVal + ";"; + } + return decoded; + } + + private static String getHttpMethodParamsStatement(String callerResourceName, List>> params, boolean isFirstCall) { + String statements = ""; + if (isFirstCall) { + statements += "Form "; + } + statements += "form = new Form();\n"; + for (Map.Entry> param: params) { + Type paramType = param.getKey(); + String paramName = param.getValue().getKey(); + String value = param.getValue().getValue(); + if (DataConstraintModel.typeList.isAncestorOf(paramType)) { + Type compType = TypeInference.getListComponentType(paramType); + String wrapperType = DataConstraintModel.getWrapperType(compType); + if (wrapperType == null) { + statements += "for (" + compType.getInterfaceTypeName() + " i: " + value + ") {\n"; + } else { + statements += "for (" + wrapperType + " i: " + value + ") {\n"; + } + if (DataConstraintModel.typeTuple.isAncestorOf(compType) || DataConstraintModel.typePair.isAncestorOf(paramType) || DataConstraintModel.typeList.isAncestorOf(compType) || DataConstraintModel.typeMap.isAncestorOf(paramType)) { + statements += "\tform.param(\"" + paramName + "\", new ObjectMapper().writeValueAsString(i));\n"; // typeTuple: {"1.0":2.0}, typePair: {"left": 1.0, "right":2.0} + } else { + statements += "\tform.param(\"" + paramName + "\", i.toString());\n"; + } + statements += "}\n"; +// return "Entity entity = Entity.entity(" + paramName + ".toString(), MediaType.APPLICATION_JSON);"; + } else if (DataConstraintModel.typeTuple.isAncestorOf(paramType) || DataConstraintModel.typePair.isAncestorOf(paramType) || DataConstraintModel.typeMap.isAncestorOf(paramType)) { + // typeTuple: {"1.0":2.0}, typePair: {"left": 1.0, "right":2.0} + statements += "form.param(\"" + paramName + "\", new ObjectMapper().writeValueAsString(" + value + "));\n"; + } else { + statements += "form.param(\"" + paramName + "\", " + CodeUtil.getToStringExp(paramType.getImplementationTypeName(), value) + ");\n"; + } + } + if (isFirstCall) { + statements += "Entity
"; + } + statements += "entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED);"; + return statements; + } + + private static String getHttpMethodCallStatement(String baseURL, String resourceName, String srcResName, String httpMethod) { + if (srcResName == null) { + return "client.target(\"" + baseURL + "\").path(\"/" + resourceName + "\").request()." + httpMethod + "(entity, String.class);"; + } else { + // For each source resource, a child resource is defined in the destination resource so that its state can be updated separately. + return "client.target(\"" + baseURL + "\").path(\"/" + resourceName + "/" + srcResName + "\").request()." + httpMethod + "(entity, String.class);"; + } + } + + private static String getHttpMethodCallStatementWithResponse(String baseURL, String resourceName, String httpMethod, String respImplName) { + String responseShortTypeName = respImplName; + if (respImplName.contains("<")) { + responseShortTypeName = respImplName.substring(0, respImplName.indexOf("<")); + } + 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; + } + return null; + } + + private static List getUpdateMethods(TypeDeclaration type) { + List updates = new ArrayList<>(); + for (MethodDeclaration m: type.getMethods()) { + if (m.getName().startsWith("update")) { + updates.add(m); + } + } + return updates; + } + + private static MethodDeclaration getGetterMethod(TypeDeclaration type) { + for (MethodDeclaration m: type.getMethods()) { + if (m.getName().startsWith("get")) return m; + } + return null; + } + + private static Map> getIOChannelsAndMembers(ResourceNode resource, DataTransferModel model) { + Map> ioChannelsAndMembers = new HashMap<>(); + for (Channel c: model.getIOChannels()) { + DataTransferChannel ch = (DataTransferChannel) c; + // I/O channel + for (ChannelMember out: ch.getOutputChannelMembers()) { + if (out.getResource().equals(resource.getResource())) { + if (out.getStateTransition().getMessageExpression() instanceof Term || out.getStateTransition().getMessageExpression() instanceof Variable) { + Set channelMembers = ioChannelsAndMembers.get(ch); + if (channelMembers == null) { + channelMembers = new HashSet<>(); + ioChannelsAndMembers.put(ch, channelMembers); + } + channelMembers.add(out); + } + } + } + } + return ioChannelsAndMembers; + } + + private static List getInputMethods(TypeDeclaration type, ResourceNode resource, DataTransferModel model) { + List inputs = new ArrayList<>(); + for (Channel c: model.getIOChannels()) { + DataTransferChannel channel = (DataTransferChannel) c; + // I/O channel + for (ChannelMember out: channel.getOutputChannelMembers()) { + if (out.getResource().equals(resource.getResource())) { + MethodDeclaration input = getInputMethod(type, out); + inputs.add(input); + } + } + } + return inputs; + } + + private static MethodDeclaration getInputMethod(TypeDeclaration type, ChannelMember out) { + MethodDeclaration input = null; + if (out.getStateTransition().getMessageExpression() instanceof Term) { + Term message = (Term) out.getStateTransition().getMessageExpression(); + input = getMethod(type, message.getSymbol().getImplName()); + } else if (out.getStateTransition().getMessageExpression() instanceof Variable) { + Variable message = (Variable) out.getStateTransition().getMessageExpression(); + input = getMethod(type, message.getName()); + } + return input; + } + + private static MethodDeclaration getMethod(TypeDeclaration type, String methodName) { + for (MethodDeclaration m: type.getMethods()) { + if (m.getName().equals(methodName)) return m; + } + return null; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/DirectedGraph.java b/AlgebraicDataflowArchitectureModel/src/models/DirectedGraph.java new file mode 100644 index 0000000..d76545d --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/DirectedGraph.java @@ -0,0 +1,72 @@ +package models; + +import java.io.Serializable; +import java.util.HashSet; +import java.util.Set; + +public class DirectedGraph implements Serializable{ + private Set nodes = null; + private Set edges = null; + + public DirectedGraph() { + nodes = new HashSet<>(); + edges = new HashSet<>(); + } + + public Set getNodes() { + return nodes; + } + + public void setNodes(Set nodes) { + this.nodes = nodes; + } + + public void addNode(Node node) { + nodes.add(node); + } + + public void removeNode(Node node) { + nodes.remove(node); + node.clearInEdges(); + node.clearOutEdges(); + for (Edge edge: edges) { + if (edge.getSource().equals(node)) { + edges.remove(edge); + } else if (edge.getDestination().equals(node)) { + edges.remove(edge); + } + } + } + + public Set getEdges() { + return edges; + } + + public void setEdges(Set edges) { + this.edges = edges; + for (Edge edge: edges) { + if (!nodes.contains(edge.getSource())) nodes.add(edge.getSource()); + if (!nodes.contains(edge.getDestination())) nodes.add(edge.getDestination()); + edge.getSource().addOutEdge(edge); + edge.getDestination().addInEdge(edge); + } + } + + public void addEdge(Edge edge) { + edges.add(edge); + if (!nodes.contains(edge.getSource())) nodes.add(edge.getSource()); + if (!nodes.contains(edge.getDestination())) nodes.add(edge.getDestination()); + edge.getSource().addOutEdge(edge); + edge.getDestination().addInEdge(edge); + } + + public void removeEdge(Edge edge) { + edges.remove(edge); + edge.getSource().removeOutEdge(edge); + edge.getDestination().removeInEdge(edge); + } + + protected void simpleAddEdge(Edge edge) { + edges.add(edge); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/Edge.java b/AlgebraicDataflowArchitectureModel/src/models/Edge.java new file mode 100644 index 0000000..71ab977 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/Edge.java @@ -0,0 +1,38 @@ +package models; + +import java.io.Serializable; + +public class Edge implements Serializable { + protected Node source; + protected Node destination; + private EdgeAttribute attribute; + + public Edge(Node src, Node dst) { + source = src; + destination = dst; + } + + public Node getSource() { + return source; + } + + public void setSource(Node source) { + this.source = source; + } + + public Node getDestination() { + return destination; + } + + public void setDestination(Node destination) { + this.destination = destination; + } + + public EdgeAttribute getAttribute() { + return attribute; + } + + public void setAttribute(EdgeAttribute attribute) { + this.attribute = attribute; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/EdgeAttribute.java b/AlgebraicDataflowArchitectureModel/src/models/EdgeAttribute.java new file mode 100644 index 0000000..4be3197 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/EdgeAttribute.java @@ -0,0 +1,7 @@ +package models; + +import java.io.Serializable; + +public class EdgeAttribute implements Serializable{ + +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/Node.java b/AlgebraicDataflowArchitectureModel/src/models/Node.java new file mode 100644 index 0000000..589cc4f --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/Node.java @@ -0,0 +1,90 @@ +package models; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +public class Node implements Cloneable, Serializable { + protected Collection inEdges = null; + protected Collection outEdges = null; + private NodeAttribute attribute; + + public Node() { + inEdges = new HashSet<>(); + outEdges = new HashSet<>(); + } + + public Collection getInEdges() { + return inEdges; + } + + public void setInEdges(Collection inEdges) { + this.inEdges = inEdges; + } + + public Collection getOutEdges() { + return outEdges; + } + + public void setOutEdges(Collection outEdges) { + this.outEdges = outEdges; + } + + public void addInEdge(Edge edge) { + inEdges.add(edge); + } + + public void addOutEdge(Edge edge) { + outEdges.add(edge); + } + + public void removeInEdge(Edge edge) { + inEdges.remove(edge); + } + + public void removeOutEdge(Edge edge) { + outEdges.remove(edge); + } + + public void clearInEdges() { + inEdges.clear(); + } + + public void clearOutEdges() { + outEdges.clear(); + } + + public int getIndegree() { + return inEdges.size(); + } + + public int getOutdegree() { + return outEdges.size(); + } + + public Collection getPredecessors() { + Collection predecessors = new ArrayList(); + for (Edge edge: inEdges) { + predecessors.add(edge.getSource()); + } + return predecessors; + } + + public Collection getSuccessors() { + Collection successors = new ArrayList(); + for (Edge edge: outEdges) { + successors.add(edge.getDestination()); + } + return successors; + } + + public NodeAttribute getAttribute() { + return attribute; + } + + public void setAttribute(NodeAttribute attribute) { + this.attribute = attribute; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/NodeAttribute.java b/AlgebraicDataflowArchitectureModel/src/models/NodeAttribute.java new file mode 100644 index 0000000..6b38a9f --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/NodeAttribute.java @@ -0,0 +1,7 @@ +package models; + +import java.io.Serializable; + +public class NodeAttribute implements Serializable{ + +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/algebra/Constant.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/Constant.java new file mode 100644 index 0000000..5f533a7 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Constant.java @@ -0,0 +1,44 @@ +package models.algebra; + +import java.util.ArrayList; + +public class Constant extends Term { + + public Constant(String value) { + super(new Symbol(value, 0), new ArrayList()); + } + + public Constant(String value, Type type) { + super(new Symbol(value, 0), new ArrayList()); + symbol.setSignature(new Type[] {type}); + } + + public Constant(Symbol symbol) { + super(symbol); + } + + @Override + public boolean equals(Object another) { + if (!(another instanceof Constant)) return false; + return symbol.equals(((Constant) another).symbol); + } + + @Override + public Object clone() { + Constant c = new Constant(symbol); + c.setType(type); + return c; + } + + public String toString() { + return symbol.getName(); + } + + public String toImplementation(String[] sideEffects) { + if (symbol.isImplGenerative()) { + 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 new file mode 100644 index 0000000..20ebbc4 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Expression.java @@ -0,0 +1,37 @@ +package models.algebra; + +import java.io.Serializable; +import java.util.HashMap; + +public abstract class Expression implements Cloneable, Serializable { + public abstract Expression getSubTerm(Position pos); + /** + * Get the unification between this expression and another expression. + * @param another another expression + * @return unified expression + */ + public abstract Expression unify(Expression another); + /** + * Get the inverse map to obtain a sub-term of a given output value back from the output value itself. + * @param outputValue an output value (usually a term) + * @param targetPos a position in outputValue + * @return inverse map + */ + public abstract Expression getInverseMap(Expression outputValue, Position targetPos); + public abstract boolean contains(Expression exp); + public abstract Object clone(); + public abstract HashMap getSubTerms(Class clazz); + + public HashMap getVariables() { + return getSubTerms(Variable.class); + } + + /** + * Get the implementation of this expression. + * @param sideEffects an array with an optional implementation that should be written before the evaluation of this expression + * @return the implementation to represent the value of this expression + */ + public String toImplementation(String[] sideEffects) { + return toString(); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/algebra/Field.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/Field.java new file mode 100644 index 0000000..f51b3c4 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Field.java @@ -0,0 +1,45 @@ +package models.algebra; + +import java.util.ArrayList; + +/** + * A field in the implementation (regarded as a constant in the algebraic system) + * @author Nitta + * + */ +public class Field extends Constant { + + public Field(String name) { + super(name); + } + + public Field(String name, Type type) { + super(name, type); + } + + public Field(Symbol symbol) { + super(symbol); + } + + public Type getType() { + if (symbol.getSignature().length >= 1) { + return symbol.getSignature()[0]; + } + return null; + } + + @Override + public boolean equals(Object another) { + if (!(another instanceof Field)) return false; + return symbol.equals(((Field) another).symbol); + } + + @Override + public Object clone() { + return new Field(symbol); + } + + public String toImplementation(String[] sideEffects) { + return "this." + super.toImplementation(sideEffects); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/algebra/FutureWorkException.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/FutureWorkException.java new file mode 100644 index 0000000..ce01dc7 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/FutureWorkException.java @@ -0,0 +1,5 @@ +package models.algebra; + +public class FutureWorkException extends Exception { + +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/algebra/InvalidMessage.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/InvalidMessage.java new file mode 100644 index 0000000..c250f2f --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/InvalidMessage.java @@ -0,0 +1,5 @@ +package models.algebra; + +public class InvalidMessage extends Exception { + +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/algebra/LambdaAbstraction.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/LambdaAbstraction.java new file mode 100644 index 0000000..00a2981 --- /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 new file mode 100644 index 0000000..739ab80 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Parameter.java @@ -0,0 +1,39 @@ +package models.algebra; + +/** + * A parameter in the implementation (regarded as a constant in the algebraic system) + * @author Nitta + * + */ +public class Parameter extends Constant { + + public Parameter(String name) { + super(name); + } + + public Parameter(String name, Type type) { + super(name, type); + } + + public Parameter(Symbol symbol) { + super(symbol); + } + + public Type getType() { + if (symbol.getSignature().length >= 1) { + return symbol.getSignature()[0]; + } + return null; + } + + @Override + public boolean equals(Object another) { + if (!(another instanceof Parameter)) return false; + return symbol.equals(((Parameter) another).symbol); + } + + @Override + public Object clone() { + return new Parameter(symbol); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/algebra/ParameterizedIdentifierIsFutureWork.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/ParameterizedIdentifierIsFutureWork.java new file mode 100644 index 0000000..f37087b --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/ParameterizedIdentifierIsFutureWork.java @@ -0,0 +1,5 @@ +package models.algebra; + +public class ParameterizedIdentifierIsFutureWork extends FutureWorkException { + +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/algebra/Position.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/Position.java new file mode 100644 index 0000000..034e231 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Position.java @@ -0,0 +1,53 @@ +package models.algebra; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +public class Position implements Cloneable, Serializable { + private ArrayList orders = new ArrayList(); + + public Position() { + } + + public Position(ArrayList orders) { + this.orders = orders; + } + + public void addHeadOrder(int order) { + orders.add(0, order); + } + + public int removeHeadOrder() { + return orders.remove(0); + } + + public List getOrders() { + return orders; + } + + 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()); + } + + public boolean equals(Object another) { + if (!(another instanceof Position)) return false; + return orders.equals(((Position) another).orders); + } + + public int hashCode() { + return orders.hashCode(); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/algebra/Symbol.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/Symbol.java new file mode 100644 index 0000000..ad03b97 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Symbol.java @@ -0,0 +1,237 @@ +package models.algebra; + +import java.io.Serializable; + +public class Symbol implements Serializable{ + protected String name; + protected String implName; + protected int arity = 0; // -1: variable number + protected Type operatorType = Type.PREFIX; + protected Type implOperatorType = Type.PREFIX; + protected Symbol[] inverses = null; + protected models.algebra.Type[] signature = null; + protected int[] implParamOrder = null; + protected IImplGenerator generator = null; + + public Symbol(String name) { + this.name = name; + this.implName = name; + this.arity = 0; + } + + public Symbol(String name, int arity) { + this.name = name; + this.implName = name; + this.arity = arity; + } + + public Symbol(String name, int arity, Type operatorType) { + this(name, arity); + this.operatorType = operatorType; + this.implOperatorType = operatorType; + } + + public Symbol(String name, int arity, Type operatorType, String implName, Type implOperatorType) { + this.name = name; + this.implName = implName; + this.arity = arity; + this.operatorType = operatorType; + this.implOperatorType = implOperatorType; + } + + public Symbol(String name, int arity, Type operatorType, String implName, Type implOperatorType, int[] implParamOrder) { + this.name = name; + this.implName = implName; + this.arity = arity; + this.operatorType = operatorType; + this.implOperatorType = implOperatorType; + this.implParamOrder = implParamOrder; + } + + public Symbol(String name, int arity, Type operatorType, IImplGenerator generator) { + this.name = name; + this.arity = arity; + this.operatorType = operatorType; + this.generator = generator; + this.implOperatorType = Type.GENERATIVE; + } + + public Symbol(String name, int arity, Type operatorType, IImplGenerator generator, boolean bSideEffect) { + this.name = name; + this.arity = arity; + this.operatorType = operatorType; + this.generator = generator; + if (!bSideEffect) { + this.implOperatorType = Type.GENERATIVE; + } else { + this.implOperatorType = Type.GENERATIVE_WITH_SIDE_EFFECT; + } + } + + public void setArity(int arity) { + this.arity = arity; + } + + public int getArity() { + return arity; + } + + public String getName() { + return name; + } + + public Type getOperatorType() { + return operatorType; + } + + public boolean isInfix() { + return (operatorType == Type.INFIX); + } + + public boolean isMethod() { + return (operatorType == Type.METHOD || operatorType == Type.METHOD_WITH_SIDE_EFFECT); + } + + public boolean isLambda() { + return (operatorType == Type.LAMBDA); + } + + public Symbol[] getInverses() { + return inverses; + } + + public void setInverses(Symbol[] inverses) { + this.inverses = inverses; + } + + public models.algebra.Type[] getSignature() { + return signature; + } + + public void setSignature(models.algebra.Type[] signature) { + this.signature = signature; + } + + public String getImplName() { + return implName; + } + + public void setImplName(String implName) { + this.implName = implName; + } + + public Type getImplOperatorType() { + return implOperatorType; + } + + public boolean isImplInfix() { + return (implOperatorType == Type.INFIX); + } + + public boolean isImplMethod() { + return (implOperatorType == Type.METHOD || implOperatorType == Type.METHOD_WITH_SIDE_EFFECT); + } + + public boolean isImplLambda() { + return (implOperatorType == Type.LAMBDA || implOperatorType == Type.LAMBDA_WITH_SIDE_EFFECT); + } + + public boolean isImplGenerative() { + return (implOperatorType == Type.GENERATIVE || implOperatorType == Type.GENERATIVE_WITH_SIDE_EFFECT); + } + + public boolean isImplWithSideEffect() { + return (implOperatorType == Type.METHOD_WITH_SIDE_EFFECT + || implOperatorType == Type.LAMBDA_WITH_SIDE_EFFECT + || implOperatorType == Type.GENERATIVE_WITH_SIDE_EFFECT); + } + + public void setImplOperatorType(Type implOperatorType) { + this.implOperatorType = implOperatorType; + } + + public int[] getImplParamOrder() { + return implParamOrder; + } + + public void setGenerator(IImplGenerator generator) { + this.generator = generator; + } + + /** + * Generate the implementation of this symbol + * @param type the type of this symbol + * @param childrenTypes the types of the children expressions + * @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, models.algebra.Type[] childrenTypes, String[] childrenImpl, String[] childrenSideEffects, String[] sideEffect) { + if (generator != null) { + return generator.generate(type, childrenTypes, childrenImpl, childrenSideEffects, sideEffect); + } + return null; + } + + public boolean equals(Object another) { + if (!(another instanceof Symbol)) return false; + return name.equals(((Symbol) another).name) && arity == ((Symbol) another).arity; + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + public String toString() { + return name; + } + + public String toImplementation() { + return implName; + } + + public enum Type { + PREFIX, + INFIX, + METHOD, + METHOD_WITH_SIDE_EFFECT, + LAMBDA, + LAMBDA_WITH_SIDE_EFFECT, + GENERATIVE, + GENERATIVE_WITH_SIDE_EFFECT + } + + public Memento createMemento() { + return new Memento(implName, implOperatorType); + } + + public void setMemento(Memento memento) { + this.implName = memento.implName; + this.implOperatorType = memento.implOperatorType; + } + + public static class Memento { + private String implName; + private Type implOperatorType = Type.PREFIX; + + public Memento(String implName, Type implOperatorType) { + this.implName = implName; + this.implOperatorType = implOperatorType; + } + } + + public interface IImplGenerator { + /** + * Generate the implementation + * @param type the type of this expression + * @param childrenTypes the types of the children expressions + * @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 generator + * @return the generated implementation + */ + public String generate(models.algebra.Type type, models.algebra.Type[] childrenTypes, String childrenImpl[], String[] childrenSideEffects, String[] sideEffect); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/algebra/Term.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/Term.java new file mode 100644 index 0000000..e78c106 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Term.java @@ -0,0 +1,401 @@ +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 List children = new ArrayList<>(); + protected Type type = null; + + public Term(Symbol symbol) { + super(); + this.symbol = symbol; + } + + 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; + } + + public int getArity() { + return symbol.getArity(); + } + + public void setType(Type type) { + this.type = type; + } + + public Type getType() { + if (type == null) { + if (symbol.getSignature() == null) return null; + return symbol.getSignature()[0]; + } + return type; + } + + public boolean addChild(Expression child) { + if (getArity() != -1 && children.size() >= getArity()) return false; + children.add(child); + return true; + } + + public void addChild(Expression child, boolean bForced) { + if (!bForced && getArity() != -1 && children.size() >= getArity()) return; + children.add(child); + } + + public Expression getChild(int n) { + return children.get(n); + } + + public List getChildren() { + return children; + } + + public HashMap getSubTerms(Class clazz) { + HashMap subTerms = new HashMap<>(); + if (clazz == this.getClass()) { + subTerms.put(new Position(), (T) this); + } + 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()); + } + } + return subTerms; + } + + public Expression getSubTerm(Position pos) { + if (pos.isEmpty()) return this; + pos = (Position) pos.clone(); + int i = pos.removeHeadOrder(); + if (i >= children.size()) return null; + return children.get(i).getSubTerm(pos); + } + + public Term substitute(Variable variable, Expression value) { + Term newTerm = (Term) this.clone(); + HashMap variables = getVariables(); + for (Entry varEnt: variables.entrySet()) { + if (varEnt.getValue().equals(variable)) { + newTerm.replaceSubTerm(varEnt.getKey(), value); + } + } + return newTerm; + } + + public void replaceSubTerm(Position pos, Expression newSubTerm) { + if (pos.isEmpty()) return; + pos = (Position) pos.clone(); + int i = pos.removeHeadOrder(); + if (pos.isEmpty()) { + children.set(i, newSubTerm); + } else { + if (!(children.get(i) instanceof Term)) return; + ((Term) children.get(i)).replaceSubTerm(pos, newSubTerm); + } + } + + @Override + public Expression unify(Expression another) { + if (another instanceof Variable) return (Expression) this.clone(); + if (another instanceof Term) { + Term anotherTerm = (Term) another; + if (!symbol.equals(anotherTerm.symbol)) return null; + if (children.size() != anotherTerm.children.size()) return null; + Term unifiedTerm = new Term(symbol); + for (int i = 0; i < children.size(); i++) { + unifiedTerm.addChild(children.get(i).unify(anotherTerm.children.get(i))); + } + return unifiedTerm; + } else { + return null; + } + } + + public Expression reduce() { + if (symbol.isLambda()) { + // Lambda beta-reduction + LambdaAbstraction newSymbol = ((LambdaAbstraction) symbol); + Term newTerm = newSymbol.getTerm(); + List newVariables = newSymbol.getVariables(); + List newChildren = children; + while (newVariables.size() > 0 && newChildren.size() > 0) { + newTerm = newTerm.substitute(newVariables.get(0), newChildren.get(0)); + newVariables = newVariables.subList(1, newVariables.size()); + newChildren = newChildren.subList(1, newChildren.size()); + newSymbol = new LambdaAbstraction(newVariables, newTerm); + } + if (newSymbol.arity == 0 && newChildren.size() == 0) { + return newTerm; + } else { + return new Term(newSymbol, newChildren); + } + } else { + // Calculate inverse map + List newChildren = new ArrayList<>(); + boolean bReduced = false; + for (Expression child: children) { + if (child instanceof Term && !(child instanceof Constant)) { + child = ((Term) (child)).reduce(); + bReduced = true; + } + newChildren.add(child); + } + if (symbol.arity == 1 && newChildren.size() == 1) { + Expression child = newChildren.get(0); + if (child instanceof Term && !(child instanceof Constant)) { + Symbol childSymbol = ((Term) child).getSymbol(); + if (childSymbol.getInverses() != null) { + for (int i = 0; i < childSymbol.getInverses().length; i++) { + if (symbol.equals(childSymbol.getInverses()[i])) { + return ((Term) child).getChild(i); + } + } + } + } + } + if (!bReduced) return this; + Term newTerm = new Term(symbol, newChildren); + newTerm.setType(type); + return newTerm; + } + } + + @Override + public Expression getInverseMap(Expression outputValue, Position targetPos) { + if (targetPos.isEmpty()) return outputValue; + targetPos = (Position) targetPos.clone(); + int i = targetPos.removeHeadOrder(); + Symbol[] inverseSymbols = symbol.getInverses(); + 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); + } + + @Override + public boolean contains(Expression exp) { + if (equals(exp)) return true; + for (Expression e: children) { + if (e.contains(exp)) return true; + } + return false; + } + + @Override + public boolean equals(Object another) { + if (!(another instanceof Term)) return false; + if (this == another) return true; + Term anotherTerm = (Term) another; + if (!symbol.equals(anotherTerm.symbol)) return false; + if (children.size() != anotherTerm.children.size()) return false; + if (type != anotherTerm.type) return false; + for (int i = 0; i < children.size(); i++) { + Expression e = children.get(i); + Expression e2 = anotherTerm.children.get(i); + if (!e.equals(e2)) return false; + } + return true; + } + + @Override + public int hashCode() { + return symbol.hashCode(); + } + + + @Override + public Object clone() { + Term newTerm = new Term(symbol); + for (Expression e: children) { + newTerm.addChild((Expression) e.clone()); + } + newTerm.type = type; + return newTerm; + } + + public String toString() { + if (getArity() == 2 && symbol.isInfix()) { + return "(" + children.get(0) + symbol.toString() + children.get(1) + ")"; + } + 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++) { + Expression e = children.get(i); + exp += (delimiter + e.toString()); + delimiter = ","; + } + return exp + ")"; + } else { + String exp = symbol.toString() + "("; + String delimiter = ""; + for (Expression e: children) { + exp += (delimiter + e.toString()); + delimiter = ","; + } + return exp + ")"; + } + } + + + public String toImplementation(String[] sideEffects) { + int[] implParamOrder = symbol.getImplParamOrder(); + if (symbol.isImplLambda()) { + String[] components = symbol.getImplName().split("->"); + String component0 = components[0].replace("(", "").replace(")", ""); + String[] params = component0.split(","); + String exp = components[1]; + if (implParamOrder == null) { + for (int i = 0; i < params.length; i++) { + exp = exp.replace(params[i], children.get(i).toImplementation(sideEffects)); + } + } else { + for (int i = 0; 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[] {""}); + } + } + 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); + childrenSideEffects[i] = childSideEffect[0]; + } + 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 + } + 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); + childrenSideEffects[i] = childSideEffect[0]; + } + 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 + } + return exp; + } + } + if (getArity() == 2 && symbol.isImplInfix()) { + 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) + ")"; + } + } + if ((getArity() >= 1 || getArity() == -1) && symbol.isImplMethod()) { + if (implParamOrder == null) { + String exp = null; + if (children.get(0) != null) { + exp = children.get(0).toImplementation(sideEffects) + "." + symbol.toImplementation() + "("; + } else { + exp = symbol.toImplementation() + "("; + } + String delimiter = ""; + for (int i = 1; i < children.size(); i++) { + Expression e = children.get(i); + exp += (delimiter + e.toImplementation(sideEffects)); + delimiter = ","; + } + exp += ")"; + if (symbol.isImplWithSideEffect()) { + sideEffects[0] = sideEffects[0] + exp + ";\n"; + if (children.get(0) != null) { + exp = children.get(0).toImplementation(new String[] {""}); + } else { + exp = ""; + } + } + return exp; + } else { + String exp = children.get(implParamOrder[0]).toImplementation(sideEffects) + "." + symbol.toImplementation() + "("; + String delimiter = ""; + for (int i = 1; i < children.size(); i++) { + Expression e = children.get(implParamOrder[i]); + exp += (delimiter + e.toImplementation(sideEffects)); + delimiter = ","; + } + exp += ")"; + if (symbol.isImplWithSideEffect()) { + sideEffects[0] = sideEffects[0] + exp + ";\n"; + exp = children.get(implParamOrder[0]).toImplementation(new String[] {""}); + } + return exp; + } + } else { + if (implParamOrder == null) { + String exp = symbol.toImplementation() + "("; + String delimiter = ""; + for (Expression e: children) { + exp += (delimiter + e.toImplementation(sideEffects)); + delimiter = ","; + } + return exp + ")"; + } else { + String exp = symbol.toImplementation() + "("; + String delimiter = ""; + for (int i = 0; i < children.size(); i++) { + Expression e = children.get(implParamOrder[i]); + exp += (delimiter + e.toImplementation(sideEffects)); + delimiter = ","; + } + return exp + ")"; + } + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/algebra/Type.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/Type.java new file mode 100644 index 0000000..060e74d --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Type.java @@ -0,0 +1,105 @@ +package models.algebra; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +public class Type implements Serializable{ + private String typeName; + private String implementationTypeName; + private String interfaceTypeName; + private List parentTypes = new ArrayList<>(); + + public Type(String typeName, String implementationTypeName) { + this.typeName = typeName; + this.implementationTypeName = implementationTypeName; + this.interfaceTypeName = implementationTypeName; + } + + public Type(String typeName, String implementationTypeName, String interfaceTypeName) { + this.typeName = typeName; + this.implementationTypeName = implementationTypeName; + this.interfaceTypeName = interfaceTypeName; + } + + public Type(String typeName, String implementationTypeName, Type parentType) { + this.typeName = typeName; + this.implementationTypeName = implementationTypeName; + this.interfaceTypeName = implementationTypeName; + this.parentTypes.add(parentType); + } + + public Type(String typeName, String implementationTypeName, String interfaceTypeName, Type parentType) { + this.typeName = typeName; + this.implementationTypeName = implementationTypeName; + this.interfaceTypeName = interfaceTypeName; + this.parentTypes.add(parentType); + } + + public String getTypeName() { + return typeName; + } + + public void setTypeName(String typeName) { + this.typeName = typeName; + } + + public String getImplementationTypeName() { + return implementationTypeName; + } + + public void setImplementationTypeName(String implementastionTypeName) { + this.implementationTypeName = implementastionTypeName; + } + + public String getInterfaceTypeName() { + return interfaceTypeName; + } + + public void setInterfaceTypeName(String interfaceTypeName) { + this.interfaceTypeName = interfaceTypeName; + } + + public List getParentTypes() { + return parentTypes; + } + + public void addParentType(Type parentType) { + parentTypes.add(parentType); + } + + public void replaceParentType(Type oldParentType, Type newParentType) { + parentTypes.set(parentTypes.indexOf(oldParentType), newParentType); + } + + public boolean isAncestorOf(Type another) { + if (this.equals(another)) return true; + if (another.getParentTypes() == null) return false; + for (Type anothersParentType: another.getParentTypes()) { + if (isAncestorOf(anothersParentType)) return true; + } + return false; + } + + public Memento createMemento() { + return new Memento(implementationTypeName, interfaceTypeName, parentTypes); + } + + public void setMemento(Memento memento) { + this.implementationTypeName = memento.implementationTypeName; + this.interfaceTypeName = memento.interfaceTypeName; + this.parentTypes = memento.parentTypes; + } + + public static class Memento { + private String implementationTypeName; + private String interfaceTypeName; + private List parentTypes; + + public Memento(String implementationTypeName, String interfaceTypeName, List parentTypes) { + this.implementationTypeName = implementationTypeName; + this.interfaceTypeName = interfaceTypeName; + this.parentTypes = parentTypes; + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/algebra/UnificationFailed.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/UnificationFailed.java new file mode 100644 index 0000000..a3227b6 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/UnificationFailed.java @@ -0,0 +1,7 @@ +package models.algebra; + +import java.io.Serializable; + +public class UnificationFailed extends Exception implements Serializable { + +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/algebra/ValueUndefined.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/ValueUndefined.java new file mode 100644 index 0000000..d23dfa7 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/ValueUndefined.java @@ -0,0 +1,7 @@ +package models.algebra; + +import java.io.Serializable; + +public class ValueUndefined extends Exception implements Serializable{ + +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/algebra/Variable.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/Variable.java new file mode 100644 index 0000000..fa0095b --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Variable.java @@ -0,0 +1,87 @@ +package models.algebra; + +import java.util.HashMap; + +public class Variable extends Expression { + private String name; + private Type type = null; + + public Variable(String name) { + super(); + this.name = name; + } + + public Variable(String name, Type type) { + super(); + this.name = name; + this.type = type; + } + + public String getName() { + return name; + } + + public Type getType() { + return type; + } + + public void setType(Type type) { + this.type = type; + } + + @Override + public HashMap getSubTerms(Class clazz) { + HashMap subTerms = new HashMap<>(); + if (clazz == this.getClass()) { + subTerms.put(new Position(), (T) this); + } + return subTerms; + } + + @Override + public Expression getSubTerm(Position pos) { + if (pos.isEmpty()) return this; + return null; + } + + @Override + public Expression unify(Expression another) { + return (Expression) another.clone(); + } + + @Override + public Expression getInverseMap(Expression outputValue, Position targetPos) { + if (targetPos.isEmpty()) return outputValue; + return null; + } + + @Override + public boolean contains(Expression exp) { + return equals(exp); + } + + @Override + public boolean equals(Object another) { + if (!(another instanceof Variable)) return false; + return name.equals(((Variable) another).name); + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public Object clone() { + return new Variable(name, type); + } + + public String toString() { + if (type == null) return name; + return name + ":" + type.getTypeName(); + } + + public String toImplementation(String[] sideEffects) { + return name; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/CallEdge.java b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/CallEdge.java new file mode 100644 index 0000000..e19986e --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/CallEdge.java @@ -0,0 +1,17 @@ +package models.controlFlowModel; + +import models.Edge; +import models.dataFlowModel.PushPullValue; + +public class CallEdge extends Edge { + private PushPullValue selectedOption = PushPullValue.PUSHorPULL; + + public CallEdge(ObjectNode src, ObjectNode dst, PushPullValue selectedOption) { + super(src, dst); + this.selectedOption = selectedOption; + } + + public PushPullValue getSelectedOption() { + return this.selectedOption; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/CallEdgeAttribute.java b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/CallEdgeAttribute.java new file mode 100644 index 0000000..0b71a93 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/CallEdgeAttribute.java @@ -0,0 +1,98 @@ +package models.controlFlowModel; + +import com.mxgraph.model.mxCell; + +import models.EdgeAttribute; +import models.dataFlowModel.PushPullValue; + +/************************************************************* + * "ObjectNode"間の呼び出しエッジの情報 + */ +public class CallEdgeAttribute extends EdgeAttribute { + private CallEdge callEdge = null; + private ObjectNode orginalSrcObjNode = null; + private mxCell srcCell = null; + private mxCell dstCell = null; + + /************************************************************* + * [ *constructor ] + /************************************************************* + * + */ + public CallEdgeAttribute(CallEdge callEdge, ObjectNode originalSrcObjNode, final mxCell srcCell, final mxCell dstCell) { + this.callEdge = callEdge; + this.callEdge.setAttribute(this); + + this.orginalSrcObjNode = originalSrcObjNode; + + this.srcCell = srcCell; + this.dstCell = dstCell; + } + + /************************************************************* + * + */ + public CallEdgeAttribute(CallEdge callEdge, final mxCell srcCell, final mxCell dstCell) { + this.callEdge = callEdge; + this.callEdge.setAttribute(this); + + this.srcCell = srcCell; + this.dstCell = dstCell; + } + + + /************************************************************* + * [ *public ] + /************************************************************* + * [ getter ] + /*************************************************************/ + public CallEdge getCallEdge() { + return callEdge; + } + + public ObjectNode getOriginalSourceObjectNode() { + return orginalSrcObjNode; + } + + public PushPullValue getSelectedOption() { + return callEdge.getSelectedOption(); + } + + public ObjectNode getSourceObjectNode() { + if( !(callEdge.getSource() instanceof ObjectNode) ) throw new ClassCastException("sourceNode isn't type of "); + return (ObjectNode)callEdge.getSource(); + } + + public ObjectNode getDestinationObjectNode() { + if( !(callEdge.getDestination() instanceof ObjectNode) ) throw new ClassCastException("destinationNode isn't type of "); + return (ObjectNode)callEdge.getDestination(); + } + + public mxCell getSourceCell() { + return srcCell; + } + + public mxCell getDestinationCell() { + return dstCell; + } + + public void setDestinationCell(final mxCell dstCell) { + this.dstCell = dstCell; + } + + /************************************************************* + * エッジの呼び出し順を表示する + */ + @Override + public String toString() { + String value = ""; + + if(2 <= callEdge.getSource().getOutEdges( ).size()) { + int order = (((ObjectNode)callEdge.getSource()).getOutEdgeCallOrder(callEdge)+ 1); + value += "[" + order + "]"; + } + + return value; + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/CallGraph.java b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/CallGraph.java new file mode 100644 index 0000000..685cc85 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/CallGraph.java @@ -0,0 +1,66 @@ +package models.controlFlowModel; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import models.DirectedGraph; +import models.Node; +import models.dataFlowModel.PushPullValue; +import models.dataFlowModel.ResourceNode; + +public class CallGraph extends DirectedGraph { + protected Map statefulObjMap = null; + + public CallGraph() { + statefulObjMap = new HashMap<>(); + } + + public void addNode(Node node) { + if (node instanceof ResourceNode) { + ResourceNode resNode = (ResourceNode) node; + StatefulObjectNode objNode = statefulObjMap.get(resNode); + if (objNode == null) { + objNode = new StatefulObjectNode(resNode); + statefulObjMap.put(resNode, objNode); + super.addNode(objNode); + } + } else if (node instanceof StatefulObjectNode) { + StatefulObjectNode objNode = (StatefulObjectNode) node; + if (statefulObjMap.get(objNode.getResource()) == null) { + statefulObjMap.put(objNode.getResource(), objNode); + super.addNode(objNode); + } + } else { + super.addNode(node); + } + } + + public void addEdge(ResourceNode srcResNode, ResourceNode dstResNode, PushPullValue selectedOption) { + addNode(srcResNode); + addNode(dstResNode); + addEdge(new CallEdge(getStatefulObjectNode(srcResNode), getStatefulObjectNode(dstResNode), selectedOption)); + } + + public void insertEdge(ObjectNode srcObjNode, ObjectNode dstObjNode, PushPullValue selectedOption, int n) { + CallEdge edge = new CallEdge(srcObjNode, dstObjNode, selectedOption); + simpleAddEdge(edge); + addNode(srcObjNode); + addNode(dstObjNode); + srcObjNode.insertOutEdge(edge, n); + dstObjNode.addInEdge(edge); + } + + public StatefulObjectNode getStatefulObjectNode(ResourceNode resNode) { + return statefulObjMap.get(resNode); + } + + public Set getRootNodes() { + Set roots = new HashSet<>(getNodes()); + for (Node n: getNodes()) { + roots.removeAll(n.getSuccessors()); + } + return roots; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/CompositeCallEdgeAttribute.java b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/CompositeCallEdgeAttribute.java new file mode 100644 index 0000000..71548e8 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/CompositeCallEdgeAttribute.java @@ -0,0 +1,40 @@ +package models.controlFlowModel; + +import java.util.ArrayList; +import java.util.List; + +/************************************************************* + * 制御フローを表す2本以上のエッジを統合したときの情報. + * e.g.) "仲介者への依存"等で2本の制御フローのエッジが1本になったときなど. + */ +public class CompositeCallEdgeAttribute extends CallEdgeAttribute { + private CallEdgeAttribute currentEdgeAttr = null; + private List mergedCallEdgeAttrs = null; + + /************************************************************* + * [ *constructor ] + /************************************************************* + * + */ + public CompositeCallEdgeAttribute(final CallEdgeAttribute callEdgeAttr) { + super(callEdgeAttr.getCallEdge(), + callEdgeAttr.getOriginalSourceObjectNode(), + callEdgeAttr.getSourceCell(), callEdgeAttr.getDestinationCell() + ); + + this.currentEdgeAttr = (this.currentEdgeAttr == null) + ? callEdgeAttr : null; + } + + /************************************************************* + * [ *public ] + /************************************************************* + * + */ + public void mergeCallEdgeAttribute(final CallEdgeAttribute mergedCallEdgeAttr) { + if (this.currentEdgeAttr == null) return; + if (this.mergedCallEdgeAttrs == null) this.mergedCallEdgeAttrs = new ArrayList(); + this.mergedCallEdgeAttrs.add(mergedCallEdgeAttr); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/ControlFlowDelegator.java b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/ControlFlowDelegator.java new file mode 100644 index 0000000..1c113ff --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/ControlFlowDelegator.java @@ -0,0 +1,237 @@ +package models.controlFlowModel; + +import java.util.ArrayList; +import java.util.List; + +import models.Edge; +import models.dataFlowModel.PushPullValue; + +/************************************************************* + * + * ToDo:CFDとDoMのロジックが一緒になっているので分離すべき. + */ +public class ControlFlowDelegator { + + private ControlFlowGraph controlFlowGraph = null; + + /************************************************************* + /************************************************************* + * [ *constructor ] + /************************************************************* + * + */ + public ControlFlowDelegator(final ControlFlowGraph controlFlowGraph) { + this.controlFlowGraph = controlFlowGraph; + } + + /************************************************************* + /************************************************************* + * [ *public ] + /************************************************************* + * CFD適用可能範囲を探索する + * @param callEdge + * @return CFD適用可能なノードのリスト + */ + public List searchDelegatableNodes(final CallEdge callEdge){ + List nodes = new ArrayList<>(); + + // 1. adding parentNode + ObjectNode delegatingNode = (ObjectNode) callEdge.getDestination(); + ObjectNode parentNode = (ObjectNode) callEdge.getSource(); + + if (parentNode == null || delegatingNode == null) + throw new NullPointerException("parentNode is null."); + if ( !(parentNode instanceof ObjectNode && delegatingNode instanceof ObjectNode)) + throw new ClassCastException("callEdge.getSource() is not ObjectNode"); + + // if the relation of "delegatingNode" to "parentNode" is 1 : 1 ? + // then return an empty list. + if (isRootNode(parentNode) && !hasChildrenNode(parentNode) ) return nodes; + + // 2. collecting for each transfer method has nodes in the common area. + collectCommonTransferNodes(nodes, parentNode, delegatingNode); + + // 3. if the transfer method is PUSH-style, + // then search delegatable area. + collectParentNodesOnPushTransfer(nodes, delegatingNode, parentNode); + + return nodes; + } + + + /************************************************************* + * DoMが適用可能な仲介者オブジェクトの探索 + *@param callEdge + */ + public List searchDependentableMediatorNodes(final CallEdge callEdge){ + List nodes = new ArrayList<>(); + + // If the transfer method is PULL-style, + // then search dependable mediator object. + collectDependableObjectNodeOnPullTransfer(nodes, callEdge); + + return nodes; + } + + /************************************************************* + * CFDを実行する. + * @param delegatingEdge 委譲するコントロールフロー + * @param dstObjNode 呼び出し元となるノード + */ + public void delegateCallEdge(CallEdge delegatingEdge, final ObjectNode dstObjNode) { + ObjectNode srcObjNode = (ObjectNode)delegatingEdge.getDestination(); + if(srcObjNode == null) throw new ClassCastException(); + + delegatingEdge.getSource().removeOutEdge(delegatingEdge); + srcObjNode.removeInEdge(delegatingEdge); + + // Reconnecting the edge to the new source object. + delegatingEdge.setDestination(srcObjNode); + delegatingEdge.setSource(dstObjNode); + + srcObjNode.addInEdge(delegatingEdge); + dstObjNode.addOutEdge(delegatingEdge); + } + + /************************************************************* + /************************************************************* + * [ *private ] + /************************************************************* + * search + /************************************************************* + * PUSH/PULLで共通するCFD適用可能なノードを再帰的に探索する + * @param nodes 適用可能なノードのリスト + * @param curObjNode + * @param delegatingObjNode + * @param selectedOption + */ + private void collectCommonTransferNodes(List nodes, ObjectNode curObjNode, final ObjectNode delegatingObjNode){ + if( !hasChildrenNode(curObjNode)) return; + + for(Edge e : curObjNode.getOutEdges()) { + ObjectNode foundNode = (ObjectNode)e.getDestination(); + + if( foundNode.equals(delegatingObjNode)) continue; + + nodes.add(foundNode); + collectCommonTransferNodes(nodes, foundNode, delegatingObjNode); + } + } + + /************************************************************* + * 順序が必要なPUSHのコントロールフローグラフ上のCFD適用可能範囲を再帰的に探索する. + * (自身の親となるノードを再帰的に探索する) + * @param nodes + * @param curObjNode + * @param parentDelegatingNode + */ + private void collectParentNodesOnPushTransfer(List nodes, ObjectNode curObjNode, final ObjectNode parentDelegatingNode) { + if( isRootNode(curObjNode) ) return; + if( isInEdgesConversingToNode(curObjNode) ) return; + + ObjectNode parentObjNode = (ObjectNode)curObjNode.getInEdge(0).getSource(); + if(parentObjNode == null) return; + + if( !parentDelegatingNode.equals(parentObjNode) ) + nodes.add(parentObjNode); + + int inEdgeCallOrder = parentObjNode.getOutEdgeCallOrder(curObjNode.getInEdge(0)); + for(Edge edge : parentObjNode.getOutEdges()) { + if( !(edge instanceof CallEdge)) continue; + + int callOrder = parentObjNode.getOutEdgeCallOrder((CallEdge)edge); + if(inEdgeCallOrder < callOrder) collectChildrenNodesOnPushTransfer(nodes, (CallEdge)edge); + } + + collectParentNodesOnPushTransfer(nodes, parentObjNode, parentDelegatingNode); + } + + /************************************************************* + * 順序が必要なPUSHのコントロールフローグラフ上のCFD適用可能範囲を再帰的に探索する. + * (あるコントロールフローについて, その呼び出し順よりも若いノードを, 呼び出し先を辿ることで再帰的に探索する) + * @param nodes + * @param callEdge + */ + private void collectChildrenNodesOnPushTransfer(List nodes, CallEdge callEdge) { + ObjectNode dstObjNode = (ObjectNode)callEdge.getDestination(); + if(dstObjNode == null) return; + + nodes.add(dstObjNode); + + if(!hasChildrenNode(dstObjNode)) return; + + for(Edge e : dstObjNode.getOutEdges()) { + CallEdge edge = (CallEdge)e; + if(edge == null) continue; + + ObjectNode foundNode = (ObjectNode)e.getDestination(); + if(foundNode == null) continue; + if(nodes.contains(foundNode))continue; + + collectChildrenNodesOnPushTransfer(nodes, edge); + } + } + + /************************************************************* + * PULLのコントロールフローにおいて, DoM適用可能な仲介者オブジェクトを探索する. + * @param nodes + * @param callEdge + */ + private void collectDependableObjectNodeOnPullTransfer(List nodes, CallEdge callEdge) { + if(callEdge.getSelectedOption() != PushPullValue.PULL) return; + + // エッジの両端が状態を持つノード(リソース)か? + StatefulObjectNode srcObjNode = (callEdge.getSource() instanceof StatefulObjectNode) + ? (StatefulObjectNode)callEdge.getSource() + : null; + + StatefulObjectNode dstObjNode = (callEdge.getDestination() instanceof StatefulObjectNode) + ? (StatefulObjectNode)callEdge.getDestination() + : null; + + if(srcObjNode == null || dstObjNode == null) return; + + // 呼び出し先が仲介者オブジェクトか? + for(Edge inEdge : dstObjNode.getInEdges()) { + CallEdge inCallEdge = (CallEdge)inEdge; + if(inCallEdge == null) continue; + + // 状態を持たないオブジェクトか?(仲介者オブジェクトか?) + ObjectNode inSrcObjNode = (inCallEdge.getSource() instanceof ObjectNode) + ? (ObjectNode)inCallEdge.getSource() + : null; + if(inSrcObjNode == null) continue; + + if(inSrcObjNode instanceof StatefulObjectNode) continue; + if(inSrcObjNode instanceof EventChannelObjectNode) continue; + + nodes.add(inSrcObjNode); + } + } + + + /************************************************************* + * あるノードがルートかどうか + * @param node + */ + private boolean isRootNode(final ObjectNode node) { + return node.getInEdges().isEmpty(); + } + + /************************************************************* + * 呼び出し先があるかどうか + * @param node + */ + private boolean hasChildrenNode(final ObjectNode node) { + if(node.getOutEdges().size() < 1) return false; + return true; + } + + /************************************************************* + * + * @param node + */ + private boolean isInEdgesConversingToNode(final ObjectNode node) { + return ( 1 < node.getInEdges().size() ); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/ControlFlowGraph.java b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/ControlFlowGraph.java new file mode 100644 index 0000000..8cf4e37 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/ControlFlowGraph.java @@ -0,0 +1,141 @@ +package models.controlFlowModel; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import models.Edge; +import models.Node; +import models.algebra.Expression; +import models.algebra.Term; +import models.algebra.Variable; +import models.dataConstraintModel.Channel; +import models.dataConstraintModel.ChannelMember; +import models.dataFlowModel.DataFlowEdge; +import models.dataFlowModel.DataFlowGraph; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.IFlowGraph; +import models.dataFlowModel.PushPullAttribute; +import models.dataFlowModel.PushPullValue; +import models.dataFlowModel.ResourceNode; + +public class ControlFlowGraph implements IFlowGraph { + private DataFlowGraph dataFlowGraph; + private CallGraph pushCallGraph; + private CallGraph pullCallGraph; + + public ControlFlowGraph(DataFlowGraph dataFlowGraph, DataTransferModel model) { + this.dataFlowGraph = dataFlowGraph; + this.pushCallGraph = new CallGraph(); + this.pullCallGraph = new CallGraph(); + for (Edge e: dataFlowGraph.getEdges()) { + PushPullAttribute pushPull = ((PushPullAttribute) ((DataFlowEdge) e).getAttribute()); + ResourceNode srcNode = (ResourceNode) e.getSource(); + ResourceNode dstNode = (ResourceNode) e.getDestination(); + if (pushPull.getOptions().get(0) == PushPullValue.PUSH) { + // same direction as the data flow + pushCallGraph.addEdge(srcNode, dstNode, PushPullValue.PUSH); + } else { + // reverse direction to the data flow + pullCallGraph.addEdge(dstNode, srcNode, PushPullValue.PULL); + } + } + for (Channel ch: model.getIOChannels()) { + DataTransferChannel cio = (DataTransferChannel) ch; + EventChannelObjectNode srcNode = new EventChannelObjectNode(cio); + for (ChannelMember cm: cio.getChannelMembers()) { + if (srcNode.getName() == null) { + Expression exp = cm.getStateTransition().getMessageExpression(); + if (exp instanceof Term) { + srcNode.setName(((Term) exp).getSymbol().getName()); + } else if (exp instanceof Variable) { + srcNode.setName(((Variable) exp).getName()); + } + } + ResourceNode dstResNode = dataFlowGraph.getResouceNode(cm.getResource()); + StatefulObjectNode dstNode = pushCallGraph.getStatefulObjectNode(dstResNode); + if (dstNode == null) { + pushCallGraph.addNode(dstResNode); + dstNode = pushCallGraph.getStatefulObjectNode(dstResNode); + } + // from an I/O channel to a resource + pushCallGraph.insertEdge(srcNode, dstNode, PushPullValue.PUSH, 0); + } + } + } + + public ControlFlowGraph(DataFlowGraph dataFlowGraph, PushPullValue priority) { + this.dataFlowGraph = dataFlowGraph; + this.pushCallGraph = new CallGraph(); + this.pullCallGraph = new CallGraph(); + if (priority == PushPullValue.PUSH) { + // push-first + for (Edge e: dataFlowGraph.getEdges()) { + ResourceNode srcNode = (ResourceNode) e.getSource(); + ResourceNode dstNode = (ResourceNode) e.getDestination(); + // same direction as the data flow + pushCallGraph.addEdge(srcNode, dstNode, PushPullValue.PUSH); + } + } else { + // pull-first + for (Edge e: dataFlowGraph.getEdges()) { + ResourceNode srcNode = (ResourceNode) e.getSource(); + ResourceNode dstNode = (ResourceNode) e.getDestination(); + PushPullAttribute pushPull = ((PushPullAttribute) ((DataFlowEdge) e).getAttribute()); + if (pushPull.getOptions().contains(PushPullValue.PULL)) { + // Pull style is selectable + // reverse direction to the data flow + pullCallGraph.addEdge(dstNode, srcNode, PushPullValue.PULL); + } else { + // Pull style is not selectable + // same direction as the data flow + pushCallGraph.addEdge(srcNode, dstNode, PushPullValue.PUSH); + } + } + } + } + + public DataFlowGraph getDataFlowGraph() { + return dataFlowGraph; + } + + public CallGraph getPushCallGraph() { + return pushCallGraph; + } + + public CallGraph getPullCallGraph() { + return pullCallGraph; + } + + @Override + public Map> getAllNodes() { + Map> allNodeSets = new HashMap<>(); + for (Node n: pushCallGraph.getNodes()) { + Set nodeSet = new HashSet<>(); + nodeSet.add(n); + allNodeSets.put(n, nodeSet); + } + for (Node n: pullCallGraph.getNodes()) { + if (n instanceof StatefulObjectNode) { + ResourceNode resNode = ((StatefulObjectNode) n).getResource(); + Set nodeSet = null; + if (pushCallGraph.getStatefulObjectNode(resNode) != null) { + // Merge a stateful object node in the push call graph and that in the pull call graph. + nodeSet = allNodeSets.get(pushCallGraph.getStatefulObjectNode(resNode)); + } else { + nodeSet = new HashSet<>(); + } + nodeSet.add(n); + allNodeSets.put(n, nodeSet); + } else { + Set nodeSet = new HashSet<>(); + nodeSet.add(n); + allNodeSets.put(n, nodeSet); + } + } + return allNodeSets; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/EventChannelObjectNode.java b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/EventChannelObjectNode.java new file mode 100644 index 0000000..94d01f7 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/EventChannelObjectNode.java @@ -0,0 +1,41 @@ +package models.controlFlowModel; + +import models.dataFlowModel.DataTransferChannel; + +/************************************************************* + * "コントロールフローモデリング"において, イベントチャンネルを表す"ObjectNode". + */ +public class EventChannelObjectNode extends ObjectNode { + DataTransferChannel ioChannel = null; + + /************************************************************* + /************************************************************* + * [ *constructor ] + /************************************************************* + * + */ + public EventChannelObjectNode(DataTransferChannel ioChannel) { + super(null); + this.ioChannel = ioChannel; + } + + public EventChannelObjectNode(String name, DataTransferChannel ioChannel) { + super(name); + this.ioChannel = ioChannel; + } + + /************************************************************* + /************************************************************* + * [ *public ] + /************************************************************* + * + */ + public DataTransferChannel getIOChannel() { + return ioChannel; + } + + public void setIOChannel(DataTransferChannel ioChannel) { + this.ioChannel = ioChannel; + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/ObjectNode.java b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/ObjectNode.java new file mode 100644 index 0000000..d99a255 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/ObjectNode.java @@ -0,0 +1,89 @@ +package models.controlFlowModel; + +import java.util.ArrayList; +import java.util.List; + +import models.Edge; +import models.Node; + +/************************************************************* +* "コントロールフローモデリング"上の汎用的なノード. +*/ +public class ObjectNode extends Node { + protected String name = ""; + + /************************************************************* + /************************************************************* + * [ *constructor] + /************************************************************* + */ + public ObjectNode(String name) { + inEdges = new ArrayList<>(); + outEdges = new ArrayList<>(); + + this.name = name; + } + + /************************************************************* + /************************************************************* + * [ *public ] + /************************************************************* + * + */ + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public CallEdge getInEdge(int i) { + return (CallEdge) ((List) inEdges).get(i); + } + + public CallEdge getOutEdge(int i) { + return (CallEdge) ((List) outEdges).get(i); + } + + public CallEdge findEdgeInInEdges(final CallEdge edge) { + for (Edge e : inEdges) { + if (e instanceof CallEdge) return (CallEdge)e; + } + return null; + } + + public void insertOutEdge(CallEdge edge, int n) { + ((List) outEdges).add(n, edge); + } + + public int getChildrenNum() { + return outEdges.size(); + } + + public int getOutEdgeCallOrder(final CallEdge callEdge) { + for (int i = 0; i < outEdges.size(); i++) { + if (callEdge.equals(getOutEdge(i))) return i; + } + return -1; + } + + public ObjectNode getChildren(int i) { + return (ObjectNode) ((List) outEdges).get(i).getDestination(); + } + + /************************************************************* + * 指定したエッジ(出力側)の呼び出し順を変更する. + * @param curOrder 現在の呼び出し順 + * @param newCallOrder 新しい呼び出し順 + */ + public void sortOutEdgesByCallOrder(final int curOrder, final int newCallOrder) { + ArrayList edges = ((ArrayList)outEdges); + Edge selectedEdge = ((List)outEdges).get(curOrder); + + Edge tempEdge = ((List)outEdges).get(newCallOrder - 1); + + edges.set(newCallOrder - 1, selectedEdge); + edges.set(curOrder, tempEdge); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/ObjectNodeAttribute.java b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/ObjectNodeAttribute.java new file mode 100644 index 0000000..ac863db --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/ObjectNodeAttribute.java @@ -0,0 +1,110 @@ +package models.controlFlowModel; + +import models.NodeAttribute; + +/************************************************************* + * "mxCell"の持つ, "ObjectNode"に関する情報. + * "ObjectNode"のインスタンスに応じて + */ +public class ObjectNodeAttribute extends NodeAttribute { + private ObjectNode objectNode = null; + private String shapeStyle = ""; + + /************************************************************* + * [ *constructor ] + /************************************************************* + * + */ + public ObjectNodeAttribute(final ObjectNode objectNode) { + this.objectNode = objectNode; + this.objectNode.setAttribute(this); + + // Setting a shape style of cell + if(objectNode instanceof StatefulObjectNode) { + shapeStyle = "shape=ellipse;perimeter=ellipsePerimeter;"; + } + else if(objectNode instanceof EventChannelObjectNode) { + shapeStyle = "shape=rectangle;perimeter=rectanglePerimeter;"; + } + else { + shapeStyle = "shape=hexagon;perimeter=hexagonPerimeter;"; + } + + // Setting a name of cell + if(objectNode.name != null) return; + if(objectNode.name.isEmpty()) return; + + if( objectNode instanceof StatefulObjectNode ) { + objectNode.name = objectNode.getName(); + } + else if(objectNode instanceof EventChannelObjectNode){ + objectNode.name = "entryPoint"; + } + } + + /************************************************************* + * [ *public ] + /************************************************************* + * [ getter ] + /************************************************************* + * + */ + public ObjectNode getObjectNode() { + return objectNode; + } + + /************************************************************* + * + */ + public String getDefaultStyle() { + String style = ";"; + + return objectNode instanceof StatefulObjectNode + ? shapeStyle + style + : shapeStyle + style; + } + + /************************************************************* + * 選択状態のセルの色を取得 + */ + public String getEnableStyle() { + String style = "fillColor=#7fffd4;"; + style += "strokeColor=#66cdaa;"; + style += "strokeWidth=2;"; + + return objectNode instanceof StatefulObjectNode + ? shapeStyle + style + : shapeStyle + style; + } + + /************************************************************* + * 非選択状態のセルの色を取得 + */ + public String getDisableStyle() { + String style = "fillColor=#999999"; + + return objectNode instanceof StatefulObjectNode + ? shapeStyle + style + : shapeStyle + style; + } + + /************************************************************* + * ”制御フローモデリング”の操作が適用可能状態のセルの色を取得 + */ + public String getDelegatingStyle() { + String style = "strokeWidth=4;"; + style += "strokeColor=#4169e;"; + + return shapeStyle + style; + } + + /************************************************************* + * GUI上に表示するセルの名前を返す. + */ + @Override + public String toString() { + return objectNode instanceof StatefulObjectNode + ? ((StatefulObjectNode) objectNode).getResource().getResource().getResourceName() + : objectNode.getName(); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/StatefulObjectNode.java b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/StatefulObjectNode.java new file mode 100644 index 0000000..d5a8b5a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/controlFlowModel/StatefulObjectNode.java @@ -0,0 +1,20 @@ +package models.controlFlowModel; + +import models.dataFlowModel.ResourceNode; + +/************************************************************* + * "制御フローモデリング"上で状態を持つ"ObjectNode" + */ +public class StatefulObjectNode extends ObjectNode { + private ResourceNode resource; + + public StatefulObjectNode(ResourceNode resource) { + super(resource.getResource().getResourceName()); + this.resource = resource; + } + + public ResourceNode getResource() { + return resource; + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Channel.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Channel.java new file mode 100644 index 0000000..4ef7058 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Channel.java @@ -0,0 +1,97 @@ +package models.dataConstraintModel; + +import java.io.Serializable; +import java.util.HashSet; +import java.util.Set; + +import models.algebra.Variable; + +public class Channel implements Serializable { + protected String channelName; + protected Set selectors = null; + protected Set channelMembers = null; + protected String sourceText = null; + + public Channel(String channelName) { + this.channelName = channelName; + selectors = new HashSet<>(); + channelMembers = new HashSet<>(); + } + + public Channel(String channelName, Set variables) { + this.channelName = channelName; + selectors = new HashSet<>(); + for (Variable var: variables) { + selectors.add(new Selector(var)); + } + channelMembers = new HashSet<>(); + } + + public String getChannelName() { + return channelName; + } + + public Set getChannelSelectors() { + return selectors; + } + + public void setChannelSelectors(Set selectors) { + this.selectors = selectors; + } + + public void addSelector(Selector selector) { + selectors.add(selector); + } + + public Set getChannelMembers() { + return channelMembers; + } + + public void setChannelMembers(Set channelMembers) { + this.channelMembers = channelMembers; + for (ChannelMember channelMember: channelMembers) { + for (Selector selector: channelMember.getSelectors()) { + addSelector(selector); + } + } + } + + public void addChannelMember(ChannelMember channelMember) { + channelMembers.add(channelMember); + for (Selector selector: channelMember.getSelectors()) { + addSelector(selector); + } + } + + public void removeChannelMember(ResourcePath id) { + for (ChannelMember cm: channelMembers) { + if (cm.getResource() == id) { + channelMembers.remove(cm); + break; + } + } + } + + public Set getResources() { + Set resources = new HashSet<>(); + for (ChannelMember member: channelMembers) { + resources.add(member.getResource()); + } + return resources; + } + + public String toString() { + return channelName; + } + + public void setSourceText(String sourceText) { + this.sourceText = sourceText; + } + + public String getSourceText() { + if (sourceText == null) { + return toString(); + } + return sourceText; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ChannelMember.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ChannelMember.java new file mode 100644 index 0000000..4e99cfb --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ChannelMember.java @@ -0,0 +1,59 @@ +package models.dataConstraintModel; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +public class ChannelMember implements Serializable { + private ResourcePath resourcePath = null; + private List selectors = null; + private StateTransition stateTransition = null; + + public ChannelMember(ResourcePath resourcePath) { + this.resourcePath = resourcePath; + selectors = new ArrayList<>(); + stateTransition = new StateTransition(); + } + + public ResourcePath getResource() { + return resourcePath; + } + + public void setResource(ResourcePath resourcePath) { + this.resourcePath = resourcePath; + } + + public List getSelectors() { + return selectors; + } + + public void setSelectors(List selectors) { + this.selectors = selectors; + } + + public ChannelMember addSelector(Selector selector) { + selectors.add(selector); + return this; + } + + public StateTransition getStateTransition() { + return stateTransition; + } + + public void setStateTransition(StateTransition stateTransition) { + this.stateTransition = stateTransition; + } + + @Override + public String toString() { + if (stateTransition.getNextStateExpression() == null) { + return resourcePath.getResourceName() + "(" + + stateTransition.getCurStateExpression() + "," + + stateTransition.getMessageExpression() + ")"; + } + return resourcePath.getResourceName() + "(" + + stateTransition.getCurStateExpression() + "," + + stateTransition.getMessageExpression() + ")" + + " == " + stateTransition.getNextStateExpression(); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/DataConstraintModel.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/DataConstraintModel.java new file mode 100644 index 0000000..e6b2936 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/DataConstraintModel.java @@ -0,0 +1,500 @@ +package models.dataConstraintModel; + +import java.io.Serializable; +import java.util.Collection; +import java.util.HashMap; + +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 implements Serializable { + 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 JsonType typeJson = new JsonType("Json", "HashMap<>", "Map"); + public static final Symbol add = new Symbol(Parser.ADD, 2, Symbol.Type.INFIX); + public static final Symbol mul = new Symbol(Parser.MUL, 2, Symbol.Type.INFIX); + public static final Symbol 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 append = new Symbol("append", 2, Symbol.Type.PREFIX, "add", Symbol.Type.METHOD_WITH_SIDE_EFFECT); + public static final Symbol remove = new Symbol("remove", 2, Symbol.Type.PREFIX, "remove", Symbol.Type.METHOD_WITH_SIDE_EFFECT); + public static final Symbol head = new Symbol("head", 1, Symbol.Type.PREFIX, "($x)->$x.get(0)", Symbol.Type.LAMBDA); + public static final Symbol tail = new Symbol("tail", 1, Symbol.Type.PREFIX, "($x)->$x.subList(1, $x.size())", Symbol.Type.LAMBDA); + public static final Symbol length = new Symbol("length", 1, Symbol.Type.PREFIX, "($x)->$x.size()", Symbol.Type.LAMBDA); + public static final Symbol get = new Symbol("get", 2, Symbol.Type.PREFIX, "get", Symbol.Type.METHOD); + public static final Symbol set = new Symbol("set", 3, Symbol.Type.PREFIX, "set", Symbol.Type.METHOD_WITH_SIDE_EFFECT); + public static final Symbol contains = new Symbol("contains", 2, Symbol.Type.PREFIX, 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] + ")"; + } + }); + public static final Symbol nil = new Symbol("nil", 0, Symbol.Type.PREFIX, new Symbol.IImplGenerator() { + @Override + public String generate(Type type, Type[] childrenTypes, String[] childrenImpl, String[] childrenSideEffects, String[] sideEffect) { + String compType = ""; + if (type != null) { + String temp = "temp_nil"; + String interfaceType = type.getInterfaceTypeName(); + if (interfaceType.contains("<")) { + compType = interfaceType.substring(interfaceType.indexOf("<") + 1, interfaceType.lastIndexOf(">")); + } + String implType = type.getImplementationTypeName(); + if (implType.indexOf('<') >= 0) { + implType = implType.substring(0, implType.indexOf('<')); + } + sideEffect[0] = interfaceType + " " + temp + " = " + "new " + implType + "<" + compType + ">();\n"; + return temp; + } + return "new ArrayList<" + compType + ">()"; + } + }); + public static final Symbol null_ = new Symbol("null", 0, Symbol.Type.PREFIX, "null", Symbol.Type.PREFIX); + public static final Symbol 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; + } + }); + + + 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, 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); + public static final Symbol delete = new Symbol("delete", 2, Symbol.Type.PREFIX, "remove", Symbol.Type.METHOD_WITH_SIDE_EFFECT); + public static final Symbol lookup = new Symbol("lookup", 2, Symbol.Type.PREFIX, new Symbol.IImplGenerator() { + final int count[] = {0}; + @Override + public String generate(Type type, Type[] childrenTypes, String[] childrenImpl, String[] childrenSideEffects, String[] sideEffect) { + String temp = "temp_get" + count[0]; + String impl = childrenSideEffects[0] + childrenSideEffects[1]; + impl += type.getInterfaceTypeName() + " " + temp + ";\n"; + impl += "if (" + childrenImpl[0] + ".get(" + childrenImpl[1] + ") != null) {\n"; + impl += "\t" + temp + " = " + childrenImpl[0] + ".get(" + childrenImpl[1] + ");\n"; + impl += "} else {\n"; + impl += "\t" + temp + " = " + getDefaultValue(type) + ";\n"; + impl += "}"; + sideEffect[0] = impl; + count[0]++; + return temp; + } + }); + public static final Symbol addMember = new Symbol("addMember", 3, Symbol.Type.PREFIX, "put", Symbol.Type.METHOD_WITH_SIDE_EFFECT); + public static final Symbol dot = new Symbol(Parser.DOT, 2, Symbol.Type.INFIX, "get", Symbol.Type.METHOD); + public static final Symbol dotParam = new Symbol(Parser.DOT, 2, Symbol.Type.INFIX, "get", Symbol.Type.METHOD); + public static final Symbol pi = new Symbol("PI", 0, Symbol.Type.PREFIX, "Math.PI", Symbol.Type.PREFIX); + public static final Symbol E = new Symbol("E", 0, Symbol.Type.PREFIX, "Math.E", Symbol.Type.PREFIX); + public static final Symbol sqrt = new Symbol("sqrt", 1, Symbol.Type.PREFIX, "Math.sqrt", Symbol.Type.PREFIX); + public static final Symbol sin = new Symbol("sin", 1, Symbol.Type.PREFIX, "Math.sin", Symbol.Type.PREFIX); + public static final Symbol cos = new Symbol("cos", 1, Symbol.Type.PREFIX, "Math.cos", Symbol.Type.PREFIX); + public static final Symbol tan = new Symbol("tan", 1, Symbol.Type.PREFIX, "Math.tan", Symbol.Type.PREFIX); + public static final Symbol asin = new Symbol("asin", 1, Symbol.Type.PREFIX, "Math.asin", Symbol.Type.PREFIX); + public static final Symbol acos = new Symbol("acos", 1, Symbol.Type.PREFIX, "Math.acos", Symbol.Type.PREFIX); + public static final Symbol atan = new Symbol("atan", 1, Symbol.Type.PREFIX, "Math.atan", Symbol.Type.PREFIX); + public static final Symbol pow = new Symbol("pow", 2, Symbol.Type.PREFIX, "Math.pow", Symbol.Type.PREFIX); + public static final Symbol exp = new Symbol("exp", 1, Symbol.Type.PREFIX, "Math.exp", Symbol.Type.PREFIX); + public static final Symbol log = new Symbol("log", 1, Symbol.Type.PREFIX, "Math.log", Symbol.Type.PREFIX); + public static final Symbol abs = new Symbol("abs", 1, Symbol.Type.PREFIX, "Math.abs", Symbol.Type.PREFIX); + + static { + add.setInverses(new Symbol[] {sub, sub}); + 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}); + append.setSignature(new Type[] {typeList, typeList, null}); + remove.setSignature(new Type[] {typeList, typeList, typeInt}); + head.setSignature(new Type[] {null, typeList}); + tail.setSignature(new Type[] {typeList, typeList}); + contains.setSignature(new Type[] {typeBoolean, null, null}); + length.setSignature(new Type[] {typeInt, null}); + get.setSignature(new Type[] {null, typeList, typeInt}); + set.setSignature(new Type[] {typeList, typeList, typeInt, null}); + 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}); + fst.setInverses(new Symbol[] {new LambdaAbstraction(new Variable("x"), new Term(tuple, new Expression[] {new Variable("x"), new Variable("y")}))}); + snd.setSignature(new Type[] {null, typeTuple}); + snd.setInverses(new Symbol[] {new LambdaAbstraction(new Variable("y"), new Term(tuple, new Expression[] {new Variable("x"), new Variable("y")}))}); + insert.setSignature(new Type[] {typeMap, typeMap, null, null}); + delete.setSignature(new Type[] {typeMap, typeMap, null}); + lookup.setSignature(new Type[] {null, typeMap, null}); + addMember.setSignature(new Type[] {typeJson, typeJson, typeString, null}); + dot.setSignature(new Type[] {null, typeJson, typeString}); + dotParam.setSignature(new Type[] {null, null, null}); + pi.setSignature(new Type[] {typeDouble}); + E.setSignature(new Type[] {typeDouble}); + sqrt.setSignature(new Type[] {typeDouble, typeDouble}); + sin.setSignature(new Type[] {typeDouble, typeDouble}); + cos.setSignature(new Type[] {typeDouble, typeDouble}); + tan.setSignature(new Type[] {typeDouble, typeDouble}); + asin.setSignature(new Type[] {typeDouble, typeDouble}); + asin.setInverses(new Symbol[] {sin}); + acos.setSignature(new Type[] {typeDouble, typeDouble}); + acos.setInverses(new Symbol[] {cos}); + atan.setSignature(new Type[] {typeDouble, typeDouble}); + atan.setInverses(new Symbol[] {tan}); + pow.setSignature(new Type[] {typeDouble, typeDouble, typeDouble}); + exp.setSignature(new Type[] {typeDouble, typeDouble}); + exp.setInverses(new Symbol[] {log}); + log.setSignature(new Type[] {typeDouble, typeDouble}); + log.setInverses(new Symbol[] {exp}); + abs.setSignature(new Type[] {typeDouble, typeDouble}); + } + + public DataConstraintModel() { + resourcePaths = new HashMap<>(); + 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); + addType(typeJson); + symbols = new HashMap<>(); + addSymbol(add); + addSymbol(mul); + addSymbol(sub); + addSymbol(div); + addSymbol(minus); + addSymbol(mod); + addSymbol(cons); + addSymbol(append); + addSymbol(remove); + 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(delete); + addSymbol(lookup); + addSymbol(addMember); + addSymbol(dot); + addSymbol(dotParam); + addSymbol(pi); + addSymbol(E); + addSymbol(sqrt); + addSymbol(sin); + addSymbol(cos); + addSymbol(tan); + addSymbol(asin); + addSymbol(acos); + addSymbol(atan); + addSymbol(pow); + addSymbol(exp); + addSymbol(log); + addSymbol(abs); + } + + public Collection getResourcePaths() { + return resourcePaths.values(); + } + + public 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; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonAccessor.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonAccessor.java new file mode 100644 index 0000000..5d9c4fb --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonAccessor.java @@ -0,0 +1,153 @@ +package models.dataConstraintModel; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.LambdaAbstraction; +import models.algebra.Position; +import models.algebra.Symbol; +import models.algebra.Term; +import models.algebra.Type; +import models.algebra.Variable; + +public class JsonAccessor extends Term { + + public JsonAccessor(Symbol symbol) { + super(symbol); + } + + public Type getType() { + if (symbol.equals(DataConstraintModel.dotParam)) { + Type valueType = null; + if (getChild(1) instanceof Term) { + valueType = ((Term) getChild(1)).getType(); + } else if (getChild(1) instanceof Variable) { + valueType = ((Variable) getChild(1)).getType(); + } + if (valueType != null) return valueType; + } + return super.getType(); + } + + + @Override + public Expression getInverseMap(Expression outputValue, Position targetPos) { + if (targetPos.isEmpty()) return outputValue; + targetPos = (Position) targetPos.clone(); + int i = targetPos.removeHeadOrder(); + Symbol[] inverseSymbols = symbol.getInverses(); + if (i == 0) { + if (symbol.equals(DataConstraintModel.dot) && getChildren().size() >= 2) { + // this term is `json.key`. + Expression expJson = getChild(0); + Expression expKey = getChild(1); + JsonType jsonType = null; + if (expJson instanceof Variable) { + jsonType = (JsonType) ((Variable) expJson).getType(); + } else if (expJson instanceof Term) { + jsonType = (JsonType) ((Term) expJson).getType(); + } + String keyName = null; + if (expKey instanceof Constant) { + keyName = ((Constant) expKey).getSymbol().getName(); + Term jsonTerm = new Constant(DataConstraintModel.nil); + jsonTerm.setType(DataConstraintModel.typeJson); + int v = 1; + Map vars = new HashMap<>(); + Set keySet = new HashSet<>(); + if (jsonType == null || jsonType == DataConstraintModel.typeJson) { + keySet.add(keyName); + } else { + keySet.addAll(jsonType.getKeys()); + } + for (String key: keySet) { + Term addMemberTerm = new Term(DataConstraintModel.addMember); // addMember(jsonTerm, key, v) + addMemberTerm.addChild(jsonTerm); + addMemberTerm.addChild(new Constant(key)); + Variable var = new Variable("v" + v); + addMemberTerm.addChild(var); + vars.put(key, var); + jsonTerm = addMemberTerm; + v++; + } + Variable var = vars.get(keyName); + LambdaAbstraction lambdaAbstraction = new LambdaAbstraction(var, jsonTerm); // v -> addMember(jsonTerm, key, v) + inverseSymbols = new Symbol[] { lambdaAbstraction }; + } + } else if (symbol.equals(DataConstraintModel.dotParam) && getChildren().size() >= 2) { + // this term is `json.{param}`. + Expression expListOrMap = getChild(0); + Expression expKey = getChild(1); + JsonType jsonType = null; + if (expListOrMap instanceof Variable) { + jsonType = (JsonType) ((Variable) expListOrMap).getType(); + } else if (expListOrMap instanceof Term) { + jsonType = (JsonType) ((Term) expListOrMap).getType(); + } + Type keyType = null; + if (expKey instanceof Variable) { + keyType = (JsonType) ((Variable) expKey).getType(); + } else if (expKey instanceof Term) { + keyType = (JsonType) ((Term) expKey).getType(); + } + if (jsonType != null && keyType != null) { + if (DataConstraintModel.typeList.isAncestorOf(jsonType) || keyType.equals(DataConstraintModel.typeInt)) { + Term setElementTerm = new Term(DataConstraintModel.set); // set(list, idx, v) + setElementTerm.addChild(new Constant(DataConstraintModel.nil)); + setElementTerm.addChild(expKey); + Variable var = new Variable("v"); + setElementTerm.addChild(var); + LambdaAbstraction lambdaAbstraction = new LambdaAbstraction(var, setElementTerm); // v -> set(list, idx, v) + inverseSymbols = new Symbol[] { lambdaAbstraction }; + } else if (DataConstraintModel.typeMap.isAncestorOf(jsonType) || keyType.equals(DataConstraintModel.typeString)) { + Term insertEntryTerm = new Term(DataConstraintModel.insert); // insert(map, key, v) + insertEntryTerm.addChild(new Constant(DataConstraintModel.nil)); + insertEntryTerm.addChild(expKey); + Variable var = new Variable("v"); + insertEntryTerm.addChild(var); + LambdaAbstraction lambdaAbstraction = new LambdaAbstraction(var, insertEntryTerm); // v -> insert(map, key, v) + inverseSymbols = new Symbol[] { lambdaAbstraction }; + } + } + } + } + if (inverseSymbols == null || i >= inverseSymbols.length || inverseSymbols[i] == null) return null; + Term inverseMap = new Term(inverseSymbols[i]); + inverseMap.addChild(outputValue); + for (int n = 0; n < inverseSymbols[i].getArity(); n++) { + if (n != i) { + inverseMap.addChild(children.get(n)); + } + } + return children.get(i).getInverseMap(inverseMap, targetPos); + } + + public String toString() { + if (symbol.equals(DataConstraintModel.dotParam)) { + return children.get(0).toString() + symbol.toString() + "{" + children.get(1).toString() + "}"; + } + return super.toString(); + } + + public String toImplementation(String[] sideEffects) { + if (symbol.equals(DataConstraintModel.dotParam)) { + return children.get(0).toImplementation(sideEffects) + symbol.toImplementation() + "{" + children.get(1).toImplementation(sideEffects) + "}"; + } + return super.toImplementation(sideEffects); + } + + @Override + public Object clone() { + JsonAccessor newTerm = new JsonAccessor(symbol); + for (Expression e: children) { + newTerm.addChild((Expression) e.clone()); + } + newTerm.type = type; + return newTerm; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonType.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonType.java new file mode 100644 index 0000000..9f9e835 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonType.java @@ -0,0 +1,65 @@ +package models.dataConstraintModel; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import models.algebra.Type; + +public class JsonType extends Type { + protected boolean isListOrMap = false; + protected Map memberTypes = null; + protected Type listOrMapElementType = null; + + public JsonType(String typeName, String implementationTypeName) { + super(typeName, implementationTypeName); + memberTypes = new HashMap<>(); + } + + public JsonType(String typeName, String implementationTypeName, String interfaceTypeName) { + super(typeName, implementationTypeName, interfaceTypeName); + memberTypes = new HashMap<>(); + } + + public JsonType(String typeName, String implementationTypeName, Type parentType) { + super(typeName, implementationTypeName, parentType); + memberTypes = new HashMap<>(); + } + + public JsonType(String typeName, String implementationTypeName, String interfaceTypeName, Type parentType) { + super(typeName, implementationTypeName, interfaceTypeName, parentType); + memberTypes = new HashMap<>(); + } + + public Map getMemberTypes() { + return memberTypes; + } + + public Type getMemberType(String key) { + return memberTypes.get(key); + } + + public Set getKeys() { + return memberTypes.keySet(); + } + + public void addMemberType(String key, Type valueType) { + memberTypes.put(key, valueType); + } + +// public boolean isListOrMap() { +// return isListOrMap; +// } +// +// public void setListOrMap(boolean isListOrMap) { +// this.isListOrMap = isListOrMap; +// } + + public Type getElementType() { + return listOrMapElementType; + } + + public void setElementType(Type listElementType) { + this.listOrMapElementType = listElementType; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourcePath.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourcePath.java new file mode 100644 index 0000000..fb2e80b --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourcePath.java @@ -0,0 +1,72 @@ +package models.dataConstraintModel; + +import java.io.Serializable; + +import models.algebra.Expression; +import models.algebra.Term; +import models.algebra.Type; + +public class ResourcePath implements Serializable { + private String resourceName = null; + private Type resourceStateType = null; + private int numParameters = 0; + private Expression initialValue = null; + protected String initText = null; + + public ResourcePath(String resourceName, int numParameters) { + this.resourceName = resourceName; + this.numParameters = numParameters; + } + + public ResourcePath(String resourceName, Type resourceStateType, int numParameters) { + this.resourceName = resourceName; + this.resourceStateType = resourceStateType; + this.numParameters = numParameters; + } + + public String getResourceName() { + return resourceName; + } + + public int getNumberOfParameters() { + return numParameters; + } + + 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 Expression getInitialValue() { + return initialValue; + } + + public void setInitialValue(Expression initialValue) { + this.initialValue = initialValue; + } + + public void setInitText(String initText) { + this.initText = initText; + } + + public String getInitText() { + return initText; + } + + public boolean equals(Object another) { + if (!(another instanceof ResourcePath)) return false; + return resourceName.equals(((ResourcePath) another).resourceName); + } + + public int hashCode() { + return resourceName.hashCode(); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Selector.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Selector.java new file mode 100644 index 0000000..f7439b2 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Selector.java @@ -0,0 +1,21 @@ +package models.dataConstraintModel; + +import java.io.Serializable; + +import models.algebra.Variable; + +public class Selector implements Serializable { + private Variable variable = null; + + public Selector(Variable variable) { + this.setVariable(variable); + } + + public Variable getVariable() { + return variable; + } + + public void setVariable(Variable variable) { + this.variable = variable; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/StateTransition.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/StateTransition.java new file mode 100644 index 0000000..5c731bc --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/StateTransition.java @@ -0,0 +1,223 @@ +package models.dataConstraintModel; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map.Entry; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.InvalidMessage; +import models.algebra.Position; +import models.algebra.Term; +import models.algebra.ValueUndefined; +import models.algebra.Variable; +import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork; + +public class StateTransition implements Serializable { + private Expression curStateExpression = null; + private Expression nextStateExpression = null; + private Expression messageExpression = null; + + public Expression getCurStateExpression() { + return curStateExpression; + } + + public void setCurStateExpression(Expression curStateExpression) { + this.curStateExpression = curStateExpression; + } + + public Expression getNextStateExpression() { + return nextStateExpression; + } + + public void setNextStateExpression(Expression nextStateExpression) { + this.nextStateExpression = nextStateExpression; + } + + public Expression getMessageExpression() { + return messageExpression; + } + + public void setMessageExpression(Expression messageExpression) { + this.messageExpression = messageExpression; + } + + public boolean isRightUnary() { + for (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 { + HashMap> bindings = new HashMap<>(); + + Expression curStateTerm = getCurStateExpression(); + HashMap curStateVars = curStateTerm.getVariables(); + for (Entry curStateVarEnt: curStateVars.entrySet()) { + Variable var = curStateVarEnt.getValue(); + Position varPos = curStateVarEnt.getKey(); + Expression valueCalc = curStateTerm.getInverseMap(curStateValue, varPos); + if (valueCalc != null) { + if (valueCalc instanceof Term && !(valueCalc instanceof Constant)) { + valueCalc = ((Term) valueCalc).reduce(); + } + ArrayList values = bindings.get(var); + if (values == null) { + values = new ArrayList(); + bindings.put(var, values); + } + values.add(valueCalc); + } + } + + Expression nextStateTerm = (Expression) getNextStateExpression().clone(); + for (Variable var: bindings.keySet()) { + HashMap vars2 = nextStateTerm.getVariables(); + for (Variable var2: vars2.values()) { + if (var.equals(var2) && bindings.get(var).size() == 1) { + if (nextStateTerm instanceof Term) { + nextStateTerm = ((Term) nextStateTerm).substitute(var, bindings.get(var).get(0)); + } else if (nextStateTerm instanceof Variable && nextStateTerm.equals(var)) { + nextStateTerm = bindings.get(var).get(0); + } + } + } + } + + HashMap nextStateVars = nextStateTerm.getVariables(); + for (Entry nextStateVarEnt: nextStateVars.entrySet()) { + Variable var = nextStateVarEnt.getValue(); + Position varPos = nextStateVarEnt.getKey(); + Expression valueCalc = nextStateTerm.getInverseMap(nextStateValue, varPos); + if (valueCalc != null) { + if (valueCalc instanceof Term) { + valueCalc = ((Term) valueCalc).reduce(); + } + ArrayList values = bindings.get(var); + if (values == null) { + values = new ArrayList(); + bindings.put(var, values); + } + values.add(valueCalc); + } + } + + Expression messageTerm = getMessageExpression(); + if (!(messageTerm instanceof Term) && !(messageTerm instanceof Variable)) 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(); + if (messageTerm instanceof Term) { + messageTerm = ((Term) messageTerm).substitute(var, bindings.get(var).iterator().next()); + } else if (messageTerm instanceof Variable) { + if (messageTerm.equals(var)) { + return bindings.get(var).iterator().next(); + } + } + } + } + return messageTerm; + } + + public Expression deriveMessageConstraintFor(Expression curStateValue) throws InvalidMessage, ResolvingMultipleDefinitionIsFutureWork { + HashMap> bindings = new HashMap<>(); + + Expression curStateTerm = getCurStateExpression(); + HashMap curStateVars = curStateTerm.getVariables(); + for (Entry curStateVarEnt: curStateVars.entrySet()) { + Variable var = curStateVarEnt.getValue(); + Position varPos = curStateVarEnt.getKey(); + Expression valueCalc = curStateTerm.getInverseMap(curStateValue, varPos); + if (valueCalc != null) { + if (valueCalc instanceof Term && !(valueCalc instanceof Constant)) { + valueCalc = ((Term) valueCalc).reduce(); + } + ArrayList values = bindings.get(var); + if (values == null) { + values = new ArrayList(); + bindings.put(var, values); + } + values.add(valueCalc); + } + } + + Expression messageTerm = getMessageExpression(); + if (!(messageTerm instanceof Term) && !(messageTerm instanceof Variable)) 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(); + if (messageTerm instanceof Term) { + messageTerm = ((Term) messageTerm).substitute(var, bindings.get(var).iterator().next()); + } else if (messageTerm instanceof Variable) { + if (messageTerm.equals(var)) { + return bindings.get(var).iterator().next(); + } + } + } + } + return messageTerm; + } + + public Expression deriveNextStateExpressionFor(Expression curStateValue, Term concreteMessage) + throws ResolvingMultipleDefinitionIsFutureWork, ValueUndefined { + HashMap bindings = new HashMap<>(); + + Expression curStateTerm = getCurStateExpression(); + HashMap curStateVars = curStateTerm.getVariables(); + for (Entry curStateVarEnt: curStateVars.entrySet()) { + Variable var = curStateVarEnt.getValue(); + Position varPos = curStateVarEnt.getKey(); + Expression valueCalc = curStateTerm.getInverseMap(curStateValue, varPos); + if (valueCalc != null) { + if (valueCalc instanceof Term && !(valueCalc instanceof Constant)) { + valueCalc = ((Term) valueCalc).reduce(); + } + if (bindings.get(var) != null) throw new ResolvingMultipleDefinitionIsFutureWork(); + bindings.put(var, valueCalc); + } + } + + Expression messageTerm = getMessageExpression(); + HashMap messageVars = messageTerm.getVariables(); + if (concreteMessage != null) { + for (Entry messageVarEnt: messageVars.entrySet()) { + Variable var = messageVarEnt.getValue(); + Position varPos = messageVarEnt.getKey(); + Expression valueCalc = concreteMessage.getSubTerm(varPos); + if (valueCalc != null) { + if (bindings.get(var) != null) throw new ResolvingMultipleDefinitionIsFutureWork(); + bindings.put(var, valueCalc); + } + } + } + + Expression nextStateTerm = getNextStateExpression(); + if (nextStateTerm instanceof Variable) { + nextStateTerm = bindings.get((Variable) nextStateTerm); + if (nextStateTerm == null) throw new ValueUndefined(); + } else { + for (Variable var: bindings.keySet()) { + nextStateTerm = ((Term) nextStateTerm).substitute(var, bindings.get(var)); + } + } + if (nextStateTerm instanceof Term && !(nextStateTerm instanceof Constant)) { + nextStateTerm = ((Term) nextStateTerm).reduce(); + } + return nextStateTerm; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataFlowEdge.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataFlowEdge.java new file mode 100644 index 0000000..9de9359 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataFlowEdge.java @@ -0,0 +1,20 @@ +package models.dataFlowModel; + +import models.*; + +public class DataFlowEdge extends Edge { + protected DataTransferChannel channel = null; + + public DataFlowEdge(ResourceNode src, ResourceNode dst, DataTransferChannel channel) { + super(src, dst); + this.channel = channel; + } + + public DataTransferChannel getChannel() { + return channel; + } + + public String toString() { + return channel.getChannelName(); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataFlowGraph.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataFlowGraph.java new file mode 100644 index 0000000..1b3a208 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataFlowGraph.java @@ -0,0 +1,64 @@ +package models.dataFlowModel; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import models.DirectedGraph; +import models.Node; +import models.dataConstraintModel.ResourcePath; + +public class DataFlowGraph extends DirectedGraph implements IFlowGraph { + protected Map nodeMap = null; + + public DataFlowGraph() { + super(); + nodeMap = new HashMap<>(); + } + + public void addNode(ResourcePath id) { + if (nodeMap.get(id) == null) { + ResourceNode node = new ResourceNode(id); + addNode(node); + nodeMap.put(id, node); + } + } + + public void addEdge(ResourcePath in, ResourcePath out, DataTransferChannel dfChannel) { + 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, dfChannel)); + } + + public Collection getResouceNodes(){ + return nodeMap.values(); + } + + public ResourceNode getResouceNode(ResourcePath resourcePath) { +// if(nodeMap.get(identifierTemplate) == null) throw new NullPointerException(identifierTemplate.getResourceName() + " was not found."); // Because with this statement, the original JumpGame.model cannot be read. + return nodeMap.get(resourcePath); + } + + @Override + public Map> getAllNodes() { + Map> allNodeSets = new HashMap<>(); + for (Node n: super.getNodes()) { + Set nodeSet = new HashSet<>(); + nodeSet.add(n); + allNodeSets.put(n, nodeSet); + } + return allNodeSets; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferChannel.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferChannel.java new file mode 100644 index 0000000..4241faa --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferChannel.java @@ -0,0 +1,273 @@ +package models.dataFlowModel; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; + +import models.algebra.Expression; +import models.algebra.InvalidMessage; +import models.algebra.Parameter; +import models.algebra.ParameterizedIdentifierIsFutureWork; +import models.algebra.Position; +import models.algebra.Term; +import models.algebra.UnificationFailed; +import models.algebra.ValueUndefined; +import models.algebra.Variable; +import models.dataConstraintModel.*; + +public class DataTransferChannel extends Channel { + protected Set inputChannelMembers = null; + protected Set outputChannelMembers = null; + protected Set referenceChannelMembers = null; + + public DataTransferChannel(String channelName) { + super(channelName); + inputChannelMembers = new HashSet<>(); + outputChannelMembers = new HashSet<>(); + referenceChannelMembers = new HashSet<>(); + } + + public Set getInputChannelMembers() { + return inputChannelMembers; + } + + public void setInputChannelMembers(Set inputChannelMembers) { + this.inputChannelMembers = inputChannelMembers; + } + + private void addInputChannelMember(ChannelMember inputChannelMember) { + inputChannelMembers.add(inputChannelMember); + } + + public Set getOutputChannelMembers() { + return outputChannelMembers; + } + + public void setOutputChannelMembers(Set outputChannelMembers) { + this.outputChannelMembers = outputChannelMembers; + } + + private void addOutputChannelMember(ChannelMember outputChannelMember) { + outputChannelMembers.add(outputChannelMember); + } + + public Set getReferenceChannelMembers() { + return referenceChannelMembers; + } + + public void setReferenceChannelMembers(Set referenceChannelMembers) { + this.referenceChannelMembers = referenceChannelMembers; + } + + private void addReferenceChannelMember(ChannelMember referenceChannelMember) { + referenceChannelMembers.add(referenceChannelMember); + } + + public void addChannelMemberAsInput(ChannelMember groupDependentResource) { + addChannelMember(groupDependentResource); + addInputChannelMember(groupDependentResource); + } + + public void addChannelMemberAsOutput(ChannelMember groupDependentResource) { + addChannelMember(groupDependentResource); + addOutputChannelMember(groupDependentResource); + } + + public void addChannelMemberAsReference(ChannelMember groupDependentResource) { + addChannelMember(groupDependentResource); + addReferenceChannelMember(groupDependentResource); + } + + public void removeChannelMember(ResourcePath id) { + for (ChannelMember cm: inputChannelMembers) { + if (cm.getResource() == id) { + inputChannelMembers.remove(cm); + super.removeChannelMember(id); + return; + } + } + for (ChannelMember cm: outputChannelMembers) { + if (cm.getResource() == id) { + outputChannelMembers.remove(cm); + super.removeChannelMember(id); + return; + } + } + for (ChannelMember cm: referenceChannelMembers) { + if (cm.getResource() == id) { + referenceChannelMembers.remove(cm); + super.removeChannelMember(id); + return; + } + } + } + + public Set getInputResources() { + Set inputResources = new HashSet<>(); + for (ChannelMember member: inputChannelMembers) { + inputResources.add(member.getResource()); + } + return inputResources; + } + + public Set getOutputResources() { + Set outputResources = new HashSet<>(); + for (ChannelMember member: outputChannelMembers) { + outputResources.add(member.getResource()); + } + return outputResources; + } + + public Set getReferenceResources() { + Set referenceResources = new HashSet<>(); + for (ChannelMember member: referenceChannelMembers) { + referenceResources.add(member.getResource()); + } + return referenceResources; + } + + /** + * Derive the update expression of the state of the target channel member. + * @param targetMember a channel member whose state is to be updated + * @return the derived update expression + * @throws ParameterizedIdentifierIsFutureWork + * @throws ResolvingMultipleDefinitionIsFutureWork + * @throws InvalidMessage + * @throws UnificationFailed + * @throws ValueUndefined + */ + public Expression deriveUpdateExpressionOf(ChannelMember targetMember) throws ParameterizedIdentifierIsFutureWork, ResolvingMultipleDefinitionIsFutureWork, InvalidMessage, UnificationFailed, ValueUndefined { + IResourceStateAccessor defaultStateAccessor = new IResourceStateAccessor() { + HashMap curStateParams = new HashMap<>(); + HashMap nextStateParams = new HashMap<>(); + + @Override + public Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from) { + String resource = target.getResourceName(); + Parameter curStateParam = curStateParams.get(resource); + if (curStateParam == null) { + curStateParam = new Parameter("cur" + resource); + curStateParams.put(resource, curStateParam); + } + return curStateParam; + } + + @Override + public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { + String resource = target.getResourceName(); + Parameter nextStateParam = nextStateParams.get(resource); + if (nextStateParam == null) { + nextStateParam = new Parameter("next" + resource); + nextStateParams.put(resource, nextStateParam); + } + return nextStateParam; + } + }; + return deriveUpdateExpressionOf(targetMember, defaultStateAccessor); + } + + /** + * Derive the update expression of the state of the target channel member with a given resource state accessor. + * @param targetMember a channel member whose state is to be updated + * @param stateAccessor a resource state accessor + * @return the derived update expression + * @throws ParameterizedIdentifierIsFutureWork + * @throws ResolvingMultipleDefinitionIsFutureWork + * @throws InvalidMessage + * @throws UnificationFailed + * @throws ValueUndefined + */ + public Expression 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) + throws ParameterizedIdentifierIsFutureWork, ResolvingMultipleDefinitionIsFutureWork, InvalidMessage, UnificationFailed, ValueUndefined { + if (!getOutputChannelMembers().contains(targetMember)) return null; + HashSet 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); + 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); + messageConstraints.add((Term) messageConstraintByReference); + } + + // Unify message constraints + Term unifiedMessage = null; + for (Term messageContraint: messageConstraints) { + if (unifiedMessage == null) { + unifiedMessage = messageContraint; + } else { + unifiedMessage = (Term) unifiedMessage.unify(messageContraint); + if (unifiedMessage == null) { + throw new UnificationFailed(); + } + } + } + + // Calculate the next state of target resource from the unified message and the current resource state + ResourcePath targetResource = targetMember.getResource(); + if (targetResource.getNumberOfParameters() > 0) { + throw new ParameterizedIdentifierIsFutureWork(); + } + Expression curOutputStateAccessor = stateAccessor.getCurrentStateAccessorFor(targetResource, targetResource); + if (unifiedMessage == null) { + // for IOChannel + if (targetMember.getStateTransition().getMessageExpression() instanceof Term) { + unifiedMessage = (Term) targetMember.getStateTransition().getMessageExpression(); + } + } + return targetMember.getStateTransition().deriveNextStateExpressionFor(curOutputStateAccessor, unifiedMessage); + } + + @Override + public String toString() { + String channelSource = "channel " + getChannelName() + " {\n"; + for (ChannelMember inputMember: inputChannelMembers) { + channelSource += "\t in " + inputMember + "\n"; + } + for (ChannelMember refMember: referenceChannelMembers) { + channelSource += "\t ref " + refMember + "\n"; + } + for (ChannelMember outputMember: outputChannelMembers) { + channelSource += "\t out " + outputMember + "\n"; + } + channelSource += "}\n"; + return channelSource; + } + + public interface IResourceStateAccessor { + Expression getCurrentStateAccessorFor(ResourcePath target, ResourcePath from); + Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferModel.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferModel.java new file mode 100644 index 0000000..1e67efb --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferModel.java @@ -0,0 +1,31 @@ +package models.dataFlowModel; + +import java.util.Set; + +import models.dataConstraintModel.Channel; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.ResourcePath; + +public class DataTransferModel extends DataConstraintModel { + public DataFlowGraph getDataFlowGraph() { + DataFlowGraph dataFlowGraph = new DataFlowGraph(); + for (Channel channel: getChannels()) { + DataTransferChannel dfChannelGen = (DataTransferChannel)channel; + Set inputResources = dfChannelGen.getInputResources(); + Set outputResources = dfChannelGen.getOutputResources(); + for (ResourcePath in: inputResources) { + for (ResourcePath out: outputResources) { + dataFlowGraph.addEdge(in ,out, dfChannelGen); + } + } + } + for (Channel channel: getIOChannels()) { + DataTransferChannel dfChannel = (DataTransferChannel)channel; + Set outputResources = dfChannel.getOutputResources(); + for (ResourcePath out: outputResources) { + dataFlowGraph.addNode(out); + } + } + return dataFlowGraph; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/IFlowGraph.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/IFlowGraph.java new file mode 100644 index 0000000..d159bd7 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/IFlowGraph.java @@ -0,0 +1,10 @@ +package models.dataFlowModel; + +import java.util.Map; +import java.util.Set; + +import models.Node; + +public interface IFlowGraph { + abstract public Map> getAllNodes(); +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ModelExtension.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ModelExtension.java new file mode 100644 index 0000000..8f6f36a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ModelExtension.java @@ -0,0 +1,229 @@ +package models.dataFlowModel; + +import models.algebra.Symbol; +import models.algebra.Type; +import models.dataConstraintModel.DataConstraintModel; + +public class ModelExtension { + private static Symbol.Memento floorMem; + private static Symbol.Memento sumMem; + private static Symbol.Memento mergeMem; + private static Symbol.Memento extractFaceDownMem; + private static Symbol.Memento sortByKeyMem; + private static Symbol.Memento selectGEMem; + private static Symbol.Memento selectLEMem; + + public static void extendModel(DataTransferModel model) { + Symbol floor = model.getSymbol("floor"); + floorMem = null; + if (floor != null) { + floorMem = floor.createMemento(); + floor.setImplName("(int)Math.floor"); + floor.setImplOperatorType(Symbol.Type.PREFIX); + } + Symbol sum = model.getSymbol("sum"); + sumMem = null; + if (sum != null) { + sumMem = sum.createMemento(); + final int[] count = new int[] {0}; + sum.setGenerator(new Symbol.IImplGenerator() { + @Override + public String generate(Type type, Type[] childrenTypes, String[] childrenImpl, String[] childrenSideEffects, String[] sideEffect) { + String compType = "Integer"; + if (type != null) { + String interfaceType = type.getInterfaceTypeName(); + if (interfaceType.contains("<")) { + compType = interfaceType.substring(interfaceType.indexOf("<") + 1, interfaceType.lastIndexOf(">")); + } + } + count[0]++; + String impl = compType + " " + "temp_sum" + count[0] + " = 0;\n"; + impl += "for (" + compType + " x: " + childrenImpl[0] + ") {\n"; + impl += "\t" + "temp_sum" + count[0] + " += x;\n"; + impl += "}\n"; + sideEffect[0] = sideEffect[0] + impl; + return "temp_sum" + count[0]; + } + }); + sum.setImplOperatorType(Symbol.Type.GENERATIVE); + sum.setSignature(new Type[] {DataConstraintModel.typeInt, DataConstraintModel.typeList}); +// sum.setImplName("stream().mapToInt(x->x).sum"); +// sum.setImplOperatorType(Symbol.Type.METHOD); + } + Symbol merge = model.getSymbol("merge"); + mergeMem = null; + if (merge != null) { + mergeMem = merge.createMemento(); + merge.setArity(2); + final int[] count = new int[] {0}; + merge.setGenerator(new Symbol.IImplGenerator() { + @Override + 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) { + implType = type.getImplementationTypeName(); + interfaceType = type.getInterfaceTypeName(); + if (interfaceType.contains("<")) { + compType = interfaceType.substring(interfaceType.indexOf("<") + 1, interfaceType.lastIndexOf(">")); + } + } + String idxGetter = ""; + if (compType.startsWith("Map.Entry")) { + idxGetter = ".getKey()"; + } + count[0]++; + String impl = ""; + impl += "" + interfaceType + " temp_l" + count[0] + " = new " + implType + "();\n"; + impl += "{\n"; + impl += "\tIterator<" + compType + "> i1 = " + childrenImpl[0] + ".iterator();\n"; + impl += "\tIterator<" + compType + "> i2 = " + childrenImpl[1] + ".iterator();\n"; + impl += "\t" + compType + " t1 = null;\n"; + impl += "\t" + compType + " t2 = null;\n"; + impl += "\twhile (i1.hasNext() || i2.hasNext() || t1 != null || t2 != null) {\n"; + impl += "\t\tif (t1 == null && i1.hasNext()) {\n"; + impl += "\t\t\tt1 = i1.next();\n"; + impl += "\t\t}\n"; + impl += "\t\tif (t2 == null && i2.hasNext()) {\n"; + impl += "\t\t\tt2 = i2.next();\n"; + impl += "\t\t}\n"; + impl += "\t\tif (t1 == null || (t2 != null && t1" + idxGetter + " < t2" + idxGetter + ")) {\n"; + impl += "\t\t\ttemp_l" + count[0] +".add(t2);\n"; + impl += "\t\t\tt2 = null;\n"; + impl += "\t\t} else {\n"; + impl += "\t\t\ttemp_l" + count[0] + ".add(t1);\n"; + impl += "\t\t\tt1 = null;\n"; + impl += "\t\t}\n"; + impl += "\t}\n"; + impl += "}\n"; + sideEffect[0] = sideEffect[0] + impl; + return "temp_l" + count[0]; + } + }); + merge.setImplOperatorType(Symbol.Type.GENERATIVE); + merge.setSignature(new Type[] {DataConstraintModel.typeList, DataConstraintModel.typeList, DataConstraintModel.typeList}); + } + Symbol extractFaceDown = model.getSymbol("extractFaceDown"); + extractFaceDownMem = null; + if (extractFaceDown != null) { + extractFaceDownMem = extractFaceDown.createMemento(); + extractFaceDown.setArity(1); + extractFaceDown.setGenerator(new Symbol.IImplGenerator() { + @Override + public String generate(Type type, Type[] childrenTypes, String[] childrenImpl, String[] childrenSideEffects, String[] sideEffect) { + return childrenImpl[0]+".stream().filter(item -> item.getValue()==false).collect(Collectors.toList())"; + } + }); + extractFaceDown.setImplOperatorType(Symbol.Type.GENERATIVE); + extractFaceDown.setSignature(new Type[] {DataConstraintModel.typeList, null}); + } + + Symbol sortByKey = model.getSymbol("sortByKey"); + sortByKeyMem = null; + if (sortByKey != null) { + sortByKeyMem = sortByKey.createMemento(); + sortByKey.setArity(1); + sortByKey.setGenerator(new Symbol.IImplGenerator() { + @Override + public String generate(Type type, Type[] childrenTypes, String[] childrenImpl, String[] childrenSideEffects, String[] sideEffect) { + String compType = ""; + String temp_sort="temp_sort"; + 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('<')); + } + + } + for (String s: childrenSideEffects) { + sideEffect[0] += s; + } + temp_sort=childrenImpl[0]+".sort(Comparator.comparing("+compType+"::getKey));\n"; + return temp_sort; + } + }); + sortByKey.setSignature(new Type[] {DataConstraintModel.typeList, DataConstraintModel.typeList}); + sortByKey.setImplOperatorType(Symbol.Type.GENERATIVE); + } + + Symbol selectGE = model.getSymbol("selectGE"); + selectGEMem = null; + if (selectGE != null) { + final int[] count = new int[] {0}; + selectGE.setArity(3); + selectGEMem = selectGE.createMemento(); + selectGE.setGenerator(new Symbol.IImplGenerator() { + @Override + public String generate(Type type, Type[] childrenTypes, String[] childrenImpl, String[] childrenSideEffects, String[] sideEffect) { + String implType = "ArrayList<>"; + String interfaceType = "List>"; + count[0]++; + String impl = ""; + impl += "" + interfaceType + " temp_l" + count[0] + " = new " + implType + "();\n"; + impl += "{\n"; + impl += "\tfor (Map item: " + childrenImpl[0] + ") {\n"; + impl += "\t\tif ((Integer) item.get(" + childrenImpl[1] + ") >= " + childrenImpl[2] + ") {\n"; + impl += "\t\t\ttemp_l" + count[0] + ".add(item);\n"; + impl += "\t\t}\n"; + impl += "\t}\n"; + impl += "}\n"; + sideEffect[0] = sideEffect[0] + impl; + return "temp_l" + count[0]; + } + }); + selectGE.setSignature(new Type[] {DataConstraintModel.typeList, DataConstraintModel.typeList, DataConstraintModel.typeString, DataConstraintModel.typeInt}); + selectGE.setImplOperatorType(Symbol.Type.GENERATIVE); + } + + Symbol selectLE = model.getSymbol("selectLE"); + selectLEMem = null; + if (selectLE != null) { + final int[] count = new int[] {0}; + selectLE.setArity(3); + selectLEMem = selectLE.createMemento(); + selectLE.setGenerator(new Symbol.IImplGenerator() { + @Override + public String generate(Type type, Type[] childrenTypes, String[] childrenImpl, String[] childrenSideEffects, String[] sideEffect) { + String implType = "ArrayList<>"; + String interfaceType = "List>"; + count[0]++; + String impl = ""; + impl += "" + interfaceType + " temp_l" + count[0] + " = new " + implType + "();\n"; + impl += "{\n"; + impl += "\tfor (Map item: " + childrenImpl[0] + ") {\n"; + impl += "\t\tif ((Integer) item.get(" + childrenImpl[1] + ") <= " + childrenImpl[2] + ") {\n"; + impl += "\t\t\ttemp_l" + count[0] + ".add(item);\n"; + impl += "\t\t}\n"; + impl += "\t}\n"; + impl += "}\n"; + sideEffect[0] = sideEffect[0] + impl; + return "temp_l" + count[0]; + } + }); + selectLE.setSignature(new Type[] {DataConstraintModel.typeList, DataConstraintModel.typeList, DataConstraintModel.typeString, DataConstraintModel.typeInt}); + selectLE.setImplOperatorType(Symbol.Type.GENERATIVE); + } + } + + public static void recoverModel(DataTransferModel model) { + Symbol floor = model.getSymbol("floor"); + if (floor != null) floor.setMemento(floorMem); + Symbol sum = model.getSymbol("sum"); + if (sum != null) sum.setMemento(sumMem); + Symbol merge = model.getSymbol("merge"); + if (merge != null) merge.setMemento(mergeMem); + Symbol extractFaceDown = model.getSymbol("extractFaceDown"); + if (extractFaceDown != null) extractFaceDown.setMemento(extractFaceDownMem); + Symbol sortByKey = model.getSymbol("sortByKey"); + if (sortByKey != null) sortByKey.setMemento(sortByKeyMem); + Symbol selectGE = model.getSymbol("selectGE"); + if (selectGE != null) selectGE.setMemento(selectGEMem); + Symbol selectLE = model.getSymbol("selectLE"); + if (selectLE != null) selectLE.setMemento(selectLEMem); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/PushPullAttribute.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/PushPullAttribute.java new file mode 100644 index 0000000..598894a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/PushPullAttribute.java @@ -0,0 +1,52 @@ +package models.dataFlowModel; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import models.EdgeAttribute; + +public class PushPullAttribute extends EdgeAttribute { + private List options; + + public PushPullAttribute() { + options = new ArrayList<>(); + } + + public PushPullAttribute(PushPullValue[] options) { + this.options = new ArrayList<>(Arrays.asList(options)); + } + + public List getOptions() { + return options; + } + + public void setOptions(List options) { + this.options = options; + } + + public void addOption(PushPullValue option) { + options.add(option); + } + + public void removeOption(PushPullValue option) { + options.remove(option); + } + + public void intersectOptions(List options) { + this.options.retainAll(options); + } + + public String[] getOptionStrings() { + String[] optionString = new String[options.size()]; + for (int i = 0; i < options.size(); i++) { + optionString[i] = options.get(i).toString(); + } + return optionString; + } + + public String toString() { + if (options == null || options.size() == 0) return ""; + return options.get(0).toString(); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/PushPullValue.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/PushPullValue.java new file mode 100644 index 0000000..28307c6 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/PushPullValue.java @@ -0,0 +1,20 @@ +package models.dataFlowModel; + +public enum PushPullValue { + PULL, + PUSHorPULL, + PUSH; + + public String toString() { + switch (this) { + case PUSHorPULL: + return "PUSH/PULL"; + case PUSH: + return "PUSH"; + case PULL: + return "PULL"; + default: + return ""; + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ResolvingMultipleDefinitionIsFutureWork.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ResolvingMultipleDefinitionIsFutureWork.java new file mode 100644 index 0000000..9444ee3 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ResolvingMultipleDefinitionIsFutureWork.java @@ -0,0 +1,7 @@ +package models.dataFlowModel; + +import models.algebra.FutureWorkException; + +public class ResolvingMultipleDefinitionIsFutureWork extends FutureWorkException { + +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ResourceNode.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ResourceNode.java new file mode 100644 index 0000000..ef286af --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ResourceNode.java @@ -0,0 +1,30 @@ +package models.dataFlowModel; + +import models.Node; +import models.dataConstraintModel.ResourcePath; + +public class ResourceNode extends Node { + protected ResourcePath resourcePath = null; + + public ResourceNode(ResourcePath resourcePath) { + this.resourcePath = resourcePath; + } + + public ResourcePath getResource() { + return resourcePath; + } + + public boolean equals(Object another) { + if (this == another) return true; + if (!(another instanceof ResourceNode)) return false; + return resourcePath.equals(((ResourceNode)another).resourcePath); + } + + public int hashCode() { + return resourcePath.hashCode(); + } + + public String toString() { + return resourcePath.getResourceName(); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ResourceNodeAttribute.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ResourceNodeAttribute.java new file mode 100644 index 0000000..a1f8615 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ResourceNodeAttribute.java @@ -0,0 +1,63 @@ +package models.dataFlowModel; + +import models.NodeAttribute; + +/************************************************************* + * + * @author k-fujii + * + */ +public class ResourceNodeAttribute extends NodeAttribute { + private ResourceNode resourceNode = null; + + /************************************************************* + * [ *constructor ] + /************************************************************* + * + */ + public ResourceNodeAttribute(ResourceNode resNode) { + this.resourceNode = resNode; + this.resourceNode.setAttribute(this); + } + + /************************************************************* + * [ *public ] + /************************************************************* + * [ getter ] + /************************************************************* + * + * @return + */ + public ResourceNode getResourceNode() { + return resourceNode; + } + + /************************************************************* + * + */ + public String getResourceName() { + return resourceNode.getResource().getResourceName(); + } + + /************************************************************* + * + * @return + */ + public String getDefaultStyle() { + String style =""; + style += "shape=ellipse;"; + style += "perimeter=ellipsePerimeter"; + + return style; + } + + + /************************************************************* + * + * @return + */ + @Override + public String toString() { + return resourceNode.getResource().getResourceName(); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/StoreAttribute.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/StoreAttribute.java new file mode 100644 index 0000000..18bd3b1 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/StoreAttribute.java @@ -0,0 +1,24 @@ +package models.dataFlowModel; + +import models.NodeAttribute; + +public class StoreAttribute extends NodeAttribute { + private boolean isNeeded = false; + private boolean isStored = false; + + public boolean isNeeded() { + return isNeeded; + } + + public void setNeeded(boolean isNeeded) { + this.isNeeded = isNeeded; + } + + public boolean isStored() { + return isStored; + } + + public void setStored(boolean isStored) { + this.isStored = isStored; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/visualModel/FormulaChannel.java b/AlgebraicDataflowArchitectureModel/src/models/visualModel/FormulaChannel.java new file mode 100644 index 0000000..9cd3e7d --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/models/visualModel/FormulaChannel.java @@ -0,0 +1,161 @@ +package models.visualModel; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import models.algebra.Expression; +import models.algebra.Symbol; +import models.algebra.Term; +import models.algebra.Variable; +import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.ResourcePath; +import models.dataConstraintModel.StateTransition; +import models.dataFlowModel.DataTransferChannel; + +public class FormulaChannel extends DataTransferChannel { + private Symbol defaultOperator = null; + private String formula = null; + private Expression formulaRhs = null; + + public FormulaChannel(String channelName, Symbol defaultOperator) { + super(channelName); + this.defaultOperator = defaultOperator; + } + + public void addChannelMemberAsInput(ChannelMember channelMember) { +// StateTransition st = new StateTransition(); +// st.setCurStateExpression(new Variable(channelMember.getIdentifierTemplate().getResourceName() + "1")); +// st.setNextStateExpression(new Variable(channelMember.getIdentifierTemplate().getResourceName() + "2")); +// channelMember.setStateTransition(st); + super.addChannelMemberAsInput(channelMember); + if (formula != null && getInputChannelMembers().size() > 1) { + formula += " " + defaultOperator + " " + channelMember.getResource().getResourceName(); + if (formulaRhs != null) { + if (formulaRhs instanceof Variable) { + Term newTerm = new Term(defaultOperator); + newTerm.addChild(formulaRhs); + newTerm.addChild(new Variable(channelMember.getResource().getResourceName()), true); + formulaRhs = newTerm; + } else if (formulaRhs instanceof Term) { + Term newTerm = new Term(defaultOperator); + newTerm.addChild(formulaRhs); + newTerm.addChild(new Variable(channelMember.getResource().getResourceName())); + formulaRhs = newTerm; + } + } + } else { + if (formula == null) formula = ""; + formula += channelMember.getResource().getResourceName(); + formulaRhs = new Variable(channelMember.getResource().getResourceName()); + } + if (formulaRhs != null) { + setFormulaTerm(formulaRhs); + } + } + + public void addChannelMemberAsOutput(ChannelMember channelMember) { +// StateTransition st = new StateTransition(); +// st.setCurStateExpression(new Variable(channelMember.getIdentifierTemplate().getResourceName() + "1")); +// channelMember.setStateTransition(st); + super.addChannelMemberAsOutput(channelMember); + if (getOutputChannelMembers().size() == 1) { + if (formula == null) formula = ""; + if (!formula.contains("==")) { + formula = channelMember.getResource().getResourceName() + " == " + formula; + } + } + if (formulaRhs != null) { + setFormulaTerm(formulaRhs); + } + } + + public Symbol getDefaultOperator() { + return defaultOperator; + } + + public void setDefaultOperator(Symbol defaultOperator) { + this.defaultOperator = defaultOperator; + } + + public void setFormulaTerm(Expression rhs) { + formulaRhs = rhs; + Collection variables; + if (rhs instanceof Variable) { + variables = new ArrayList<>(); + variables.add((Variable) rhs); + } else if (rhs instanceof Term) { + variables = ((Term) rhs).getVariables().values(); + } else { + return; + } + Map curStates = new HashMap<>(); + Map nextStates = new HashMap<>(); + Map resToNextVar = new HashMap<>(); + for (ChannelMember cm: this.getInputChannelMembers()) { + ResourcePath id = cm.getResource(); + String resName = id.getResourceName(); + Variable curVar = new Variable(resName + "1"); + Variable nextVar = new Variable(resName + "2"); + curStates.put(id, curVar); + nextStates.put(id, nextVar); + for (Variable var: variables) { + if (var.getName().equals(resName)) { + resToNextVar.put(var, nextVar); + break; + } + } + } + Symbol update = new Symbol("update"); + update.setArity(resToNextVar.keySet().size()); + for (ChannelMember cm: getInputChannelMembers()) { + ResourcePath id = cm.getResource(); + StateTransition st = new StateTransition(); + st.setCurStateExpression(curStates.get(id)); + st.setNextStateExpression(nextStates.get(id)); + Term message = new Term(update); + for (Variable var: resToNextVar.values()) { + message.addChild(var); + } + st.setMessageExpression(message); + cm.setStateTransition(st); + } + + if (rhs instanceof Variable) { + rhs = resToNextVar.get((Variable) rhs); + } else if (rhs instanceof Term) { + formulaRhs = rhs; + for (Variable var: resToNextVar.keySet()) { + rhs = ((Term) rhs).substitute(var, resToNextVar.get(var)); + } + } + for (ChannelMember cm: getOutputChannelMembers()) { + ResourcePath id = cm.getResource(); + StateTransition st = new StateTransition(); + String resName = id.getResourceName(); + Variable curVar = new Variable(resName + "1"); + st.setCurStateExpression(curVar); + st.setNextStateExpression(rhs); + Term message = new Term(update); + for (Variable var: resToNextVar.values()) { + message.addChild(var); + } + st.setMessageExpression(message); + cm.setStateTransition(st); + } + } + + public Expression getFormulaTerm() { + return formulaRhs; + } + + public void setFormula(String formula) { + this.formula = formula; + } + + public String getFormula() { + return formula; + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/parser/Parser.java b/AlgebraicDataflowArchitectureModel/src/parser/Parser.java new file mode 100644 index 0000000..080f7da --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/parser/Parser.java @@ -0,0 +1,627 @@ +package parser; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Symbol; +import models.algebra.Term; +import models.algebra.Type; +import models.algebra.Variable; +import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.JsonAccessor; +import models.dataConstraintModel.ResourcePath; +import models.dataConstraintModel.StateTransition; +import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.DataTransferChannel; +import parser.Parser.TokenStream; +import parser.exceptions.ExpectedAssignment; +import parser.exceptions.ExpectedChannel; +import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; +import parser.exceptions.ExpectedEquals; +import parser.exceptions.ExpectedInOrOutOrRefKeyword; +import parser.exceptions.ExpectedLeftCurlyBracket; +import parser.exceptions.ExpectedRHSExpression; +import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; +import parser.exceptions.WrongLHSExpression; +import parser.exceptions.WrongRHSExpression; + +public class Parser { + protected TokenStream stream; + + public static final String CHANNEL = "channel"; + public static final String INIT = "init"; + public static final String LEFT_CURLY_BRACKET = "{"; + public static final String RIGHT_CURLY_BRACKET = "}"; + public static final String LEFT_CURLY_BRACKET_REGX = "\\{"; + public static final String RIGHT_CURLY_BRACKET_REGX = "\\}"; + public static final String LEFT_BRACKET = "("; + 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 MINUS = "-"; + 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 ASSIGNMENT = "="; + public static final String COMMA = ","; + public static final String COLON = ":"; + public static final String DOT = "."; + public static final String DOT_REGX = "\\."; + + public Parser(final TokenStream stream) { + this.stream = stream; + } + + public Parser(final BufferedReader reader) { + this.stream = new TokenStream(); + try { + String line; + while ((line = reader.readLine()) != null) { + stream.addLine(line); + } + reader.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public DataTransferModel doParse() + throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedInOrOutOrRefKeyword, + ExpectedStateTransition, ExpectedEquals, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, WrongJsonExpression, ExpectedColon { + return parseDataFlowModel(); + } + + public DataTransferModel parseDataFlowModel() + throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedInOrOutOrRefKeyword, + ExpectedStateTransition, ExpectedEquals, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, WrongJsonExpression, ExpectedColon { + DataTransferModel model = new DataTransferModel(); + DataTransferChannel channel; + while ((channel = parseChannel(model)) != null) { + if (channel.getInputChannelMembers().size() == 0) { + model.addIOChannel(channel); + } else { + model.addChannel(channel); + } + } + return model; + } + + public DataTransferChannel parseChannel(DataTransferModel model) + throws + ExpectedLeftCurlyBracket, ExpectedRightBracket, ExpectedAssignment, + ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, + ExpectedChannel, ExpectedChannelName, ExpectedInOrOutOrRefKeyword, + ExpectedStateTransition, ExpectedEquals, WrongJsonExpression, ExpectedColon + { + if (!stream.hasNext()) return null; + if (stream.checkNext().equals(RIGHT_CURLY_BRACKET)) return null; + + String channelOrInitKeyword = stream.next(); + if (!channelOrInitKeyword.equals(CHANNEL)) { + if (!channelOrInitKeyword.equals(INIT)) throw new ExpectedChannel(stream.getLine()); + parseInit(model); + channelOrInitKeyword = stream.next(); + } + if (!stream.hasNext()) throw new ExpectedChannelName(stream.getLine()); + + String channelName = stream.next(); + if (channelName.equals(LEFT_CURLY_BRACKET)) throw new ExpectedChannelName(stream.getLine()); + + int fromLine = stream.getLine(); + DataTransferChannel channel = new DataTransferChannel(channelName); + String 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)) { + ChannelMember channelMember = null; + if (inOrOutOrRef.equals(IN)) { + channelMember = parseChannelMember(model, inOrOutOrRef); + if (channelMember != null) { + channel.addChannelMemberAsInput(channelMember); + } + } else if (inOrOutOrRef.equals(OUT)) { + channelMember = parseChannelMember(model, inOrOutOrRef); + if (channelMember != null) { + channel.addChannelMemberAsOutput(channelMember); + } + } else if (inOrOutOrRef.equals(REF)) { + channelMember = parseChannelMember(model, inOrOutOrRef); + if (channelMember != null) { + channel.addChannelMemberAsReference(channelMember); + } + } else { + throw new ExpectedInOrOutOrRefKeyword(stream.getLine()); + } + } + int toLine = stream.getLine(); + channel.setSourceText(stream.getSourceText(fromLine, toLine)); + return channel; + } + + public void parseInit(DataTransferModel model) + throws + ExpectedLeftCurlyBracket, ExpectedAssignment, ExpectedRHSExpression, WrongRHSExpression, ExpectedRightBracket, WrongJsonExpression, ExpectedColon + { + String leftBracket = stream.next(); + if (!leftBracket.equals(LEFT_CURLY_BRACKET)) throw new ExpectedLeftCurlyBracket(stream.getLine()); + String resourceName = null; + while (stream.hasNext() && !(resourceName = stream.next()).equals(RIGHT_CURLY_BRACKET)) { + int fromLine = stream.getLine(); + ResourcePath resource = model.getResourcePath(resourceName); + if (resource == null) { + resource = new ResourcePath(resourceName, 0); + model.addResourcePath(resource); + } + + if (!stream.hasNext()) throw new ExpectedAssignment(stream.getLine()); + String colon = stream.next(); + if (!colon.equals(COLON)) throw new ExpectedAssignment(stream.getLine()); + if (!stream.hasNext()) throw new ExpectedAssignment(stream.getLine()); + String equals = stream.next(); + if (!equals.equals(ASSIGNMENT)) throw new ExpectedAssignment(stream.getLine()); + + int toLine = stream.getLine(); + Expression rightTerm = null; + if (!stream.hasNext()) throw new ExpectedRHSExpression(stream.getLine()); + rightTerm = parseTerm(stream, model); + if (rightTerm == null) throw new WrongRHSExpression(stream.getLine()); + + resource.setInitialValue(rightTerm); + resource.setInitText(stream.getSourceText(fromLine, toLine)); + } + } + + public ChannelMember parseChannelMember(DataTransferModel model, final String inOrOutOrRef) + throws + ExpectedRightBracket, ExpectedStateTransition, ExpectedEquals, + ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, WrongJsonExpression, ExpectedColon + { + if (!stream.hasNext()) throw new ExpectedStateTransition(stream.getLine()); + Expression leftTerm = parseTerm(stream, model); + if (leftTerm == null || !(leftTerm instanceof Term)) throw new WrongLHSExpression(stream.getLine()); + Expression rightTerm = null; + + if (!inOrOutOrRef.equals(REF)) { + if (!stream.hasNext()) throw new ExpectedEquals(stream.getLine()); + String equals = stream.next(); + if (!equals.equals(EQUALS)) throw new ExpectedEquals(stream.getLine()); + + if (!stream.hasNext()) throw new ExpectedRHSExpression(stream.getLine()); + rightTerm = parseTerm(stream, model); + if (rightTerm == null) throw new WrongRHSExpression(stream.getLine()); + } + + String resourceName = ((Term) leftTerm).getSymbol().getName(); + ResourcePath resource = model.getResourcePath(resourceName); + if (resource == null) { + resource = new ResourcePath(resourceName, 0); + model.addResourcePath(resource); + } + ChannelMember channelMember = new ChannelMember(resource); + StateTransition stateTransition = new StateTransition(); + stateTransition.setCurStateExpression(((Term) leftTerm).getChild(0)); + stateTransition.setMessageExpression(((Term) leftTerm).getChild(1)); + if (!inOrOutOrRef.equals(REF)) stateTransition.setNextStateExpression(rightTerm); + channelMember.setStateTransition(stateTransition); + // for type definition + if (resource.getResourceStateType() == null && ((Term) leftTerm).getChild(0) instanceof Variable) { + Variable stateVar = (Variable) ((Term) leftTerm).getChild(0); + if (stateVar.getType() != null) { + resource.setResourceStateType(stateVar.getType()); + } + } + if (((Term) leftTerm).getChild(1) instanceof Term) { + Term messageTerm = (Term) ((Term) leftTerm).getChild(1); + if (messageTerm.getSymbol().getSignature() == null && messageTerm.getChildren().size() > 0) { + Type[] signature = new Type[messageTerm.getChildren().size() + 1]; + int i = 1; + for (Expression e: messageTerm.getChildren()) { + if (e instanceof Variable && ((Variable) e).getType() != null) { + signature[i] = ((Variable) e).getType(); + } + i++; + } + messageTerm.getSymbol().setSignature(signature); + } + } + return channelMember; + } + + public Expression parseTerm(TokenStream stream, DataTransferModel model) + throws ExpectedRightBracket, WrongJsonExpression, ExpectedColon + { + ArrayList expressions = new ArrayList<>(); + ArrayList operators = new ArrayList<>(); + String operator = null; + for (;;) { + String leftBracketOrMinus = stream.next(); + if (leftBracketOrMinus.equals(LEFT_BRACKET)) { + Expression exp = parseTerm(stream, model); + String rightBracket = stream.next(); + if (!rightBracket.equals(RIGHT_BRACKET)) throw new ExpectedRightBracket(stream.getLine()); + expressions.add(exp); + } else if (leftBracketOrMinus.equals(LEFT_CURLY_BRACKET)) { + Expression exp = parseJsonTerm(stream, model); + String rightBracket = stream.next(); + if (!rightBracket.equals(RIGHT_CURLY_BRACKET)) throw new ExpectedRightBracket(stream.getLine()); + expressions.add(exp); + } else if (leftBracketOrMinus.equals(LEFT_SQUARE_BRACKET)) { + Expression exp = parseListTerm(stream, model); + String rightBracket = stream.next(); + if (!rightBracket.equals(RIGHT_SQUARE_BRACKET)) throw new ExpectedRightBracket(stream.getLine()); + expressions.add(exp); + } else { + Symbol minus = null; + String symbolName = null; + if (leftBracketOrMinus.equals(MINUS)) { + minus = DataTransferModel.minus; // not sub + symbolName = stream.next(); + } else { + symbolName = leftBracketOrMinus; + } + Expression exp = null; + if (stream.checkNext() != null && stream.checkNext().equals(LEFT_BRACKET)) { + // a function symbol + Symbol symbol = model.getSymbol(symbolName); + if (symbol == null) { + symbol = new Symbol(symbolName); + model.addSymbol(symbol); + } + Term term = new Term(symbol); + 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()); + symbol.setArity(arity); + exp = term; + } 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); + // a numerical value + if (symbolName.contains(DOT)) { + exp = new Constant(symbolName, DataTransferModel.typeDouble); + } else { + exp = new Constant(symbolName, DataTransferModel.typeInt); + } + } else { + // a variable + exp = parseVariable(stream, model, symbolName); + } + } + } + if (minus != null) { + Term minusTerm = new Term(minus); + minusTerm.addChild(exp); + expressions.add(minusTerm); + } else { + expressions.add(exp); + } + } + operator = stream.checkNext(); + 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(DOT)) { + // json accessor + Expression exp = expressions.remove(0); + stream.next(); // DOT + if (stream.checkNext() == null) throw new WrongJsonExpression(stream.getLine()); + String literalOrLeftCurlyBracket = stream.next(); + Expression paramTerm = null; + if (literalOrLeftCurlyBracket.equals(LEFT_CURLY_BRACKET)) { + // parameter + paramTerm = parseTerm(stream, model); + String rightCurlyBracket = stream.next(); + if (rightCurlyBracket == null || !rightCurlyBracket.equals(RIGHT_CURLY_BRACKET)) throw new WrongJsonExpression(stream.getLine()); + } else { + // literal + paramTerm = new Constant(literalOrLeftCurlyBracket, DataTransferModel.typeString); + } + Type paramType = null; + if (paramTerm instanceof Variable) { + paramType = ((Variable) paramTerm).getType(); + } else if (paramTerm instanceof Term) { + paramType = ((Term) paramTerm).getType(); + } + Term term = null; + if (paramType != null && DataConstraintModel.typeInt.isAncestorOf(paramType)) { + term = new JsonAccessor(DataConstraintModel.dotParam); + } else { + term = new JsonAccessor(DataConstraintModel.dot); + } + term.addChild(exp); + term.addChild(paramTerm); + expressions.add(term); + operator = stream.checkNext(); + if (operator == null || !operator.equals(DOT)) break; + } else { + break; + } + } + if (expressions.size() == 1) { + // no arithmetic operators + return expressions.get(0); + } + ArrayList monomials = new ArrayList<>(); + ArrayList addSubs = new ArrayList<>(); + Expression first = expressions.get(0); + int i = 1; + for (Symbol op: operators) { + Expression second = expressions.get(i); + if (op.getName().equals(MUL) || op.getName().equals(DIV)) { + Term term = new Term(op); + term.addChild(first); + term.addChild(second); + first = term; + } else { + // add or sub ==> new monomial + monomials.add(first); + addSubs.add(op); + first = second; + } + i++; + } + if (first != null) monomials.add(first); + Expression firstMonomial = monomials.get(0); + i = 1; + for (Symbol op: addSubs) { + Expression secondMonomial = monomials.get(i); + Term term = new Term(op); + term.addChild(firstMonomial); + term.addChild(secondMonomial); + firstMonomial = term; + i++; + } + return firstMonomial; + } + + private Expression parseJsonTerm(TokenStream stream, DataTransferModel model) throws ExpectedRightBracket, WrongJsonExpression, ExpectedColon { + Term jsonTerm = new Constant(DataConstraintModel.nil); + jsonTerm.setType(DataConstraintModel.typeJson); + while (stream.checkNext() != null && !stream.checkNext().equals(RIGHT_CURLY_BRACKET)) { + String key = stream.next(); + Constant keyExp = new Constant(key); + keyExp.setType(DataConstraintModel.typeString); + if (stream.checkNext() == null || !stream.checkNext().equals(COLON)) throw new ExpectedColon(stream.getLine()); + String colon = stream.next(); + Expression value = parseTerm(stream, model); + Term nextTerm = new Term(DataConstraintModel.addMember); + nextTerm.addChild(jsonTerm); + nextTerm.addChild(keyExp); + nextTerm.addChild(value); + jsonTerm = nextTerm; + if (stream.checkNext() == null || !stream.checkNext().equals(COMMA)) break; + String comma = stream.next(); + } + return jsonTerm; + } + + private Expression parseListTerm(TokenStream stream2, DataTransferModel model) throws ExpectedRightBracket, WrongJsonExpression, ExpectedColon { + Term listTerm = new Constant(DataConstraintModel.nil); + listTerm.setType(DataConstraintModel.typeList); + while (stream.checkNext() != null && !stream.checkNext().equals(RIGHT_SQUARE_BRACKET)) { + Expression element = parseTerm(stream, model); + Term nextTerm = new Term(DataConstraintModel.cons); + nextTerm.addChild(element); + nextTerm.addChild(listTerm); + listTerm = nextTerm; + if (stream.checkNext() == null || !stream.checkNext().equals(COMMA)) break; + String comma = stream.next(); + } + return listTerm; + } + + private Variable parseVariable(TokenStream stream, DataTransferModel model, String symbolName) { + Variable var; + if (stream.checkNext() != null && stream.checkNext().equals(COLON)) { + // when a type is specified. + stream.next(); + String typeName = stream.next(); + Type type = model.getType(typeName); + if (type == null) { + type = new Type(typeName, typeName); + } + var = new Variable(symbolName, type); + } else { + var = new Variable(symbolName); + } + return var; + } + + /**-------------------------------------------------------------------------------- + * [protected] + /**-------------------------------------------------------------------------------- + * checking the token has a token. + * + * @param token + * @param specificTokenName + */ + protected Boolean isMatchKeyword(final String token, final String specificTokenName) { + if(token == null) return false; + if(specificTokenName == null) return false; + return token.equals(specificTokenName); + } + + /**-------------------------------------------------------------------------------- + * [inner class] + * "TokenStream" has a token what is read from description of "Architecture Language Model". + */ + public static class TokenStream { + private ArrayList> tokens = new ArrayList<>(); + private ArrayList lines = new ArrayList<>(); + private int line = 0; + private int n = 0; + + public TokenStream() { + line = 0; + n = 0; + } + + public void addLine(String line) { + lines.add(line); + line = line.trim(); + tokens.add( + splitBy( + splitBy( + splitBy( + splitBy( + splitBy( + splitBy( + splitBy( + splitBy( + splitBy( + splitBy( + splitBy( + splitBy( + splitBy( + splitBy( + Arrays.asList(line.split("[ \t]")), + ADD, + ADD_REGX), + MUL, + MUL_REGX), + SUB, + SUB_REGX), + DIV, + DIV_REGX), + DOT, + DOT_REGX), + COMMA, + COMMA), + COLON, + COLON), + LEFT_BRACKET, + LEFT_BRACKET_REGX), + RIGHT_BRACKET, + RIGHT_BRACKET_REGX), + EQUALS, + EQUALS), + LEFT_CURLY_BRACKET, + LEFT_CURLY_BRACKET_REGX), + RIGHT_CURLY_BRACKET, + RIGHT_CURLY_BRACKET_REGX), + LEFT_SQUARE_BRACKET, + LEFT_SQUARE_BRACKET_REGX), + RIGHT_SQUARE_BRACKET, + RIGHT_SQUARE_BRACKET_REGX)); + } + + private ArrayList splitBy(final List tokens, final String delimiter, final String delimiterRegx) { + 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); + } + if (t.length() > 0) { + newTokens.add(t); + } + fFirstToken = false; + } + while (token.endsWith(delimiter)) { + newTokens.add(delimiter); + token = token.substring(0, token.length() - 1); + } + } + return newTokens; + } + + public String next() { + if (line >= tokens.size()) return null; + while (n >= tokens.get(line).size()) { + line++; + n = 0; + if (line >= tokens.size()) return null; + } + String token = tokens.get(line).get(n); + n++; + return token; + } + + public String checkNext() { + if (line >= tokens.size()) return null; + while (n >= tokens.get(line).size()) { + line++; + n = 0; + if (line >= tokens.size()) return null; + } + return tokens.get(line).get(n); + } + + public boolean hasNext() { + if (line >= tokens.size()) return false; + while (n >= tokens.get(line).size()) { + line++; + n = 0; + if (line >= tokens.size()) return false; + } + return true; + } + + public int getLine() { + return line; + } + + public String getSourceText(int from, int to) { + String text = ""; + for (int l = from; l <= to; l++) { + text += lines.get(l) + "\n"; + } + return text; + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/parser/ParserDTRAM.java b/AlgebraicDataflowArchitectureModel/src/parser/ParserDTRAM.java new file mode 100644 index 0000000..2369375 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/parser/ParserDTRAM.java @@ -0,0 +1,199 @@ +package parser; + +import java.io.BufferedReader; +import java.io.IOException; + +import com.mxgraph.model.mxCell; +import com.mxgraph.model.mxGeometry; +import com.mxgraph.model.mxIGraphModel; +import com.mxgraph.view.mxCellState; +import com.mxgraph.view.mxGraph; +import com.mxgraph.view.mxGraphView; + +import application.editor.Stage; +import models.dataFlowModel.DataTransferModel; +import parser.exceptions.ExpectedAssignment; +import parser.exceptions.ExpectedChannel; +import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; +import parser.exceptions.ExpectedEquals; +import parser.exceptions.ExpectedFormulaChannel; +import parser.exceptions.ExpectedGeometry; +import parser.exceptions.ExpectedInOrOutOrRefKeyword; +import parser.exceptions.ExpectedIoChannel; +import parser.exceptions.ExpectedLeftCurlyBracket; +import parser.exceptions.ExpectedModel; +import parser.exceptions.ExpectedNode; +import parser.exceptions.ExpectedRHSExpression; +import parser.exceptions.ExpectedResource; +import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; +import parser.exceptions.WrongLHSExpression; +import parser.exceptions.WrongRHSExpression; + +public class ParserDTRAM extends Parser { + + private static final String MODEL_GROUP = "model"; + private static final String GEOMETRY_GROUP = "geometry"; + private static final String GEOMETORY_NODE = "node"; + private static final String RESOURCE_NODE = "r"; + private static final String CHANNEL_NODE = "c"; + private static final String FORMULA_CHANNEL_NODE = "fc"; + private static final String IO_CHANNEL_NODE = "ioc"; + + /**-------------------------------------------------------------------------------- + * [Constructor] + /**-------------------------------------------------------------------------------- + * + * @param stream + */ + public ParserDTRAM(final TokenStream stream) { + super(stream); + } + /**-------------------------------------------------------------------------------- + * + * @param reader + */ + public ParserDTRAM(final BufferedReader reader) { + super(reader); + } + + /**-------------------------------------------------------------------------------- + * [public] + /**-------------------------------------------------------------------------------- + * + * @param reader + * @throws WrongJsonExpression + * @throws ExpectedColon + */ + public DataTransferModel doParseModel() + throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedInOrOutOrRefKeyword, ExpectedStateTransition, ExpectedEquals, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, ExpectedModel, ExpectedGeometry, WrongJsonExpression, ExpectedColon { + DataTransferModel model = getParsedModel(); + return model; + } + + /**-------------------------------------------------------------------------------- + * + * @param graph + */ + public void doParseGeometry(mxGraph graph) + throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedInOrOutOrRefKeyword, ExpectedStateTransition, ExpectedEquals, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, ExpectedModel, ExpectedGeometry, ExpectedNode, ExpectedResource, ExpectedFormulaChannel, ExpectedIoChannel{ + + parseGeometry(graph); + } + + /**-------------------------------------------------------------------------------- + * [private] + /**-------------------------------------------------------------------------------- + * + * @param stream + * @throws WrongJsonExpression + * @throws ExpectedColon + */ + private DataTransferModel getParsedModel() + throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedInOrOutOrRefKeyword, ExpectedStateTransition, ExpectedEquals, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, ExpectedModel, ExpectedGeometry, WrongJsonExpression, ExpectedColon { + + if (!stream.hasNext()) throw new NullPointerException(); + + String modelKeyword = stream.next(); + if (!modelKeyword.equals(MODEL_GROUP)) throw new ExpectedModel(stream.getLine()); + if (!stream.hasNext()) throw new ExpectedModel(stream.getLine()); + + String leftBracket = stream.next(); + if (!leftBracket.equals(LEFT_CURLY_BRACKET)) throw new ExpectedLeftCurlyBracket(stream.getLine()); + + DataTransferModel model = parseDataFlowModel(); + + String rightBracket = stream.next(); + if(!rightBracket.equals(RIGHT_CURLY_BRACKET))throw new ExpectedRightBracket(stream.getLine()); + + return model; + } + + /**-------------------------------------------------------------------------------- + * change graph's geometries from "DTRAM" file. + * @param stream + * @param graph + */ + private void parseGeometry(mxGraph graph) + throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedInOrOutOrRefKeyword, ExpectedStateTransition, ExpectedEquals, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment,ExpectedModel, ExpectedGeometry, ExpectedNode, ExpectedResource, ExpectedFormulaChannel, ExpectedIoChannel { + + if (!isMatchKeyword(stream.next(), GEOMETRY_GROUP)) throw new ExpectedGeometry(stream.getLine()); + + if (!isMatchKeyword(stream.next(), LEFT_CURLY_BRACKET)) throw new ExpectedLeftCurlyBracket(stream.getLine()); + + String node = stream.next(); + while (node.equals(GEOMETORY_NODE)) { + + String rOrFcOrIocOrC = stream.next(); + if (!rOrFcOrIocOrC.equals(RESOURCE_NODE) + && !rOrFcOrIocOrC.equals(FORMULA_CHANNEL_NODE) + && !rOrFcOrIocOrC.equals(CHANNEL_NODE) + && !rOrFcOrIocOrC.equals(IO_CHANNEL_NODE)) + throw new ExpectedNode(stream.getLine()); + + String name = stream.next(); + + if (!isMatchKeyword(stream.next(), COLON)) throw new ExpectedAssignment(stream.getLine()); + + 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()); + + String y = stream.next(); + int yC = Integer.parseInt(y); + + if (!isMatchKeyword(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()); + + String h = stream.next(); + int hC = Integer.parseInt(h); + + mxIGraphModel graphModel = graph.getModel(); + mxCell root = (mxCell) graph.getDefaultParent(); + mxCell nodeLayer = (mxCell) root.getChildAt(Stage.NODE_LAYER); + mxCell dataFlowLayer = (mxCell) root.getChildAt(Stage.DATA_FLOW_LAYER); + for (int i = 0; i < graph.getModel().getChildCount(nodeLayer); i++) { + + Object cell = graph.getModel().getChildAt(nodeLayer, i); + if (!graph.getModel().isVertex(cell)) continue; + + mxGeometry geom = (mxGeometry) ((mxCell) cell).getGeometry().clone(); + mxGraphView view = graph.getView(); + mxCellState state = view.getState(cell); + + if (!name.equals(state.getLabel())) continue; + + geom.setX(xC); + geom.setY(yC); + graphModel.setGeometry(cell, geom); + } + for (int i = 0; i < graph.getModel().getChildCount(dataFlowLayer); i++) { + + Object cell = graph.getModel().getChildAt(dataFlowLayer, i); + if (!graph.getModel().isVertex(cell)) continue; + + mxGeometry geom = (mxGeometry) ((mxCell) cell).getGeometry().clone(); + mxGraphView view = graph.getView(); + mxCellState state = view.getState(cell); + + if (!name.equals(state.getLabel())) continue; + + geom.setX(xC); + geom.setY(yC); + graphModel.setGeometry(cell, geom); + } + node = stream.next(); + } + + if (!node.equals(RIGHT_CURLY_BRACKET)) throw new ExpectedRightBracket(stream.getLine()); + } +} + + diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedAssignment.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedAssignment.java new file mode 100644 index 0000000..85385ff --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedAssignment.java @@ -0,0 +1,8 @@ +package parser.exceptions; + +public class ExpectedAssignment extends ParseException { + + public ExpectedAssignment(int line) { + super(line); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedChannel.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedChannel.java new file mode 100644 index 0000000..385248a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedChannel.java @@ -0,0 +1,9 @@ +package parser.exceptions; + +public class ExpectedChannel extends ParseException { + + public ExpectedChannel(int line) { + super(line); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedChannelName.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedChannelName.java new file mode 100644 index 0000000..72d0336 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedChannelName.java @@ -0,0 +1,9 @@ +package parser.exceptions; + +public class ExpectedChannelName extends ParseException { + + public ExpectedChannelName(int line) { + super(line); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedColon.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedColon.java new file mode 100644 index 0000000..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/ExpectedEquals.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedEquals.java new file mode 100644 index 0000000..ed322d8 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedEquals.java @@ -0,0 +1,9 @@ +package parser.exceptions; + +public class ExpectedEquals extends ParseException { + + public ExpectedEquals(int line) { + super(line); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedFormulaChannel.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedFormulaChannel.java new file mode 100644 index 0000000..e1e9f2b --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedFormulaChannel.java @@ -0,0 +1,9 @@ +package parser.exceptions; + +public class ExpectedFormulaChannel extends ParseException { + + public ExpectedFormulaChannel(int line) { + super(line); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedGeometry.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedGeometry.java new file mode 100644 index 0000000..1521507 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedGeometry.java @@ -0,0 +1,9 @@ +package parser.exceptions; + +public class ExpectedGeometry extends ParseException { + + public ExpectedGeometry(int line) { + super(line); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedInOrOutOrRefKeyword.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedInOrOutOrRefKeyword.java new file mode 100644 index 0000000..a44a604 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedInOrOutOrRefKeyword.java @@ -0,0 +1,9 @@ +package parser.exceptions; + +public class ExpectedInOrOutOrRefKeyword extends ParseException { + + public ExpectedInOrOutOrRefKeyword(int line) { + super(line); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedIoChannel.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedIoChannel.java new file mode 100644 index 0000000..fba4cc7 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedIoChannel.java @@ -0,0 +1,9 @@ +package parser.exceptions; + +public class ExpectedIoChannel extends ParseException { + + public ExpectedIoChannel(int line) { + super(line); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedLeftCurlyBracket.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedLeftCurlyBracket.java new file mode 100644 index 0000000..30f92b0 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedLeftCurlyBracket.java @@ -0,0 +1,9 @@ +package parser.exceptions; + +public class ExpectedLeftCurlyBracket extends ParseException { + + public ExpectedLeftCurlyBracket(int line) { + super(line); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedModel.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedModel.java new file mode 100644 index 0000000..222522a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedModel.java @@ -0,0 +1,9 @@ +package parser.exceptions; + +public class ExpectedModel extends ParseException { + + public ExpectedModel(int line) { + super(line); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedNode.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedNode.java new file mode 100644 index 0000000..b3af50c --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedNode.java @@ -0,0 +1,9 @@ +package parser.exceptions; + +public class ExpectedNode extends ParseException { + + public ExpectedNode(int line) { + super(line); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedRHSExpression.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedRHSExpression.java new file mode 100644 index 0000000..994bf77 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedRHSExpression.java @@ -0,0 +1,9 @@ +package parser.exceptions; + +public class ExpectedRHSExpression extends ParseException { + + public ExpectedRHSExpression(int line) { + super(line); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedResource.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedResource.java new file mode 100644 index 0000000..dd5f00a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedResource.java @@ -0,0 +1,9 @@ +package parser.exceptions; + +public class ExpectedResource extends ParseException { + + public ExpectedResource(int line) { + super(line); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedRightBracket.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedRightBracket.java new file mode 100644 index 0000000..627c57b --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedRightBracket.java @@ -0,0 +1,9 @@ +package parser.exceptions; + +public class ExpectedRightBracket extends ParseException { + + public ExpectedRightBracket(int line) { + super(line); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedStateTransition.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedStateTransition.java new file mode 100644 index 0000000..02d27b4 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ExpectedStateTransition.java @@ -0,0 +1,9 @@ +package parser.exceptions; + +public class ExpectedStateTransition extends ParseException { + + public ExpectedStateTransition(int line) { + super(line); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ParseException.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ParseException.java new file mode 100644 index 0000000..be5aade --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/ParseException.java @@ -0,0 +1,10 @@ +package parser.exceptions; + +public class ParseException extends Exception { + protected int line; + + public ParseException(int line) { + super("at line " + (line + 1)); + this.line = 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/WrongLHSExpression.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/WrongLHSExpression.java new file mode 100644 index 0000000..0ed087a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/WrongLHSExpression.java @@ -0,0 +1,9 @@ +package parser.exceptions; + +public class WrongLHSExpression extends ParseException { + + public WrongLHSExpression(int line) { + super(line); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/WrongRHSExpression.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/WrongRHSExpression.java new file mode 100644 index 0000000..0a82a6c --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/WrongRHSExpression.java @@ -0,0 +1,9 @@ +package parser.exceptions; + +public class WrongRHSExpression extends ParseException { + + public WrongRHSExpression(int line) { + super(line); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/webService/ExpectedBaseURL.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/webService/ExpectedBaseURL.java new file mode 100644 index 0000000..ac4f008 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/webService/ExpectedBaseURL.java @@ -0,0 +1,11 @@ +package parser.exceptions.webService; + +import parser.exceptions.ParseException; + +public class ExpectedBaseURL extends ParseException{ + + public ExpectedBaseURL(int line) { + super(line); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/webService/ExpectedResources.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/webService/ExpectedResources.java new file mode 100644 index 0000000..d7e655f --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/webService/ExpectedResources.java @@ -0,0 +1,11 @@ +package parser.exceptions.webService; + +import parser.exceptions.ParseException; + +public class ExpectedResources extends ParseException{ + + public ExpectedResources(int line) { + super(line); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/parser/exceptions/webService/ExpectedWebService.java b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/webService/ExpectedWebService.java new file mode 100644 index 0000000..7a1b51b --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/parser/exceptions/webService/ExpectedWebService.java @@ -0,0 +1,11 @@ +package parser.exceptions.webService; + +import parser.exceptions.ParseException; + +public class ExpectedWebService extends ParseException{ + + public ExpectedWebService(int line) { + super(line); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/tests/CodeGeneratorTest.java b/AlgebraicDataflowArchitectureModel/src/tests/CodeGeneratorTest.java new file mode 100644 index 0000000..f439501 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/tests/CodeGeneratorTest.java @@ -0,0 +1,53 @@ +package tests; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.util.ArrayList; + +import algorithms.*; +import code.ast.CompilationUnit; +import code.ast.TypeDeclaration; +import generators.DataTransferMethodAnalyzer; +import generators.JavaCodeGenerator; +import generators.JavaMethodBodyGenerator; +import models.dataFlowModel.*; +import parser.*; +import parser.exceptions.ExpectedAssignment; +import parser.exceptions.ExpectedChannel; +import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; +import parser.exceptions.ExpectedEquals; +import parser.exceptions.ExpectedInOrOutOrRefKeyword; +import parser.exceptions.ExpectedLeftCurlyBracket; +import parser.exceptions.ExpectedRHSExpression; +import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; +import parser.exceptions.WrongLHSExpression; +import parser.exceptions.WrongRHSExpression; + +public class CodeGeneratorTest { + public static void main(String[] args) { + File file = new File("models/POS.model"); + try { + Parser parser = new Parser(new BufferedReader(new FileReader(file))); + DataTransferModel model; + try { + model = parser.doParse(); + DataFlowGraph graph = DataTransferModelAnalyzer.createDataFlowGraphWithStateStoringAttribute(model); + DataTransferModelAnalyzer.annotateWithSelectableDataTransferAttiribute(graph); + DataTransferMethodAnalyzer.decideToStoreResourceStates(graph); + ArrayList codetree = JavaMethodBodyGenerator.doGenerate(graph, model, JavaCodeGenerator.doGenerate(graph, model)); + System.out.println(codetree); + } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | WrongJsonExpression | ExpectedColon e) { + e.printStackTrace(); + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/tests/DataConstraintModelTest.java b/AlgebraicDataflowArchitectureModel/src/tests/DataConstraintModelTest.java new file mode 100644 index 0000000..e621180 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/tests/DataConstraintModelTest.java @@ -0,0 +1,59 @@ +package tests; + +import static org.junit.Assert.*; + +import org.junit.Test; + +import models.dataConstraintModel.*; + +public class DataConstraintModelTest { + + @Test + public void test() { + // Construct a data constraint architecture model. + DataConstraintModel model = new DataConstraintModel(); + ResourcePath customer_off = new ResourcePath("customers.{customer_id}.off", 1); + ResourcePath customer_add = new ResourcePath("customers.{customer_id}.add", 1); + ResourcePath company_add = new ResourcePath("companies.{company_id}.add", 1); + + Channel gin_1 = new Channel("gin_1"); // set customer's office +// GroupSelector x1 = new GroupSelector(); + 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()); + + Channel gin_2 = new Channel("gin_2"); // set companie's address +// GroupSelector x2 = new GroupSelector(); + 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()); + + Channel g = new Channel("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); +// 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()); + + 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()); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/tests/DataFlowModelTest.java b/AlgebraicDataflowArchitectureModel/src/tests/DataFlowModelTest.java new file mode 100644 index 0000000..cea2cee --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/tests/DataFlowModelTest.java @@ -0,0 +1,88 @@ +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 new file mode 100644 index 0000000..e4799a2 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/tests/DataStorageDecisionTest.java @@ -0,0 +1,51 @@ +package tests; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; + +import algorithms.*; +import generators.DataTransferMethodAnalyzer; +import models.Node; +import models.dataFlowModel.*; +import parser.*; +import parser.exceptions.ExpectedAssignment; +import parser.exceptions.ExpectedChannel; +import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; +import parser.exceptions.ExpectedEquals; +import parser.exceptions.ExpectedInOrOutOrRefKeyword; +import parser.exceptions.ExpectedLeftCurlyBracket; +import parser.exceptions.ExpectedRHSExpression; +import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; +import parser.exceptions.WrongLHSExpression; +import parser.exceptions.WrongRHSExpression; + +public class DataStorageDecisionTest { + public static void main(String[] args) { + File file = new File("models/POS2.model"); + try { + Parser parser = new Parser(new BufferedReader(new FileReader(file))); + DataTransferModel model = null; + try { + model = parser.doParse(); + System.out.println(model); + 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()); + } + } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | WrongJsonExpression | ExpectedColon e) { + e.printStackTrace(); + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/tests/DataStorageNecessityTest.java b/AlgebraicDataflowArchitectureModel/src/tests/DataStorageNecessityTest.java new file mode 100644 index 0000000..75cde4f --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/tests/DataStorageNecessityTest.java @@ -0,0 +1,51 @@ +package tests; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; + +import algorithms.DataTransferModelAnalyzer; +import models.Node; +import models.dataFlowModel.*; +import parser.Parser; +import parser.exceptions.ExpectedAssignment; +import parser.exceptions.ExpectedChannel; +import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; +import parser.exceptions.ExpectedEquals; +import parser.exceptions.ExpectedInOrOutOrRefKeyword; +import parser.exceptions.ExpectedLeftCurlyBracket; +import parser.exceptions.ExpectedRHSExpression; +import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; +import parser.exceptions.WrongLHSExpression; +import parser.exceptions.WrongRHSExpression; + +public class DataStorageNecessityTest { + public static void main(String[] args) { + File file = new File("models/POS2.model"); + try { + Parser parser = new Parser(new BufferedReader(new FileReader(file))); + DataTransferModel model; + try { + model = parser.doParse(); + System.out.println(model); + DataFlowGraph graph = DataTransferModelAnalyzer.createDataFlowGraphWithStateStoringAttribute(model); + for (Node n:graph.getNodes()) { + ResourceNode resource = (ResourceNode)n; + if((StoreAttribute)resource.getAttribute() != null) { + System.out.println(resource.toString() + ":" + ((StoreAttribute)resource.getAttribute()).isNeeded()); + } + } + } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | WrongJsonExpression | ExpectedColon e) { + e.printStackTrace(); + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/tests/DirectedGraphTest.java b/AlgebraicDataflowArchitectureModel/src/tests/DirectedGraphTest.java new file mode 100644 index 0000000..4f6db8e --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/tests/DirectedGraphTest.java @@ -0,0 +1,60 @@ +package tests; + +import static org.junit.Assert.*; + +import org.junit.Test; + +import models.*; + +public class DirectedGraphTest { + + @Test + public void test() { + // Build a directed graph. + DirectedGraph g = new DirectedGraph(); + Node n1 = new Node(); + Node n2 = new Node(); + Node n3 = new Node(); + Node n4 = new Node(); + Edge e1 = new Edge(n1, n2); + Edge e2 = new Edge(n2, n3); + Edge e3 = new Edge(n3, n4); + Edge e4 = new Edge(n4, n1); + Edge e5 = new Edge(n1, n3); + g.addEdge(e1); + g.addEdge(e2); + g.addEdge(e3); + g.addEdge(e4); + g.addEdge(e5); + // Check the number of nodes. + assertEquals(4, g.getNodes().size()); + // Check the number of edges. + assertEquals(5, g.getEdges().size()); + // Check indegrees and outdegrees. + assertEquals(1, n1.getIndegree()); + assertEquals(2, n1.getOutdegree()); + assertEquals(1, n2.getIndegree()); + assertEquals(1, n2.getOutdegree()); + assertEquals(2, n3.getIndegree()); + assertEquals(1, n3.getOutdegree()); + assertEquals(1, n4.getIndegree()); + assertEquals(1, n4.getOutdegree()); + + // Remove an edge. + g.removeEdge(e5); + // Re-check the number of nodes. + assertEquals(4, g.getNodes().size()); + // Re-check the number of edges. + assertEquals(4, g.getEdges().size()); + // Re-check indegrees and outdegrees. + assertEquals(1, n1.getIndegree()); + assertEquals(1, n1.getOutdegree()); + assertEquals(1, n2.getIndegree()); + assertEquals(1, n2.getOutdegree()); + assertEquals(1, n3.getIndegree()); + assertEquals(1, n3.getOutdegree()); + assertEquals(1, n4.getIndegree()); + assertEquals(1, n4.getOutdegree()); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/tests/EdgeTransitionSelectableTest.java b/AlgebraicDataflowArchitectureModel/src/tests/EdgeTransitionSelectableTest.java new file mode 100644 index 0000000..150b1b6 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/tests/EdgeTransitionSelectableTest.java @@ -0,0 +1,50 @@ +package tests; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; + +import algorithms.*; +import models.Edge; +import models.dataFlowModel.*; +import parser.*; +import parser.exceptions.ExpectedAssignment; +import parser.exceptions.ExpectedChannel; +import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; +import parser.exceptions.ExpectedEquals; +import parser.exceptions.ExpectedInOrOutOrRefKeyword; +import parser.exceptions.ExpectedLeftCurlyBracket; +import parser.exceptions.ExpectedRHSExpression; +import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; +import parser.exceptions.WrongLHSExpression; +import parser.exceptions.WrongRHSExpression; + +public class EdgeTransitionSelectableTest { + public static void main(String[] args) { + File file = new File("models/POS2.model"); + try { + Parser parser = new Parser(new BufferedReader(new FileReader(file))); + DataTransferModel model; + try { + model = parser.doParse(); + System.out.println(model); + DataFlowGraph graph = DataTransferModelAnalyzer.createDataFlowGraphWithStateStoringAttribute(model); + DataTransferModelAnalyzer.annotateWithSelectableDataTransferAttiribute(graph); + for(Edge e:graph.getEdges()) { + DataFlowEdge re = (DataFlowEdge) e; + System.out.println(re.getSource() + "-" + re.getDestination() + ":" + ((PushPullAttribute)(re.getAttribute())).getOptions()); + } + } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | WrongJsonExpression | ExpectedColon e) { + e.printStackTrace(); + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/tests/FormulaChannelTest.java b/AlgebraicDataflowArchitectureModel/src/tests/FormulaChannelTest.java new file mode 100644 index 0000000..7c805ef --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/tests/FormulaChannelTest.java @@ -0,0 +1,55 @@ +package tests; + +import org.junit.Test; + +import models.algebra.Symbol; +import models.dataConstraintModel.ChannelMember; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.ResourcePath; +import models.visualModel.FormulaChannel; + +public class FormulaChannelTest { + + @Test + public void test() { + ResourcePath id1 = new ResourcePath("r1", 0); + ResourcePath id2 = new ResourcePath("r2", 0); + ResourcePath id3 = new ResourcePath("r3", 0); + + 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)); + System.out.println(ch1.getFormula()); + System.out.println(ch1.getFormulaTerm()); + System.out.println(ch1.getSourceText()); + ch1.addChannelMemberAsInput(new ChannelMember(id2)); + System.out.println(ch1.getFormula()); + System.out.println(ch1.getFormulaTerm()); + System.out.println(ch1.getSourceText()); + ch1.addChannelMemberAsOutput(new ChannelMember(id3)); + System.out.println(ch1.getFormula()); + System.out.println(ch1.getFormulaTerm()); + System.out.println(ch1.getSourceText()); + + FormulaChannel ch2 = new FormulaChannel("ch2", DataConstraintModel.mul); + System.out.println(ch2.getFormula()); + System.out.println(ch2.getFormulaTerm()); + System.out.println(ch2.getSourceText()); + ch2.addChannelMemberAsOutput(new ChannelMember(id3)); + System.out.println(ch2.getFormula()); + System.out.println(ch2.getFormulaTerm()); + System.out.println(ch2.getSourceText()); + ch2.addChannelMemberAsInput(new ChannelMember(id1)); + System.out.println(ch2.getFormula()); + System.out.println(ch2.getFormulaTerm()); + System.out.println(ch2.getSourceText()); + ch2.addChannelMemberAsInput(new ChannelMember(id2)); + System.out.println(ch2.getFormula()); + System.out.println(ch2.getFormulaTerm()); + System.out.println(ch2.getSourceText()); + + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/tests/GraphicalViewer.java b/AlgebraicDataflowArchitectureModel/src/tests/GraphicalViewer.java new file mode 100644 index 0000000..5a2480b --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/tests/GraphicalViewer.java @@ -0,0 +1,260 @@ +package tests; + + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.EventObject; +import java.util.Map; + +import javax.swing.BorderFactory; +import javax.swing.CellRendererPane; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JScrollPane; +import javax.swing.border.BevelBorder; + +import com.mxgraph.canvas.mxICanvas; +import com.mxgraph.canvas.mxImageCanvas; +import com.mxgraph.model.mxCell; +import com.mxgraph.model.mxGeometry; +import com.mxgraph.model.mxIGraphModel; +import com.mxgraph.shape.mxITextShape; +import com.mxgraph.swing.mxGraphComponent; +import com.mxgraph.swing.handler.mxRubberband; +import com.mxgraph.swing.view.mxCellEditor; +import com.mxgraph.swing.view.mxICellEditor; +import com.mxgraph.swing.view.mxInteractiveCanvas; +import com.mxgraph.util.mxConstants; +import com.mxgraph.util.mxPoint; +import com.mxgraph.util.mxUtils; +import com.mxgraph.view.mxCellState; +import com.mxgraph.view.mxGraph; + +public class GraphicalViewer extends JFrame +{ + + /** + * + */ + private static final long serialVersionUID = -844106998814982739L; + + final int PORT_DIAMETER = 8; + final int PORT_RADIUS = PORT_DIAMETER / 2; + + public GraphicalViewer() + { + super("Graphical Viewer"); + + // Demonstrates the use of a Swing component for rendering vertices. + // Note: Use the heavyweight feature to allow for event handling in + // the Swing component that is used for rendering the vertex. + + mxGraph graph = new mxGraph() { + public boolean isPort(Object cell) { + mxGeometry geo = getCellGeometry(cell); + + return (geo != null) ? geo.isRelative() : false; + } + + public boolean isCellFoldable(Object cell, boolean collapse) { + return false; + } + }; + + Object parent = graph.getDefaultParent(); + + graph.getModel().beginUpdate(); + try { + mxGeometry geo1 = new mxGeometry(0, 0.5, PORT_DIAMETER, PORT_DIAMETER); + geo1.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS)); + geo1.setRelative(true); + + mxGeometry geo2 = new mxGeometry(1.0, 0.5, PORT_DIAMETER, PORT_DIAMETER); + geo2.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS)); + geo2.setRelative(true); + + Object c1 = graph.insertVertex(parent, null, "c1", 150, 20, 30, 30); + mxCell c1_in = new mxCell(null, geo1, "shape=ellipse;perimter=ellipsePerimeter"); + c1_in.setVertex(true); + graph.addCell(c1_in, c1); + mxCell c1_out = new mxCell(null, geo2, "shape=ellipse;perimter=ellipsePerimeter"); + c1_out.setVertex(true); + graph.addCell(c1_out, c1); + + Object c2 = graph.insertVertex(parent, null, "c2", 150, 100, 30, 30); + mxCell c2_in = new mxCell(null, geo1, "shape=ellipse;perimter=ellipsePerimeter"); + c2_in.setVertex(true); + graph.addCell(c2_in, c2); + mxCell c2_out = new mxCell(null, geo2, "shape=ellipse;perimter=ellipsePerimeter"); + c2_out.setVertex(true); + graph.addCell(c2_out, c2); + + Object payment = graph.insertVertex(parent, null, "payment", 20, 20, 80, 30, "shape=ellipse;perimeter=ellipsePerimeter"); + Object points = graph.insertVertex(parent, null, "points", 240, 20, 80, 30, "shape=ellipse;perimeter=ellipsePerimeter"); + Object history = graph.insertVertex(parent, null, "history", 240, 150, 80, 30, "shape=ellipse;perimeter=ellipsePerimeter"); + + graph.insertEdge(parent, null, "PUSH/PULL", payment, c1_in); + graph.insertEdge(parent, null, "", c1_out, points); + + graph.insertEdge(parent, null, "PUSH/PULL", payment, c2_in); + graph.insertEdge(parent, null, "", c2_out, history); + } finally { + graph.getModel().endUpdate(); + } + + mxGraphComponent graphComponent = new mxGraphComponent(graph) { + protected mxICellEditor createCellEditor() { + final mxGraphComponent graphComponent = this; + return new mxICellEditor() { + /** + * + */ + public int DEFAULT_MIN_WIDTH = 70; + public int DEFAULT_MIN_HEIGHT = 30; + public double DEFAULT_MINIMUM_EDITOR_SCALE = 1; + + protected double minimumEditorScale = DEFAULT_MINIMUM_EDITOR_SCALE; + protected int minimumWidth = DEFAULT_MIN_WIDTH; + protected int minimumHeight = DEFAULT_MIN_HEIGHT; + + private Object editingCell; + private EventObject trigger; + private JComboBox comboBox; + + @Override + public Object getEditingCell() { + return editingCell; + } + + @Override + public void startEditing(Object cell, EventObject evt) { + if (editingCell != null) { + stopEditing(true); + } + + mxCellState state = graphComponent.getGraph().getView().getState(cell); + if (state != null && state.getLabel() != null && !state.getLabel().equals("")) { + editingCell = cell; + trigger = evt; + + double scale = Math.max(minimumEditorScale, graphComponent.getGraph().getView().getScale()); + if (comboBox == null) { + comboBox = new JComboBox<>(new String[]{"PUSH", "PULL"}); + comboBox.setBorder(BorderFactory.createEmptyBorder()); + comboBox.setOpaque(false); + } + comboBox.setBounds(getEditorBounds(state, scale)); + comboBox.setVisible(true); + graphComponent.getGraphControl().add(comboBox, 0); + comboBox.updateUI(); + } + } + + @Override + public void stopEditing(boolean cancel) { + if (editingCell != null) { + comboBox.transferFocusUpCycle(); + Object cell = editingCell; + editingCell = null; + if (!cancel) { + EventObject trig = trigger; + trigger = null; + graphComponent.labelChanged(cell, getCurrentValue(), 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; + } + }; + } +// +// public Component[] createComponents(mxCellState state) { +// if (getGraph().getModel().isEdge(state.getCell())) +// { +// return new Component[] { new JComboBox(new String[]{"PUSH", "PULL"}) }; +// } +// +// return null; +// } + }; + + graphComponent.getGraphControl().addMouseListener(new MouseAdapter() { + + public void mouseReleased(MouseEvent e) { + Object cell = graphComponent.getCellAt(e.getX(), e.getY()); + + if (cell != null) { + System.out.println("cell="+graph.getLabel(cell)); + } + } + }); + getContentPane().add(graphComponent); + } + + public static void main(String[] args) { + GraphicalViewer frame = new GraphicalViewer(); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setSize(400, 320); + frame.setVisible(true); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/tests/InverseTest.java b/AlgebraicDataflowArchitectureModel/src/tests/InverseTest.java new file mode 100644 index 0000000..165013e --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/tests/InverseTest.java @@ -0,0 +1,118 @@ +package tests; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.util.HashMap; + +import org.junit.Test; + +import models.algebra.Expression; +import models.algebra.Position; +import models.algebra.Term; +import models.algebra.Variable; +import models.dataConstraintModel.DataConstraintModel; +import models.dataConstraintModel.JsonType; +import models.dataFlowModel.DataTransferModel; +import parser.Parser; +import parser.Parser.TokenStream; +import parser.exceptions.ExpectedColon; +import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.WrongJsonExpression; + +public class InverseTest { + @Test + public void test() { + DataTransferModel model = new DataTransferModel(); + try { + String lhs = "y"; + String rhs = "(a * x + b) * c"; + + TokenStream stream = new Parser.TokenStream(); + Parser parser = new Parser(stream); + stream.addLine(rhs); + + Expression rhsExp = parser.parseTerm(stream, model); + System.out.println("=== solve{" + lhs + " = " + rhsExp + "} for a, b, d, x ==="); + + HashMap rhsVars = rhsExp.getVariables(); + assertEquals(4, rhsVars.size()); + + // Solve {y = (a * x + b) + c} for a, b, c, x + Variable y = new Variable(lhs); + for (Position vPos: rhsVars.keySet()) { + Variable v = rhsVars.get(vPos); + Expression inv = rhsExp.getInverseMap(y, vPos); // inverse map to get v back from the output value y + assertTrue(inv.contains(y)); + assertFalse(inv.contains(v)); + System.out.println(rhsVars.get(vPos) + " = " + inv); + } + + // Extract an element in a tuple + TokenStream stream2 = new Parser.TokenStream(); + Parser parser2 = new Parser(stream2); + stream2.addLine("fst(tuple(x, y))"); + Expression tupleExp = parser2.parseTerm(stream2, model); + stream2.addLine("snd(tuple(x, y))"); + Expression tupleExp2 = parser2.parseTerm(stream2, model); + Expression reduced = ((Term) tupleExp).reduce(); + Expression reduced2 = ((Term) tupleExp2).reduce(); + Variable x = new Variable("x"); + assertEquals(reduced, x); + assertEquals(reduced2, y); + System.out.println("=== simplify ==="); + System.out.println(tupleExp + " = " + reduced); + System.out.println(tupleExp2 + " = " + reduced2); + + // Solve {z = fst(x)} for x + TokenStream stream3 = new Parser.TokenStream(); + Parser parser3 = new Parser(stream3); + stream3.addLine("fst(x)"); + Expression rhsExp3 = parser3.parseTerm(stream3, model); + Variable z = new Variable("z"); + System.out.println("=== solve{" + z + " = " + rhsExp3 + "} for x ==="); + HashMap rhsVars3 = rhsExp3.getVariables(); + for (Position vPos: rhsVars3.keySet()) { + Variable v = rhsVars3.get(vPos); + Expression inv = rhsExp3.getInverseMap(z, vPos); // inverse map to get v back from the output value z + if (inv instanceof Term) { + inv = ((Term) inv).reduce(); + } + assertTrue(inv.contains(z)); + assertFalse(inv.contains(v)); + System.out.println(rhsVars3.get(vPos) + " = " + inv); + } + + // Solve {z = x.id} for x + TokenStream stream4 = new Parser.TokenStream(); + Parser parser4 = new Parser(stream4); + stream4.addLine("x.id"); + Expression rhsExp4 = parser4.parseTerm(stream4, model); + System.out.println("=== solve{" + z + " = " + rhsExp4 + "} for x ==="); + HashMap rhsVars4 = rhsExp4.getVariables(); + for (Position vPos: rhsVars4.keySet()) { + Variable v = rhsVars4.get(vPos); + if (x.getName().equals("x")) { + JsonType jsonType = new JsonType("Json", "HashMap<>", DataConstraintModel.typeJson); + jsonType.addMemberType("id", DataConstraintModel.typeInt); + jsonType.addMemberType("name", DataConstraintModel.typeString); + v.setType(jsonType); + } + Expression inv = rhsExp4.getInverseMap(z, vPos); // inverse map to get v back from the output value z + if (inv instanceof Term) { + inv = ((Term) inv).reduce(); + } + System.out.println(rhsVars4.get(vPos) + " = " + inv); + assertTrue(inv.contains(z)); + assertFalse(inv.contains(v)); + } + } catch (ExpectedRightBracket | WrongJsonExpression | ExpectedColon e) { + e.printStackTrace(); + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/tests/SimplifiedDataFlowModelTest.java b/AlgebraicDataflowArchitectureModel/src/tests/SimplifiedDataFlowModelTest.java new file mode 100644 index 0000000..acb5333 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/tests/SimplifiedDataFlowModelTest.java @@ -0,0 +1,95 @@ +package tests; + +import static org.junit.Assert.*; + +import org.junit.Test; + +import models.*; +import models.dataConstraintModel.*; +import models.dataFlowModel.*; + +public class SimplifiedDataFlowModelTest { + + @Test + public void test() { + // Construct a data-flow architecture model. + DataTransferModel model = new DataTransferModel(); + ResourcePath payment = new ResourcePath("payment", 0); // a resource to specify the payment resource + ResourcePath loyalty = new ResourcePath("loyalty", 0); // a resource to specify the loyalty resource + ResourcePath history = new ResourcePath("history", 0); // a resource to specify the payment history resource + ResourcePath total = new ResourcePath("total", 0); // a resource to specify the total payment resource + + // === cin === + // + // payment(p1, purchase(x)) == x + // + DataTransferChannel cin = new DataTransferChannel("cin"); + ChannelMember cin_payment = new ChannelMember(payment); + cin.addChannelMember(cin_payment); + assertEquals(cin.getChannelMembers().size(), 1); + + // === c1 === + // + // payment(p1, update1(y)) == y + // loyalty(l, update1(y)) == floor(y * 0.05) + // + DataTransferChannel c1 = new DataTransferChannel("c1"); + ChannelMember c1_payment = new ChannelMember(payment); + ChannelMember c1_loyalty = new ChannelMember(loyalty); + c1.addChannelMemberAsInput(c1_payment); + c1.addChannelMemberAsOutput(c1_loyalty); + assertEquals(c1.getChannelMembers().size(), 2); + assertEquals(c1.getInputChannelMembers().size(), 1); + assertEquals(c1.getOutputChannelMembers().size(), 1); + + // === c2 === + // + // payment(p1, update2(z)) == z + // history(h, update2(z)) == cons(z, h) + // + DataTransferChannel c2 = new DataTransferChannel("c2"); + ChannelMember c2_payment = new ChannelMember(payment); + ChannelMember c2_history = new ChannelMember(history); + c2.addChannelMemberAsInput(c2_payment); + c2.addChannelMemberAsOutput(c2_history); + assertEquals(c2.getChannelMembers().size(), 2); + assertEquals(c2.getInputChannelMembers().size(), 1); + assertEquals(c2.getOutputChannelMembers().size(), 1); + + // === c3 === + // + // history(h, update3(u)) == u + // total(t, update3(u)) == sum(u) + // + DataTransferChannel c3 = new DataTransferChannel("c3"); + ChannelMember c3_history = new ChannelMember(history); + ChannelMember c3_total = new ChannelMember(total); + c3.addChannelMemberAsInput(c3_history); + c3.addChannelMemberAsOutput(c3_total); + assertEquals(c3.getChannelMembers().size(), 2); + assertEquals(c3.getInputChannelMembers().size(), 1); + assertEquals(c3.getOutputChannelMembers().size(), 1); + + // Construct a data-flow architecture model. + model.addIOChannel(cin); + model.addChannel(c1); + model.addChannel(c2); + model.addChannel(c3); + + // Check the model. + assertEquals(4, model.getResourcePaths().size()); + assertEquals(1, model.getIOChannels().size()); + assertEquals(3, model.getChannels().size()); + + // Extract the resource dependency graph. + DataFlowGraph resourceDependencyGraph = model.getDataFlowGraph(); + + // Check the graph. + assertEquals(4, resourceDependencyGraph.getNodes().size()); + assertEquals(3, resourceDependencyGraph.getEdges().size()); + for (Edge e: resourceDependencyGraph.getEdges()) { + System.out.println(e.getSource() + "-(" + e + ")->" + e.getDestination()); + } + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/tests/TermTest.java b/AlgebraicDataflowArchitectureModel/src/tests/TermTest.java new file mode 100644 index 0000000..9ae2ec0 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/tests/TermTest.java @@ -0,0 +1,52 @@ +package tests; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; +import org.junit.Test; + +import models.algebra.Constant; +import models.algebra.Symbol; +import models.algebra.Term; +import models.algebra.Variable; + +public class TermTest { + + @Test + public void test() { + Symbol add = new Symbol("add", 2); + Symbol mul = new Symbol("mul", 2); + Constant one = new Constant("1"); + Constant two = new Constant("2"); + Variable x = new Variable("x"); + Variable y = new Variable("y"); + Variable z = new Variable("z"); + Term t1 = new Term(add); // add(1, x) + t1.addChild(one); + t1.addChild(x); + Term t2 = new Term(mul); // mul(add(1, x), y) + t2.addChild(t1); + t2.addChild(y); + Term t3 = new Term(add); // add(1, x) + t3.addChild(one); + t3.addChild(x); + + assertTrue(t1.contains(x)); + assertFalse(t1.contains(y)); + assertTrue(t2.contains(x)); + assertTrue(t2.contains(y)); + assertTrue(t2.contains(t1)); + assertTrue(t2.contains(t3)); + assertFalse(t2.contains(z)); + assertFalse(t2.contains(two)); + assertTrue(one.equals(one)); + assertTrue(two.equals(two)); + assertTrue(x.equals(x)); + assertTrue(y.equals(y)); + assertFalse(x.equals(y)); + assertFalse(x.equals(one)); + assertFalse(t1.equals(x)); + System.out.println(t1); + System.out.println(t2); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/tests/UpdateCodeGenerationTest.java b/AlgebraicDataflowArchitectureModel/src/tests/UpdateCodeGenerationTest.java new file mode 100644 index 0000000..69a5b1f --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/tests/UpdateCodeGenerationTest.java @@ -0,0 +1,300 @@ +package tests; + +import models.algebra.Constant; +import models.algebra.Expression; +import models.algebra.Field; +import models.algebra.InvalidMessage; +import models.algebra.Parameter; +import models.algebra.ParameterizedIdentifierIsFutureWork; +import models.algebra.Symbol; +import models.algebra.Term; +import models.algebra.UnificationFailed; +import models.algebra.ValueUndefined; +import models.algebra.Variable; +import models.dataConstraintModel.*; +import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.DataTransferChannel; +import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor; +import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork; + +public class UpdateCodeGenerationTest { + + public static void main(String[] args) { + // Pre-defined symbols + Symbol floor = new Symbol("floor", 1, Symbol.Type.PREFIX, "(int)Math.floor", Symbol.Type.PREFIX); + 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 + + // fields in the Java program + final Field fPayment = new Field("payment", DataConstraintModel.typeInt); + final Field fLoyalty = new Field("loyalty", 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 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; + } + 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; + 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 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; + } + return null; + } + @Override + public Expression getNextStateAccessorFor(ResourcePath target, ResourcePath from) { + if (target.equals(payment)) { + Term getter = new Term(paymentGetter); + getter.addChild(fPayment); + return getter; + } + if (target.equals(loyalty)) { + Term getter = new Term(loyltyGetter); + getter.addChild(fLoyalty); + return getter; + } + if (target.equals(history)) { + Term getter = new Term(historyGetter); + getter.addChild(fHistory); + return getter; + } + if (target.equals(total)) { + Term getter = new Term(totalGetter); + getter.addChild(fTotal); + return getter; + } + return null; + } + }; + + // === c1 === + // + // payment(p1, update1(y)) == y + // loyalty(l, update1(y)) == floor(y * 0.05) + // + DataTransferChannel c1 = new DataTransferChannel("c1"); + ChannelMember c1_payment = new ChannelMember(payment); + ChannelMember c1_loyalty = new ChannelMember(loyalty); + c1.addChannelMemberAsInput(c1_payment); + c1.addChannelMemberAsOutput(c1_loyalty); + + Variable p1 = new Variable("p1"); + Variable y = new Variable("y"); + Variable l = new Variable("l"); + Constant c_0_05 = new Constant("0.05"); + Symbol update1 = new Symbol("update1", 1); + Term c1_message = new Term(update1); // update1(y) + 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); + + StateTransition c1_payment_transition = new StateTransition(); + c1_payment_transition.setCurStateExpression(p1); + c1_payment_transition.setMessageExpression(c1_message); + 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); + + System.out.println(c1); + + try { + String[] sideEffects = new String[] {""}; + System.out.println("-----"); + System.out.println(c1.deriveUpdateExpressionOf(c1_loyalty).toImplementation(sideEffects)); + + System.out.println("-- PUSH --"); + Expression loyaltyPushUpdate = c1.deriveUpdateExpressionOf(c1_loyalty, pushAccessor); + Parameter param = null; + for (Parameter p: loyaltyPushUpdate.getSubTerms(Parameter.class).values()) { + if (p.equals(pPayment) || p.equals(pLoyalty) || 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("}"); + + 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("}"); + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage + | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + + System.out.println("=========="); + + // === c2 === + // + // payment(p1, update2(z)) == z + // history(h, update2(z)) == cons(z, h) + // + DataTransferChannel c2 = new DataTransferChannel("c2"); + ChannelMember c2_payment = new ChannelMember(payment); + ChannelMember c2_history = new ChannelMember(history); + c2.addChannelMemberAsInput(c2_payment); + c2.addChannelMemberAsOutput(c2_history); + + Variable z = new Variable("z"); + Variable h = new Variable("h"); + Symbol update2 = new Symbol("update2", 1); + Term c2_message = new Term(update2); // update2(z) + c2_message.addChild(z); + Term nextHistory = new Term(DataTransferModel.cons); // cons(z, h) + nextHistory.addChild(z); + nextHistory.addChild(h); + + StateTransition c2_payment_transition = new StateTransition(); + c2_payment_transition.setCurStateExpression(p1); + c2_payment_transition.setMessageExpression(c2_message); + c2_payment_transition.setNextStateExpression(z); + c2_payment.setStateTransition(c2_payment_transition); + + StateTransition c2_history_transition = new StateTransition(); + c2_history_transition.setCurStateExpression(h); + c2_history_transition.setMessageExpression(c2_message); + c2_history_transition.setNextStateExpression(nextHistory); + c2_history.setStateTransition(c2_history_transition); + + System.out.println(c2); + + try { + String[] sideEffects = new String[] {""}; + System.out.println("-----"); + System.out.println(c2.deriveUpdateExpressionOf(c2_history).toImplementation(sideEffects)); + + System.out.println("-- PUSH --"); + Expression historyPushUpdate = c2.deriveUpdateExpressionOf(c2_history, pushAccessor); + Parameter param = null; + for (Parameter p: historyPushUpdate.getSubTerms(Parameter.class).values()) { + if (p.equals(pPayment) || p.equals(pLoyalty) || p.equals(pHistory) || p.equals(pTotal)) { + param = p; + break; + } + } + System.out.println("void update(" + param.getType().getImplementationTypeName() + " " + param.toImplementation(sideEffects) + ") {"); + System.out.println("\t" + fHistory + " = " + historyPushUpdate.toImplementation(sideEffects) + ";"); + System.out.println("}"); + + 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("}"); + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage + | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + + System.out.println("=========="); + + // === c3 === + // + // history(h, update3(u)) = u + // total(t, update3(u)) = sum(u) + // + DataTransferChannel c3 = new DataTransferChannel("c3"); + ChannelMember c3_history = new ChannelMember(history); + ChannelMember c3_total = new ChannelMember(total); + c3.addChannelMemberAsInput(c3_history); + c3.addChannelMemberAsOutput(c3_total); + + Variable u = new Variable("u"); + Variable t = new Variable("t"); + Symbol update3 = new Symbol("update3", 1); + Term c3_message = new Term(update3); // update3(u) + c3_message.addChild(u); + Expression nextHistory2 = u; + Term nextTotal = new Term(sum); + nextTotal.addChild(u); + + StateTransition c3_history_transition = new StateTransition(); + c3_history_transition.setCurStateExpression(h); + c3_history_transition.setMessageExpression(c3_message); + c3_history_transition.setNextStateExpression(nextHistory2); + c3_history.setStateTransition(c3_history_transition); + + StateTransition c3_total_transition = new StateTransition(); + c3_total_transition.setCurStateExpression(t); + c3_total_transition.setMessageExpression(c3_message); + c3_total_transition.setNextStateExpression(nextTotal); + c3_total.setStateTransition(c3_total_transition); + + System.out.println(c3); + + try { + String[] sideEffects = new String[] {""}; + System.out.println("-----"); + System.out.println(c3.deriveUpdateExpressionOf(c3_total).toImplementation(sideEffects)); + + System.out.println("-- PUSH --"); + Expression totalPushUpdate = c3.deriveUpdateExpressionOf(c3_total, pushAccessor); + Parameter param = null; + for (Parameter p: totalPushUpdate.getSubTerms(Parameter.class).values()) { + if (p.equals(pPayment) || p.equals(pLoyalty) || p.equals(pHistory) || p.equals(pTotal)) { + param = p; + break; + } + } + System.out.println("void update(" + param.getType().getImplementationTypeName() + " " + param.toImplementation(sideEffects) + ") {"); + System.out.println("\t" + fTotal + " = " + totalPushUpdate.toImplementation(sideEffects) + ";"); + System.out.println("}"); + + 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("}"); + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage + | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + + System.out.println("=========="); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/tests/UpdateConflictCheckTest.java b/AlgebraicDataflowArchitectureModel/src/tests/UpdateConflictCheckTest.java new file mode 100644 index 0000000..7001ea8 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/tests/UpdateConflictCheckTest.java @@ -0,0 +1,77 @@ +package tests; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; + +import algorithms.*; +import models.dataFlowModel.DataTransferModel; +import parser.Parser; +import parser.exceptions.ExpectedAssignment; +import parser.exceptions.ExpectedChannel; +import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; +import parser.exceptions.ExpectedEquals; +import parser.exceptions.ExpectedInOrOutOrRefKeyword; +import parser.exceptions.ExpectedLeftCurlyBracket; +import parser.exceptions.ExpectedRHSExpression; +import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; +import parser.exceptions.WrongLHSExpression; +import parser.exceptions.WrongRHSExpression; + +public class UpdateConflictCheckTest { + public static void main(String[] args) { + File file = new File("models/POS2.model"); + try { + Parser parser = new Parser(new BufferedReader(new FileReader(file))); + try { + DataTransferModel model = parser.doParse(); + System.out.println(Validation.checkUpdateConflict(model)); + } catch (ExpectedRightBracket e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (ExpectedChannel e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (ExpectedChannelName e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (ExpectedLeftCurlyBracket e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (ExpectedInOrOutOrRefKeyword e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (ExpectedStateTransition e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (ExpectedEquals e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (ExpectedRHSExpression e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (WrongLHSExpression e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (WrongRHSExpression e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (ExpectedAssignment e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (WrongJsonExpression e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (ExpectedColon e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/tests/controlFlowModel/ControlFlowDelegatorTest.java b/AlgebraicDataflowArchitectureModel/src/tests/controlFlowModel/ControlFlowDelegatorTest.java new file mode 100644 index 0000000..57e4351 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/tests/controlFlowModel/ControlFlowDelegatorTest.java @@ -0,0 +1,26 @@ +package tests.controlFlowModel; + +import org.junit.Test; + +import models.dataConstraintModel.ResourcePath; +import models.dataFlowModel.DataTransferModel; + +import static org.junit.Assert.*; + + +/**-------------------------------------------------------------------------------- + * + */ +public class ControlFlowDelegatorTest { + @Test + public void test() { + // Construct a data-flow architecture model. + DataTransferModel model = new DataTransferModel(); + ResourcePath customer_off = new ResourcePath("customers.{x1}.off", 1); // a resource to specify a customer's office resource + ResourcePath company_add = new ResourcePath("companies.{x2}.add", 1); // a resource to specify a companie's address resource + ResourcePath customer_add = new ResourcePath("customers.{x1}.add", 1); // a resource to specify a customer's address resource + + + + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/tests/parser/ParseTest.java b/AlgebraicDataflowArchitectureModel/src/tests/parser/ParseTest.java new file mode 100644 index 0000000..0f0e40e --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/tests/parser/ParseTest.java @@ -0,0 +1,67 @@ +package tests.parser; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; + +import models.Edge; +import models.algebra.InvalidMessage; +import models.algebra.ParameterizedIdentifierIsFutureWork; +import models.algebra.UnificationFailed; +import models.algebra.ValueUndefined; +import models.dataConstraintModel.Channel; +import models.dataConstraintModel.ChannelMember; +import models.dataFlowModel.*; +import parser.Parser; +import parser.exceptions.ExpectedAssignment; +import parser.exceptions.ExpectedChannel; +import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; +import parser.exceptions.ExpectedEquals; +import parser.exceptions.ExpectedInOrOutOrRefKeyword; +import parser.exceptions.ExpectedLeftCurlyBracket; +import parser.exceptions.ExpectedRHSExpression; +import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; +import parser.exceptions.WrongLHSExpression; +import parser.exceptions.WrongRHSExpression; + +public class ParseTest { + + public static void main(String[] args) { + File file = new File("models/POS.model"); + try { + Parser parser = new Parser(new BufferedReader(new FileReader(file))); + DataTransferModel model; + try { + model = parser.doParse(); + System.out.println(model); + + for (Channel c: model.getChannels()) { + for (ChannelMember out: ((DataTransferChannel) c).getOutputChannelMembers()) { + String[] sideEffects = new String[] {""}; + System.out.println("next" + out.getResource().getResourceName() + " = " + ((DataTransferChannel) c).deriveUpdateExpressionOf(out).toImplementation(sideEffects)); + } + } + + System.out.println(); + + DataFlowGraph resourceDependencyGraph = model.getDataFlowGraph(); + for (Edge e: resourceDependencyGraph.getEdges()) { + System.out.println(e.getSource() + "-(" + e + ")->" + e.getDestination()); + } + } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ParameterizedIdentifierIsFutureWork + | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage + | UnificationFailed | ValueUndefined | ExpectedAssignment | WrongJsonExpression | ExpectedColon e) { + e.printStackTrace(); + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + } + +} diff --git a/docs/2024HandoverFujii.png b/docs/2024HandoverFujii.png new file mode 100644 index 0000000..b2f2fc1 --- /dev/null +++ b/docs/2024HandoverFujii.png Binary files differ diff --git a/docs/2024HandoverFujii.puml b/docs/2024HandoverFujii.puml new file mode 100644 index 0000000..d13f53c --- /dev/null +++ b/docs/2024HandoverFujii.puml @@ -0,0 +1,80 @@ +@startuml "" + +class "mxGraph\nComponent" + +package "application"{ + class "Application\nLanguage" + + package "actions"{ + abstract class "Abstract\nPopupAction" + } + + package "editor"{ + class "Editor" + abstract class "Stage" + + package "stages"{ + class "ControlFlow\nDelegationStage" + class "ControlFlow\nDelegationCellEditor" + '------------------------------------------ + "Stage" <|--- "ControlFlow\nDelegationStage" + ControlFlow\nDelegationCellEditor o-up-> ControlFlow\nDelegationStage + } + + package "views"{ + abstract class "PopupMenuBase" + class "Navigation\nWindow" + + package "controlFlowDelegation"{ + class "ControlFlow\nDelegationStage\nPopupMenu" + class "FlowLayerWindow" + '------------------------------------------ + "PopupMenuBase" <|-- "ControlFlow\nDelegationStage\nPopupMenu" + "FlowLayerWindow" o---> "ControlFlow\nDelegationStage" + } + } + + "Editor" o-up-> "mxGraph\nComponent" + "Editor" o---> "Stage" + + ControlFlow\nDelegationStage\nPopupMenu o--> ControlFlow\nDelegationStage + } +} + +package "models"{ + package "controlFlowModel"{ + class "CallEdge" + + class "CallEdge\nAttribute" + class "Composite\nCallEdgeAttribute" + + class "EventChannel\nObjectNode" + class "ObjectNode" + class "Stateful\nObjectNode" + + class "ObjectNode\nAttribute" + + class "CallGraph" + class "ControlFlowGraph" + + class "ControlFlow\nDelegator" + + '------------------------------------------ + "ObjectNode" <|-- "Stateful\nObjectNode" + "ObjectNode" <|-- "EventChannel\nObjectNode" + + "CallEdge\nAttribute" o-r-> "CallEdge" + "CallEdge\nAttribute" o--> "ObjectNode" + + "Composite\nCallEdgeAttribute" "1 " o----> "1..*" "CallEdge\nAttribute" + "CallEdge\nAttribute" <|---- "Composite\nCallEdgeAttribute" + + "ObjectNode\nAttribute" o--> "ObjectNode" + + "ControlFlow\nDelegator" o--> "ControlFlowGraph" + + "ControlFlowGraph" "1" o--> "2" "CallGraph" + } +} + +@enduml \ No newline at end of file