diff --git a/AlgebraicDataflowArchitectureModel/models/OnlineBattleGame2.model b/AlgebraicDataflowArchitectureModel/models/OnlineBattleGame2.model index 291c9eb..ccd15a7 100644 --- a/AlgebraicDataflowArchitectureModel/models/OnlineBattleGame2.model +++ b/AlgebraicDataflowArchitectureModel/models/OnlineBattleGame2.model @@ -27,7 +27,7 @@ channel UpdatePoint(rid:Str) { in rooms.{rid}.battle(prevState, updatePoint(hasWon, mid)) = hasWon for EachMember(mno:Int) { - in rooms.{rid}.members.{mno}.id(prevMid:Str, updatePoint(hasWon, mid)) = mid + ref rooms.{rid}.members.{mno}.id(mid:Str, updatePoint(hasWon, mid)) out accounts.{mid}.point(prevPoint:Int, updatePoint(hasWon, mid)) = if(hasWon, prevPoint + 1, prevPoint) } -} +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/models/VotingSystem.model b/AlgebraicDataflowArchitectureModel/models/VotingSystem.model new file mode 100644 index 0000000..ff425db --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/VotingSystem.model @@ -0,0 +1,14 @@ +channel Signup { + out accounts(acDB:Map, signUp(aid:Str, name:Str)) = insert(acDB, aid, {"name": name, "vote": null}) +} + +channel Cast(aid:Str) { + out accounts.{aid}.vote(preV, cast(v:Str)) = v +} + +channel Collect { + for EachAccount(aid:Str) { + in accounts.{aid}.vote(preV:Str, collect(m)) = m.{aid} + } + out counts(preCnts:Json, collect(m)) = m +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Account.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Account.java index f444138..159f403 100644 --- a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Account.java +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Account.java @@ -8,7 +8,10 @@ return temp_nil1; } public Map getNotifications() { - return this.notifications; + return notifications; + } + public void updateNotificationsFromMessages(String self, String gid, int mno, List messages, String members) { + this.notifications.put(gid,true); } public void hasRead(String aid, String gid) { this.notifications.remove(gid); diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Accounts.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Accounts.java index 91184e1..6183f07 100644 --- a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Accounts.java +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Accounts.java @@ -18,11 +18,10 @@ public Account getAccount(String v1) { return this.value.get(v1); } - @Path("/{v1}/notifications") - @Produces(MediaType.APPLICATION_JSON) - @GET - public Map getNotificationsValue(@PathParam("v1") String v1) { - return getAccount(v1).getNotifications(); + @Path("accounts/{v1}/notifications") + @POST + public void updateNotificationsFromMessages(@PathParam("v1") String v1, @FormParam("gid") String gid, @FormParam("mno") int mno, @FormParam("messages") List messages, @FormParam("members") String members) { + getAccount(v1).updateNotificationsFromMessages(m.get(mno), gid, mno, messages, members); } @Path("/{v1}") @Produces(MediaType.APPLICATION_JSON) @@ -30,6 +29,12 @@ public Map getAccountValue(@PathParam("v1") String v1) { return getAccount(v1).getValue(); } + @Path("/{v1}/notifications") + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getNotificationsValue(@PathParam("v1") String v1) { + return getAccount(v1).getNotifications(); + } @Path("/{aid}/notifications") @DELETE public void hasRead(@PathParam("aid") String aid, @FormParam("gid") String gid) { diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Group.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Group.java index bd5e825..08338b0 100644 --- a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Group.java +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Group.java @@ -1,12 +1,14 @@ import java.util.*; +import javax.ws.rs.client.*; public class Group { - private List messages; private List members; + private Client client = ClientBuilder.newClient(); + private List messages; public Map getValue() { Map temp_nil0 = new HashMap<>(); - temp_nil0.put("members",this.getMembers()); temp_nil0.put("messages",this.getMessages()); + temp_nil0.put("members",this.getMembers()); return temp_nil0; } public List getMessages() { @@ -15,7 +17,22 @@ public List getMembers() { return this.members; } - public void postMessage(String gid, String message) { + public String getMember(int mno) { + return this.members.get(mno); + } + public void postMessage(String gid, String message) throws JsonProcessingException { + for (int mno = 0; mno < members.size(); mno++) { + String member = getMember(mno); + Form form = new Form(); + form.param("gid", gid.toString()); + form.param("mno", Integer.toString(mno)); + for (String i: messages) { + form.param("messages", i.toString()); + } + form.param("member", member.toString()); + Entity
entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED); + String result = client.target("http://localhost:8080").path("/accounts."+member+".notifications").request().post(entity, String.class); + } this.messages.add(message); } public void addGroupMember(String gid, String aid) { diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Groups.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Groups.java index eef2dcf..cc91714 100644 --- a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Groups.java +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Groups.java @@ -18,18 +18,6 @@ public Group getGroup(String gid) { return this.value.get(gid); } - @Path("/{gid}/messages") - @Produces(MediaType.APPLICATION_JSON) - @GET - public List getMessagesValue(@PathParam("gid") String gid) { - return getGroup(gid).getMessages(); - } - @Path("/{gid}/members") - @Produces(MediaType.APPLICATION_JSON) - @GET - public List getMembersValue(@PathParam("gid") String gid) { - return getGroup(gid).getMembers(); - } @Path("/{gid}") @Produces(MediaType.APPLICATION_JSON) @GET @@ -37,8 +25,30 @@ return getGroup(gid).getValue(); } @Path("/{gid}/messages") + @Produces(MediaType.APPLICATION_JSON) + @GET + public List getMessagesValue(@PathParam("gid") String gid) { + return getGroup(gid).getMessages(); + } + @Path("/{gid}/members/{mno}") + @Produces(MediaType.APPLICATION_JSON) + @GET + public String getMemberValue(@PathParam("gid") String gid, @PathParam("mno") int mno) { + return getGroup(gid).getMember(mno); + } + @Path("/{gid}/members") + @Produces(MediaType.APPLICATION_JSON) + @GET + public List getMembersValue(@PathParam("gid") String gid) { + return getGroup(gid).getMembers(); + } @POST - public void postMessage(@PathParam("gid") String gid, @FormParam("message") String message) { + public void createGroup(@FormParam("gid") String gid) { + this.value.put(gid,new Group(new ArrayList<>(), new ArrayList<>())); + } + @Path("/{gid}/messages") + @POST + public void postMessage(@PathParam("gid") String gid, @FormParam("message") String message) throws JsonProcessingException { getGroup(gid).postMessage(gid, message); } @Path("/{gid}/members") @@ -46,8 +56,4 @@ public void addGroupMember(@PathParam("gid") String gid, @FormParam("aid") String aid) { getGroup(gid).addGroupMember(gid, aid); } - @POST - public void createGroup(@FormParam("gid") String gid) { - this.value.put(gid,new Group(new ArrayList<>(), new ArrayList<>())); - } } \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Account.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Account.java new file mode 100644 index 0000000..ff5dda3 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Account.java @@ -0,0 +1,34 @@ +import java.util.*; + +public class Account { + private String name; + private int point; + public Map getValue() { + Map temp_nil9 = new HashMap<>(); + temp_nil9.put("point",this.getPoint()); + temp_nil9.put("name",this.getName()); + return temp_nil9; + } + public String getName() { + return this.name; + } + public int getPoint() { + return point; + } + public void updatePointFromBattle(String self, String rid, int mno, boolean battle, String id) { + int temp_if0; + if (hasWon) { + temp_if0 = (this.point+1); + } else { + temp_if0 = this.point; + } + this.point = temp_if0; + } + public void changeName(String aid, String name) { + this.name = name; + } + public Account(String name, int point) { + this.name = name; + this.point = point; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Accounts.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Accounts.java new file mode 100644 index 0000000..0e9528b --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Accounts.java @@ -0,0 +1,53 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/accounts") +@Component +public class Accounts { + private Map value = new HashMap<>(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getValue() { + return new HashMap<>(value); + } + public Account getAccount(String mid) { + return this.value.get(mid); + } + @Path("accounts/{mid}/point") + @POST + public void updatePointFromBattle(@PathParam("mid") String mid, @FormParam("rid") String rid, @FormParam("mno") int mno, @FormParam("battle") boolean battle, @FormParam("id") String id) { + getAccount(mid).updatePointFromBattle(mid, rid, mno, battle, id); + } + @Path("/{mid}") + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getAccountValue(@PathParam("mid") String mid) { + return getAccount(mid).getValue(); + } + @Path("/{mid}/point") + @Produces(MediaType.APPLICATION_JSON) + @GET + public int getPointValue(@PathParam("mid") String mid) { + return getAccount(mid).getPoint(); + } + @Path("/{mid}/name") + @Produces(MediaType.APPLICATION_JSON) + @GET + public String getNameValue(@PathParam("mid") String mid) { + return getAccount(mid).getName(); + } + @POST + public void signUp(@FormParam("name") String name, @FormParam("aid") String aid) { + this.value.put(aid,new Account(name, 0)); + } + @Path("/{aid}/name") + @PUT + public void changeName(@PathParam("aid") String aid, @FormParam("name") String name) { + getAccount(aid).changeName(aid, name); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Member.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Member.java new file mode 100644 index 0000000..f721703 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Member.java @@ -0,0 +1,23 @@ +import java.util.*; +import javax.ws.rs.client.*; + +public class Member { + private Client client = ClientBuilder.newClient(); + private String id; + public Map getValue() { + Map temp_nil10 = new HashMap<>(); + temp_nil10.put("name",this.getName()); + temp_nil10.put("id",this.getId()); + return temp_nil10; + } + public String getName() { + String name = client.target("http://localhost:8080").path("/accounts."+id+".name").request().get(String.class); + return name; + } + public String getId() { + return this.id; + } + public Member(String id) { + this.id = id; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Members.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Members.java new file mode 100644 index 0000000..0dd75c8 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Members.java @@ -0,0 +1,14 @@ +import java.util.*; + +public class Members { + private List value = new ArrayList<>(); + public List getValue() { + return new ArrayList<>(value); + } + public Member getMember(int mno) { + return this.value.get(mno); + } + public void addRoomMember(String rid, String id) { + this.value.add(new Member(id)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Room.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Room.java new file mode 100644 index 0000000..581174c --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Room.java @@ -0,0 +1,36 @@ +import java.util.*; +import javax.ws.rs.client.*; + +public class Room { + private Members members = new Members(); + private boolean battle; + private Client client = ClientBuilder.newClient(); + public Map getValue() { + Map temp_nil8 = new HashMap<>(); + temp_nil8.put("members",this.members.getValue()); + temp_nil8.put("battle",this.getBattle()); + return temp_nil8; + } + public Members getMembers() { + return this.members; + } + public boolean getBattle() { + return this.battle; + } + public void battle(String rid, boolean hasWon) throws JsonProcessingException { + for (int mno = 0; mno < members.getValue().size(); mno++) { + String id = getMembers().getMember(mno).getId(); + Form form = new Form(); + form.param("rid", rid.toString()); + form.param("mno", Integer.toString(mno)); + form.param("battle", Boolean.toString(battle)); + form.param("id", id.toString()); + Entity entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED); + String result = client.target("http://localhost:8080").path("/accounts."+id+".point").request().post(entity, String.class); + } + this.battle = hasWon; + } + public Room(boolean battle) { + this.battle = battle; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Rooms.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Rooms.java new file mode 100644 index 0000000..8e0cde3 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Rooms.java @@ -0,0 +1,71 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/rooms") +@Component +public class Rooms { + private Map value = new HashMap<>(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getValue() { + return new HashMap<>(value); + } + public Room getRoom(String rid) { + return this.value.get(rid); + } + @Path("/{rid}/members/{mno}/name") + @Produces(MediaType.APPLICATION_JSON) + @GET + public String getNameValue(@PathParam("rid") String rid, @PathParam("mno") int mno) { + return getRoom(rid).getMembers().getMember(mno).getName(); + } + @Path("/{rid}/members/{mno}") + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getMemberValue(@PathParam("rid") String rid, @PathParam("mno") int mno) { + return getRoom(rid).getMembers().getMember(mno).getValue(); + } + @Path("/{rid}/members/{mno}/id") + @Produces(MediaType.APPLICATION_JSON) + @GET + public String getIdValue(@PathParam("rid") String rid, @PathParam("mno") int mno) { + return getRoom(rid).getMembers().getMember(mno).getId(); + } + @Path("/{rid}") + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getRoomValue(@PathParam("rid") String rid) { + return getRoom(rid).getValue(); + } + @Path("/{rid}/members") + @Produces(MediaType.APPLICATION_JSON) + @GET + public List getMembersValue(@PathParam("rid") String rid) { + return getRoom(rid).getMembers().getValue(); + } + @Path("/{rid}/battle") + @Produces(MediaType.APPLICATION_JSON) + @GET + public boolean getBattleValue(@PathParam("rid") String rid) { + return getRoom(rid).getBattle(); + } + @POST + public void createRoom(@FormParam("rid") String rid) { + this.value.put(rid,new Room(false)); + } + @Path("/{rid}/members") + @POST + public void addRoomMember(@PathParam("rid") String rid, @FormParam("id") String id) { + getRoom(rid).getMembers().addRoomMember(rid, id); + } + @Path("/{rid}/battle") + @PUT + public void battle(@PathParam("rid") String rid, @FormParam("hasWon") boolean hasWon) throws JsonProcessingException { + getRoom(rid).battle(rid, hasWon); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/VotingSystem/Account.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/VotingSystem/Account.java new file mode 100644 index 0000000..fce3635 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/VotingSystem/Account.java @@ -0,0 +1,19 @@ +import java.util.*; + +public class Account { + private String vote; + public Map getValue() { + Map temp_nil0 = new HashMap<>(); + temp_nil0.put("vote",this.getVote()); + return temp_nil0; + } + public String getVote() { + return this.vote; + } + public void cast(String aid, String v) { + this.vote = v; + } + public Account(String vote) { + this.vote = vote; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/VotingSystem/Accounts.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/VotingSystem/Accounts.java new file mode 100644 index 0000000..90e5808 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/VotingSystem/Accounts.java @@ -0,0 +1,42 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/accounts") +@Component +public class Accounts { + private Map value = new HashMap<>(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getValue() { + return new HashMap<>(value); + } + public Account getAccount(String aid) { + return this.value.get(aid); + } + @Path("/{aid}") + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getAccountValue(@PathParam("aid") String aid) { + return getAccount(aid).getValue(); + } + @Path("/{aid}/vote") + @Produces(MediaType.APPLICATION_JSON) + @GET + public String getVoteValue(@PathParam("aid") String aid) { + return getAccount(aid).getVote(); + } + @POST + public void signUp(@FormParam("name") String name, @FormParam("aid") String aid) { + this.value.put(aid,new Account(null)); + } + @Path("/{aid}/vote") + @PUT + public void cast(@PathParam("aid") String aid, @FormParam("v") String v) { + getAccount(aid).cast(aid, v); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/VotingSystem/Counts.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/VotingSystem/Counts.java new file mode 100644 index 0000000..f841a6f --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/VotingSystem/Counts.java @@ -0,0 +1,30 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/counts") +@Component +public class Counts { + private Map value = new HashMap<>(); + private Client client = ClientBuilder.newClient(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getValue() { + Map v0 = new HashMap<>(); + Map> accounts_json = client.target("http://localhost:8080").path("/accounts").request().get(HashMap.class); + Map> accounts = new HashMap<>(); + accounts = accounts_json; + for (String aid: accounts.keySet()) { + String vote = client.target("http://localhost:8080").path("/accounts."+aid+".vote").request().get(String.class); + v0.put(aid,vote); + } + return v0; + } + public Counts(Map counts) { + this.counts = counts; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/PULL-first/Account.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/PULL-first/Account.java index 4bed2b8..6aa36f4 100644 --- a/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/PULL-first/Account.java +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/PULL-first/Account.java @@ -3,12 +3,15 @@ public class Account { private Map notifications; public Map getValue() { - Map temp_nil3 = new HashMap<>(); - temp_nil3.put("notifications",this.getNotifications()); - return temp_nil3; + Map temp_nil5 = new HashMap<>(); + temp_nil5.put("notifications",this.getNotifications()); + return temp_nil5; } public Map getNotifications() { - return this.notifications; + return new HashMap<>(notifications); + } + public void updateNotificationsFromMessages(String gid, int mno, List messages, String members) { + this.notifications.put(gid,true); } public void hasRead(String aid, String gid) { this.notifications.remove(gid); diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/PULL-first/Group.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/PULL-first/Group.java index 1bba44d..7a9159a 100644 --- a/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/PULL-first/Group.java +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/PULL-first/Group.java @@ -1,28 +1,39 @@ import java.util.*; public class Group { - private List messages; private List members; + private List messages; + private Account account; + private Accounts accounts; public Map getValue() { - Map temp_nil2 = new HashMap<>(); - temp_nil2.put("members",this.getMembers()); - temp_nil2.put("messages",this.getMessages()); - return temp_nil2; - } - public List getMessages() { - return this.messages; + Map temp_nil4 = new HashMap<>(); + temp_nil4.put("messages",this.getMessages()); + temp_nil4.put("members",this.getMembers()); + return temp_nil4; } public List getMembers() { return this.members; } + public List getMessages() { + return this.messages; + } + public String getMember(int mno) { + return this.members.get(mno); + } public void postMessage(String gid, String message) { this.messages.add(message); + for (int mno = 0; mno < this.members.size(); mno++) { + String member = getMember(mno); + this.account = accounts.getAccount(member); + this.account.updateNotificationsFromMessages(gid, mno, messages, member); + } } public void addGroupMember(String gid, String aid) { this.members.add(aid); } - public Group(List messages, List members) { - this.messages = messages; + public Group(List members, List messages, Accounts accounts) { this.members = members; + this.messages = messages; + this.accounts = accounts; } } \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/PULL-first/GroupChat.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/PULL-first/GroupChat.java index f3ddadd..201f338 100644 --- a/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/PULL-first/GroupChat.java +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/PULL-first/GroupChat.java @@ -7,23 +7,20 @@ accounts = new Accounts(); groups = new Groups(); } - public Map getAccounts() { - return this.accounts.getValue(); + public List getMembers(String gid) { + return this.groups.getGroup(gid).getMembers(); } - public void signUp(String aid) { - this.accounts.signUp(aid); + public void addGroupMember(String gid, String aid) { + this.groups.getGroup(gid).addGroupMember(gid, aid); } public Map getNotifications(String v1) { - return this.accounts.getAccount(v1).getNotifications(); + return this.accounts.getAccount(m.get(mno)).getNotifications(); } public void hasRead(String aid, String gid) { this.accounts.getAccount(aid).hasRead(aid, gid); } - public Map getGroups() { - return this.groups.getValue(); - } - public void createGroup(String gid) { - this.groups.createGroup(gid); + public Map getGroup(String gid) { + return this.groups.getGroup(gid).getValue(); } public List getMessages(String gid) { return this.groups.getGroup(gid).getMessages(); @@ -31,16 +28,22 @@ public void postMessage(String gid, String message) { this.groups.getGroup(gid).postMessage(gid, message); } - public List getMembers(String gid) { - return this.groups.getGroup(gid).getMembers(); + public Map getAccounts() { + return this.accounts.getValue(); } - public void addGroupMember(String gid, String aid) { - this.groups.getGroup(gid).addGroupMember(gid, aid); - } - public Map getGroup(String gid) { - return this.groups.getGroup(gid).getValue(); + public void signUp(String aid) { + this.accounts.signUp(aid); } public Map getAccount(String v1) { - return this.accounts.getAccount(v1).getValue(); + return this.accounts.getAccount(m.get(mno)).getValue(); + } + public Map getGroups() { + return this.groups.getValue(); + } + public void createGroup(String gid) { + this.groups.createGroup(gid); + } + public String getMember(String gid, int mno) { + return this.groups.getGroup(gid).getMember(mno); } } \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/PULL-first/Groups.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/PULL-first/Groups.java index 7e1cc35..014bc27 100644 --- a/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/PULL-first/Groups.java +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/PULL-first/Groups.java @@ -9,6 +9,6 @@ return this.value.get(gid); } public void createGroup(String gid) { - this.value.put(gid,new Group(new ArrayList<>(), new ArrayList<>())); + this.value.put(gid,new Group(new ArrayList<>(), new ArrayList<>(), accounts)); } } \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/PUSH-first/Account.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/PUSH-first/Account.java index 31e9bb2..830c8ce 100644 --- a/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/PUSH-first/Account.java +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/PUSH-first/Account.java @@ -8,7 +8,10 @@ return temp_nil7; } public Map getNotifications() { - return this.notifications; + return new HashMap<>(notifications); + } + public void updateNotificationsFromMessages(String gid, int mno, List messages, String members) { + this.notifications.put(gid,true); } public void hasRead(String aid, String gid) { this.notifications.remove(gid); diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/PUSH-first/Group.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/PUSH-first/Group.java index d97238f..660e433 100644 --- a/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/PUSH-first/Group.java +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/PUSH-first/Group.java @@ -1,28 +1,39 @@ import java.util.*; public class Group { - private List messages; private List members; + private List messages; + private Account account; + private Accounts accounts; public Map getValue() { Map temp_nil6 = new HashMap<>(); - temp_nil6.put("members",this.getMembers()); temp_nil6.put("messages",this.getMessages()); + temp_nil6.put("members",this.getMembers()); return temp_nil6; } - public List getMessages() { - return this.messages; - } public List getMembers() { return this.members; } + public List getMessages() { + return this.messages; + } + public String getMember(int mno) { + return this.members.get(mno); + } public void postMessage(String gid, String message) { this.messages.add(message); + for (int mno = 0; mno < this.members.size(); mno++) { + String member = getMember(mno); + this.account = accounts.getAccount(member); + this.account.updateNotificationsFromMessages(gid, mno, messages, member); + } } public void addGroupMember(String gid, String aid) { this.members.add(aid); } - public Group(List messages, List members) { - this.messages = messages; + public Group(List members, List messages, Accounts accounts) { this.members = members; + this.messages = messages; + this.accounts = accounts; } } \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/PUSH-first/GroupChat.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/PUSH-first/GroupChat.java index f3ddadd..201f338 100644 --- a/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/PUSH-first/GroupChat.java +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/PUSH-first/GroupChat.java @@ -7,23 +7,20 @@ accounts = new Accounts(); groups = new Groups(); } - public Map getAccounts() { - return this.accounts.getValue(); + public List getMembers(String gid) { + return this.groups.getGroup(gid).getMembers(); } - public void signUp(String aid) { - this.accounts.signUp(aid); + public void addGroupMember(String gid, String aid) { + this.groups.getGroup(gid).addGroupMember(gid, aid); } public Map getNotifications(String v1) { - return this.accounts.getAccount(v1).getNotifications(); + return this.accounts.getAccount(m.get(mno)).getNotifications(); } public void hasRead(String aid, String gid) { this.accounts.getAccount(aid).hasRead(aid, gid); } - public Map getGroups() { - return this.groups.getValue(); - } - public void createGroup(String gid) { - this.groups.createGroup(gid); + public Map getGroup(String gid) { + return this.groups.getGroup(gid).getValue(); } public List getMessages(String gid) { return this.groups.getGroup(gid).getMessages(); @@ -31,16 +28,22 @@ public void postMessage(String gid, String message) { this.groups.getGroup(gid).postMessage(gid, message); } - public List getMembers(String gid) { - return this.groups.getGroup(gid).getMembers(); + public Map getAccounts() { + return this.accounts.getValue(); } - public void addGroupMember(String gid, String aid) { - this.groups.getGroup(gid).addGroupMember(gid, aid); - } - public Map getGroup(String gid) { - return this.groups.getGroup(gid).getValue(); + public void signUp(String aid) { + this.accounts.signUp(aid); } public Map getAccount(String v1) { - return this.accounts.getAccount(v1).getValue(); + return this.accounts.getAccount(m.get(mno)).getValue(); + } + public Map getGroups() { + return this.groups.getValue(); + } + public void createGroup(String gid) { + this.groups.createGroup(gid); + } + public String getMember(String gid, int mno) { + return this.groups.getGroup(gid).getMember(mno); } } \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/PUSH-first/Groups.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/PUSH-first/Groups.java index 7e1cc35..014bc27 100644 --- a/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/PUSH-first/Groups.java +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/PUSH-first/Groups.java @@ -9,6 +9,6 @@ return this.value.get(gid); } public void createGroup(String gid) { - this.value.put(gid,new Group(new ArrayList<>(), new ArrayList<>())); + this.value.put(gid,new Group(new ArrayList<>(), new ArrayList<>(), accounts)); } } \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PULL-first/Account.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PULL-first/Account.java new file mode 100644 index 0000000..ecc737a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PULL-first/Account.java @@ -0,0 +1,33 @@ +import java.util.*; + +public class Account { + private String name; + private int point; + public Map getValue() { + Map temp_nil9 = new HashMap<>(); + temp_nil9.put("name",this.getName()); + temp_nil9.put("point",this.getPoint()); + return temp_nil9; + } + public String getName() { + return this.name; + } + public int getPoint() { + return point; + } + public void updatePointFromBattle(String self, String rid, int mno, boolean battle, String id) { + int temp_if0; + if (hasWon) { + temp_if0 = (this.point+1); + } else { + temp_if0 = this.point; + }this.point = temp_if0; + } + public void changeName(String aid, String name) { + this.name = name; + } + public Account(String name, int point) { + this.name = name; + this.point = point; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PULL-first/Accounts.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PULL-first/Accounts.java new file mode 100644 index 0000000..6d92c89 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PULL-first/Accounts.java @@ -0,0 +1,14 @@ +import java.util.*; + +public class Accounts { + private Map value = new HashMap<>(); + public Map getValue() { + return new HashMap<>(value); + } + public Account getAccount(String mid) { + return this.value.get(mid); + } + public void signUp(String name, String aid) { + this.value.put(aid,new Account(name, 0)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PULL-first/Member.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PULL-first/Member.java new file mode 100644 index 0000000..33658d5 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PULL-first/Member.java @@ -0,0 +1,23 @@ +import java.util.*; + +public class Member { + private String id; + private Account account; + private Accounts accounts; + public Map getValue() { + Map temp_nil10 = new HashMap<>(); + temp_nil10.put("id",this.getId()); + temp_nil10.put("name",this.getName()); + return temp_nil10; + } + public String getId() { + return this.id; + } + public String getName() { + return this.account.getName(); + } + public Member(String id, Accounts accounts) { + this.id = id; + this.accounts = accounts; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PULL-first/Members.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PULL-first/Members.java new file mode 100644 index 0000000..091b561 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PULL-first/Members.java @@ -0,0 +1,14 @@ +import java.util.*; + +public class Members { + private List value = new ArrayList<>(); + public List getValue() { + return new ArrayList<>(value); + } + public Member getMember(int mno) { + return this.value.get(mno); + } + public void addRoomMember(String rid, String id) { + this.value.add(new Member(id, accounts)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PULL-first/OnlineBattleGame2.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PULL-first/OnlineBattleGame2.java new file mode 100644 index 0000000..69fc17e --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PULL-first/OnlineBattleGame2.java @@ -0,0 +1,58 @@ +import java.util.*; + +public class OnlineBattleGame2 { + private Accounts accounts; + private Rooms rooms; + public OnlineBattleGame2() { + accounts = new Accounts(); + rooms = new Rooms(accounts); + } + public String getId(String rid, int mno) { + return this.rooms.getRoom(rid).getMembers().getMember(mno).getId(); + } + public Map getAccount(String mid) { + return this.accounts.getAccount(mid).getValue(); + } + public String getName(String mid) { + return this.accounts.getAccount(mid).getName(); + } + public void changeName(String aid, String name) { + this.accounts.getAccount(aid).changeName(aid, name); + } + public int getPoint(String mid) { + return this.accounts.getAccount(mid).getPoint(); + } + public Map getRoom(String rid) { + return this.rooms.getRoom(rid).getValue(); + } + public List getMembers(String rid) { + return this.rooms.getRoom(rid).getMembers().getValue(); + } + public void addRoomMember(String rid, String id) { + this.rooms.getRoom(rid).getMembers().addRoomMember(rid, id); + } + public String getName(String rid, int mno) { + return this.rooms.getRoom(rid).getMembers().getMember(mno).getName(); + } + public Map getMember(String rid, int mno) { + return this.rooms.getRoom(rid).getMembers().getMember(mno).getValue(); + } + public boolean getBattle(String rid) { + return this.rooms.getRoom(rid).getBattle(); + } + public void battle(String rid, boolean hasWon) { + this.rooms.getRoom(rid).battle(rid, hasWon); + } + public Map getAccounts() { + return this.accounts.getValue(); + } + public void signUp(String name, String aid) { + this.accounts.signUp(name, aid); + } + public Map getRooms() { + return this.rooms.getValue(); + } + public void createRoom(String rid) { + this.rooms.createRoom(rid); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PULL-first/Room.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PULL-first/Room.java new file mode 100644 index 0000000..ff4155c --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PULL-first/Room.java @@ -0,0 +1,32 @@ +import java.util.*; + +public class Room { + private Members members = new Members(); + private boolean battle; + private Account account; + private Accounts accounts; + public Map getValue() { + Map temp_nil8 = new HashMap<>(); + temp_nil8.put("battle",this.getBattle()); + temp_nil8.put("members",this.members.getValue()); + return temp_nil8; + } + public Members getMembers() { + return this.members; + } + public boolean getBattle() { + return this.battle; + } + public void battle(String rid, boolean hasWon) { + this.battle = hasWon; + for (int mno = 0; mno < this.members.getValue().size(); mno++) { + String id = this.members.getMember(mno).getId(); + this.account = accounts.getAccount(id); + this.account.updatePointFromBattle(mid, rid, mno, battle, id); + } + } + public Room(boolean battle, Accounts accounts) { + this.battle = battle; + this.accounts = accounts; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PULL-first/Rooms.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PULL-first/Rooms.java new file mode 100644 index 0000000..d4230fa --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PULL-first/Rooms.java @@ -0,0 +1,18 @@ +import java.util.*; + +public class Rooms { + private Map value = new HashMap<>(); + private Accounts accounts; + public Map getValue() { + return new HashMap<>(value); + } + public Room getRoom(String rid) { + return this.value.get(rid); + } + public void createRoom(String rid) { + this.value.put(rid,new Room(false, accounts)); + } + public Rooms(Accounts accounts) { + this.accounts = accounts; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PUSH-first/Account.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PUSH-first/Account.java new file mode 100644 index 0000000..5ad3e48 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PUSH-first/Account.java @@ -0,0 +1,33 @@ +import java.util.*; + +public class Account { + private int point; + private String name; + public Map getValue() { + Map temp_nil12 = new HashMap<>(); + temp_nil12.put("name",this.getName()); + temp_nil12.put("point",this.getPoint()); + return temp_nil12; + } + public int getPoint() { + return point; + } + public String getName() { + return this.name; + } + public void updatePointFromBattle(String self, String rid, int mno, boolean battle, String id) { + int temp_if1; + if (hasWon) { + temp_if1 = (this.point+1); + } else { + temp_if1 = this.point; + }this.point = temp_if1; + } + public void changeName(String aid, String name) { + this.name = name; + } + public Account(int point, String name) { + this.point = point; + this.name = name; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PUSH-first/Accounts.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PUSH-first/Accounts.java new file mode 100644 index 0000000..b5e61df --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PUSH-first/Accounts.java @@ -0,0 +1,14 @@ +import java.util.*; + +public class Accounts { + private Map value = new HashMap<>(); + public Map getValue() { + return new HashMap<>(value); + } + public Account getAccount(String mid) { + return this.value.get(mid); + } + public void signUp(String name, String aid) { + this.value.put(aid,new Account(0, name)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PUSH-first/Member.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PUSH-first/Member.java new file mode 100644 index 0000000..7e548af --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PUSH-first/Member.java @@ -0,0 +1,27 @@ +import java.util.*; + +public class Member { + private Account account; + private Accounts accounts; + private String id; + public Map getValue() { + Map temp_nil13 = new HashMap<>(); + temp_nil13.put("id",this.getId()); + temp_nil13.put("name",this.getName()); + return temp_nil13; + } + public String getName() { + return name; + } + public String getId() { + return this.id; + } + public void updateNameFromId(String self, int self2, String rid, int mno, String id) { + this.name = name; + this.id = id; + } + public Member(Accounts accounts, String id) { + this.accounts = accounts; + this.id = id; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PUSH-first/Members.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PUSH-first/Members.java new file mode 100644 index 0000000..dd86377 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PUSH-first/Members.java @@ -0,0 +1,14 @@ +import java.util.*; + +public class Members { + private List value = new ArrayList<>(); + public List getValue() { + return new ArrayList<>(value); + } + public Member getMember(int mno) { + return this.value.get(mno); + } + public void addRoomMember(String rid, String id) { + this.value.add(new Member(accounts, id)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PUSH-first/OnlineBattleGame2.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PUSH-first/OnlineBattleGame2.java new file mode 100644 index 0000000..a2fbee5 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PUSH-first/OnlineBattleGame2.java @@ -0,0 +1,58 @@ +import java.util.*; + +public class OnlineBattleGame2 { + private Accounts accounts; + private Rooms rooms; + public OnlineBattleGame2() { + accounts = new Accounts(); + rooms = new Rooms(accounts); + } + public Map getAccount(String mid) { + return this.accounts.getAccount(mid).getValue(); + } + public int getPoint(String mid) { + return this.accounts.getAccount(mid).getPoint(); + } + public Map getRoom(String rid) { + return this.rooms.getRoom(rid).getValue(); + } + public List getMembers(String rid) { + return this.rooms.getRoom(rid).getMembers().getValue(); + } + public void addRoomMember(String rid, String id) { + this.rooms.getRoom(rid).getMembers().addRoomMember(rid, id); + } + public String getName(String rid, int mno) { + return this.rooms.getRoom(rid).getMembers().getMember(mno).getName(); + } + public String getId(String rid, int mno) { + return this.rooms.getRoom(rid).getMembers().getMember(mno).getId(); + } + public String getName(String mid) { + return this.accounts.getAccount(mid).getName(); + } + public void changeName(String aid, String name) { + this.accounts.getAccount(aid).changeName(aid, name); + } + public Map getMember(String rid, int mno) { + return this.rooms.getRoom(rid).getMembers().getMember(mno).getValue(); + } + public boolean getBattle(String rid) { + return this.rooms.getRoom(rid).getBattle(); + } + public void battle(String rid, boolean hasWon) { + this.rooms.getRoom(rid).battle(rid, hasWon); + } + public Map getAccounts() { + return this.accounts.getValue(); + } + public void signUp(String name, String aid) { + this.accounts.signUp(name, aid); + } + public Map getRooms() { + return this.rooms.getValue(); + } + public void createRoom(String rid) { + this.rooms.createRoom(rid); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PUSH-first/Room.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PUSH-first/Room.java new file mode 100644 index 0000000..ccc1b77 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PUSH-first/Room.java @@ -0,0 +1,32 @@ +import java.util.*; + +public class Room { + private Members members = new Members(); + private boolean battle; + private Account account; + private Accounts accounts; + public Map getValue() { + Map temp_nil11 = new HashMap<>(); + temp_nil11.put("battle",this.getBattle()); + temp_nil11.put("members",this.members.getValue()); + return temp_nil11; + } + public Members getMembers() { + return this.members; + } + public boolean getBattle() { + return this.battle; + } + public void battle(String rid, boolean hasWon) { + this.battle = hasWon; + for (int mno = 0; mno < this.members.getValue().size(); mno++) { + String id = this.members.getMember(mno).getId(); + this.account = accounts.getAccount(id); + this.account.updatePointFromBattle(mid, rid, mno, battle, id); + } + } + public Room(boolean battle, Accounts accounts) { + this.battle = battle; + this.accounts = accounts; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PUSH-first/Rooms.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PUSH-first/Rooms.java new file mode 100644 index 0000000..d4230fa --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/PUSH-first/Rooms.java @@ -0,0 +1,18 @@ +import java.util.*; + +public class Rooms { + private Map value = new HashMap<>(); + private Accounts accounts; + public Map getValue() { + return new HashMap<>(value); + } + public Room getRoom(String rid) { + return this.value.get(rid); + } + public void createRoom(String rid) { + this.value.put(rid,new Room(false, accounts)); + } + public Rooms(Accounts accounts) { + this.accounts = accounts; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/Account.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/Account.java new file mode 100644 index 0000000..7dec970 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/Account.java @@ -0,0 +1,19 @@ +import java.util.*; + +public class Account { + private String vote; + public Map getValue() { + Map temp_nil1 = new HashMap<>(); + temp_nil1.put("vote",this.getVote()); + return temp_nil1; + } + public String getVote() { + return this.vote; + } + public void cast(String aid, String v) { + this.vote = v; + } + public Account(String vote) { + this.vote = vote; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/Accounts.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/Accounts.java new file mode 100644 index 0000000..d0b8bb3 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/Accounts.java @@ -0,0 +1,14 @@ +import java.util.*; + +public class Accounts { + private Map value = new HashMap<>(); + public Map getValue() { + return new HashMap<>(value); + } + public Account getAccount(String aid) { + return this.value.get(aid); + } + public void signUp(String name, String aid) { + this.value.put(aid,new Account(null)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/Counts.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/Counts.java new file mode 100644 index 0000000..a0d5b9f --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/Counts.java @@ -0,0 +1,18 @@ +import java.util.*; + +public class Counts { + private Map value = new HashMap<>(); + private Account account; + private Accounts accounts; + public Map getValue() { + Map v0 = new HashMap<>(); + for (String aid: this.accounts.getValue().keySet()) { + String vote = this.accounts.getAccount(aid).getVote(); + v0.put(aid,vote); + } + return v0; + } + public Counts(Accounts accounts) { + this.accounts = accounts; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/VotingSystem.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/VotingSystem.java new file mode 100644 index 0000000..50ae847 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/VotingSystem.java @@ -0,0 +1,28 @@ +import java.util.*; + +public class VotingSystem { + private Counts counts; + private Accounts accounts; + public VotingSystem() { + counts = new Counts(); + accounts = new Accounts(); + } + public Map getCounts() { + return this.counts.getValue(); + } + public Map getAccount(String aid) { + return this.accounts.getAccount(aid).getValue(); + } + public Map getAccounts() { + return this.accounts.getValue(); + } + public void signUp(String name, String aid) { + this.accounts.signUp(name, aid); + } + public String getVote(String aid) { + return this.accounts.getAccount(aid).getVote(); + } + public void cast(String aid, String v) { + this.accounts.getAccount(aid).cast(aid, v); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/src/generators/CodeGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/CodeGenerator.java index ed4339b..d3257f0 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/CodeGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/CodeGenerator.java @@ -371,14 +371,16 @@ } } - protected void fillDescendantGetterMethod(MethodDeclaration descendantGetter, ResourceHierarchy descendant, ResourceHierarchy ancestor, TypeDeclaration ancestorComponent, ILanguageSpecific langSpec) { + protected void fillDescendantGetterMethod(MethodDeclaration descendantGetter, ResourceHierarchy descendant, + ResourceHierarchy child, ResourceHierarchy ancestor, TypeDeclaration ancestorComponent, ILanguageSpecific langSpec) { + // (#4) descendant getter method (the implementation must be kept consistent with #3) Expression selector; if (DataConstraintModel.typeList.isAncestorOf(ancestor.getResourceStateType())) { selector = new Variable(langSpec.getFieldAccessor(fieldOfResourceState)); } else if (DataConstraintModel.typeMap.isAncestorOf(ancestor.getResourceStateType())) { selector = new Variable(langSpec.getFieldAccessor(fieldOfResourceState)); } else { - String fieldName = langSpec.toVariableName(getComponentName(descendant, langSpec)); + String fieldName = langSpec.toVariableName(getComponentName(child, langSpec)); selector = new Variable(langSpec.getFieldAccessor(fieldName)); } if (descendantGetter.getParameters() != null) { @@ -521,16 +523,16 @@ : DataConstraintModel.typeInt); } // use the cached value as the current state - return new Field(targetRes.getLeafResourceName(), + return new Field(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } @Override public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { - ResourcePath targerRes= target.getResource(); - return new Parameter(targerRes.getLeafResourceName(), - targerRes.getResourceStateType() != null ? targerRes.getResourceStateType() + ResourcePath targetRes= target.getResource(); + return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } @@ -542,7 +544,7 @@ : DataConstraintModel.typeInt); } // for reference channel member - return new Parameter(targetRes.getLeafResourceName(), + return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } @@ -610,11 +612,25 @@ : DataConstraintModel.typeInt); } // for reference channel member - Term getter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); - getter.addChild(new Field(targetRes.getLeafResourceName(), targetRes.getResourceStateType())); - return getter; + if (fromRes.isAncestorOf(targetRes)) { + Stack pathStack = new Stack<>(); + ResourcePath curPath = targetRes; + do { + pathStack.push(curPath); + curPath = curPath.getParent(); + } while (!curPath.equals(fromRes)); + // iterate from the fromRes resource + return getRelativePath(targetRes, pathStack); + } + if (generatesComponent(targetRes.getResourceHierarchy())) { + Term getter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); + getter.addChild(new Field(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), targetRes.getResourceStateType())); + return getter; + } else { + return new Field(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), targetRes.getResourceStateType()); + } } else { - // access from the outside of the hierarchy + // (#3) access from the outside of the hierarchy (must be kept consistent with #4) Stack pathStack = new Stack<>(); ResourcePath curPath = targetRes; do { @@ -622,86 +638,76 @@ curPath = curPath.getParent(); } while (curPath != null); // iterate from the root resource - Term getter = null; - int v = 1; - int arity = 2; - while (!pathStack.empty()) { - curPath = pathStack.pop(); - String typeName = getComponentName(curPath.getResourceHierarchy(), langSpec); - if (getter == null) { - // root resource - String fieldName = langSpec.toVariableName(typeName); - getter = new Field(fieldName, new Type(typeName, typeName)); - } else { - if (generatesComponent(curPath.getResourceHierarchy())) { - if (arity == 2) { - Term newGetter = new Term(new Symbol(getterPrefix + typeName, -1, Symbol.Type.METHOD)); - newGetter.addChild(getter); - if (curPath.getResourceHierarchy().getNumParameters() > 0) { - Variable var = null; - Expression param = curPath.getLastParam(); - if (param instanceof Variable) { - var = (Variable) param; - } else if (param instanceof Term) { - var = new Variable("v" + v, ((Term) param).getType()); - } - if (var != null) { - newGetter.addChild(var); - newGetter.getSymbol().setArity(2); - } - v++; - } - getter = newGetter; - } else { - if (curPath.getResourceHierarchy().getNumParameters() > 0) { - Variable var = null; - Expression param = curPath.getLastParam(); - if (param instanceof Variable) { - var = (Variable) param; - } else if (param instanceof Term) { - var = new Variable("v" + v, ((Term) param).getType()); - } - if (var != null) { - getter.getSymbol().setArity(arity); - getter.addChild(var); - } - v++; - } - } - arity = 2; - } else { - // to get a descendant resource directly. - if (arity == 2) { - Term newGetter = new Term(new Symbol(getterPrefix + typeName, -1, Symbol.Type.METHOD)); - newGetter.addChild(getter); - getter = newGetter; - } + return getRelativePath(targetRes, pathStack); + } + } + + private Expression getRelativePath(ResourcePath targetRes, Stack pathStack) { + ResourcePath curPath; + Term getter = null; + int arity = 2; + boolean doesChainInvocations = true; + while (!pathStack.empty()) { + curPath = pathStack.pop(); + String typeName = getComponentName(curPath.getResourceHierarchy(), langSpec); + if (getter == null && generatesComponent(curPath.getResourceHierarchy())) { + // root resource + String fieldName = langSpec.toVariableName(typeName); + getter = new Field(fieldName, new Type(typeName, typeName)); + } else { + if (generatesComponent(curPath.getResourceHierarchy())) { + if (arity == 2) { + Term newGetter = new Term(new Symbol(getterPrefix + typeName, -1, Symbol.Type.METHOD)); + newGetter.addChild(getter); if (curPath.getResourceHierarchy().getNumParameters() > 0) { - Variable var = null; Expression param = curPath.getLastParam(); - if (param instanceof Variable) { - var = (Variable) param; - } else if (param instanceof Term) { - var = new Variable("v" + v, ((Term) param).getType()); + if (param != null) { + newGetter.addChild(param); + newGetter.getSymbol().setArity(2); } - if (var != null) { + } + getter = newGetter; + } else { + // add the last path parameter. + if (curPath.getResourceHierarchy().getNumParameters() > 0) { + Expression param = curPath.getLastParam(); + if (param != null) { getter.getSymbol().setArity(arity); - getter.addChild(var); - arity++; + getter.addChild(param); } - v++; + } + } + arity = 2; + doesChainInvocations = true; + } else { + // to get a descendant resource directly. (e.g, .todos.{year}.{month}.{day}.{id} ==> .getTodos().getTodo(year, month, day, id)) + if (doesChainInvocations) { + Term newGetter = new Term(new Symbol(getterPrefix + typeName, -1, Symbol.Type.METHOD)); + newGetter.addChild(getter); + getter = newGetter; + doesChainInvocations = false; + } + if (curPath.getResourceHierarchy().getNumParameters() > 0) { + // may change the symbol name + getter.getSymbol().changeName("get" + typeName); + // add a path parameter. + Expression param = curPath.getLastParam(); + if (param != null) { + getter.getSymbol().setArity(arity); + getter.addChild(param); + arity++; } } } } - - if (generatesComponent(targetRes.getResourceHierarchy())) { - Term newGetter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); - newGetter.addChild(getter); - getter = newGetter; - } - return getter; } + + if (generatesComponent(targetRes.getResourceHierarchy())) { + Term newGetter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); + newGetter.addChild(getter); + getter = newGetter; + } + return getter; } }; } @@ -718,16 +724,16 @@ : DataConstraintModel.typeInt); } // use the cached value as the current state - return new Parameter(targetRes.getLeafResourceName(), + return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } @Override public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { - ResourcePath targerRes= target.getResource(); - return new Parameter(targerRes.getLeafResourceName(), - targerRes.getResourceStateType() != null ? targerRes.getResourceStateType() + ResourcePath targetRes= target.getResource(); + return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } @@ -739,7 +745,7 @@ : DataConstraintModel.typeInt); } // for reference channel member - return new Parameter(targetRes.getLeafResourceName(), + return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } diff --git a/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java b/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java index e5b3ce5..31fbea9 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java @@ -110,7 +110,7 @@ declareAccessorInMainComponent(mainComponent, resourceNode, stateGetter, langSpec); } if (component != null) { - // Declare the getter methods in this resource to obtain the descendant resources. + // (#1) Declare the getter methods in this resource to obtain the descendant resources. (complementary to #2) declareDescendantGetterMethods(resourceNode, component, descendantGetters, langSpec); } } @@ -145,7 +145,7 @@ // Declare the fields to refer to other resources in the parent/this component, and the state field in the parent component. declareFieldsToReferToOtherResourcesAndStateFieldInParentComponent(resourceNode, component, parentComponent, constructorParams, langSpec); - // Declare the getter method to obtain the resource state in an ancestor resource. + // (#2) Declare the getter method to obtain the resource state in an ancestor resource. (complementary to #1) if (component == null) { MethodDeclaration stateGetter = declareStateGetterMethodInAncestor(resourceNode, resourceComponents, resStateType, langSpec); @@ -247,24 +247,26 @@ if (jsonTerm instanceof JsonTerm || jsonTerm.getSymbol().equals(DataConstraintModel.addMember)) { MethodDeclaration childConstructor = getConstructor(descendantComponent); List params = new ArrayList<>(); - for (VariableDeclaration var: childConstructor.getParameters()) { - JsonAccessor jsonMember = new JsonAccessor(DataConstraintModel.dot); - jsonMember.addChild(jsonTerm); - jsonMember.addChild(new Constant(var.getName(), DataConstraintModel.typeString)); - Expression param = jsonMember.reduce(); - if (param != null) { - if (param instanceof Term) { - if (((Term) param).getType() == null) { - ((Term) param).setType(var.getType()); + if (childConstructor != null) { + for (VariableDeclaration var: childConstructor.getParameters()) { + JsonAccessor jsonMember = new JsonAccessor(DataConstraintModel.dot); + jsonMember.addChild(jsonTerm); + jsonMember.addChild(new Constant(var.getName(), DataConstraintModel.typeString)); + Expression param = jsonMember.reduce(); + if (param != null) { + if (param instanceof Term) { + if (((Term) param).getType() == null) { + ((Term) param).setType(var.getType()); + } + } else if (param instanceof Variable) { + if (((Variable) param).getType() == null) { + ((Variable) param).setType(var.getType()); + } } - } else if (param instanceof Variable) { - if (((Variable) param).getType() == null) { - ((Variable) param).setType(var.getType()); - } + params.add(param.toImplementation(null)); + } else { + params.add(var.getName()); } - params.add(param.toImplementation(new String[] {""})); - } else { - params.add(var.getName()); } } ((Term) exp).replaceSubTerm(termEnt.getKey(), new Constant(langSpec.getConstructorInvocation(replacingClassName, params))); @@ -302,6 +304,9 @@ String updateStatement; if (exp instanceof Term && ((Term) exp).getSymbol().isImplWithSideEffect()) { updateStatement = sideEffects[0]; + if (updateStatement.endsWith("\n")) { + updateStatement = updateStatement.substring(0, updateStatement.length() - 1); + } } else { updateStatement = sideEffects[0] + langSpec.getFieldAccessor(fieldOfResourceState) + langSpec.getAssignment() + newState + langSpec.getStatementDelimiter(); } @@ -440,18 +445,32 @@ boolean noPullTransfer = true; for (Edge resToCh : resourceNode.getOutEdges()) { DataFlowEdge re = (DataFlowEdge) resToCh; - DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + ChannelNode directDstChNode = (ChannelNode) re.getDestination(); + DataTransferChannel directDstCh = directDstChNode.getChannel(); // Check if the source resource is outside of the channel scope. boolean outsideInputResource = false; - for (ChannelMember cm: ch.getInputChannelMembers()) { - if (resourceNode.getOutSideResources().contains(cm.getResource()) && cm.isOutside()) { + for (ChannelMember cm: directDstCh.getInputChannelMembers()) { + if (cm.getResource().equals(resourceNode.getOutSideResource(directDstCh)) && cm.isOutside()) { outsideInputResource = true; // Regarded as pull transfer. break; } } - for (Edge chToRes: re.getDestination().getOutEdges()) { + // Should take into account the channel hierarchy. + Set ancestorDstChannels = directDstChNode.getAncestors(); + Set descendantDstChannels = directDstChNode.getDescendants(); + Set outEdges = new HashSet<>(); + outEdges.addAll(directDstChNode.getOutEdges()); + for (ChannelNode ancestorDst: ancestorDstChannels) { + outEdges.addAll(ancestorDst.getOutEdges()); + } + for (ChannelNode descendantDst: descendantDstChannels) { + outEdges.addAll(descendantDst.getOutEdges()); + } + for (Edge chToRes: outEdges) { if (chToRes.getDestination() instanceof ResourceNode) { ResourceHierarchy dstRes = ((ResourceNode) chToRes.getDestination()).getResourceHierarchy(); + ChannelNode chNode = (ChannelNode) chToRes.getSource(); + DataTransferChannel ch = chNode.getChannel(); // Check if the destination resource is outside of the channel scope. boolean outsideOutputResource = false; for (ChannelMember cm: ch.getOutputChannelMembers()) { @@ -460,6 +479,13 @@ break; } } + // Also take into account the channel hierarchy to determine push/pull transfer. + if (descendantDstChannels.contains(chNode)) { + outsideOutputResource = true; // Regarded as (broadcasting) push transfer. + } + if (ancestorDstChannels.contains(chNode)) { + outsideInputResource = true; // Regarded as (collecting) pull transfer. + } if ((((PushPullAttribute) re.getAttribute()).getSelectedOption() == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) { // Declare a field in the parent component to refer to the destination resource of push transfer. if (!generatesComponent(dstRes)) { @@ -643,7 +669,8 @@ MethodDeclaration stateGetter = langSpec.newMethodDeclaration(getterOfResourceState, resStateType); component.addMethod(stateGetter); - if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { + boolean hasDescendantIn = hasDescendantInput(resourceNode); + if (((StoreAttribute) resourceNode.getAttribute()).isStored() && !hasDescendantIn) { fillStateGetterMethod(stateGetter, resourceNode.getResourceHierarchy(), resStateType, langSpec); } else { // invocations to other getter methods when at least one incoming data-flow edges is PULL-style. @@ -656,9 +683,11 @@ private MethodDeclaration declareStateGetterMethodInAncestor(ResourceNode resourceNode, Map resourceComponents, Type resStateType, ILanguageSpecific langSpec) { // Search an ancestor in which the getter method is declared. ResourceNode ancestorNode = resourceNode; + ResourceNode childNode = null; Stack ancestors = new Stack<>(); do { ancestors.push(ancestorNode); + childNode = ancestorNode; ancestorNode = ancestorNode.getParent(); } while (!generatesComponent(ancestorNode.getResourceHierarchy())); TypeDeclaration ancestorComponent = resourceComponents.get(ancestorNode.getResourceHierarchy()); @@ -694,8 +723,9 @@ ancestorComponent.addMethod(stateGetter); } - if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { - fillDescendantGetterMethod(stateGetter, resourceNode.getResourceHierarchy(), ancestorNode.getResourceHierarchy(), ancestorComponent, langSpec); + boolean hasDescendantIn = hasDescendantInput(resourceNode); + if (((StoreAttribute) resourceNode.getAttribute()).isStored() && !hasDescendantIn) { + fillDescendantGetterMethod(stateGetter, resourceNode.getResourceHierarchy(), childNode.getResourceHierarchy(), ancestorNode.getResourceHierarchy(), ancestorComponent, langSpec); } else { addOtherGetterInvocationsToStateGatter(stateGetter, resourceNode, langSpec); } @@ -703,55 +733,209 @@ return stateGetter; } - private void addOtherGetterInvocationsToStateGatter(MethodDeclaration stateGetter, ResourceNode resourceNode, - ILanguageSpecific langSpec) { - boolean isContainedPush = false; - DataTransferChannel ch = null; - HashMap inputResourceToStateAccessor = new HashMap<>(); - for (Edge chToRes: resourceNode.getInEdges()) { - DataTransferChannel ch2 = ((ChannelNode) chToRes.getSource()).getChannel(); - for (Edge resToCh: chToRes.getSource().getInEdges()) { - DataFlowEdge dIn = (DataFlowEdge) resToCh; - ChannelMember in = null; - for (ChannelMember cm: ch2.getInputChannelMembers()) { - if (((ResourceNode) dIn.getSource()).getOutSideResources().contains(cm.getResource())) { - in = cm; - break; - } - } - if (((PushPullAttribute) dIn.getAttribute()).getSelectedOption() == PushPullValue.PUSH) { - // PUSH transfer - isContainedPush = true; - inputResourceToStateAccessor.put(in, getPushAccessor()); - } else { - // PULL transfer - inputResourceToStateAccessor.put(in, getPullAccessor()); - ch = ((ChannelNode) resToCh.getDestination()).getChannel(); // pull containing input side channel is always one. + private boolean hasDescendantInput(ResourceNode resourceNode) { + boolean hasDescendantIn = false; + outer: for (Edge chToRes: resourceNode.getInEdges()) { + ChannelNode chNode = (ChannelNode) chToRes.getSource(); + Set descendantChannels = chNode.getDescendants(); + for (ChannelNode descendantCh: descendantChannels) { + if (descendantCh.getIndegree() > 0) { + hasDescendantIn = true; + break outer; } } } - // for reference channel members. - for (ChannelMember c: ch.getReferenceChannelMembers()) { - inputResourceToStateAccessor.put(c, getPullAccessor()); // by pull data transfer - } - - // generate a return statement. + return hasDescendantIn; + } + + private void addOtherGetterInvocationsToStateGatter(MethodDeclaration stateGetter, ResourceNode resourceNode, + ILanguageSpecific langSpec) { try { - for (ChannelMember out: ch.getOutputChannelMembers()) { - if (resourceNode.getInSideResources().contains(out.getResource())) { - String[] sideEffects = new String[] {""}; - if (!isContainedPush) { - // All incoming edges are in PULL-style. - String curState = ch.deriveUpdateExpressionOf(out, getPullAccessor()).getKey().toImplementation(sideEffects); - stateGetter.addStatement(sideEffects[0] + langSpec.getReturnStatement(curState) + langSpec.getStatementDelimiter()); - } else { - // At least one incoming edge is in PUSH-style. - String curState = ch.deriveUpdateExpressionOf(out, getPullAccessor(), inputResourceToStateAccessor).getKey().toImplementation(sideEffects); - stateGetter.addStatement(sideEffects[0] + langSpec.getReturnStatement(curState) + langSpec.getStatementDelimiter()); + // Data transfer on the same channel hierarchy. + boolean isContainedPush = false; + DataTransferChannel ch = null; + DataTransferChannel ch2 = null; + HashMap inputResourceToStateAccessor = new HashMap<>(); + for (Edge chToRes: resourceNode.getInEdges()) { + ch2 = ((ChannelNode) chToRes.getSource()).getChannel(); + for (Edge resToCh: chToRes.getSource().getInEdges()) { + DataFlowEdge dIn = (DataFlowEdge) resToCh; + ChannelMember in = null; + for (ChannelMember cm: ch2.getInputChannelMembers()) { + if (((ResourceNode) dIn.getSource()).getOutSideResources().contains(cm.getResource())) { + in = cm; + break; + } } + if (((PushPullAttribute) dIn.getAttribute()).getSelectedOption() == PushPullValue.PUSH) { + // PUSH transfer + isContainedPush = true; + inputResourceToStateAccessor.put(in, getPushAccessor()); + } else { + // PULL transfer + inputResourceToStateAccessor.put(in, getPullAccessor()); + ch = ((ChannelNode) resToCh.getDestination()).getChannel(); // pull containing input side channel is at most one. + } + } + } + if (ch == null) { + ch = ch2; + } + ChannelMember out = null; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (resourceNode.getInSideResources().contains(cm.getResource())) { + out = cm; break; } } + + // for reference channel members. + for (ChannelMember c: ch.getReferenceChannelMembers()) { + inputResourceToStateAccessor.put(c, getPullAccessor()); // by pull data transfer + } + + // Construct the base message. + Map.Entry>>, Term> resourcePathsAndMessage; + if (!isContainedPush) { + // All incoming edges are in PULL-style. + resourcePathsAndMessage = ch.fillOutsideResourcePaths(out, JavaCodeGenerator.pullAccessor, null); + } else { + // At least one incoming edge is in PUSH-style. + resourcePathsAndMessage = ch.fillOutsideResourcePaths(out, JavaCodeGenerator.pullAccessor, inputResourceToStateAccessor); + } + Map>> resourcePaths = resourcePathsAndMessage.getKey(); + Term messageTerm = resourcePathsAndMessage.getValue(); + // Data transfer from the descendant channel hierarchies. + Stack> channelItrStack = new Stack<>(); + DataTransferChannel curChannel = ch; + if (curChannel.getChildren() != null && curChannel.getChildren().size() > 0) { + // retrieve descendant channels recursively. + Iterator chItr = curChannel.getChildren().iterator(); + do { + if (!chItr.hasNext()) { + chItr = channelItrStack.pop(); + } else { + curChannel = (DataTransferChannel) chItr.next(); + // generate pull data transfers. + Set chMems = new HashSet<>(curChannel.getInputChannelMembers()); + chMems.addAll(curChannel.getReferenceChannelMembers()); + for (ChannelMember cm2: chMems) { + if (resourcePaths == null || !resourcePaths.keySet().contains(cm2)) { + // not a depending channel member. + ResourcePath src2 = cm2.getResource(); + Type srcResType2 = src2.getResourceStateType(); + String srcResName2 = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(src2.getResourceHierarchy())); + String srcGetter = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(src2, resourceNode.getInSideResource(curChannel)).toImplementation(new String[] {}); + stateGetter.addStatement(srcResType2.getInterfaceTypeName() + " " + srcResName2 + " = " + srcGetter + ";"); + } else { + // a depending channel member. + ResourcePath src2 = resourcePaths.get(cm2).getKey(); + // get outside src2 resource state by pull data transfer. + if (cm2.isOutside() || src2.getCommonPrefix(resourceNode.getInSideResource(curChannel)) == null) { + // generate a pull data transfer from a depending in/ref resource. + Type srcResType2 = src2.getResourceStateType(); + String srcResName2 = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(src2.getResourceHierarchy())); + String dependingGetter = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(src2, resourceNode.getInSideResource(curChannel)).toImplementation(new String[] {}); + stateGetter.addStatement(srcResType2.getInterfaceTypeName() + " " + srcResName2 + " = " + dependingGetter + ";"); + } + } + } + // collect the message constraints by a descendant channel. + List varsForSideEffects = new ArrayList<>(); + int v = 0; + resourcePathsAndMessage = curChannel.fillOutsideResourcePaths(out, JavaCodeGenerator.pullAccessor, null); + if (resourcePathsAndMessage != null) { + resourcePaths = resourcePathsAndMessage.getKey(); + Term messageTermSub = resourcePathsAndMessage.getValue(); + for (Entry fieldEnt: ((Term) messageTermSub).getSubTerms(Field.class).entrySet()) { + Position pos = fieldEnt.getKey(); + Field field = fieldEnt.getValue(); + Variable var = new Variable(field.getSymbol().getName(), field.getType()); + ((Term) messageTermSub).replaceSubTerm(pos, var); + } + for (Map.Entry subTermEnt: messageTermSub.getSubTerms(Term.class).entrySet()) { + Term subTerm = subTermEnt.getValue(); + if (!(subTerm instanceof Constant) && subTerm.getSymbol().isImplWithSideEffect()) { + Variable var = new Variable("v" + v, subTerm.getType()); + varsForSideEffects.add(var); + v++; + // Add a side effect statement within the loop + Position pos = new Position(); + pos.addHeadOrder(0); + subTerm.replaceSubTerm(pos, var); + String[] sideEffects = new String[] {""}; + String curState = messageTermSub.toImplementation(sideEffects); + stateGetter.addStatement(sideEffects[0].replaceAll("\n", "")); + // Cancel the side effects in the return value. + pos = subTermEnt.getKey(); + messageTermSub.replaceSubTerm(pos, var); + } + } + if (messageTerm == null) { + messageTerm = messageTermSub; + } else { + messageTerm = (Term) messageTerm.unify(messageTermSub); + } + if (messageTerm == null) { + throw new UnificationFailed(); + } + } + // enclosed by a for loop + Expression selExp = curChannel.getSelectors().get(0).getExpression(); + Type selType = null; + String varName = null; + if (selExp instanceof Variable) { + selType = ((Variable) selExp).getType(); + varName = ((Variable) selExp).getName(); + ChannelMember insideChMem = null; + for (ChannelMember cm2 :curChannel.getInputChannelMembers()) { + if (!cm2.isOutside()) { + insideChMem = cm2; + break; + } + } + if (insideChMem == null) { + for (ChannelMember cm2 :curChannel.getReferenceChannelMembers()) { + if (!cm2.isOutside()) { + insideChMem = cm2; + break; + } + } + } + ResourcePath insideResPath = insideChMem.getResource(); + while (insideResPath.getParent() != null && (insideResPath.getLastParam() == null || !insideResPath.getLastParam().equals(selExp))) { + insideResPath = insideResPath.getParent(); + } + insideResPath = insideResPath.getParent(); + String parent = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(insideResPath, resourceNode.getInSideResource(curChannel)).toImplementation(new String[] {}); + if (insideResPath != null) { + if (selType.equals(DataConstraintModel.typeInt)) { + // make a for loop (for a list) for data collecting. + stateGetter.addFirstStatement("for (int " + varName + " = 0; " + varName +" < " + parent + ".size(); " + varName + "++) {"); + } else if (selType.equals(DataConstraintModel.typeString)) { + // make a for loop (for a map) for data collecting. + stateGetter.addFirstStatement("for (String " + varName + ": " + parent + ".keySet()) {"); + } + } + } + // initialize the variables to hold side effects within the loop + for (Variable var: varsForSideEffects) { + stateGetter.addFirstStatement(var.getType().getInterfaceTypeName() + " " + var.getName() + " = new " + var.getType().getImplementationTypeName() + "();"); + } + // end of the loop + stateGetter.addStatement("}"); + if (curChannel.getChildren() != null && curChannel.getChildren().size() > 0) { + channelItrStack.push(chItr); + chItr = curChannel.getChildren().iterator(); + } + } + } while (!channelItrStack.isEmpty()); + } + + // generate a return statement. + String[] sideEffects = new String[] {""}; + String curState = ch.deriveUpdateExpressionOf(out, messageTerm, getPullAccessor()).toImplementation(sideEffects); + stateGetter.addStatement(sideEffects[0] + langSpec.getReturnStatement(curState) + langSpec.getStatementDelimiter()); } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage | UnificationFailed | ValueUndefined e) { e.printStackTrace(); @@ -796,7 +980,7 @@ descendantGetter = langSpec.newMethodDeclaration(getterPrefix + descendantCompName, false, descendantType, params); } - fillDescendantGetterMethod(descendantGetter, descendant.getResourceHierarchy(), resourceNode.getResourceHierarchy(), component, langSpec); + fillDescendantGetterMethod(descendantGetter, descendant.getResourceHierarchy(), child.getResourceHierarchy(), resourceNode.getResourceHierarchy(), component, langSpec); component.addMethod(descendantGetter); } break; @@ -812,15 +996,30 @@ List constructorStatements = new ArrayList<>(); Map>> updateStatements = new HashMap<>(); for (Edge chToRes: resourceNode.getInEdges()) { - for (Edge resToCh: chToRes.getSource().getInEdges()) { - DataTransferChannel ch = ((ChannelNode) resToCh.getDestination()).getChannel(); + ChannelNode directSrcChannel = (ChannelNode) chToRes.getSource(); + DataTransferChannel ch = directSrcChannel.getChannel(); + // Should take into account the channel hierarchy. + Set ancestorSrcChannels = directSrcChannel.getAncestors(); + Set descendantSrcChannels = directSrcChannel.getDescendants(); + Set inEdges = new HashSet<>(); + inEdges.addAll(directSrcChannel.getInEdges()); + for (ChannelNode ancestorSrc: ancestorSrcChannels) { + inEdges.addAll(ancestorSrc.getInEdges()); + } + for (ChannelNode descendantSrc: descendantSrcChannels) { + inEdges.addAll(descendantSrc.getInEdges()); + } + for (Edge resToCh: inEdges) { + // For each data transfer from srcResPath:ResourcePath to resourceNode:ResourceNode. DataFlowEdge re = (DataFlowEdge) resToCh; - ResourcePath srcResPath = ((ResourceNode) re.getSource()).getOutSideResource(ch); + ChannelNode indirectSrcChNode = (ChannelNode) re.getDestination(); + DataTransferChannel indirectSrcCh = indirectSrcChNode.getChannel(); + ResourcePath srcResPath = ((ResourceNode) re.getSource()).getOutSideResource(indirectSrcCh); String srcResComponentName = getComponentName(srcResPath.getResourceHierarchy(), langSpec); String srcResName = langSpec.toVariableName(srcResComponentName); // Check if the input resource is outside of the channel scope. boolean outsideInputResource = false; - for (ChannelMember cm: ch.getInputChannelMembers()) { + for (ChannelMember cm: indirectSrcCh.getInputChannelMembers()) { if (cm.getResource().equals(srcResPath) && cm.isOutside()) { outsideInputResource = true; // Regarded as pull transfer. break; @@ -838,6 +1037,13 @@ } } } + // Also take into account the channel hierarchy to determine push/pull transfer. + if (ancestorSrcChannels.contains(indirectSrcChNode)) { + outsideOutputResource = true; // Regarded as (broadcasting) push transfer. + } + if (descendantSrcChannels.contains(indirectSrcChNode)) { + outsideInputResource = true; // Regarded as (collecting) pull transfer. + } if ((((PushPullAttribute) re.getAttribute()).getSelectedOption() == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) { // for push data transfer @@ -940,14 +1146,20 @@ if (generatesComponent(outRes)) { if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { updateStatement = sideEffects[0]; + if (updateStatement.endsWith("\n")) { + updateStatement = updateStatement.substring(0, updateStatement.length() - 1); + } } else { updateStatement = sideEffects[0] + langSpec.getFieldAccessor(fieldOfResourceState) + langSpec.getAssignment() + newState + langSpec.getStatementDelimiter(); // this.value = ... } } else { if (sideEffects[0] != null) { - updateStatement = sideEffects[0]; + updateStatement = sideEffects[0]; String resourceName = langSpec.toVariableName(getComponentName(outRes, langSpec)); updateStatement = updateStatement.replace(langSpec.getFieldAccessor(fieldOfResourceState), langSpec.getFieldAccessor(resourceName)); + if (updateStatement.endsWith("\n")) { + updateStatement = updateStatement.substring(0, updateStatement.length() - 1); + } } if (DataConstraintModel.typeList.isAncestorOf(resourceNode.getParent().getResourceStateType())) { Term selector = new Term(DataConstraintModel.set); @@ -1018,8 +1230,8 @@ } // Declare the field to cache the state of the source resource in the type of the destination resource. - if (resToCh.getDestination().getIndegree() > 1 - || (resToCh.getDestination().getIndegree() == 1 && ch.getInputChannelMembers().iterator().next().getStateTransition().isRightPartial())) { + if (resToCh.getDestination().getIndegree() + ch.getInputChannelMembers().size() > 1 + || (ch.getInputChannelMembers().size() == 1 && ch.getInputChannelMembers().iterator().next().getStateTransition().isRightPartial())) { // If incoming edges are multiple, or the current state of an input member is needed. if (langSpec.declareField()) { // Declare the cache field. @@ -1099,13 +1311,14 @@ // Add an invocation to another update method (for a chain of update method invocations). for (Edge resToCh2: resourceNode.getOutEdges()) { DataFlowEdge dOut = (DataFlowEdge) resToCh2; - DataTransferChannel ch2 = ((ChannelNode) resToCh2.getDestination()).getChannel(); + ChannelNode directDstChNode = (ChannelNode) resToCh2.getDestination(); + DataTransferChannel directDstCh = directDstChNode.getChannel(); // Check if the input resource is outside of the channel scope. boolean outsideInputResource2 = false; ChannelMember in = null; Set outsideInputMembers2 = new HashSet<>(); - for (ChannelMember cm: ch2.getInputChannelMembers()) { - if (resourceNode.getOutSideResources().contains(cm.getResource())) { + for (ChannelMember cm: directDstCh.getInputChannelMembers()) { + if (cm.getResource().equals(resourceNode.getOutSideResource(directDstCh))) { if (cm.isOutside()) { outsideInputResource2 = true; // Regarded as pull transfer. } @@ -1115,8 +1328,22 @@ outsideInputMembers2.add(cm); } } - for (Edge chToRes2: resToCh2.getDestination().getOutEdges()) { + // Should take into account the channel hierarchy. + Set ancestorDstChannels = directDstChNode.getAncestors(); + Set descendantDstChannels = directDstChNode.getDescendants(); + Set outEdges = new HashSet<>(); + outEdges.addAll(directDstChNode.getOutEdges()); + for (ChannelNode ancestorDst: ancestorDstChannels) { + outEdges.addAll(ancestorDst.getOutEdges()); + } + for (ChannelNode descendantDst: descendantDstChannels) { + outEdges.addAll(descendantDst.getOutEdges()); + } + for (Edge chToRes2: outEdges) { + // For each data transfer to dstNode:ResourceNode. ResourceNode dstNode = ((ResourceNode) chToRes2.getDestination()); + ChannelNode chNode2 = (ChannelNode) chToRes2.getSource(); + DataTransferChannel ch2 = chNode2.getChannel(); // Check if the output resource is outside of the channel scope. boolean outsideOutputResource2 = false; ChannelMember out = null; @@ -1129,45 +1356,89 @@ } } } + // Also take into account the channel hierarchy to determine push/pull transfer. + if (descendantDstChannels.contains(chNode2)) { + outsideOutputResource2 = true; // Regarded as (broadcasting) push transfer. + } + if (ancestorDstChannels.contains(chNode2)) { + outsideInputResource2 = true; // Regarded as (collecting) pull transfer. + } if ((((PushPullAttribute) dOut.getAttribute()).getSelectedOption() == PushPullValue.PUSH && !outsideInputResource2) || outsideOutputResource2) { // PUSH transfer + boolean addForStatement = false; + String forVarName = null; + if (descendantDstChannels.contains(chNode2)) { + // For hierarchical channels (broadcasting push transfer). + if (ch2.getSelectors() != null && ch2.getSelectors().size() > 0) { + Expression selExp = ch2.getSelectors().get(0).getExpression(); + Type selType = null; + if (selExp instanceof Variable) { + selType = ((Variable) selExp).getType(); + forVarName = ((Variable) selExp).getName(); + ChannelMember insideChMem = null; + for (ChannelMember cm :ch2.getInputChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + if (insideChMem == null) { + for (ChannelMember cm :ch2.getReferenceChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + } + if (insideChMem == null) { + for (ChannelMember cm :ch2.getOutputChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + } + ResourcePath insideResPath = insideChMem.getResource(); + while (insideResPath.getParent() != null && (insideResPath.getLastParam() == null || !insideResPath.getLastParam().equals(selExp))) { + insideResPath = insideResPath.getParent(); + } + insideResPath = insideResPath.getParent(); + String parent = getPullAccessor().getDirectStateAccessorFor(insideResPath, resourceNode.getPrimaryResourcePath()).toImplementation(new String[] {}); + if (insideResPath != null) { + if (selType.equals(DataConstraintModel.typeInt)) { + // make a for loop (for a list) for broadcasting. + update.addStatement(langSpec.getForStatement(forVarName, parent)); + addForStatement = true; + } else if (selType.equals(DataConstraintModel.typeString)) { + // make a for loop (for a map) for broadcasting. + update.addStatement(langSpec.getForStatement(forVarName, DataConstraintModel.typeString.getInterfaceTypeName(), parent)); + addForStatement = true; + } + } + } else if (selExp instanceof Term) { + // not supported. + } + } + } + // Get the value of reference member to call the update method. List params = new ArrayList<>(); - // Values of path parameters. - for (Expression pathParam: out.getResource().getPathParams()) { - if (pathParam instanceof Variable) { - Variable pathVar = (Variable) pathParam; - params.add(pathVar.getName()); - } - } - // Values of channel parameters. - for (Selector selector: ch.getAllSelectors()) { - if (selector.getExpression() instanceof Variable) { - Variable selVar = (Variable) selector.getExpression(); - params.add(selVar.getName()); - } - } - // Value of the source side (input side) resource. - ResourceHierarchy srcRes2 = resourceNode.getResourceHierarchy(); - if (generatesComponent(srcRes2)) { - params.add(langSpec.getFieldAccessor(fieldOfResourceState)); - } else { - params.add(langSpec.getFieldAccessor(langSpec.toVariableName(srcRes2.getResourceName()))); - srcRes2 = srcRes2.getParent(); - } Map> referredResources = new HashMap<>(); Set referredSet = referredResources.get(update); for (ChannelMember rc: ch2.getReferenceChannelMembers()) { - // to get the value of reference member. ResourcePath ref = rc.getResource(); if (referredSet == null) { referredSet = new HashSet<>(); referredResources.put(update, referredSet); } if (!resourceNode.getInSideResources().contains(ref)) { - String refVarName = ref.getLeafResourceName(); + String refVarName = langSpec.toVariableName(getComponentName(ref.getResourceHierarchy(), langSpec)); if (!referredSet.contains(ref)) { referredSet.add(ref); - Expression refGetter = getPullAccessor().getCurrentStateAccessorFor(rc, in); + ResourcePath srcRes = in.getResource(); + if (!generatesComponent(srcRes.getResourceHierarchy())) { + srcRes = srcRes.getParent(); + } + Expression refGetter = getPullAccessor().getDirectStateAccessorFor(ref, srcRes); String[] sideEffects = new String[] {""}; String refExp = refGetter.toImplementation(sideEffects); String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); @@ -1176,6 +1447,62 @@ params.add(refVarName); } } + // Update fields to refer to outside resources. + try { + Map>> resourcePaths = ch2.fillOutsideResourcePaths(out, getPullAccessor()); + if (resourcePaths != null && resourcePaths.size() > 0) { + for (ChannelMember outsideMember: resourcePaths.keySet()) { + ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey(); + if (!generatesComponent(outsidePath.getResourceHierarchy())) { + outsidePath = outsidePath.getParent(); + } + String outsideResName = langSpec.toVariableName(getComponentName(outsidePath.getResourceHierarchy(), langSpec)); + Expression outsideExp = getPullAccessor().getDirectStateAccessorFor(outsidePath, null); + if (generatesComponent(outsidePath.getResourceHierarchy())) { + outsideExp = ((Term) outsideExp).getChild(0); + } + if (outsideExp instanceof Field) { + outsideExp = new Variable(((Field) outsideExp).getSymbol().getName(), ((Field) outsideExp).getType()); + } else if (outsideExp instanceof Term) { + for (Entry fieldEnt: ((Term) outsideExp).getSubTerms(Field.class).entrySet()) { + Position pos = fieldEnt.getKey(); + Field field = fieldEnt.getValue(); + Variable var = new Variable(field.getSymbol().getName(), field.getType()); + ((Term) outsideExp).replaceSubTerm(pos, var); + } + } + String[] sideEffects = new String[] {""}; + String outsideAccessor = outsideExp.toImplementation(sideEffects); + update.addStatement(langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter()); // change the reference field. + } + } + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork + | InvalidMessage | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + // Values of path parameters to call the update method. + for (Expression pathParam: out.getResource().getPathParams()) { + if (pathParam instanceof Variable) { + Variable pathVar = (Variable) pathParam; + params.add(pathVar.getName()); + } + } + // Values of channel parameters to call the update method. + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + params.add(selVar.getName()); + } + } + // Value of the source side (input side) resource to call the update method. + ResourceHierarchy srcRes2 = resourceNode.getResourceHierarchy(); + if (generatesComponent(srcRes2)) { + params.add(langSpec.getFieldAccessor(fieldOfResourceState)); + } else { + params.add(langSpec.getFieldAccessor(langSpec.toVariableName(srcRes2.getResourceName()))); + srcRes2 = srcRes2.getParent(); + } + // Call the update method. String updateMethodName = null; ResourceHierarchy dstRes = dstNode.getResourceHierarchy(); if (!generatesComponent(dstRes)) { @@ -1199,60 +1526,60 @@ update.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, params) + langSpec.getStatementDelimiter()); // this.dst.updateDstFromSrc(value, refParams); } + if (addForStatement) { + // Close the for loop + update.addStatement(langSpec.getEndForStatement(forVarName)); + } } } if (outsideInputMembers2.size() > 0) { if (!generatesComponent(resourceNode.getResourceHierarchy())) { // srcRes2 does not have a component. - ResourcePath srcRes2 = resourceNode.getOutSideResource(ch2); - for (ChannelMember out: ch2.getOutputChannelMembers()) { - if (!generatesComponent(out.getResource().getResourceHierarchy())) { - // Also dstRes2 does not have a component. - ResourcePath dstRes2 = out.getResource(); - if (srcRes2.getParent().equals(dstRes2.getParent())) { - Map>> resourcePaths = null; - try { - resourcePaths = ch2.fillOutsideResourcePaths(out, getPullAccessor()); - if (resourcePaths != null && resourcePaths.size() > 0) { - for (ChannelMember outsideMember: outsideInputMembers2) { - for (ChannelMember dependingMember: resourcePaths.get(outsideMember).getValue()) { - if (dependingMember.getResource().equals(srcRes2)) { - // An outside input resource path depends on srcRes. - ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey(); - if (!generatesComponent(outsidePath.getResourceHierarchy())) { - outsidePath = outsidePath.getParent(); - } - String outsideResName = langSpec.toVariableName(getComponentName(outsidePath.getResourceHierarchy(), langSpec)); - Expression outsideExp = getPullAccessor().getDirectStateAccessorFor(outsidePath, null); - if (generatesComponent(outsidePath.getResourceHierarchy())) { - outsideExp = ((Term) outsideExp).getChild(0); - } - Expression nextExp = dependingMember.getStateTransition().getNextStateExpression(); - if (nextExp != null && outsideExp instanceof Term) { - if (nextExp instanceof Variable) { - outsideExp = ((Term) outsideExp).substitute((Variable) nextExp, new Field(langSpec.toVariableName(getComponentName(dependingMember.getResource().getResourceHierarchy(), langSpec)))); - } else { - // ToDo. + ResourcePath srcRes2 = resourceNode.getOutSideResource(directDstCh); + for (Edge chToRes2: outEdges) { + ChannelNode chNode2 = (ChannelNode) chToRes2.getSource(); + DataTransferChannel ch2 = chNode2.getChannel(); + for (ChannelMember out: ch2.getOutputChannelMembers()) { + if (!generatesComponent(out.getResource().getResourceHierarchy())) { + // Also dstRes2 does not have a component. + ResourcePath dstRes2 = out.getResource(); + if (srcRes2.getParent().equals(dstRes2.getParent())) { + Map>> resourcePaths = null; + try { + resourcePaths = ch2.fillOutsideResourcePaths(out, getPullAccessor()); + if (resourcePaths != null && resourcePaths.size() > 0) { + for (ChannelMember outsideMember: outsideInputMembers2) { + for (ChannelMember dependedMember: resourcePaths.get(outsideMember).getValue()) { + if (dependedMember.getResource().equals(srcRes2)) { + // An outside input resource path depends on srcRes. + ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey(); + if (!generatesComponent(outsidePath.getResourceHierarchy())) { + outsidePath = outsidePath.getParent(); } - } - String[] sideEffects = new String[] {""}; - String outsideAccessor = outsideExp.toImplementation(sideEffects); - String updateReference = langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter(); - update.addStatement(updateReference); // Update the reference field. - // Update constructor. - if (component != null) { - MethodDeclaration constructor = getConstructor(component); - constructor.addStatement(updateReference); // Initialize the reference field. - } else if (parentComponent != null) { - constructorStatements.add(updateReference); + String outsideResName = langSpec.toVariableName(getComponentName(outsidePath.getResourceHierarchy(), langSpec)); + Expression outsideExp = getPullAccessor().getDirectStateAccessorFor(outsidePath, null); + if (generatesComponent(outsidePath.getResourceHierarchy())) { + outsideExp = ((Term) outsideExp).getChild(0); + } + String[] sideEffects = new String[] {""}; + String outsideAccessor = outsideExp.toImplementation(sideEffects); + String updateReference = langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter(); + update.addStatement(updateReference); // Update the reference field. + // Update constructor. + if (component != null) { + MethodDeclaration constructor = getConstructor(component); + constructor.addStatement(updateReference); // Initialize the reference field. + } else if (parentComponent != null) { + constructorStatements.add(updateReference); + } } } } } + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork + | InvalidMessage | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); } - } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork - | InvalidMessage | UnificationFailed | ValueUndefined e) { - e.printStackTrace(); } } } @@ -1558,6 +1885,9 @@ String updateStatement; if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { updateStatement = sideEffects[0]; + if (updateStatement.endsWith("\n")) { + updateStatement = updateStatement.substring(0, updateStatement.length() - 1); + } } else { updateStatement = sideEffects[0] + langSpec.getFieldAccessor(fieldOfResourceState) + langSpec.getAssignment() + newState + langSpec.getStatementDelimiter(); } @@ -1568,6 +1898,9 @@ updateStatement = sideEffects[0]; String resourceName = langSpec.toVariableName(getComponentName(resource, langSpec)); updateStatement = updateStatement.replace(langSpec.getFieldAccessor(fieldOfResourceState), langSpec.getFieldAccessor(resourceName)); + if (updateStatement.endsWith("\n")) { + updateStatement = updateStatement.substring(0, updateStatement.length() - 1); + } } if (DataConstraintModel.typeList.isAncestorOf(resourceNode.getParent().getResourceStateType())) { Term selector = new Term(DataConstraintModel.set); @@ -1602,12 +1935,13 @@ // Add an invocation to an update method (for a chain of update method invocations). for (Edge resToCh: resourceNode.getOutEdges()) { DataFlowEdge dOut = (DataFlowEdge) resToCh; - DataTransferChannel ch2 = ((ChannelNode) resToCh.getDestination()).getChannel(); + ChannelNode directDstChNode = (ChannelNode) resToCh.getDestination(); + DataTransferChannel directDstCh = directDstChNode.getChannel(); // Check if the input resource is outside of the channel scope. boolean outsideInputResource2 = false; ChannelMember in = null; Set outsideInputMembers2 = new HashSet<>(); - for (ChannelMember cm: ch2.getInputChannelMembers()) { + for (ChannelMember cm: directDstCh.getInputChannelMembers()) { if (resourceNode.getOutSideResources().contains(cm.getResource())) { if (cm.isOutside()) { outsideInputResource2 = true; // Regarded as pull transfer. @@ -1618,8 +1952,22 @@ outsideInputMembers2.add(cm); } } - for (Edge chToRes: resToCh.getDestination().getOutEdges()) { + // Should take into account the channel hierarchy. + Set ancestorDstChannels = directDstChNode.getAncestors(); + Set descendantDstChannels = directDstChNode.getDescendants(); + Set outEdges = new HashSet<>(); + outEdges.addAll(directDstChNode.getOutEdges()); + for (ChannelNode ancestorDst: ancestorDstChannels) { + outEdges.addAll(ancestorDst.getOutEdges()); + } + for (ChannelNode descendantDst: descendantDstChannels) { + outEdges.addAll(descendantDst.getOutEdges()); + } + for (Edge chToRes: outEdges) { + // For each data transfer to dstNode:ResourceNode. ResourceNode dstNode = ((ResourceNode) chToRes.getDestination()); + ChannelNode chNode2 = (ChannelNode) chToRes.getSource(); + DataTransferChannel ch2 = chNode2.getChannel(); // Check if the output resource is outside of the channel scope. boolean outsideOutputResource2 = false; ChannelMember out2 = null; @@ -1632,45 +1980,89 @@ } } } + // Also take into account the channel hierarchy to determine push/pull transfer. + if (descendantDstChannels.contains(chNode2)) { + outsideOutputResource2 = true; // Regarded as (broadcasting) push transfer. + } + if (ancestorDstChannels.contains(chNode2)) { + outsideInputResource2 = true; // Regarded as (collecting) pull transfer. + } if ((((PushPullAttribute) dOut.getAttribute()).getSelectedOption() == PushPullValue.PUSH && !outsideInputResource2) || outsideOutputResource2) { // PUSH transfer - Map> referredResources = new HashMap<>(); + boolean addForStatement = false; + String forVarName = null; + if (descendantDstChannels.contains(chNode2)) { + // For hierarchical channels (broadcasting push transfer). + if (ch2.getSelectors() != null && ch2.getSelectors().size() > 0) { + Expression selExp = ch2.getSelectors().get(0).getExpression(); + Type selType = null; + if (selExp instanceof Variable) { + selType = ((Variable) selExp).getType(); + forVarName = ((Variable) selExp).getName(); + ChannelMember insideChMem = null; + for (ChannelMember cm :ch2.getInputChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + if (insideChMem == null) { + for (ChannelMember cm :ch2.getReferenceChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + } + if (insideChMem == null) { + for (ChannelMember cm :ch2.getOutputChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + } + ResourcePath insideResPath = insideChMem.getResource(); + while (insideResPath.getParent() != null && (insideResPath.getLastParam() == null || !insideResPath.getLastParam().equals(selExp))) { + insideResPath = insideResPath.getParent(); + } + insideResPath = insideResPath.getParent(); + String parent = getPullAccessor().getDirectStateAccessorFor(insideResPath, resourceNode.getPrimaryResourcePath()).toImplementation(new String[] {}); + if (insideResPath != null) { + if (selType.equals(DataConstraintModel.typeInt)) { + // make a for loop (for a list) for broadcasting. + input.addStatement(langSpec.getForStatement(forVarName, parent)); + addForStatement = true; + } else if (selType.equals(DataConstraintModel.typeString)) { + // make a for loop (for a map) for broadcasting. + input.addStatement(langSpec.getForStatement(forVarName, DataConstraintModel.typeString.getInterfaceTypeName(), parent)); + addForStatement = true; + } + } + } else if (selExp instanceof Term) { + // not supported. + } + } + } + // Get the value of reference member to call the update method. List params = new ArrayList<>(); - // Values of path parameters. - for (Expression pathParam: out2.getResource().getPathParams()) { - if (pathParam instanceof Variable) { - Variable pathVar = (Variable) pathParam; - params.add(pathVar.getName()); - } - } - // Values of channel parameters. - for (Selector selector: ch2.getAllSelectors()) { - if (selector.getExpression() instanceof Variable) { - Variable selVar = (Variable) selector.getExpression(); - params.add(selVar.getName()); - } - } - // Value of the source side (input side) resource. - ResourceHierarchy srcRes = resourceNode.getResourceHierarchy(); - if (generatesComponent(srcRes)) { - params.add(langSpec.getFieldAccessor(fieldOfResourceState)); - } else { - params.add(langSpec.getFieldAccessor(langSpec.toVariableName(srcRes.getResourceName()))); - srcRes = srcRes.getParent(); - } + Map> referredResources = new HashMap<>(); Set referredSet = referredResources.get(input); for (ChannelMember rc: ch2.getReferenceChannelMembers()) { - // to get the value of reference member. ResourcePath ref = rc.getResource(); if (referredSet == null) { referredSet = new HashSet<>(); referredResources.put(input, referredSet); } if (!resourceNode.getOutSideResources().contains(ref)) { - String refVarName = ref.getLeafResourceName(); + String refVarName = langSpec.toVariableName(getComponentName(ref.getResourceHierarchy(), langSpec)); if (!referredSet.contains(ref)) { referredSet.add(ref); - Expression refGetter = getPullAccessor().getCurrentStateAccessorFor(rc, in); + ResourcePath srcRes = in.getResource(); + if (!generatesComponent(srcRes.getResourceHierarchy())) { + srcRes = srcRes.getParent(); + } + Expression refGetter = getPullAccessor().getDirectStateAccessorFor(ref, srcRes); String[] sideEffects = new String[] {""}; String refExp = refGetter.toImplementation(sideEffects); String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); @@ -1679,6 +2071,62 @@ params.add(refVarName); } } + // Update fields to refer to outside resources. + try { + Map>> resourcePaths = ch2.fillOutsideResourcePaths(out2, getPullAccessor()); + if (resourcePaths != null && resourcePaths.size() > 0) { + for (ChannelMember outsideMember: resourcePaths.keySet()) { + ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey(); + if (!generatesComponent(outsidePath.getResourceHierarchy())) { + outsidePath = outsidePath.getParent(); + } + String outsideResName = langSpec.toVariableName(getComponentName(outsidePath.getResourceHierarchy(), langSpec)); + Expression outsideExp = getPullAccessor().getDirectStateAccessorFor(outsidePath, null); + if (generatesComponent(outsidePath.getResourceHierarchy())) { + outsideExp = ((Term) outsideExp).getChild(0); + } + if (outsideExp instanceof Field) { + outsideExp = new Variable(((Field) outsideExp).getSymbol().getName(), ((Field) outsideExp).getType()); + } else if (outsideExp instanceof Term) { + for (Entry fieldEnt: ((Term) outsideExp).getSubTerms(Field.class).entrySet()) { + Position pos = fieldEnt.getKey(); + Field field = fieldEnt.getValue(); + Variable var = new Variable(field.getSymbol().getName(), field.getType()); + ((Term) outsideExp).replaceSubTerm(pos, var); + } + } + String[] sideEffects = new String[] {""}; + String outsideAccessor = outsideExp.toImplementation(sideEffects); + input.addStatement(langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter()); // change the reference field. + } + } + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork + | InvalidMessage | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + // Values of path parameters to call the update method. + for (Expression pathParam: out2.getResource().getPathParams()) { + if (pathParam instanceof Variable) { + Variable pathVar = (Variable) pathParam; + params.add(pathVar.getName()); + } + } + // Values of channel parameters to call the update method. + for (Selector selector: ch2.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + params.add(selVar.getName()); + } + } + // Value of the source side (input side) resource to call the update method. + ResourceHierarchy srcRes = resourceNode.getResourceHierarchy(); + if (generatesComponent(srcRes)) { + params.add(langSpec.getFieldAccessor(fieldOfResourceState)); + } else { + params.add(langSpec.getFieldAccessor(langSpec.toVariableName(srcRes.getResourceName()))); + srcRes = srcRes.getParent(); + } + // Call the update method. String updateMethodName = null; ResourceHierarchy dstRes = dstNode.getResourceHierarchy(); if (!generatesComponent(dstRes)) { @@ -1702,22 +2150,26 @@ input.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, params) + langSpec.getStatementDelimiter()); // this.dst.updateDstFromSrc(value, refParams); } + if (addForStatement) { + // Close the for loop. + input.addStatement(langSpec.getEndForStatement(forVarName)); + } } } if (outsideInputMembers2.size() > 0) { if (!generatesComponent(resourceNode.getResourceHierarchy())) { - ResourcePath srcRes2 = resourceNode.getOutSideResource(ch2); - for (ChannelMember out2: ch2.getOutputChannelMembers()) { + ResourcePath srcRes2 = resourceNode.getOutSideResource(directDstCh); + for (ChannelMember out2: directDstCh.getOutputChannelMembers()) { if (!generatesComponent(out2.getResource().getResourceHierarchy())) { ResourcePath dstRes2 = out2.getResource(); if (srcRes2.getParent().equals(dstRes2.getParent())) { Map>> resourcePaths = null; try { - resourcePaths = ch2.fillOutsideResourcePaths(out2, getPullAccessor()); + resourcePaths = directDstCh.fillOutsideResourcePaths(out2, getPullAccessor()); if (resourcePaths != null && resourcePaths.size() > 0) { for (ChannelMember outsideMember: outsideInputMembers2) { - for (ChannelMember dependingMember: resourcePaths.get(outsideMember).getValue()) { - if (dependingMember.getResource().equals(srcRes2)) { + for (ChannelMember dependedMember: resourcePaths.get(outsideMember).getValue()) { + if (dependedMember.getResource().equals(srcRes2)) { // An outside input resource path depends on srcRes. ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey(); if (!generatesComponent(outsidePath.getResourceHierarchy())) { @@ -1728,14 +2180,6 @@ if (generatesComponent(outsidePath.getResourceHierarchy())) { outsideExp = ((Term) outsideExp).getChild(0); } - Expression nextExp = dependingMember.getStateTransition().getNextStateExpression(); - if (nextExp != null && outsideExp instanceof Term) { - if (nextExp instanceof Variable) { - outsideExp = ((Term) outsideExp).substitute((Variable) nextExp, new Field(langSpec.toVariableName(getComponentName(dependingMember.getResource().getResourceHierarchy(), langSpec)))); - } else { - // ToDo. - } - } String[] sideEffects = new String[] {""}; String outsideAccessor = outsideExp.toImplementation(sideEffects); input.addStatement(langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter()); // change the reference field. diff --git a/AlgebraicDataflowArchitectureModel/src/generators/ILanguageSpecific.java b/AlgebraicDataflowArchitectureModel/src/generators/ILanguageSpecific.java index 6c86540..baf40f3 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/ILanguageSpecific.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/ILanguageSpecific.java @@ -35,6 +35,10 @@ String getConstructorInvocation(String componentName, List parameters); String getReturnStatement(String returnValue); String getIfStatement(Term condition, String block); + String getForStatement(String varName, String list); + String getForStatement(String varName, String varType, String map); + String getEndForStatement(); + String getEndForStatement(String varName); String toComponentName(String name); String toVariableName(String name); String getMainComponentName(); diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JavaCodeGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/JavaCodeGenerator.java index 5dce75b..b39e9fc 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/JavaCodeGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/JavaCodeGenerator.java @@ -279,7 +279,7 @@ declareAccessorMethodInMainComponent(resourceNode, mainComponent); } if (component != null) { - // Declare the getter methods in this resource to obtain descendant resources. + // (#1) Declare the getter methods in this resource to obtain descendant resources. (complementary to #2) Set descendants = descendantGetters.get(resourceNode.getResourceHierarchy()); if (descendants == null) { descendants = new HashSet<>(); @@ -365,7 +365,7 @@ } } - // Declare the getter method to obtain the resource state in an ancestor component. + // (#2) Declare the getter method to obtain the resource state in an ancestor component. (complementary to #1) if (component == null) { // No component is created for this resource. ResourceNode ancestorNode = resourceNode; @@ -417,17 +417,31 @@ // Declare reference fields for push data transfer. for (Edge resToCh : resourceNode.getOutEdges()) { DataFlowEdge re = (DataFlowEdge) resToCh; - DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + ChannelNode directDstChNode = (ChannelNode) re.getDestination(); + DataTransferChannel directDstCh = directDstChNode.getChannel(); // Check if the input resource is outside of the channel scope. boolean outsideInputResource = false; - for (ChannelMember cm: ch.getInputChannelMembers()) { - if (resourceNode.getOutSideResources().contains(cm.getResource()) && cm.isOutside()) { + for (ChannelMember cm: directDstCh.getInputChannelMembers()) { + if (cm.getResource().equals(resourceNode.getOutSideResource(directDstCh)) && cm.isOutside()) { outsideInputResource = true; // Regarded as pull transfer. break; } } - for (Edge chToRes: re.getDestination().getOutEdges()) { + // Should take into account the channel hierarchy. + Set ancestorDstChannels = directDstChNode.getAncestors(); + Set descendantDstChannels = directDstChNode.getDescendants(); + Set outEdges = new HashSet<>(); + outEdges.addAll(directDstChNode.getOutEdges()); + for (ChannelNode ancestorDst: ancestorDstChannels) { + outEdges.addAll(ancestorDst.getOutEdges()); + } + for (ChannelNode descendantDst: descendantDstChannels) { + outEdges.addAll(descendantDst.getOutEdges()); + } + for (Edge chToRes: outEdges) { ResourceHierarchy dstRes = ((ResourceNode) chToRes.getDestination()).getResourceHierarchy(); + ChannelNode chNode = (ChannelNode) chToRes.getSource(); + DataTransferChannel ch = chNode.getChannel(); // Check if the output resource is outside of the channel scope. boolean outsideOutputResource = false; for (ChannelMember cm: ch.getOutputChannelMembers()) { @@ -436,6 +450,13 @@ break; } } + // Also take into account the channel hierarchy to determine push/pull transfer. + if (descendantDstChannels.contains(chNode)) { + outsideOutputResource = true; // Regarded as (broadcasting) push transfer. + } + if (ancestorDstChannels.contains(chNode)) { + outsideInputResource = true; // Regarded as (collecting) pull transfer. + } if ((((PushPullAttribute) re.getAttribute()).getSelectedOption() == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) { // Declare a field to refer to the destination resource of push transfer. if (!generatesComponent(dstRes)) { @@ -484,16 +505,32 @@ } } } - // Declare update methods for push data transfer and reference fields for pull data transfer. + // Declare update methods called by other resources for push data transfer + // and reference fields for pull data transfer. for (Edge chToRes : resourceNode.getInEdges()) { - for (Edge resToCh: chToRes.getSource().getInEdges()) { + ChannelNode directSrcChannel = (ChannelNode) chToRes.getSource(); + DataTransferChannel ch = directSrcChannel.getChannel(); + // Should take into account the channel hierarchy. + Set ancestorSrcChannels = directSrcChannel.getAncestors(); + Set descendantSrcChannels = directSrcChannel.getDescendants(); + Set inEdges = new HashSet<>(); + inEdges.addAll(directSrcChannel.getInEdges()); + for (ChannelNode ancestorSrc: ancestorSrcChannels) { + inEdges.addAll(ancestorSrc.getInEdges()); + } + for (ChannelNode descendantSrc: descendantSrcChannels) { + inEdges.addAll(descendantSrc.getInEdges()); + } + for (Edge resToCh: inEdges) { + // For each data transfer from srcResPath:ResourcePath to resourceNode:ResourceNode. DataFlowEdge re = (DataFlowEdge) resToCh; - DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); - ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(ch); + ChannelNode indirectSrcChNode = (ChannelNode) re.getDestination(); + DataTransferChannel indirectSrcCh = indirectSrcChNode.getChannel(); + ResourcePath srcResPath = ((ResourceNode) re.getSource()).getOutSideResource(indirectSrcCh); // Check if the input resource is outside of the channel scope. boolean outsideInputResource = false; - for (ChannelMember cm: ch.getInputChannelMembers()) { - if (cm.getResource().equals(srcRes) && cm.isOutside()) { + for (ChannelMember cm: indirectSrcCh.getInputChannelMembers()) { + if (cm.getResource().equals(srcResPath) && cm.isOutside()) { outsideInputResource = true; // Regarded as pull transfer. break; } @@ -510,11 +547,18 @@ } } } - String srcResName = getComponentName(srcRes.getResourceHierarchy()); - ResourcePath srcRes2 = srcRes; + // Also take into account the channel hierarchy to determine push/pull transfer. + if (ancestorSrcChannels.contains(indirectSrcChNode)) { + outsideOutputResource = true; // Regarded as (broadcasting) push transfer. + } + if (descendantSrcChannels.contains(indirectSrcChNode)) { + outsideInputResource = true; // Regarded as (collecting) pull transfer. + } + String srcResName = getComponentName(srcResPath.getResourceHierarchy()); + ResourcePath srcRes2 = srcResPath; String srcResName2 = srcResName; - if (!generatesComponent(srcRes.getResourceHierarchy())) { - srcRes2 = srcRes.getParent(); + if (!generatesComponent(srcResPath.getResourceHierarchy())) { + srcRes2 = srcResPath.getParent(); srcResName2 = getComponentName(srcRes2.getResourceHierarchy()); } if ((((PushPullAttribute) re.getAttribute()).getSelectedOption() != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { @@ -579,7 +623,7 @@ params.add(chParam); // A channel parameter to specify the context of the collaboration. } } - params.add(new VariableDeclaration(srcRes.getResourceStateType(), srcRes.getLeafResourceName())); // The state of the source resource to carry the data-flow. + params.add(new VariableDeclaration(srcResPath.getResourceStateType(), srcResPath.getLeafResourceName())); // The state of the source resource to carry the data-flow. for (ResourcePath ref: ch.getReferenceResources()) { if (!ref.equals(resourceNode.getInSideResource(ch))) { params.add(new VariableDeclaration(ref.getResourceStateType(), ref.getLeafResourceName())); @@ -1170,7 +1214,7 @@ : DataConstraintModel.typeInt); } // use the cached value as the current state - return new Field(targetRes.getLeafResourceName(), + return new Field(toVariableName(getComponentName(targetRes.getResourceHierarchy())), targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } @@ -1178,7 +1222,7 @@ @Override public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { ResourcePath targetRes = target.getResource(); - return new Parameter(targetRes.getLeafResourceName(), + return new Parameter(toVariableName(getComponentName(targetRes.getResourceHierarchy())), targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } @@ -1191,7 +1235,7 @@ : DataConstraintModel.typeInt); } // for reference channel member - return new Parameter(targetRes.getLeafResourceName(), + return new Parameter(toVariableName(getComponentName(targetRes.getResourceHierarchy())), targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } @@ -1257,11 +1301,25 @@ : DataConstraintModel.typeInt); } // for reference channel member - Term getter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); - getter.addChild(new Field(targetRes.getLeafResourceName(), targetRes.getResourceStateType())); - return getter; + if (fromRes.isAncestorOf(targetRes)) { + Stack pathStack = new Stack<>(); + ResourcePath curPath = targetRes; + do { + pathStack.push(curPath); + curPath = curPath.getParent(); + } while (!curPath.equals(fromRes)); + // iterate from the fromRes resource + return getRelativePath(targetRes, pathStack); + } + if (generatesComponent(targetRes.getResourceHierarchy())) { + Term getter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); + getter.addChild(new Field(toVariableName(getComponentName(targetRes.getResourceHierarchy())), targetRes.getResourceStateType())); + return getter; + } else { + return new Field(toVariableName(getComponentName(targetRes.getResourceHierarchy())), targetRes.getResourceStateType()); + } } else { - // access from the outside of the hierarchy + // (#3) access from the outside of the hierarchy (must be kept consistent with #4) Stack pathStack = new Stack<>(); ResourcePath curPath = targetRes; do { @@ -1269,85 +1327,75 @@ curPath = curPath.getParent(); } while (curPath != null); // iterate from the root resource - Term getter = null; - int v = 1; - int arity = 2; - while (!pathStack.empty()) { - curPath = pathStack.pop(); - String typeName = getComponentName(curPath.getResourceHierarchy()); - if (getter == null) { - // root resource - String fieldName = toVariableName(typeName); - getter = new Field(fieldName, new Type(typeName, typeName)); - } else { - if (generatesComponent(curPath.getResourceHierarchy())) { - if (arity == 2) { - Term newGetter = new Term(new Symbol("get" + typeName, -1, Symbol.Type.METHOD)); - newGetter.addChild(getter); - if (curPath.getResourceHierarchy().getNumParameters() > 0) { - Variable var = null; - Expression param = curPath.getLastParam(); - if (param instanceof Variable) { - var = (Variable) param; - } else if (param instanceof Term) { - var = new Variable("v" + v, ((Term) param).getType()); - } - if (var != null) { - newGetter.addChild(var); - newGetter.getSymbol().setArity(2); - } - v++; - } - getter = newGetter; - } else { - if (curPath.getResourceHierarchy().getNumParameters() > 0) { - Variable var = null; - Expression param = curPath.getLastParam(); - if (param instanceof Variable) { - var = (Variable) param; - } else if (param instanceof Term) { - var = new Variable("v" + v, ((Term) param).getType()); - } - if (var != null) { - getter.getSymbol().setArity(arity); - getter.addChild(var); - } - v++; - } - } - arity = 2; - } else { - // to get a descendant resource directly. - if (arity == 2) { - Term newGetter = new Term(new Symbol("get" + typeName, -1, Symbol.Type.METHOD)); - newGetter.addChild(getter); - getter = newGetter; - } + return getRelativePath(targetRes, pathStack); + } + } + + private Expression getRelativePath(ResourcePath targetRes, Stack pathStack) { + ResourcePath curPath; + Term getter = null; + int arity = 2; + boolean doesChainInvocations = true; + while (!pathStack.empty()) { + curPath = pathStack.pop(); + String typeName = getComponentName(curPath.getResourceHierarchy()); + if (getter == null && generatesComponent(curPath.getResourceHierarchy())) { + // root resource + String fieldName = toVariableName(typeName); + getter = new Field(fieldName, new Type(typeName, typeName)); + } else { + if (generatesComponent(curPath.getResourceHierarchy())) { + if (arity == 2) { + Term newGetter = new Term(new Symbol("get" + typeName, -1, Symbol.Type.METHOD)); + newGetter.addChild(getter); if (curPath.getResourceHierarchy().getNumParameters() > 0) { - Variable var = null; Expression param = curPath.getLastParam(); - if (param instanceof Variable) { - var = (Variable) param; - } else if (param instanceof Term) { - var = new Variable("v" + v, ((Term) param).getType()); + if (param != null) { + newGetter.addChild(param); + newGetter.getSymbol().setArity(2); } - if (var != null) { + } + getter = newGetter; + } else { + // add the last path parameter. + if (curPath.getResourceHierarchy().getNumParameters() > 0) { + Expression param = curPath.getLastParam(); + if (param != null) { getter.getSymbol().setArity(arity); - getter.addChild(var); - arity++; + getter.addChild(param); } - v++; + } + } + arity = 2; + doesChainInvocations = true; + } else { + // to get a descendant resource directly. (e.g, .todos.{year}.{month}.{day}.{id} ==> .getTodos().getTodo(year, month, day, id)) + if (doesChainInvocations) { + Term newGetter = new Term(new Symbol("get" + typeName, -1, Symbol.Type.METHOD)); + newGetter.addChild(getter); + getter = newGetter; + doesChainInvocations = false; + } + if (curPath.getResourceHierarchy().getNumParameters() > 0) { + // may change the symbol name + getter.getSymbol().changeName("get" + typeName); + // add a path parameter. + Expression param = curPath.getLastParam(); + if (param != null) { + getter.getSymbol().setArity(arity); + getter.addChild(param); + arity++; } } } } - if (generatesComponent(targetRes.getResourceHierarchy())) { - Term newGetter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); - newGetter.addChild(getter); - getter = newGetter; - } - return getter; } + if (generatesComponent(targetRes.getResourceHierarchy())) { + Term newGetter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); + newGetter.addChild(getter); + getter = newGetter; + } + return getter; } }; @@ -1362,7 +1410,7 @@ : DataConstraintModel.typeInt); } // for reference channel member - return new Parameter(targetRes.getLeafResourceName(), + return new Parameter(toVariableName(getComponentName(targetRes.getResourceHierarchy())), targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } @@ -1370,7 +1418,7 @@ @Override public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { ResourcePath targetRes = target.getResource(); - return new Parameter(targetRes.getLeafResourceName(), + return new Parameter(toVariableName(getComponentName(targetRes.getResourceHierarchy())), targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } @@ -1383,7 +1431,7 @@ : DataConstraintModel.typeInt); } // for reference channel member - return new Parameter(targetRes.getLeafResourceName(), + return new Parameter(toVariableName(getComponentName(targetRes.getResourceHierarchy())), targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java index affec83..58eaa0f 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/JavaMethodBodyGenerator.java @@ -8,6 +8,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.Stack; import java.util.Map.Entry; import code.ast.CompilationUnit; @@ -66,13 +67,28 @@ if (!resToCh.isChannelToResource()) { PushPullAttribute pushPull = (PushPullAttribute) resToCh.getAttribute(); ResourceNode src = (ResourceNode) resToCh.getSource(); - for (Edge chToRes: resToCh.getDestination().getOutEdges()) { + ChannelNode directDstChNode = (ChannelNode) resToCh.getDestination(); + DataTransferChannel directDstCh = directDstChNode.getChannel(); + // Should take into account the channel hierarchy. + Set ancestorDstChannels = directDstChNode.getAncestors(); + Set descendantDstChannels = directDstChNode.getDescendants(); + Set outEdges = new HashSet<>(); + outEdges.addAll(directDstChNode.getOutEdges()); + for (ChannelNode ancestorDst: ancestorDstChannels) { + outEdges.addAll(ancestorDst.getOutEdges()); + } + for (ChannelNode descendantDst: descendantDstChannels) { + outEdges.addAll(descendantDst.getOutEdges()); + } + for (Edge chToRes: outEdges) { ResourceNode dst = (ResourceNode) chToRes.getDestination(); String srcResourceName = JavaCodeGenerator.getComponentName(src.getResourceHierarchy()); String dstResourceName = JavaCodeGenerator.getComponentName(dst.getResourceHierarchy()); TypeDeclaration srcComponent = componentMap.get(srcResourceName); TypeDeclaration dstComponent = componentMap.get(dstResourceName); - DataTransferChannel ch = ((ChannelNode) resToCh.getDestination()).getChannel(); +// DataTransferChannel ch = ((ChannelNode) resToCh.getDestination()).getChannel(); + ChannelNode chNode = (ChannelNode) chToRes.getSource(); + DataTransferChannel ch = chNode.getChannel(); for (ChannelMember out: ch.getOutputChannelMembers()) { if (dst.getInSideResources().contains(out.getResource())) { // Check if the input resource is outside of the channel scope. @@ -85,6 +101,13 @@ } // Check if the output resource is outside of the channel scope. boolean outsideOutputResource = out.isOutside(); + // Take into account the channel hierarchy. + if (descendantDstChannels.contains(chNode)) { + outsideOutputResource = true; // Regarded as (broadcasting) push transfer. + } + if (ancestorDstChannels.contains(chNode)) { + outsideInputResource = true; // Regarded as (collecting) pull transfer. + } if ((pushPull.getSelectedOption() == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) { // for push data transfer MethodDeclaration update = null; @@ -152,6 +175,9 @@ if (JavaCodeGenerator.generatesComponent(outRes)) { if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { updateStatement = sideEffects[0]; + if (updateStatement.endsWith("\n")) { + updateStatement = updateStatement.substring(0, updateStatement.length() - 1); + } } else { updateStatement = sideEffects[0] + "this.value = " + newState + ";"; } @@ -159,6 +185,9 @@ if (sideEffects[0] != null) { updateStatement = sideEffects[0]; updateStatement = updateStatement.replace(".value", "." + JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(outRes))); + if (updateStatement.endsWith("\n")) { + updateStatement = updateStatement.substring(0, updateStatement.length() - 1); + } } if (DataConstraintModel.typeList.isAncestorOf(outRes.getParent().getResourceStateType())) { Term selector = new Term(DataConstraintModel.set); @@ -221,7 +250,7 @@ } } if (resToCh.getDestination().getIndegree() > 1 - || (resToCh.getDestination().getIndegree() == 1 && ch.getInputChannelMembers().iterator().next().getStateTransition().isRightPartial())) { + || (resToCh.getDestination().getIndegree() == 1 && directDstCh.getInputChannelMembers().iterator().next().getStateTransition().isRightPartial())) { // update a cache of src side resource (when incoming edges are multiple) String cacheStatement = "this." + JavaCodeGenerator.toVariableName(srcResourceName) + " = " + JavaCodeGenerator.toVariableName(srcResourceName) + ";"; if (update.getBody() == null || !update.getBody().getStatements().contains(cacheStatement)) { @@ -279,7 +308,7 @@ } // src side (for a chain of update method invocations) ChannelMember in = null; - for (ChannelMember cm: ch.getInputChannelMembers()) { + for (ChannelMember cm: directDstCh.getInputChannelMembers()) { if (src.getOutSideResources().contains(cm.getResource())) { in = cm; break; @@ -291,9 +320,64 @@ srcComponent = componentMap.get(srcParentResourceName); srcResName = srcResourceName; } + // For caller update methods for (MethodDeclaration srcUpdate: getUpdateMethods(srcComponent, srcResName)) { ResourcePath dstRes = out.getResource(); - // Values of path parameters. + // Get the value of reference member to call the update method. + String refParams = ""; + Set referredSet = referredResources.get(srcUpdate); + for (ChannelMember rc: ch.getReferenceChannelMembers()) { + ResourcePath ref = rc.getResource(); + if (referredSet == null) { + referredSet = new HashSet<>(); + referredResources.put(srcUpdate, referredSet); + } + if (!dst.getInSideResources().contains(ref)) { + String refVarName = JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(ref.getResourceHierarchy())); + if (!referredSet.contains(ref)) { + referredSet.add(ref); + ResourcePath srcRes = in.getResource(); + if (!JavaCodeGenerator.generatesComponent(srcRes.getResourceHierarchy())) { + srcRes = srcRes.getParent(); + } + Expression refGetter = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(ref, srcRes); + String[] sideEffects = new String[] {""}; + String refExp = refGetter.toImplementation(sideEffects); + String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); + srcUpdate.addStatement(sideEffects[0] + refTypeName + " " + refVarName + " = " + refExp + ";"); + } + refParams += ", " + refVarName; + } + } + // Update fields to refer to outside resources. + Map>> resourcePaths = ch.fillOutsideResourcePaths(out, JavaCodeGenerator.pullAccessor); + if (resourcePaths != null && resourcePaths.size() > 0) { + for (ChannelMember outsideMember: resourcePaths.keySet()) { + ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey(); + if (!JavaCodeGenerator.generatesComponent(outsidePath.getResourceHierarchy())) { + outsidePath = outsidePath.getParent(); + } + String outsideResName = JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(outsidePath.getResourceHierarchy())); + Expression outsideExp = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(outsidePath, null); + if (JavaCodeGenerator.generatesComponent(outsidePath.getResourceHierarchy())) { + outsideExp = ((Term) outsideExp).getChild(0); + } + if (outsideExp instanceof Field) { + outsideExp = new Variable(((Field) outsideExp).getSymbol().getName(), ((Field) outsideExp).getType()); + } else if (outsideExp instanceof Term) { + for (Entry fieldEnt: ((Term) outsideExp).getSubTerms(Field.class).entrySet()) { + Position pos = fieldEnt.getKey(); + Field field = fieldEnt.getValue(); + Variable var = new Variable(field.getSymbol().getName(), field.getType()); + ((Term) outsideExp).replaceSubTerm(pos, var); + } + } + String[] sideEffects = new String[] {""}; + String outsideAccessor = outsideExp.toImplementation(sideEffects); + srcUpdate.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // change the reference field. + } + } + // Values of path parameters to call the update method. String pathParams = ""; for (Expression pathParam: dstRes.getPathParams()) { if (pathParam instanceof Variable) { @@ -301,7 +385,7 @@ pathParams += pathVar.getName() + ", "; } } - // Values of channel parameters. + // Values of channel parameters to call the update method. String chParams = ""; for (Selector selector: ch.getAllSelectors()) { if (selector.getExpression() instanceof Variable) { @@ -309,28 +393,7 @@ chParams += selVar.getName() + ", "; } } - String refParams = ""; - Set referredSet = referredResources.get(srcUpdate); - for (ChannelMember rc: ch.getReferenceChannelMembers()) { - // to get the value of reference member. - ResourcePath ref = rc.getResource(); - if (referredSet == null) { - referredSet = new HashSet<>(); - referredResources.put(srcUpdate, referredSet); - } - if (!dst.getInSideResources().contains(ref)) { - String refVarName = ref.getLeafResourceName(); - if (!referredSet.contains(ref)) { - referredSet.add(ref); - Expression refGetter = JavaCodeGenerator.pullAccessor.getCurrentStateAccessorFor(rc, in); - String[] sideEffects = new String[] {""}; - String refExp = refGetter.toImplementation(sideEffects); - String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); - srcUpdate.addFirstStatement(sideEffects[0] + refTypeName + " " + refVarName + " = " + refExp + ";"); - } - refParams += ", " + refVarName; - } - } + // Call the update method. String updateMethodName = null; if (JavaCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { updateMethodName = "updateFrom" + srcResourceName; @@ -353,10 +416,119 @@ // Use the reference field to refer to outside destination resource. srcUpdate.addStatement("this." + JavaCodeGenerator.toVariableName(dstComponent.getTypeName()) + "." + updateMethodName + "(" + pathParams + chParams + srcFieldName + refParams + ");"); } + if (descendantDstChannels.contains(chNode)) { + // For hierarchical channels (broadcasting push transfer). + if (ch.getSelectors() != null && ch.getSelectors().size() > 0) { + Expression selExp = ch.getSelectors().get(0).getExpression(); + Type selType = null; + String varName = null; + if (selExp instanceof Variable) { + selType = ((Variable) selExp).getType(); + varName = ((Variable) selExp).getName(); + ChannelMember insideChMem = null; + for (ChannelMember cm :ch.getInputChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + if (insideChMem == null) { + for (ChannelMember cm :ch.getReferenceChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + } + if (insideChMem == null) { + for (ChannelMember cm :ch.getOutputChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + } + ResourcePath insideResPath = insideChMem.getResource(); + while (insideResPath.getParent() != null && (insideResPath.getLastParam() == null || !insideResPath.getLastParam().equals(selExp))) { + insideResPath = insideResPath.getParent(); + } + insideResPath = insideResPath.getParent(); + String parent = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(insideResPath, src.getPrimaryResourcePath()).toImplementation(new String[] {}); + if (insideResPath != null) { + if (selType.equals(DataConstraintModel.typeInt)) { + // make a for loop (for a list) for broadcasting. + srcUpdate.addFirstStatement("for (int " + varName + " = 0; " + varName +" < " + parent + ".size(); " + varName + "++) {"); + srcUpdate.addStatement("}"); + } else if (selType.equals(DataConstraintModel.typeString)) { + // make a for loop (for a map) for broadcasting. + srcUpdate.addFirstStatement("for (String " + varName + ": " + parent + ".keySet()) {"); + srcUpdate.addStatement("}"); + } + } + } else if (selExp instanceof Term) { + // not supported. + } + } + } } + // For caller input methods for (MethodDeclaration srcInput: getInputMethods(srcComponent, src, model)) { ResourcePath dstRes = out.getResource(); - // Values of path parameters. + // Get the value of reference member to call the update method. + String refParams = ""; + Set referredSet = referredResources.get(srcInput); + for (ChannelMember rc: ch.getReferenceChannelMembers()) { + ResourcePath ref = rc.getResource(); + if (referredSet == null) { + referredSet = new HashSet<>(); + referredResources.put(srcInput, referredSet); + } + if (!dst.getInSideResources().contains(ref)) { + String refVarName = JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(ref.getResourceHierarchy())); + if (!referredSet.contains(ref)) { + referredSet.add(ref); + ResourcePath srcRes = in.getResource(); + if (!JavaCodeGenerator.generatesComponent(srcRes.getResourceHierarchy())) { + srcRes = srcRes.getParent(); + } + Expression refGetter = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(ref, srcRes); + String[] sideEffects = new String[] {""}; + String refExp = refGetter.toImplementation(sideEffects); + String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); + srcInput.addStatement(sideEffects[0] + refTypeName + " " + refVarName + " = " + refExp + ";"); + } + refParams += ", " + refVarName; + } + } + // Update fields to refer to outside resources. + Map>> resourcePaths = ch.fillOutsideResourcePaths(out, JavaCodeGenerator.pullAccessor); + if (resourcePaths != null && resourcePaths.size() > 0) { + for (ChannelMember outsideMember: resourcePaths.keySet()) { + ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey(); + if (!JavaCodeGenerator.generatesComponent(outsidePath.getResourceHierarchy())) { + outsidePath = outsidePath.getParent(); + } + String outsideResName = JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(outsidePath.getResourceHierarchy())); + Expression outsideExp = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(outsidePath, null); + if (JavaCodeGenerator.generatesComponent(outsidePath.getResourceHierarchy())) { + outsideExp = ((Term) outsideExp).getChild(0); + } + if (outsideExp instanceof Field) { + outsideExp = new Variable(((Field) outsideExp).getSymbol().getName(), ((Field) outsideExp).getType()); + } else if (outsideExp instanceof Term) { + for (Entry fieldEnt: ((Term) outsideExp).getSubTerms(Field.class).entrySet()) { + Position pos = fieldEnt.getKey(); + Field field = fieldEnt.getValue(); + Variable var = new Variable(field.getSymbol().getName(), field.getType()); + ((Term) outsideExp).replaceSubTerm(pos, var); + } + } + String[] sideEffects = new String[] {""}; + String outsideAccessor = outsideExp.toImplementation(sideEffects); + srcInput.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // change the reference field. + } + } + // Values of path parameters to call the update method. String pathParams = ""; for (Expression pathParam: dstRes.getPathParams()) { if (pathParam instanceof Variable) { @@ -364,7 +536,7 @@ pathParams += pathVar.getName() + ", "; } } - // Values of channel parameters. + // Values of channel parameters to call the update method. String chParams = ""; for (Selector selector: ch.getAllSelectors()) { if (selector.getExpression() instanceof Variable) { @@ -372,34 +544,14 @@ chParams += selVar.getName() + ", "; } } - String refParams = ""; - Set referredSet = referredResources.get(srcInput); - for (ChannelMember rc: ch.getReferenceChannelMembers()) { - // to get the value of reference member. - ResourcePath ref = rc.getResource(); - if (referredSet == null) { - referredSet = new HashSet<>(); - referredResources.put(srcInput, referredSet); - } - if (!dst.getInSideResources().contains(ref)) { - String refVarName = ref.getLeafResourceName(); - if (!referredSet.contains(ref)) { - referredSet.add(ref); - Expression refGetter = JavaCodeGenerator.pullAccessor.getCurrentStateAccessorFor(rc, in); - String[] sideEffects = new String[] {""}; - String refExp = refGetter.toImplementation(sideEffects); - String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); - srcInput.addFirstStatement(sideEffects[0] + refTypeName + " " + refVarName + " = " + refExp + ";"); - } - refParams += ", " + refVarName; - } - } + // Call the update method. String updateMethodName = null; if (JavaCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { updateMethodName = "updateFrom" + srcResourceName; } else { updateMethodName = "update" + dstResourceName + "From" + srcResourceName; } + // Value of the source side (input side) resource. String srcFieldName = "value"; if (!JavaCodeGenerator.generatesComponent(src.getResourceHierarchy())) { srcFieldName = JavaCodeGenerator.toVariableName(srcResourceName); @@ -415,6 +567,60 @@ // Use the reference field to refer to outside destination resource. srcInput.addStatement("this." + JavaCodeGenerator.toVariableName(dstComponent.getTypeName()) + "." + updateMethodName + "(" + pathParams + chParams + srcFieldName + refParams + ");"); } + if (descendantDstChannels.contains(chNode)) { + // For hierarchical channels (broadcasting push transfer). + if (ch.getSelectors() != null && ch.getSelectors().size() > 0) { + Expression selExp = ch.getSelectors().get(0).getExpression(); + Type selType = null; + String varName = null; + if (selExp instanceof Variable) { + selType = ((Variable) selExp).getType(); + varName = ((Variable) selExp).getName(); + ChannelMember insideChMem = null; + for (ChannelMember cm :ch.getInputChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + if (insideChMem == null) { + for (ChannelMember cm :ch.getReferenceChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + } + if (insideChMem == null) { + for (ChannelMember cm :ch.getOutputChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + } + ResourcePath insideResPath = insideChMem.getResource(); + while (insideResPath.getParent() != null && (insideResPath.getLastParam() == null || !insideResPath.getLastParam().equals(selExp))) { + insideResPath = insideResPath.getParent(); + } + insideResPath = insideResPath.getParent(); + String parent = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(insideResPath, src.getPrimaryResourcePath()).toImplementation(new String[] {}); + if (insideResPath != null) { + if (selType.equals(DataConstraintModel.typeInt)) { + // make a for loop (for a list) for broadcasting. + srcInput.addFirstStatement("for (int " + varName + " = 0; " + varName +" < " + parent + ".size(); " + varName + "++) {"); + srcInput.addStatement("}"); + } else if (selType.equals(DataConstraintModel.typeString)) { + // make a for loop (for a map) for broadcasting. + srcInput.addFirstStatement("for (String " + varName + ": " + parent + ".keySet()) {"); + srcInput.addStatement("}"); + } + } + } else if (selExp instanceof Term) { + // not supported. + } + } + } } } else if ((pushPull.getSelectedOption() != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { // for pull (or push/pull) data transfer @@ -429,6 +635,9 @@ getter = getGetterMethod(dstComponent, dstResourceName); } if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { + // The first time to fill the getter method's body. + // Data transfer on the same channel hierarchy. + String[] sideEffects = new String[] {""}; boolean isContainedPush = false; Map inputResourceToStateAccessor = new HashMap<>(); for (Edge chToRes2: dst.getInEdges()) { @@ -454,18 +663,148 @@ for (ChannelMember c: ch.getReferenceChannelMembers()) { inputResourceToStateAccessor.put(c, JavaCodeGenerator.pullAccessor); // by pull data transfer } - String[] sideEffects = new String[] {""}; - // generate a return statement. - // An input resource is outside. + Map.Entry>>, Term> resourcePathsAndMessage; + + // Construct the base message. if (!isContainedPush) { - // All incoming edges are in PULL style. - String curState = ch.deriveUpdateExpressionOf(out, JavaCodeGenerator.pullAccessor).getKey().toImplementation(sideEffects); - getter.addStatement(sideEffects[0] + "return " + curState + ";"); + // All incoming edges are in PULL-style. + resourcePathsAndMessage = ch.fillOutsideResourcePaths(out, JavaCodeGenerator.pullAccessor, null); } else { - // At least one incoming edge is in PUSH style. - String curState = ch.deriveUpdateExpressionOf(out, JavaCodeGenerator.pullAccessor, inputResourceToStateAccessor).getKey().toImplementation(sideEffects); - getter.addStatement(sideEffects[0] + "return " + curState + ";"); + // At least one incoming edge is in PUSH-style. + resourcePathsAndMessage = ch.fillOutsideResourcePaths(out, JavaCodeGenerator.pullAccessor, inputResourceToStateAccessor); } + Map>> resourcePaths = resourcePathsAndMessage.getKey(); + Term messageTerm = resourcePathsAndMessage.getValue(); + // Data transfer from the descendant channel hierarchies. + Stack> channelItrStack = new Stack<>(); + DataTransferChannel curChannel = ch; + if (curChannel.getChildren() != null && curChannel.getChildren().size() > 0) { + // retrieve descendant channels recursively. + Iterator chItr = curChannel.getChildren().iterator(); + do { + if (!chItr.hasNext()) { + chItr = channelItrStack.pop(); + } else { + curChannel = (DataTransferChannel) chItr.next(); + // generate pull data transfers. + Set chMems = new HashSet<>(curChannel.getInputChannelMembers()); + chMems.addAll(curChannel.getReferenceChannelMembers()); + for (ChannelMember cm2: chMems) { + if (resourcePaths == null || !resourcePaths.keySet().contains(cm2)) { + // not a depending channel member. + ResourcePath src2 = cm2.getResource(); + Type srcResType2 = src2.getResourceStateType(); + String srcResName2 = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(src2.getResourceHierarchy())); + String srcGetter = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(src2, dst.getInSideResource(curChannel)).toImplementation(new String[] {}); + getter.addStatement(srcResType2.getInterfaceTypeName() + " " + srcResName2 + " = " + srcGetter + ";"); + } else { + // a depending channel member. + ResourcePath src2 = resourcePaths.get(cm2).getKey(); + // get outside src2 resource state by pull data transfer. + if (cm2.isOutside() || src2.getCommonPrefix(dst.getInSideResource(curChannel)) == null) { + // generate a pull data transfer from a depending in/ref resource. + Type srcResType2 = src2.getResourceStateType(); + String srcResName2 = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(src2.getResourceHierarchy())); + String dependingGetter = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(src2, dst.getInSideResource(curChannel)).toImplementation(new String[] {}); + getter.addStatement(srcResType2.getInterfaceTypeName() + " " + srcResName2 + " = " + dependingGetter + ";"); + } + } + } + // collect the message constraints by a descendant channel. + List varsForSideEffects = new ArrayList<>(); + int v = 0; + resourcePathsAndMessage = curChannel.fillOutsideResourcePaths(out, JavaCodeGenerator.pullAccessor, null); + if (resourcePathsAndMessage != null) { + resourcePaths = resourcePathsAndMessage.getKey(); + Term messageTermSub = resourcePathsAndMessage.getValue(); + for (Entry fieldEnt: ((Term) messageTermSub).getSubTerms(Field.class).entrySet()) { + Position pos = fieldEnt.getKey(); + Field field = fieldEnt.getValue(); + Variable var = new Variable(field.getSymbol().getName(), field.getType()); + ((Term) messageTermSub).replaceSubTerm(pos, var); + } + for (Map.Entry subTermEnt: messageTermSub.getSubTerms(Term.class).entrySet()) { + Term subTerm = subTermEnt.getValue(); + if (!(subTerm instanceof Constant) && subTerm.getSymbol().isImplWithSideEffect()) { + Variable var = new Variable("v" + v, subTerm.getType()); + varsForSideEffects.add(var); + v++; + // Add a side effect statement within the loop + Position pos = new Position(); + pos.addHeadOrder(0); + subTerm.replaceSubTerm(pos, var); + sideEffects = new String[] {""}; + String curState = messageTermSub.toImplementation(sideEffects); + getter.addStatement(sideEffects[0].replaceAll("\n", "")); + // Cancel the side effects in the return value. + pos = subTermEnt.getKey(); + messageTermSub.replaceSubTerm(pos, var); + } + } + if (messageTerm == null) { + messageTerm = messageTermSub; + } else { + messageTerm = (Term) messageTerm.unify(messageTermSub); + } + if (messageTerm == null) { + throw new UnificationFailed(); + } + } + // enclosed by a for loop + Expression selExp = curChannel.getSelectors().get(0).getExpression(); + Type selType = null; + String varName = null; + if (selExp instanceof Variable) { + selType = ((Variable) selExp).getType(); + varName = ((Variable) selExp).getName(); + ChannelMember insideChMem = null; + for (ChannelMember cm2 :curChannel.getInputChannelMembers()) { + if (!cm2.isOutside()) { + insideChMem = cm2; + break; + } + } + if (insideChMem == null) { + for (ChannelMember cm2 :curChannel.getReferenceChannelMembers()) { + if (!cm2.isOutside()) { + insideChMem = cm2; + break; + } + } + } + ResourcePath insideResPath = insideChMem.getResource(); + while (insideResPath.getParent() != null && (insideResPath.getLastParam() == null || !insideResPath.getLastParam().equals(selExp))) { + insideResPath = insideResPath.getParent(); + } + insideResPath = insideResPath.getParent(); + String parent = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(insideResPath, src.getPrimaryResourcePath()).toImplementation(new String[] {}); + if (insideResPath != null) { + if (selType.equals(DataConstraintModel.typeInt)) { + // make a for loop (for a list) for data collecting. + getter.addFirstStatement("for (int " + varName + " = 0; " + varName +" < " + parent + ".size(); " + varName + "++) {"); + } else if (selType.equals(DataConstraintModel.typeString)) { + // make a for loop (for a map) for data collecting. + getter.addFirstStatement("for (String " + varName + ": " + parent + ".keySet()) {"); + } + } + } + // initialize the variables to hold side effects within the loop. + for (Variable var: varsForSideEffects) { + getter.addFirstStatement(var.getType().getInterfaceTypeName() + " " + var.getName() + " = new " + var.getType().getImplementationTypeName() + "();"); + } + // end of the loop + getter.addStatement("}"); + if (curChannel.getChildren() != null && curChannel.getChildren().size() > 0) { + channelItrStack.push(chItr); + chItr = curChannel.getChildren().iterator(); + } + } + } while (!channelItrStack.isEmpty()); + } + // generate a return statement. + sideEffects = new String[] {""}; + String curState = ch.deriveUpdateExpressionOf(out, messageTerm, JavaCodeGenerator.pullAccessor).toImplementation(sideEffects); + getter.addStatement(sideEffects[0] + "return " + curState + ";"); } if (outsideInputResource) { // Update fields to refer to outside resources. @@ -477,51 +816,43 @@ outsidePath = outsidePath.getParent(); } String outsideResName = JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(outsidePath.getResourceHierarchy())); - Set dependingMembers = resourcePaths.get(outsideMember).getValue(); - for (ChannelMember dependingMember: dependingMembers) { - ResourcePath dependingRes = dependingMember.getResource(); - ResourceNode dependingNode = null; + Set dependedMembers = resourcePaths.get(outsideMember).getValue(); + for (ChannelMember dependedMember: dependedMembers) { + ResourcePath dependedRes = dependedMember.getResource(); + ResourceNode dependedNode = null; PushPullAttribute pushPull2 = null; for (Edge resToCh2: resToCh.getDestination().getInEdges()) { - if (((ResourceNode) resToCh2.getSource()).getOutSideResources().contains(dependingRes)) { - dependingNode = (ResourceNode) resToCh2.getSource(); + if (((ResourceNode) resToCh2.getSource()).getOutSideResources().contains(dependedRes)) { + dependedNode = (ResourceNode) resToCh2.getSource(); pushPull2 = (PushPullAttribute) resToCh.getAttribute(); } } - TypeDeclaration dependingComponent = null; - if (JavaCodeGenerator.generatesComponent(dependingRes.getResourceHierarchy())) { - String dependingResourceName = JavaCodeGenerator.getComponentName(dependingRes.getResourceHierarchy()); - dependingComponent = componentMap.get(dependingResourceName); + TypeDeclaration dependedComponent = null; + if (JavaCodeGenerator.generatesComponent(dependedRes.getResourceHierarchy())) { + String dependedResourceName = JavaCodeGenerator.getComponentName(dependedRes.getResourceHierarchy()); + dependedComponent = componentMap.get(dependedResourceName); } else { - String dependingParentResourceName = JavaCodeGenerator.getComponentName(dependingRes.getParent().getResourceHierarchy()); - dependingComponent = componentMap.get(dependingParentResourceName); + String dependedParentResourceName = JavaCodeGenerator.getComponentName(dependedRes.getParent().getResourceHierarchy()); + dependedComponent = componentMap.get(dependedParentResourceName); } Expression outsideExp = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(outsidePath, null); if (JavaCodeGenerator.generatesComponent(outsidePath.getResourceHierarchy())) { outsideExp = ((Term) outsideExp).getChild(0); } - Expression nextExp = dependingMember.getStateTransition().getNextStateExpression(); - if (nextExp != null && outsideExp instanceof Term) { - if (nextExp instanceof Variable) { - outsideExp = ((Term) outsideExp).substitute((Variable) nextExp, new Field(JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(dependingRes.getResourceHierarchy())))); - } else { - // ToDo. - } - } - if (dstComponent == dependingComponent) { + if (dstComponent == dependedComponent) { // In the common parent. - if (dependingNode != null) { // Inspect further dependency. - for (Edge chToRes2: dependingNode.getInEdges()) { + if (dependedNode != null) { // Inspect further dependency. + for (Edge chToRes2: dependedNode.getInEdges()) { DataTransferChannel ch2 = ((ChannelNode) chToRes2.getSource()).getChannel(); if (ch2.getInputChannelMembers().size() == 0) { // In an input method of the parent component. Set outs = ch2.getOutputChannelMembers(); - MethodDeclaration input = getInputMethod(dependingComponent, outs.iterator().next(), outs.size()); + MethodDeclaration input = getInputMethod(dependedComponent, outs.iterator().next(), outs.size()); String[] sideEffects = new String[] {""}; String outsideAccessor = outsideExp.toImplementation(sideEffects); input.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // change the reference field. // Update constructor. - MethodDeclaration constructor = getConstructor(dependingComponent); + MethodDeclaration constructor = getConstructor(dependedComponent); constructor.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // initialize the reference field. } else { boolean isPush = true; @@ -538,16 +869,16 @@ // In an update method of the parent component. ResourceNode dependingResSrc = (ResourceNode) resToCh2.getSource(); MethodDeclaration update = null; - if (JavaCodeGenerator.generatesComponent(dependingRes.getResourceHierarchy())) { - update = getUpdateMethod(dependingComponent, null, JavaCodeGenerator.getComponentName(dependingResSrc.getResourceHierarchy())); + if (JavaCodeGenerator.generatesComponent(dependedRes.getResourceHierarchy())) { + update = getUpdateMethod(dependedComponent, null, JavaCodeGenerator.getComponentName(dependingResSrc.getResourceHierarchy())); } else { - String dependingResName = JavaCodeGenerator.getComponentName(dependingRes.getResourceHierarchy()); - update = getUpdateMethod(dependingComponent, dependingResName, JavaCodeGenerator.getComponentName(dependingResSrc.getResourceHierarchy())); + String dependingResName = JavaCodeGenerator.getComponentName(dependedRes.getResourceHierarchy()); + update = getUpdateMethod(dependedComponent, dependingResName, JavaCodeGenerator.getComponentName(dependingResSrc.getResourceHierarchy())); } update.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // change the reference field. } // Update constructor. - MethodDeclaration constructor = getConstructor(dependingComponent); + MethodDeclaration constructor = getConstructor(dependedComponent); constructor.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // initialize the reference field. } } @@ -558,10 +889,10 @@ // In an update method of the destination component. MethodDeclaration update = null; if (JavaCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { - update = getUpdateMethod(dstComponent, null, JavaCodeGenerator.getComponentName(dependingRes.getResourceHierarchy())); + update = getUpdateMethod(dstComponent, null, JavaCodeGenerator.getComponentName(dependedRes.getResourceHierarchy())); } else { String dstResName = JavaCodeGenerator.getComponentName(dst.getResourceHierarchy()); - update = getUpdateMethod(dstComponent, dstResName, JavaCodeGenerator.getComponentName(dependingRes.getResourceHierarchy())); + update = getUpdateMethod(dstComponent, dstResName, JavaCodeGenerator.getComponentName(dependedRes.getResourceHierarchy())); } String[] sideEffects = new String[] {""}; String outsideAccessor = outsideExp.toImplementation(sideEffects); @@ -644,7 +975,7 @@ } } - // descendant getter method + // (#4) descendant getter method (the implementation must be kept consistent with #3) if (resource.getChildren().size() > 0) { for (ResourceHierarchy child: resource.getChildren()) { ResourceHierarchy parent = resource; @@ -668,6 +999,7 @@ for (MethodDeclaration getter: getGetterMethods(component, methodName)) { if ((getter.getParameters() == null && params == 0) || (getter.getParameters() != null && getter.getParameters().size() == params)) { descendantGetter = getter; + break; } } if (descendantGetter != null) { @@ -677,14 +1009,12 @@ newSelector.addChild(new Variable(descendantGetter.getParameters().get(descendantGetter.getParameters().size() - 1).getName())); newSelector.setType(descendantGetter.getReturnType()); selector = newSelector; - params++; } else if (DataConstraintModel.typeMap.isAncestorOf(parent.getResourceStateType())) { Term newSelector = new Term(DataConstraintModel.lookup); newSelector.addChild(selector); newSelector.addChild(new Variable(descendantGetter.getParameters().get(descendantGetter.getParameters().size() - 1).getName())); newSelector.setType(descendantGetter.getReturnType()); selector = newSelector; - params++; } if (descendantGetter != null && (descendantGetter.getBody() == null || descendantGetter.getBody().getStatements().size() == 0)) { String[] sideEffects = new String[] {null}; @@ -698,6 +1028,11 @@ break; } parent = descendant; + if (DataConstraintModel.typeList.isAncestorOf(parent.getResourceStateType())) { + params++; + } else if (DataConstraintModel.typeMap.isAncestorOf(parent.getResourceStateType())) { + params++; + } children = descendant.getChildren(); } while (children != null && children.size() == 1 && (descendant = children.iterator().next()) != null); } @@ -761,6 +1096,9 @@ String updateStatement; if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { updateStatement = sideEffects[0]; + if (updateStatement.endsWith("\n")) { + updateStatement = updateStatement.substring(0, updateStatement.length() - 1); + } } else { updateStatement = sideEffects[0] + "this.value = " + newState + ";"; } @@ -772,6 +1110,9 @@ if (sideEffects[0] != null) { updateStatement = sideEffects[0]; updateStatement = updateStatement.replace(".value", "." + JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(resource))); + if (updateStatement.endsWith("\n")) { + updateStatement = updateStatement.substring(0, updateStatement.length() - 1); + } } if (DataConstraintModel.typeList.isAncestorOf(resource.getParent().getResourceStateType())) { Term selector = new Term(DataConstraintModel.set); @@ -902,26 +1243,28 @@ String constructorInvocation = "new " + replacingClassName + "("; MethodDeclaration descendantConstructor = getConstructor(descendantComponent); String delimiter = ""; - for (VariableDeclaration var: descendantConstructor.getParameters()) { - JsonAccessor jsonMember = new JsonAccessor(DataConstraintModel.dot); - jsonMember.addChild(jsonTerm); - jsonMember.addChild(new Constant(var.getName(), DataConstraintModel.typeString)); - Expression param = jsonMember.reduce(); - if (param != null) { - if (param instanceof Term) { - if (((Term) param).getType() == null) { - ((Term) param).setType(var.getType()); + if (descendantConstructor != null) { + for (VariableDeclaration var: descendantConstructor.getParameters()) { + JsonAccessor jsonMember = new JsonAccessor(DataConstraintModel.dot); + jsonMember.addChild(jsonTerm); + jsonMember.addChild(new Constant(var.getName(), DataConstraintModel.typeString)); + Expression param = jsonMember.reduce(); + if (param != null) { + if (param instanceof Term) { + if (((Term) param).getType() == null) { + ((Term) param).setType(var.getType()); + } + } else if (param instanceof Variable) { + if (((Variable) param).getType() == null) { + ((Variable) param).setType(var.getType()); + } } - } else if (param instanceof Variable) { - if (((Variable) param).getType() == null) { - ((Variable) param).setType(var.getType()); - } + constructorInvocation = constructorInvocation + delimiter + param.toImplementation(null); + } else { + constructorInvocation = constructorInvocation + delimiter + var.getName(); } - constructorInvocation = constructorInvocation + delimiter + param.toImplementation(null); - } else { - constructorInvocation = constructorInvocation + delimiter + var.getName(); + delimiter = ", "; } - delimiter = ", "; } constructorInvocation += ")"; ((Term) exp).replaceSubTerm(termEnt.getKey(), new Constant(constructorInvocation, descendantType)); diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JavaSpecific.java b/AlgebraicDataflowArchitectureModel/src/generators/JavaSpecific.java index 6134a5f..dfe5850 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/JavaSpecific.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/JavaSpecific.java @@ -192,6 +192,26 @@ } @Override + public String getForStatement(String varName, String list) { + return "for (int " + varName + getAssignment() + "0; " + varName + " < " + list + ".size(); " + varName + "++) {"; + } + + @Override + public String getForStatement(String varName, String varType, String map) { + return "for (" + varType + " " + varName + ": " + map + ".keySet()) {"; + } + + @Override + public String getEndForStatement() { + return "}"; + } + + @Override + public String getEndForStatement(String varName) { + return "}"; + } + + @Override public String toComponentName(String name) { return name.substring(0, 1).toUpperCase() + name.substring(1); } diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JerseyCodeGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/JerseyCodeGenerator.java index ce5c62d..ae93b32 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/JerseyCodeGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/JerseyCodeGenerator.java @@ -225,7 +225,7 @@ component.addMethod(stateGetter); } if (component != null) { - // Declare the getter methods in this resource to obtain descendant resources. + // (#1) Declare the getter methods in this resource to obtain descendant resources. (complementary to #2) Set descendants = descendantGetters.get(resourceNode.getResourceHierarchy()); if (descendants == null) { descendants = new HashSet<>(); @@ -391,7 +391,7 @@ } } - // Declare the getter method to obtain the resource state in an ancestor component. + // (#2) Declare the getter method to obtain the resource state in an ancestor component. (complementary to #1) if (component == null) { // No component is created for this resource. ResourceNode ancestorNode = resourceNode; @@ -470,22 +470,55 @@ getterAccessors.put(resourceNode.getResourceHierarchy(), getterAccessor); } - // Declare a client field and update methods from other resources. + // Declare a client field for push data transfer. for (Edge resToCh: resourceNode.getOutEdges()) { DataFlowEdge re = (DataFlowEdge) resToCh; - DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + ChannelNode directDstChNode = (ChannelNode) re.getDestination(); + DataTransferChannel directDstCh = directDstChNode.getChannel(); // Check if the input resource is outside of the channel scope. boolean outsideInputResource = false; - for (ChannelMember cm: ch.getInputChannelMembers()) { - if (cm.getResource().equals(resourceNode.getOutSideResource(ch)) && cm.isOutside()) { + for (ChannelMember cm: directDstCh.getInputChannelMembers()) { + if (cm.getResource().equals(resourceNode.getOutSideResource(directDstCh)) && cm.isOutside()) { outsideInputResource = true; // Regarded as pull transfer. break; } } - for (ChannelMember cm: ch.getOutputChannelMembers()) { - ResourcePath dstRes = cm.getResource(); + // Should take into account the channel hierarchy. + Set ancestorDstChannels = directDstChNode.getAncestors(); + Set descendantDstChannels = directDstChNode.getDescendants(); + Set outEdges = new HashSet<>(); + outEdges.addAll(directDstChNode.getOutEdges()); + for (ChannelNode ancestorDst: ancestorDstChannels) { + outEdges.addAll(ancestorDst.getOutEdges()); + } + for (ChannelNode descendantDst: descendantDstChannels) { + outEdges.addAll(descendantDst.getOutEdges()); + } + for (Edge chToRes: outEdges) { + // For each data transfer to dstNode:ResourceNode. + ResourceNode dstNode = ((ResourceNode) chToRes.getDestination()); + ChannelNode chNode = (ChannelNode) chToRes.getSource(); + DataTransferChannel ch = chNode.getChannel(); // Check if the output resource is outside of the channel scope. - boolean outsideOutputResource = cm.isOutside(); + boolean outsideOutputResource = false; + ChannelMember out = null; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (dstNode.getInSideResources().contains(cm.getResource())) { + out = cm; + if (cm.isOutside()) { + outsideOutputResource = true; + break; + } + } + } + ResourcePath dstRes = out.getResource(); + // Also take into account the channel hierarchy to determine push/pull transfer. + if (descendantDstChannels.contains(chNode)) { + outsideOutputResource = true; // Regarded as (broadcasting) push transfer. + } + if (ancestorDstChannels.contains(chNode)) { + outsideInputResource = true; // Regarded as (collecting) pull transfer. + } if (!bDeclareClientField && ((((PushPullAttribute) re.getAttribute()).getSelectedOption() == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource)) { // For push transfer. if (!generatesComponent(dstRes.getResourceHierarchy())) { @@ -525,15 +558,32 @@ } } } + // Declare update methods called by other resources for push data transfer + // and reference fields for pull data transfer. for (Edge chToRes : resourceNode.getInEdges()) { - for (Edge resToCh: chToRes.getSource().getInEdges()) { + ChannelNode directSrcChannel = (ChannelNode) chToRes.getSource(); + DataTransferChannel ch = directSrcChannel.getChannel(); + // Should take into account the channel hierarchy. + Set ancestorSrcChannels = directSrcChannel.getAncestors(); + Set descendantSrcChannels = directSrcChannel.getDescendants(); + Set inEdges = new HashSet<>(); + inEdges.addAll(directSrcChannel.getInEdges()); + for (ChannelNode ancestorSrc: ancestorSrcChannels) { + inEdges.addAll(ancestorSrc.getInEdges()); + } + for (ChannelNode descendantSrc: descendantSrcChannels) { + inEdges.addAll(descendantSrc.getInEdges()); + } + for (Edge resToCh: inEdges) { + // For each data transfer from srcResPath:ResourcePath to resourceNode:ResourceNode. DataFlowEdge re = (DataFlowEdge) resToCh; - DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); - ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(ch); + ChannelNode indirectSrcChNode = (ChannelNode) re.getDestination(); + DataTransferChannel indirectSrcCh = indirectSrcChNode.getChannel(); + ResourcePath srcResPath = ((ResourceNode) re.getSource()).getOutSideResource(indirectSrcCh); // Check if the input resource is outside of the channel scope. boolean outsideInputResource = false; - for (ChannelMember cm: ch.getInputChannelMembers()) { - if (cm.getResource().equals(srcRes) && cm.isOutside()) { + for (ChannelMember cm: indirectSrcCh.getInputChannelMembers()) { + if (cm.getResource().equals(srcResPath) && cm.isOutside()) { outsideInputResource = true; // Regarded as pull transfer. break; } @@ -550,14 +600,21 @@ } } } - String srcResName = getComponentName(srcRes.getResourceHierarchy()); - Type srcType = srcRes.getResourceStateType(); - if (!generatesComponent(srcRes.getResourceHierarchy())) { - srcRes = srcRes.getParent(); + // Also take into account the channel hierarchy to determine push/pull transfer. + if (ancestorSrcChannels.contains(indirectSrcChNode)) { + outsideOutputResource = true; // Regarded as (broadcasting) push transfer. + } + if (descendantSrcChannels.contains(indirectSrcChNode)) { + outsideInputResource = true; // Regarded as (collecting) pull transfer. + } + String srcResName = getComponentName(srcResPath.getResourceHierarchy()); + Type srcType = srcResPath.getResourceStateType(); + if (!generatesComponent(srcResPath.getResourceHierarchy())) { + srcResPath = srcResPath.getParent(); } if ((((PushPullAttribute) re.getAttribute()).getSelectedOption() != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { // For pull transfer. - if (outsideInputResource || (resourceNode.getInSideResource(ch).getCommonPrefix(srcRes) == null && differentTreesAsDifferentServices)) { + if (outsideInputResource || (resourceNode.getInSideResource(ch).getCommonPrefix(srcResPath) == null && differentTreesAsDifferentServices)) { // Inter-service if (!bDeclareClientField) { // Declare a client field to connect to the source resource of pull transfer. @@ -577,12 +634,12 @@ FieldDeclaration srcRefField = new FieldDeclaration(new Type(srcResName, srcResName), toVariableName(srcResName)); if (component != null) { // A component is created for this resource. - if (resourceNode.getResourceHierarchy() != srcRes.getResourceHierarchy()) { + if (resourceNode.getResourceHierarchy() != srcResPath.getResourceHierarchy()) { component.addField(srcRefField); } } else { // No component is created for this resource. - if (resourceNode.getParent().getResourceHierarchy() != srcRes.getResourceHierarchy()) { + if (resourceNode.getParent().getResourceHierarchy() != srcResPath.getResourceHierarchy()) { fields.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), srcRefField)); } } @@ -591,7 +648,7 @@ // For push transfer. boolean hasRestAPI = false; boolean isRestAPI = false; - if (outsideOutputResource || (resourceNode.getInSideResource(ch).getCommonPrefix(srcRes) == null && differentTreesAsDifferentServices)) { + if (outsideOutputResource || (resourceNode.getInSideResource(ch).getCommonPrefix(srcResPath) == null && differentTreesAsDifferentServices)) { // Inter-service hasRestAPI = true; if (resourceNode.getParent() == null) { @@ -1350,7 +1407,7 @@ : DataConstraintModel.typeInt); } // use the cached value as the current state - return new Field(targetRes.getLeafResourceName(), + return new Field(toVariableName(getComponentName(targetRes.getResourceHierarchy())), targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } @@ -1358,7 +1415,7 @@ @Override public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { ResourcePath targetRes = target.getResource(); - return new Parameter(targetRes.getLeafResourceName(), + return new Parameter(toVariableName(getComponentName(targetRes.getResourceHierarchy())), targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } @@ -1384,7 +1441,7 @@ } } // for reference channel member - return new Parameter(targetRes.getLeafResourceName(), + return new Parameter(toVariableName(getComponentName(targetRes.getResourceHierarchy())), targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } @@ -1398,7 +1455,7 @@ return getDirectStateAccessorFor(targetRes, fromRes); } } - return new Parameter(targetRes.getLeafResourceName(), + return new Parameter(toVariableName(getComponentName(targetRes.getResourceHierarchy())), targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } @@ -1412,11 +1469,11 @@ : DataConstraintModel.typeInt); } // for reference channel member - return new Parameter(targetRes.getLeafResourceName(), + return new Parameter(toVariableName(getComponentName(targetRes.getResourceHierarchy())), targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } else { - // access from an ancestor or outside of the hierarchy + // (#3) access from an ancestor or outside of the hierarchy (must be kept consistent with #4) Stack pathStack = new Stack<>(); ResourcePath curPath = targetRes; do { @@ -1426,8 +1483,8 @@ } while (curPath != null); // iterate from the `from' resource Term getter = null; - int v = 1; int arity = 2; + boolean doesChainInvocations = true; while (!pathStack.empty()) { curPath = pathStack.pop(); String typeName = getComponentName(curPath.getResourceHierarchy()); @@ -1441,58 +1498,43 @@ Term newGetter = new Term(new Symbol("get" + typeName, -1, Symbol.Type.METHOD)); newGetter.addChild(getter); if (curPath.getResourceHierarchy().getNumParameters() > 0) { - Variable var = null; Expression param = curPath.getLastParam(); - if (param instanceof Variable) { - var = (Variable) param; - } else if (param instanceof Term) { - var = new Variable("v" + v, ((Term) param).getType()); - } - if (var != null) { - newGetter.addChild(var); + if (param != null) { + newGetter.addChild(param); newGetter.getSymbol().setArity(2); } - v++; } getter = newGetter; } else { + // add the last path parameter. if (curPath.getResourceHierarchy().getNumParameters() > 0) { - Variable var = null; Expression param = curPath.getLastParam(); - if (param instanceof Variable) { - var = (Variable) param; - } else if (param instanceof Term) { - var = new Variable("v" + v, ((Term) param).getType()); - } - if (var != null) { + if (param != null) { getter.getSymbol().setArity(arity); - getter.addChild(var); + getter.addChild(param); } - v++; } } arity = 2; + doesChainInvocations = true; } else { - // to get a descendant resource directly. - if (arity == 2) { + // to get a descendant resource directly. (e.g, .todos.{year}.{month}.{day}.{id} ==> .getTodos().getTodo(year, month, day, id)) + if (doesChainInvocations) { Term newGetter = new Term(new Symbol("get" + typeName, -1, Symbol.Type.METHOD)); newGetter.addChild(getter); getter = newGetter; + doesChainInvocations = false; } if (curPath.getResourceHierarchy().getNumParameters() > 0) { - Variable var = null; + // may change the symbol name + getter.getSymbol().changeName("get" + typeName); + // add a path parameter. Expression param = curPath.getLastParam(); - if (param instanceof Variable) { - var = (Variable) param; - } else if (param instanceof Term) { - var = new Variable("v" + v, ((Term) param).getType()); - } - if (var != null) { + if (param != null) { getter.getSymbol().setArity(arity); - getter.addChild(var); + getter.addChild(param); arity++; } - v++; } } } @@ -1518,7 +1560,7 @@ : DataConstraintModel.typeInt); } // for reference channel member - return new Parameter(targetRes.getLeafResourceName(), + return new Parameter(toVariableName(getComponentName(targetRes.getResourceHierarchy())), targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } @@ -1526,7 +1568,7 @@ @Override public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { ResourcePath targetRes = target.getResource(); - return new Parameter(targetRes.getLeafResourceName(), + return new Parameter(toVariableName(getComponentName(targetRes.getResourceHierarchy())), targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java index ab32c63..706eec2 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/JerseyMethodBodyGenerator.java @@ -9,6 +9,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.Stack; import algorithms.TypeInference; import code.ast.CodeUtil; @@ -17,6 +18,7 @@ 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; @@ -71,19 +73,35 @@ if (!resToCh.isChannelToResource()) { PushPullAttribute pushPull = (PushPullAttribute) resToCh.getAttribute(); ResourceNode src = (ResourceNode) resToCh.getSource(); - for (Edge chToRes: resToCh.getDestination().getOutEdges()) { + ChannelNode directDstChNode = (ChannelNode) resToCh.getDestination(); + DataTransferChannel directDstCh = directDstChNode.getChannel(); + // Should take into account the channel hierarchy. + Set ancestorDstChannels = directDstChNode.getAncestors(); + Set descendantDstChannels = directDstChNode.getDescendants(); + Set outEdges = new HashSet<>(); + outEdges.addAll(directDstChNode.getOutEdges()); + for (ChannelNode ancestorDst: ancestorDstChannels) { + outEdges.addAll(ancestorDst.getOutEdges()); + } + for (ChannelNode descendantDst: descendantDstChannels) { + outEdges.addAll(descendantDst.getOutEdges()); + } + for (Edge chToRes: outEdges) { + // For each data transfer from src:ResourceNode to dst:ResourceNode. ResourceNode dst = (ResourceNode) chToRes.getDestination(); String srcResourceName = JerseyCodeGenerator.getComponentName(src.getResourceHierarchy()); String dstResourceName = JerseyCodeGenerator.getComponentName(dst.getResourceHierarchy()); TypeDeclaration srcComponent = componentMap.get(srcResourceName); TypeDeclaration dstComponent = componentMap.get(dstResourceName); - DataTransferChannel ch = ((ChannelNode) resToCh.getDestination()).getChannel(); +// DataTransferChannel ch = ((ChannelNode) resToCh.getDestination()).getChannel(); + ChannelNode chNode = (ChannelNode) chToRes.getSource(); + DataTransferChannel ch = chNode.getChannel(); for (ChannelMember out: ch.getOutputChannelMembers()) { if (dst.getInSideResources().contains(out.getResource())) { // Check if the input resource is outside of the channel scope. boolean outsideInputResource = false; ChannelMember in = null; - for (ChannelMember cm: ch.getInputChannelMembers()) { + for (ChannelMember cm: directDstCh.getInputChannelMembers()) { if (src.getOutSideResources().contains(cm.getResource())) { in = cm; if (cm.isOutside()) { @@ -94,6 +112,13 @@ } // Check if the output resource is outside of the channel scope. boolean outsideOutputResource = out.isOutside(); + // Also take into account the channel hierarchy to determine push/pull transfer. + if (descendantDstChannels.contains(chNode)) { + outsideOutputResource = true; // Regarded as (broadcasting) push transfer. + } + if (ancestorDstChannels.contains(chNode)) { + outsideInputResource = true; // Regarded as (collecting) pull transfer. + } if ((pushPull.getSelectedOption() == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) { // for push data transfer MethodDeclaration update = null; @@ -253,7 +278,7 @@ } int inDegree = inResources.size(); if (inDegree > 1 - || (inDegree == 1 && ch.getInputChannelMembers().iterator().next().getStateTransition().isRightPartial())) { + || (inDegree == 1 && directDstCh.getInputChannelMembers().iterator().next().getStateTransition().isRightPartial())) { // update a cache of src side resource (when incoming edges are multiple) String cacheStatement = "this." + JerseyCodeGenerator.toVariableName(srcResourceName) + " = " + JerseyCodeGenerator.toVariableName(srcResourceName) + ";"; if (update.getBody() == null || !update.getBody().getStatements().contains(cacheStatement)) { @@ -285,7 +310,11 @@ int v = 0; for (VariableDeclaration var: update2.getParameters()) { if (v < out.getResource().getPathParams().size()) { - args += delimiter + ((Variable) out.getResource().getPathParams().get(v)).getName(); + if (out.getResource().getPathParams().get(v) instanceof Variable) { + args += delimiter + ((Variable) out.getResource().getPathParams().get(v)).getName(); + } else if (out.getResource().getPathParams().get(v) instanceof JsonAccessor) { + args += delimiter + ((JsonAccessor) out.getResource().getPathParams().get(v)).toImplementation(new String[] {}); // ToDo. + } } else { args += delimiter + var.getName(); } @@ -407,6 +436,7 @@ srcComponent = componentMap.get(srcParentResourceName); srcName = srcResourceName; } + // For caller update methods for (MethodDeclaration srcUpdate: getUpdateMethods(srcComponent, srcName)) { if (srcUpdate != null) { List>> params = new ArrayList<>(); @@ -436,7 +466,7 @@ referredResources.put(srcUpdate, referredSet); } if (!dst.getInSideResources().contains(ref)) { - String refResourceName = ref.getLeafResourceName(); + String refResourceName = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(ref.getResourceHierarchy())); Type refResourceType = ref.getResourceStateType(); if (!referredSet.contains(ref)) { referredSet.add(ref); @@ -474,21 +504,85 @@ if (inDegree <= 1) { srcResName = null; } + Entry> filledEntry = ch.fillOutsideResourcePaths(out, JerseyCodeGenerator.pushAccessor).get(out); + String dstPath = null; + if (filledEntry != null) { + ResourcePath filledDstPath = filledEntry.getKey(); + dstPath = filledDstPath.toString().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + } else { + dstPath = dstRes.getResourceHierarchy().toResourcePath(pathParams); + } if (!chainedCalls.contains(srcUpdate)) { // The first call to an update method in this method srcUpdate.addStatement(getHttpMethodParamsStatement(srcComponent.getTypeName(), params, true)); - srcUpdate.addStatement("String result = " + getHttpMethodCallStatement(baseURL, - dstRes.getResourceHierarchy().toResourcePath(pathParams), - srcResName, - httpMethod)); + srcUpdate.addStatement("String result = " + getHttpMethodCallStatement(baseURL, dstPath, srcResName, httpMethod)); chainedCalls.add(srcUpdate); } else { // After the second time of call to update methods in this method srcUpdate.addStatement(getHttpMethodParamsStatement(srcComponent.getTypeName(), params, false)); - srcUpdate.addStatement("result = " + getHttpMethodCallStatement(baseURL, - dstRes.getResourceHierarchy().toResourcePath(pathParams), - srcResName, - httpMethod)); + srcUpdate.addStatement("result = " + getHttpMethodCallStatement(baseURL, dstPath, srcResName, httpMethod)); + } + if (descendantDstChannels.contains(chNode)) { + // For hierarchical channels (broadcasting push transfer). + if (ch.getSelectors() != null && ch.getSelectors().size() > 0) { + Expression selExp = ch.getSelectors().get(0).getExpression(); + Type selType = null; + String varName = null; + if (selExp instanceof Variable) { + selType = ((Variable) selExp).getType(); + varName = ((Variable) selExp).getName(); + ChannelMember insideChMem = null; + for (ChannelMember cm :ch.getInputChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + if (insideChMem == null) { + for (ChannelMember cm :ch.getReferenceChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + } + if (insideChMem == null) { + for (ChannelMember cm :ch.getOutputChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + } + ResourcePath insideResPath = insideChMem.getResource(); + while (insideResPath.getParent() != null && (insideResPath.getLastParam() == null || !insideResPath.getLastParam().equals(selExp))) { + insideResPath = insideResPath.getParent(); + } + insideResPath = insideResPath.getParent(); + String parent = null; + if (JerseyCodeGenerator.generatesComponent(insideResPath.getResourceHierarchy())) { + Expression getter = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(insideResPath, src.getPrimaryResourcePath()); + Term valueGetter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); + valueGetter.addChild(getter); + parent = valueGetter.toImplementation(new String[] {}); + } else { + parent = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(insideResPath, src.getPrimaryResourcePath()).toImplementation(new String[] {}); + } + if (insideResPath != null) { + if (selType.equals(DataConstraintModel.typeInt)) { + // make a for loop (for a list) for broadcasting. + srcUpdate.addFirstStatement("for (int " + varName + " = 0; " + varName +" < " + parent + ".size(); " + varName + "++) {"); + srcUpdate.addStatement("}"); + } else if (selType.equals(DataConstraintModel.typeString)) { + // make a for loop (for a map) for broadcasting. + srcUpdate.addFirstStatement("for (String " + varName + ": " + parent + ".keySet()) {"); + srcUpdate.addStatement("}"); + } + } + } else if (selExp instanceof Term) { + // not supported. + } + } } srcUpdate.addThrow("JsonProcessingException"); } else { @@ -525,6 +619,7 @@ } } } + // For caller input methods for (MethodDeclaration srcInput: getInputMethods(srcComponent, src, model)) { List>> params = new ArrayList<>(); ResourcePath dstRes = out.getResource(); @@ -552,7 +647,7 @@ referredResources.put(srcInput, referredSet); } if (!dst.getInSideResources().contains(ref)) { - String refResourceName = ref.getLeafResourceName(); + String refResourceName = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(ref.getResourceHierarchy())); Type refResourceType = ref.getResourceStateType(); if (!referredSet.contains(ref)) { referredSet.add(ref); @@ -571,7 +666,7 @@ Expression refGetter = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(ref, srcRes); String refExp = refGetter.toImplementation(sideEffects); String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); - srcInput.addFirstStatement(sideEffects[0] + refTypeName + " " + ref.getLeafResourceName() + " = " + refExp + ";"); + srcInput.addFirstStatement(sideEffects[0] + refTypeName + " " + refResourceName + " = " + refExp + ";"); } } // Value of a reference side resource. @@ -589,21 +684,85 @@ if (inDegree <= 1) { srcResName = null; } + Entry> filledEntry = ch.fillOutsideResourcePaths(out, JerseyCodeGenerator.pushAccessor).get(out); + String dstPath = null; + if (filledEntry != null) { + ResourcePath filledDstPath = filledEntry.getKey(); + dstPath = filledDstPath.toString().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + } else { + dstPath = dstRes.getResourceHierarchy().toResourcePath(pathParams); + } if (!chainedCalls.contains(srcInput)) { // First call to an update method in this method srcInput.addStatement(getHttpMethodParamsStatement(srcComponent.getTypeName(), params, true)); - srcInput.addStatement("String result = " + getHttpMethodCallStatement(baseURL, - dstRes.getResourceHierarchy().toResourcePath(pathParams), - srcResName, - httpMethod)); + srcInput.addStatement("String result = " + getHttpMethodCallStatement(baseURL, dstPath, srcResName, httpMethod)); chainedCalls.add(srcInput); } else { // After the second time of call to update methods in this method srcInput.addStatement(getHttpMethodParamsStatement(srcComponent.getTypeName(), params, false)); - srcInput.addStatement("result = " + getHttpMethodCallStatement(baseURL, - dstRes.getResourceHierarchy().toResourcePath(pathParams), - srcResName, - httpMethod)); + srcInput.addStatement("result = " + getHttpMethodCallStatement(baseURL, dstPath, srcResName, httpMethod)); + } + if (descendantDstChannels.contains(chNode)) { + // For hierarchical channels (broadcasting push transfer). + if (ch.getSelectors() != null && ch.getSelectors().size() > 0) { + Expression selExp = ch.getSelectors().get(0).getExpression(); + Type selType = null; + String varName = null; + if (selExp instanceof Variable) { + selType = ((Variable) selExp).getType(); + varName = ((Variable) selExp).getName(); + ChannelMember insideChMem = null; + for (ChannelMember cm :ch.getInputChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + if (insideChMem == null) { + for (ChannelMember cm :ch.getReferenceChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + } + if (insideChMem == null) { + for (ChannelMember cm :ch.getOutputChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + } + ResourcePath insideResPath = insideChMem.getResource(); + while (insideResPath.getParent() != null && (insideResPath.getLastParam() == null || !insideResPath.getLastParam().equals(selExp))) { + insideResPath = insideResPath.getParent(); + } + insideResPath = insideResPath.getParent(); + String parent = null; + if (JerseyCodeGenerator.generatesComponent(insideResPath.getResourceHierarchy())) { + Expression getter = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(insideResPath, src.getPrimaryResourcePath()); + Term valueGetter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); + valueGetter.addChild(getter); + parent = valueGetter.toImplementation(new String[] {}); + } else { + parent = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(insideResPath, src.getPrimaryResourcePath()).toImplementation(new String[] {}); + } + if (insideResPath != null) { + if (selType.equals(DataConstraintModel.typeInt)) { + // make a for loop (for a list) for broadcasting. + srcInput.addFirstStatement("for (int " + varName + " = 0; " + varName +" < " + parent + ".size(); " + varName + "++) {"); + srcInput.addStatement("}"); + } else if (selType.equals(DataConstraintModel.typeString)) { + // make a for loop (for a map) for broadcasting. + srcInput.addFirstStatement("for (String " + varName + ": " + parent + ".keySet()) {"); + srcInput.addStatement("}"); + } + } + } else if (selExp instanceof Term) { + // not supported. + } + } } srcInput.addThrow("JsonProcessingException"); } else { @@ -652,20 +811,17 @@ getter = getGetterMethod(dstComponent, dstResourceName); } if (getter.getBody() == null || getter.getBody().getStatements().size() == 0) { - // generate a return statement. - Expression curExp = ch.deriveUpdateExpressionOf(out, JerseyCodeGenerator.pullAccessor).getKey(); // no pull data transfer is included. - Map>> resourcePaths = ch.fillOutsideResourcePaths(out, JerseyCodeGenerator.pullAccessor); + // The first time to fill the getter method's body. String[] sideEffects = new String[] {""}; - String curState = curExp.toImplementation(sideEffects); - getter.addStatement(sideEffects[0] + "return " + curState + ";"); // For each reference channel member, get the current state of the reference side resource by pull data transfer. for (ChannelMember rc: ch.getReferenceChannelMembers()) { ResourcePath refRes = rc.getResource(); - String refResourceName = refRes.getLeafResourceName(); + String refResourceName = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(refRes.getResourceHierarchy())); Type refResourceType = refRes.getResourceStateType(); if (rc.isOutside()) { List pathParams = new ArrayList<>(); for (Expression pathExp: refRes.getPathParams()) { + sideEffects = new String[] {""}; pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); } generatePullDataTransfer(getter, refResourceName, refRes.getResourceHierarchy().toResourcePath(pathParams), refResourceType); @@ -675,24 +831,179 @@ dstRes = dstRes.getParent(); } Expression refGetter = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(refRes, dstRes); + sideEffects = new String[] {""}; String refExp = refGetter.toImplementation(sideEffects); String refTypeName = refResourceType.getInterfaceTypeName(); getter.addFirstStatement(sideEffects[0] + refTypeName + " " + refResourceName + " = " + refExp + ";"); } } + Map.Entry>>, Term> resourcePathsAndMessage = ch.fillOutsideResourcePaths(out, JerseyCodeGenerator.pullAccessor, null); + Map>> resourcePaths = resourcePathsAndMessage.getKey(); + Term messageTerm = resourcePathsAndMessage.getValue(); for (Entry>> pathEnt: resourcePaths.entrySet()) { ChannelMember cm = pathEnt.getKey(); ResourcePath src2 = pathEnt.getValue().getKey(); // get outside src resource state by pull data transfer. if (cm.isOutside() || src2.getCommonPrefix(dst.getInSideResource(ch)) == null) { - Type srcResourceType = src2.getResourceStateType(); List pathParams = new ArrayList<>(); for (Expression pathExp: src2.getPathParams()) { + sideEffects = new String[] {""}; pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); } - generatePullDataTransfer(getter, src2.getLeafResourceName(), src2.getResourceHierarchy().toResourcePath(pathParams), srcResourceType); + // generate a pull data transfer from a depending in/ref resource. + Type srcResourceType = src2.getResourceStateType(); + String srcResName2 = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(src2.getResourceHierarchy())); + String srcPath2 = src2.toString().replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + generatePullDataTransfer(getter, srcResName2, srcPath2, srcResourceType); } } + // Should take into account the channel hierarchy. + Stack> channelItrStack = new Stack<>(); + DataTransferChannel curChannel = ch; + if (curChannel.getChildren() != null && curChannel.getChildren().size() > 0) { + // retrieve descendant channels recursively. + Iterator chItr = curChannel.getChildren().iterator(); + do { + if (!chItr.hasNext()) { + chItr = channelItrStack.pop(); + } else { + curChannel = (DataTransferChannel) chItr.next(); + // generate pull data transfers. + Set chMems = new HashSet<>(curChannel.getInputChannelMembers()); + chMems.addAll(curChannel.getReferenceChannelMembers()); + for (ChannelMember cm2: chMems) { + if (resourcePaths == null || !resourcePaths.keySet().contains(cm2)) { + // not a depending channel member. + ResourcePath src2 = cm2.getResource(); + Type srcResType2 = src2.getResourceStateType(); + String srcResName2 = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(src2.getResourceHierarchy())); + String srcPath2 = src2.toString().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + generatePullDataTransfer(getter, srcResName2, srcPath2, srcResType2); + } else { + // a depending channel member. + ResourcePath src2 = resourcePaths.get(cm2).getKey(); + // get outside src2 resource state by pull data transfer. + if (cm2.isOutside() || src2.getCommonPrefix(dst.getInSideResource(curChannel)) == null) { + // generate a pull data transfer from a depending in/ref resource. + Type srcResType2 = src2.getResourceStateType(); + String srcResName2 = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(src2.getResourceHierarchy())); + String srcPath2 = src2.toString().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + generatePullDataTransfer(getter, srcResName2, srcPath2, srcResType2); + } + } + } + // collect the message constraints by a descendant channel. + List varsForSideEffects = new ArrayList<>(); + int v = 0; + resourcePathsAndMessage = curChannel.fillOutsideResourcePaths(out, JerseyCodeGenerator.pullAccessor, null); + if (resourcePathsAndMessage != null) { + resourcePaths = resourcePathsAndMessage.getKey(); + Term messageTermSub = resourcePathsAndMessage.getValue(); + for (Map.Entry subTermEnt: messageTermSub.getSubTerms(Term.class).entrySet()) { + Term subTerm = subTermEnt.getValue(); + if (!(subTerm instanceof Constant) && subTerm.getSymbol().isImplWithSideEffect()) { + Variable var = new Variable("v" + v, subTerm.getType()); + varsForSideEffects.add(var); + v++; + // Add a side effect statement within the loop + Position pos = new Position(); + pos.addHeadOrder(0); + subTerm.replaceSubTerm(pos, var); + sideEffects = new String[] {""}; + String curState = messageTermSub.toImplementation(sideEffects); + getter.addStatement(sideEffects[0].replaceAll("\n", "")); + // Cancel the side effects in the return value. + pos = subTermEnt.getKey(); + messageTermSub.replaceSubTerm(pos, var); + } + } + if (messageTerm == null) { + messageTerm = messageTermSub; + } else { + messageTerm = (Term) messageTerm.unify(messageTermSub); + } + if (messageTerm == null) { + throw new UnificationFailed(); + } + } + // enclosed by a for loop + Expression selExp = curChannel.getSelectors().get(0).getExpression(); + Type selType = null; + String varName = null; + if (selExp instanceof Variable) { + selType = ((Variable) selExp).getType(); + varName = ((Variable) selExp).getName(); + ChannelMember insideChMem = null; + for (ChannelMember cm2 :curChannel.getInputChannelMembers()) { + if (!cm2.isOutside()) { + insideChMem = cm2; + break; + } + } + if (insideChMem == null) { + for (ChannelMember cm2 :curChannel.getReferenceChannelMembers()) { + if (!cm2.isOutside()) { + insideChMem = cm2; + break; + } + } + } + ResourcePath insideResPath = insideChMem.getResource(); + while (insideResPath.getParent() != null && (insideResPath.getLastParam() == null || !insideResPath.getLastParam().equals(selExp))) { + insideResPath = insideResPath.getParent(); + } + insideResPath = insideResPath.getParent(); + String parent = null; + if (insideResPath != null) { + if (insideResPath.getCommonPrefix(dst.getInSideResource(ch)) != null) { + if (JerseyCodeGenerator.generatesComponent(insideResPath.getResourceHierarchy())) { + Expression parentGetter = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(insideResPath, dst.getInSideResource(ch)); + Term valueGetter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); + valueGetter.addChild(parentGetter); + parent = valueGetter.toImplementation(new String[] {}); + } else { + parent = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(insideResPath, dst.getInSideResource(ch)).toImplementation(new String[] {}); + } + } else { + parent = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(insideResPath.getResourceHierarchy())); + } + if (selType.equals(DataConstraintModel.typeInt)) { + // make a for loop (for a list) for data collecting. + getter.addFirstStatement("for (int " + varName + " = 0; " + varName +" < " + parent + ".size(); " + varName + "++) {"); + } else if (selType.equals(DataConstraintModel.typeString)) { + // make a for loop (for a map) for data collecting. + getter.addFirstStatement("for (String " + varName + ": " + parent + ".keySet()) {"); + } + if (insideResPath.getCommonPrefix(dst.getInSideResource(ch)) == null) { + Type parentResType = insideResPath.getResourceStateType(); + String parentResName = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(insideResPath.getResourceHierarchy())); + String parentResPath = insideResPath.toString().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + generatePullDataTransfer(getter, parentResName, parentResPath, parentResType); + } + } + } + // initialize the variables to hold side effects within the loop + for (Variable var: varsForSideEffects) { + getter.addFirstStatement(var.getType().getInterfaceTypeName() + " " + var.getName() + " = new " + var.getType().getImplementationTypeName() + "();"); + } + // end of the loop + getter.addStatement("}"); + if (curChannel.getChildren() != null && curChannel.getChildren().size() > 0) { + channelItrStack.push(chItr); + chItr = curChannel.getChildren().iterator(); + } + } + } while (!channelItrStack.isEmpty()); + } + // generate a return statement. + Expression curExp = ch.deriveUpdateExpressionOf(out, messageTerm, JerseyCodeGenerator.pullAccessor); + sideEffects = new String[] {""}; + String curState = curExp.toImplementation(sideEffects); + if (ch.getChildren() == null || ch.getChildren().size() == 0) { + getter.addStatement(sideEffects[0] + "return " + curState + ";"); + } else { + getter.addStatement("return " + curState + ";"); + } } // get src resource state by pull data transfer. if (src.getNumberOfParameters() == 0 && src.getOutSideResource(ch).getCommonPrefix(dst.getInSideResource(ch)) == null) { @@ -768,7 +1079,7 @@ } } - // descendant getter method + // (#4) descendant getter method (the implementation must be kept consistent with #3) if (resource.getChildren().size() > 0) { for (ResourceHierarchy child: resource.getChildren()) { ResourceHierarchy parent = resource; @@ -792,6 +1103,7 @@ for (MethodDeclaration getter: getGetterMethods(component, methodName)) { if ((getter.getParameters() == null && params == 0) || (getter.getParameters() != null && getter.getParameters().size() == params)) { descendantGetter = getter; + break; } } if (descendantGetter != null) { @@ -801,14 +1113,12 @@ newSelector.addChild(new Variable(descendantGetter.getParameters().get(descendantGetter.getParameters().size() - 1).getName())); newSelector.setType(descendantGetter.getReturnType()); selector = newSelector; - params++; } else if (DataConstraintModel.typeMap.isAncestorOf(parent.getResourceStateType())) { Term newSelector = new Term(DataConstraintModel.lookup); newSelector.addChild(selector); newSelector.addChild(new Variable(descendantGetter.getParameters().get(descendantGetter.getParameters().size() - 1).getName())); newSelector.setType(descendantGetter.getReturnType()); selector = newSelector; - params++; } if (descendantGetter != null && (descendantGetter.getBody() == null || descendantGetter.getBody().getStatements().size() == 0)) { String[] sideEffects = new String[] {null}; @@ -822,6 +1132,11 @@ break; } parent = descendant; + if (DataConstraintModel.typeList.isAncestorOf(parent.getResourceStateType())) { + params++; + } else if (DataConstraintModel.typeMap.isAncestorOf(parent.getResourceStateType())) { + params++; + } children = descendant.getChildren(); } while (children != null && children.size() == 1 && (descendant = children.iterator().next()) != null); } @@ -1022,27 +1337,29 @@ if (jsonTerm instanceof JsonTerm || jsonTerm.getSymbol().equals(DataConstraintModel.addMember)) { String constructorInvocation = "new " + replacingClassName + "("; MethodDeclaration descendantConstructor = getConstructor(descendantComponent); - String delimiter = ""; - for (VariableDeclaration var: descendantConstructor.getParameters()) { - JsonAccessor jsonMember = new JsonAccessor(DataConstraintModel.dot); - jsonMember.addChild(jsonTerm); - jsonMember.addChild(new Constant(var.getName(), DataConstraintModel.typeString)); - Expression param = jsonMember.reduce(); - if (param != null) { - if (param instanceof Term) { - if (((Term) param).getType() == null) { - ((Term) param).setType(var.getType()); + if (descendantConstructor != null) { + String delimiter = ""; + for (VariableDeclaration var: descendantConstructor.getParameters()) { + JsonAccessor jsonMember = new JsonAccessor(DataConstraintModel.dot); + jsonMember.addChild(jsonTerm); + jsonMember.addChild(new Constant(var.getName(), DataConstraintModel.typeString)); + Expression param = jsonMember.reduce(); + if (param != null) { + if (param instanceof Term) { + if (((Term) param).getType() == null) { + ((Term) param).setType(var.getType()); + } + } else if (param instanceof Variable) { + if (((Variable) param).getType() == null) { + ((Variable) param).setType(var.getType()); + } } - } else if (param instanceof Variable) { - if (((Variable) param).getType() == null) { - ((Variable) param).setType(var.getType()); - } + constructorInvocation = constructorInvocation + delimiter + param.toImplementation(null); + } else { + constructorInvocation = constructorInvocation + delimiter + var.getName(); } - constructorInvocation = constructorInvocation + delimiter + param.toImplementation(null); - } else { - constructorInvocation = constructorInvocation + delimiter + var.getName(); + delimiter = ", "; } - delimiter = ", "; } constructorInvocation += ")"; ((Term) exp).replaceSubTerm(termEnt.getKey(), new Constant(constructorInvocation)); diff --git a/AlgebraicDataflowArchitectureModel/src/models/algebra/Symbol.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/Symbol.java index 980fc53..b358747 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/algebra/Symbol.java +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Symbol.java @@ -117,6 +117,11 @@ public String getName() { return name; } + + public void changeName(String name) { + this.name = name; + this.implName = name; + } public Type getOperatorType() { return operatorType; diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonAccessor.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonAccessor.java index 30a7465..43e428c 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonAccessor.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonAccessor.java @@ -25,19 +25,53 @@ public Expression reduce() { Expression reducedTerm = super.reduce(); if (reducedTerm instanceof Term) { - if ((symbol == DataConstraintModel.dot || symbol == DataConstraintModel.dotParam) && getChildren().size() >= 2) { + if (symbol == DataConstraintModel.dot && getChildren().size() >= 2) { // this term is `json.key`. Expression expJson = getChild(0); Expression expKey = getChild(1); if (expKey instanceof Constant && expJson instanceof Term) { reducedTerm = getValue((Term) expJson, (Constant) expKey); } + } else if (symbol == DataConstraintModel.dotParam && getChildren().size() >= 2) { + // this term is `json.{param}`. + Expression expJson = getChild(0); + Expression expKey = getChild(1); + if (expKey instanceof Variable && expJson instanceof Term) { + reducedTerm = getValue((Term) expJson, (Variable) expKey); + } } } return reducedTerm; } - private Expression getValue(Term json, Constant key) { + private Expression getValue(Term json, Expression key) { + if (json instanceof JsonTerm) { + if (key instanceof Constant) { + return ((JsonTerm) json).get((Constant) key); + } else if (key instanceof Variable) { + return ((JsonTerm) json).get((Variable) key); + } + } + if (key instanceof Constant || key instanceof Variable) { + if (json.getSymbol().equals(DataConstraintModel.addMember) || json.getSymbol().equals(DataConstraintModel.set)) { + if (json.getChild(1).equals(key)) { + Expression value = json.getChild(2); + if (value == null) { + return new Constant(DataConstraintModel.null_); + } + return value; + } + if (json.getChild(0) == null + || (json.getChild(0) instanceof Term && + (((Term) json.getChild(0)).getSymbol().equals(DataConstraintModel.null_)) + || ((Term) json.getChild(0)).getSymbol().equals(DataConstraintModel.nil))) return null; + return getValue((Term) json.getChild(0), key); + } + } + return new Constant(DataConstraintModel.null_); + } + + private Expression getValue(Term json, Variable key) { if (json instanceof JsonTerm) { return ((JsonTerm) json).get(key); } @@ -55,6 +89,20 @@ || ((Term) json.getChild(0)).getSymbol().equals(DataConstraintModel.nil))) return null; return getValue((Term) json.getChild(0), key); } + if (json.getSymbol().equals(DataConstraintModel.insert)) { + if (json.getChild(1).equals(key)) { + Expression value = json.getChild(2); + if (value == null) { + return new Constant(DataConstraintModel.null_); + } + return value; + } + if (json.getChild(0) == null + || (json.getChild(0) instanceof Term && + (((Term) json.getChild(0)).getSymbol().equals(DataConstraintModel.null_)) + || ((Term) json.getChild(0)).getSymbol().equals(DataConstraintModel.nil))) return null; + return getValue((Term) json.getChild(0), key); + } return new Constant(DataConstraintModel.null_); } diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonTerm.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonTerm.java index 8ccdfd5..3998afa 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonTerm.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonTerm.java @@ -9,6 +9,7 @@ import models.algebra.Expression; import models.algebra.Symbol; import models.algebra.Term; +import models.algebra.Variable; import parser.Parser; public class JsonTerm extends Term { @@ -41,6 +42,11 @@ if (keyToIndex.get(key.getValue()) == null) return null; return getChild(keyToIndex.get(key.getValue())); } + + public Expression get(Variable key) { + if (keyToIndex.get(key.getName()) == null) return null; + return getChild(keyToIndex.get(key.getName())); + } @Override public Expression unify(Expression another) { diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourcePath.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourcePath.java index c1125f1..b24568f 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourcePath.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourcePath.java @@ -80,7 +80,11 @@ public ResourcePath(ResourcePath another) { super(another.name); - this.parent = another.parent; + if (another.parent != null) { + this.parent = new ResourcePath(another.parent); + } else { + this.parent = null; + } this.resourceHierarchy = another.resourceHierarchy; this.pathParams = new ArrayList<>(another.getPathParamsAndConstraints()); } @@ -132,6 +136,13 @@ public void addPathParamWithConstraint(Expression pathParam, Expression pathConstraint) { pathParams.add(new AbstractMap.SimpleEntry<>(pathParam, pathConstraint)); } + + public void replacePathParam(int i, Expression pathParam, Expression pathConstraint) { + if (i < pathParams.size()) { + pathParams.set(i, new AbstractMap.SimpleEntry<>(pathParam, pathConstraint)); + parent.replacePathParam(i, pathParam, pathConstraint); + } + } public boolean endsWithParam() { if (resourceHierarchy.getNumParameters() > 0) return true; diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ChannelNode.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ChannelNode.java index 108bc21..0371ca0 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ChannelNode.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ChannelNode.java @@ -23,6 +23,27 @@ public Set getChildren() { return children; } + + public Set getAncestors(){ + if (parent == null) { + return new HashSet<>(); + } + Set ancestors = parent.getAncestors(); + ancestors.add(parent); + return ancestors; + } + + public Set getDescendants() { + if (children == null) { + return new HashSet<>(); + } + Set descendants = new HashSet<>(); + for (ChannelNode child: children) { + descendants.addAll(child.getDescendants()); + descendants.add(child); + } + return descendants; + } public void addChild(ChannelNode child) { children.add(child); diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferChannel.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferChannel.java index dc09114..23a6c65 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferChannel.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferChannel.java @@ -54,6 +54,15 @@ return inputChannelMembers; } + public Set getAllInputChannelMembers() { + Set allInputChannelMembers = new HashSet<>(); + allInputChannelMembers.addAll(inputChannelMembers); + for (Channel child: children) { + allInputChannelMembers.addAll(((DataTransferChannel) child).getAllInputChannelMembers()); + } + return allInputChannelMembers; + } + public void setInputChannelMembers(Set inputChannelMembers) { this.inputChannelMembers = inputChannelMembers; } @@ -269,12 +278,12 @@ */ public Map>> fillOutsideResourcePaths(ChannelMember targetMember, IResourceStateAccessor stateAccessor) throws ParameterizedIdentifierIsFutureWork, ResolvingMultipleDefinitionIsFutureWork, InvalidMessage, UnificationFailed, ValueUndefined { - return fillOutsideResourcePaths(targetMember, stateAccessor, null); + if (!getOutputChannelMembers().contains(targetMember)) return null; + return fillOutsideResourcePaths(targetMember, stateAccessor, null).getKey(); } - public Map>> fillOutsideResourcePaths(ChannelMember targetMember, IResourceStateAccessor stateAccessor, Map inputResourceToStateAccessor) + public Map.Entry>>, Term> fillOutsideResourcePaths(ChannelMember targetMember, IResourceStateAccessor stateAccessor, Map inputResourceToStateAccessor) throws ParameterizedIdentifierIsFutureWork, ResolvingMultipleDefinitionIsFutureWork, InvalidMessage, UnificationFailed, ValueUndefined { - if (!getOutputChannelMembers().contains(targetMember)) return null; Map>> resourcePaths = new HashMap<>(); // Calculate unified message constraints from input and reference state transitions @@ -341,7 +350,7 @@ } } } - return resourcePaths; + return new AbstractMap.SimpleEntry<>(resourcePaths, unifiedMessage); } /** @@ -556,7 +565,7 @@ if (pathParam instanceof Variable) { if (pathValue == null) { if (bindings.get((Variable) pathParam) != null) { - dstParams.set(i, new AbstractMap.SimpleEntry<>(bindings.get((Variable) pathParam).getValue(), null)); // Replace a path parameter with a value in the unified message. + filledResourcePath.replacePathParam(i, bindings.get((Variable) pathParam).getValue(), null); // Replace a path parameter with a value in the unified message. dependingVarPosInMessage.add(bindings.get((Variable) pathParam).getKey()); // The position of the replaced variable in the message. } } else { @@ -572,7 +581,7 @@ if (!(pathValue instanceof Constant)) { pathValue = ((Term) pathValue).reduce(); } - dstParams.set(i, new AbstractMap.SimpleEntry<>(pathValue, null)); // Replace a path parameter with the substituted term. + filledResourcePath.replacePathParam(i, pathValue, null); // Replace a path parameter with the substituted term. } } } else if (pathParam instanceof Term) { @@ -586,7 +595,7 @@ if (!(pathParam instanceof Constant)) { pathParam = ((Term) pathParam).reduce(); } - dstParams.set(i, new AbstractMap.SimpleEntry<>(pathParam, null)); // Replace a path parameter with the substituted term. + filledResourcePath.replacePathParam(i, pathParam, null); // Replace a path parameter with the substituted term. } } return filledResourcePath; diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferModel.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferModel.java index b27156b..ad92b74 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferModel.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferModel.java @@ -18,6 +18,9 @@ addResourceToChannelEdges(dataFlowGraph, channel, null, channelLocalResMap); } for (Channel channel: getChannels()) { + addReferenceResources(dataFlowGraph, channel, null, channelLocalResMap); + } + for (Channel channel: getChannels()) { addChannelToResourceEdges(dataFlowGraph, channel, null, channelLocalResMap); } for (Channel channel: getInputChannels()) { @@ -66,6 +69,44 @@ } } + private void addReferenceResources(DataFlowGraph dataFlowGraph, Channel dstChannel, ChannelNode parentChannelNode, + Map>> channelLocalResMap) { + DataTransferChannel dstDfChannel = (DataTransferChannel) dstChannel; + ChannelNode dstChannelNode = dataFlowGraph.addChannelNode(parentChannelNode, dstDfChannel); + for (ResourcePath srcRes: dstDfChannel.getReferenceResources()) { + Map> chLocalResNodes = channelLocalResMap.get(srcRes.getResourceHierarchy()); + if (srcRes.getNumberOfParameters() == 0) { + // ResourcePath without parameter corresponds to a global ResourceNode. + ResourceNode srcResNode = addResourceNodes(dataFlowGraph, srcRes, dstDfChannel, channelLocalResMap, false); + } else { + if (chLocalResNodes == null) { + // There is no channel-local ResourcePath. + // Then, create a new channel-local ResourceNode. + ResourceNode srcResNode = addResourceNodes(dataFlowGraph, srcRes, dstDfChannel, channelLocalResMap, false); + } else { + boolean bExists = false; + for (Channel ch: chLocalResNodes.keySet()) { + Set nodes = chLocalResNodes.get(ch); + for (ResourceNode node: nodes) { + ResourcePath r = node.getOutSideResource((DataTransferChannel) ch); + if (r.toString().equals(srcRes.toString())) { + bExists = true; // There exists the same ResourecPath within some channel. + break; + } + } + } + if (!bExists) { + // Create a new channel-local ResourceNode. + ResourceNode srcResNode = addResourceNodes(dataFlowGraph, srcRes, dstDfChannel, channelLocalResMap, false); + } + } + } + } + for (Channel childChannel: dstDfChannel.getChildren()) { + addReferenceResources(dataFlowGraph, childChannel, dstChannelNode, channelLocalResMap); + } + } + private void addChannelToResourceEdges(DataFlowGraph dataFlowGraph, Channel srcChannel, ChannelNode parentChannelNode, Map>> channelLocalResMap) { DataTransferChannel srcDfChannel = (DataTransferChannel) srcChannel; diff --git a/AlgebraicDataflowArchitectureModel/src/parser/Parser.java b/AlgebraicDataflowArchitectureModel/src/parser/Parser.java index d10e0ec..951bcb0 100644 --- a/AlgebraicDataflowArchitectureModel/src/parser/Parser.java +++ b/AlgebraicDataflowArchitectureModel/src/parser/Parser.java @@ -120,7 +120,7 @@ DataTransferModel model = new DataTransferModel(); DataTransferChannel channel; while ((channel = parseChannel(model)) != null) { - if (channel.getInputChannelMembers().size() == 0) { + if (channel.getAllInputChannelMembers().size() == 0) { model.addInputChannel(channel); } else { model.addChannel(channel); diff --git a/AlgebraicDataflowArchitectureModel/src/tests/JAXRSCodeGeneratorTest.java b/AlgebraicDataflowArchitectureModel/src/tests/JAXRSCodeGeneratorTest.java index 7eddd76..f72baa9 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/JAXRSCodeGeneratorTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/JAXRSCodeGeneratorTest.java @@ -58,8 +58,10 @@ testGroupChat(); testInventoryManagement(); testOnlineBattleGame(); + testOnlineBattleGame2(); testPOS(); testSimpleTwitter(); + testVotingSystem(); testWeatherObservationSystem(); } @@ -395,8 +397,65 @@ Entry, // arg types Integer>>>>>>> // lines of code exprectedStructure = new HashMap<>(); + exprectedStructure.put("Groups", Map.entry(Set.of("@Path(\"/groups\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "Map")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getGroup", Map.entry(Set.of(), + Map.entry("Group", + Map.entry(List.of("String"), + 1)))), + Map.entry("getMembersValue", Map.entry(Set.of("@Path(\"/{gid}/members\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("List", + Map.entry(List.of("String"), + 1)))), + Map.entry("getMessagesValue", Map.entry(Set.of("@Path(\"/{gid}/messages\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("List", + Map.entry(List.of("String"), + 1)))), + Map.entry("getGroupValue", Map.entry(Set.of("@Path(\"/{gid}\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of("String"), + 1)))), + Map.entry("addGroupMember", Map.entry(Set.of("@Path(\"/{gid}/members\")","@POST"), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))), + Map.entry("postMessage", Map.entry(Set.of("@Path(\"/{gid}/messages\")","@POST"), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))), + Map.entry("createGroup", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("String"), + 1)))))))); + exprectedStructure.put("Account", Map.entry(Set.of(), + Map.entry(Map.ofEntries(Map.entry("notifications", "Map")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of(), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getNotifications", Map.entry(Set.of(), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("updateNotificationsFromMessages", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String","String","int","List","String"), + 1)))), + Map.entry("hasRead", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))), + Map.entry("Account", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("Map"), + 1)))))))); exprectedStructure.put("Group", Map.entry(Set.of(), Map.entry(Map.ofEntries(Map.entry("messages", "List"), + Map.entry("client", "Client"), Map.entry("members", "List")), Map.ofEntries(Map.entry("getValue", Map.entry(Set.of(), Map.entry("Map", @@ -413,7 +472,7 @@ Map.entry("postMessage", Map.entry(Set.of(), Map.entry("void", Map.entry(List.of("String","String"), - 1)))), + 6)))), Map.entry("addGroupMember", Map.entry(Set.of(), Map.entry("void", Map.entry(List.of("String","String"), @@ -432,6 +491,10 @@ Map.entry("Account", Map.entry(List.of("String"), 1)))), + Map.entry("updateNotificationsFromMessages", Map.entry(Set.of("@Path(\"accounts/{v1}/notifications\")","@POST"), + Map.entry("void", + Map.entry(List.of("String","String","int","List","String"), + 1)))), Map.entry("getNotificationsValue", Map.entry(Set.of("@Path(\"/{v1}/notifications\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), Map.entry("Map", Map.entry(List.of("String"), @@ -448,58 +511,6 @@ Map.entry("void", Map.entry(List.of("String"), 1)))))))); - exprectedStructure.put("Account", Map.entry(Set.of(), - Map.entry(Map.ofEntries(Map.entry("notifications", "Map")), - Map.ofEntries(Map.entry("getValue", Map.entry(Set.of(), - Map.entry("Map", - Map.entry(List.of(), - 1)))), - Map.entry("getNotifications", Map.entry(Set.of(), - Map.entry("Map", - Map.entry(List.of(), - 1)))), - Map.entry("hasRead", Map.entry(Set.of(), - Map.entry("void", - Map.entry(List.of("String","String"), - 1)))), - Map.entry("Account", Map.entry(Set.of(), - Map.entry("void", - Map.entry(List.of("Map"), - 1)))))))); - exprectedStructure.put("Groups", Map.entry(Set.of("@Path(\"/groups\")","@Component"), - Map.entry(Map.ofEntries(Map.entry("value", "Map")), - Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), - Map.entry("Map", - Map.entry(List.of(), - 1)))), - Map.entry("getGroup", Map.entry(Set.of(), - Map.entry("Group", - Map.entry(List.of("String"), - 1)))), - Map.entry("getMessagesValue", Map.entry(Set.of("@Path(\"/{gid}/messages\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), - Map.entry("List", - Map.entry(List.of("String"), - 1)))), - Map.entry("getGroupValue", Map.entry(Set.of("@Path(\"/{gid}\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), - Map.entry("Map", - Map.entry(List.of("String"), - 1)))), - Map.entry("getMembersValue", Map.entry(Set.of("@Path(\"/{gid}/members\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), - Map.entry("List", - Map.entry(List.of("String"), - 1)))), - Map.entry("postMessage", Map.entry(Set.of("@Path(\"/{gid}/messages\")","@POST"), - Map.entry("void", - Map.entry(List.of("String","String"), - 1)))), - Map.entry("addGroupMember", Map.entry(Set.of("@Path(\"/{gid}/members\")","@POST"), - Map.entry("void", - Map.entry(List.of("String","String"), - 1)))), - Map.entry("createGroup", Map.entry(Set.of("@POST"), - Map.entry("void", - Map.entry(List.of("String"), - 1)))))))); checkStructure(generatedCode, exprectedStructure); // generateCheckCode(generatedCode); @@ -727,6 +738,196 @@ } } + + private void testOnlineBattleGame2() { + try { + // check PULL-first + ArrayList generatedCode = generateCode("models/OnlineBattleGame2.model", null); + Map, // class annotations + Entry, // field name to type + Map, // class annotations + Entry, // arg types + Integer>>>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Rooms", Map.entry(Set.of("@Path(\"/rooms\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "Map")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getRoom", Map.entry(Set.of(), + Map.entry("Room", + Map.entry(List.of("String"), + 1)))), + Map.entry("getIdValue", Map.entry(Set.of("@Path(\"/{rid}/members/{mno}/id\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("String", + Map.entry(List.of("String","int"), + 1)))), + Map.entry("getBattleValue", Map.entry(Set.of("@Path(\"/{rid}/battle\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("boolean", + Map.entry(List.of("String"), + 1)))), + Map.entry("getRoomValue", Map.entry(Set.of("@Path(\"/{rid}\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of("String"), + 1)))), + Map.entry("getMemberValue", Map.entry(Set.of("@Path(\"/{rid}/members/{mno}\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of("String","int"), + 1)))), + Map.entry("getMembersValue", Map.entry(Set.of("@Path(\"/{rid}/members\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("List", + Map.entry(List.of("String"), + 1)))), + Map.entry("getNameValue", Map.entry(Set.of("@Path(\"/{rid}/members/{mno}/name\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("String", + Map.entry(List.of("String","int"), + 1)))), + Map.entry("battle", Map.entry(Set.of("@Path(\"/{rid}/battle\")","@PUT"), + Map.entry("void", + Map.entry(List.of("String","boolean"), + 1)))), + Map.entry("addRoomMember", Map.entry(Set.of("@Path(\"/{rid}/members\")","@POST"), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))), + Map.entry("createRoom", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("String"), + 1)))))))); + exprectedStructure.put("Member", Map.entry(Set.of(), + Map.entry(Map.ofEntries(Map.entry("id", "String"), + Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of(), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getId", Map.entry(Set.of(), + Map.entry("String", + Map.entry(List.of(), + 1)))), + Map.entry("getName", Map.entry(Set.of(), + Map.entry("String", + Map.entry(List.of(), + 2)))), + Map.entry("Member", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String"), + 1)))))))); + exprectedStructure.put("Account", Map.entry(Set.of(), + Map.entry(Map.ofEntries(Map.entry("name", "String"), + Map.entry("point", "int")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of(), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getName", Map.entry(Set.of(), + Map.entry("String", + Map.entry(List.of(), + 1)))), + Map.entry("getPoint", Map.entry(Set.of(), + Map.entry("int", + Map.entry(List.of(), + 1)))), + Map.entry("updatePointFromBattle", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String","String","int","boolean","String"), + 1)))), + Map.entry("changeName", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))), + Map.entry("Account", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String","int"), + 2)))))))); + exprectedStructure.put("Room", Map.entry(Set.of(), + Map.entry(Map.ofEntries(Map.entry("members", "Members"), + Map.entry("battle", "boolean"), + Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of(), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getMembers", Map.entry(Set.of(), + Map.entry("Members", + Map.entry(List.of(), + 1)))), + Map.entry("getBattle", Map.entry(Set.of(), + Map.entry("boolean", + Map.entry(List.of(), + 1)))), + Map.entry("battle", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String","boolean"), + 6)))), + Map.entry("Room", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("boolean"), + 1)))))))); + exprectedStructure.put("Accounts", Map.entry(Set.of("@Path(\"/accounts\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "Map")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getAccount", Map.entry(Set.of(), + Map.entry("Account", + Map.entry(List.of("String"), + 1)))), + Map.entry("updatePointFromBattle", Map.entry(Set.of("@Path(\"accounts/{mid}/point\")","@POST"), + Map.entry("void", + Map.entry(List.of("String","String","int","boolean","String"), + 1)))), + Map.entry("getAccountValue", Map.entry(Set.of("@Path(\"/{mid}\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of("String"), + 1)))), + Map.entry("getNameValue", Map.entry(Set.of("@Path(\"/{mid}/name\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("String", + Map.entry(List.of("String"), + 1)))), + Map.entry("getPointValue", Map.entry(Set.of("@Path(\"/{mid}/point\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("int", + Map.entry(List.of("String"), + 1)))), + Map.entry("signUp", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))), + Map.entry("changeName", Map.entry(Set.of("@Path(\"/{aid}/name\")","@PUT"), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))))))); + exprectedStructure.put("Members", Map.entry(Set.of(), + Map.entry(Map.ofEntries(Map.entry("value", "List")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of(), + Map.entry("List", + Map.entry(List.of(), + 1)))), + Map.entry("getMember", Map.entry(Set.of(), + Map.entry("Member", + Map.entry(List.of("int"), + 1)))), + Map.entry("addRoomMember", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + private void testPOS() { try { // check PULL-first @@ -925,6 +1126,85 @@ e.printStackTrace(); } } + + private void testVotingSystem() { + try { + ArrayList generatedCode = generateCode("models/VotingSystem.model", null); + Map, // class annotations + Entry, // field name to type + Map, // class annotations + Entry, // arg types + Integer>>>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Counts", Map.entry(Set.of("@Path(\"/counts\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "Map"), + Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of(), + 8)))), + Map.entry("Counts", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("Map"), + 1)))))))); + exprectedStructure.put("Account", Map.entry(Set.of(), + Map.entry(Map.ofEntries(Map.entry("vote", "String")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of(), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getVote", Map.entry(Set.of(), + Map.entry("String", + Map.entry(List.of(), + 1)))), + Map.entry("cast", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))), + Map.entry("Account", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String"), + 1)))))))); + exprectedStructure.put("Accounts", Map.entry(Set.of("@Path(\"/accounts\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "Map")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getAccount", Map.entry(Set.of(), + Map.entry("Account", + Map.entry(List.of("String"), + 1)))), + Map.entry("getAccountValue", Map.entry(Set.of("@Path(\"/{aid}\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of("String"), + 1)))), + Map.entry("getVoteValue", Map.entry(Set.of("@Path(\"/{aid}/vote\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("String", + Map.entry(List.of("String"), + 1)))), + Map.entry("signUp", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))), + Map.entry("cast", Map.entry(Set.of("@Path(\"/{aid}/vote\")","@PUT"), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } private void testWeatherObservationSystem() { try { diff --git a/AlgebraicDataflowArchitectureModel/src/tests/JavaCodeGeneratorTest.java b/AlgebraicDataflowArchitectureModel/src/tests/JavaCodeGeneratorTest.java index a553098..d9e266f 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/JavaCodeGeneratorTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/JavaCodeGeneratorTest.java @@ -55,8 +55,10 @@ testGroupChat(); testInventoryManagement(); // testOnlineBattleGame(); // A feature has not been implemented for Java prototype generation. + testOnlineBattleGame2(); // Two methods with the same signature are generated. testPOS(); testSimpleTwitter(); + testVotingSystem(); testWeatherObservationSystem(); } @@ -433,7 +435,7 @@ 2))), Map.entry("setOrganization", Map.entry("void", Map.entry(List.of("String","String"), - 3))), + 4))), Map.entry("Customer", Map.entry("void", Map.entry(List.of("Companies","String"), 3)))))); @@ -496,128 +498,33 @@ Entry, // arg types Integer>>>>> // lines of code - exprectedStructure = new HashMap<>(); - exprectedStructure.put("Main", Map.entry(Map.ofEntries(Map.entry("accounts", "Accounts"), - Map.entry("groups", "Groups")), - Map.ofEntries(Map.entry("Main", Map.entry("void", - Map.entry(List.of(), - 2))), - Map.entry("getGroup", Map.entry("Map", - Map.entry(List.of("String"), - 1))), - Map.entry("getMessages", Map.entry("List", - Map.entry(List.of("String"), - 1))), - Map.entry("postMessage", Map.entry("void", - Map.entry(List.of("String","String"), - 1))), - Map.entry("getAccounts", Map.entry("Map", - Map.entry(List.of(), - 1))), - Map.entry("signUp", Map.entry("void", - Map.entry(List.of("String"), - 1))), - Map.entry("getGroups", Map.entry("Map", - Map.entry(List.of(), - 1))), - Map.entry("createGroup", Map.entry("void", - Map.entry(List.of("String"), - 1))), - Map.entry("getAccount", Map.entry("Map", - Map.entry(List.of("String"), - 1))), - Map.entry("getNotifications", Map.entry("Map", - Map.entry(List.of("String"), - 1))), - Map.entry("hasRead", Map.entry("void", - Map.entry(List.of("String","String"), - 1))), - Map.entry("getMembers", Map.entry("List", - Map.entry(List.of("String"), - 1))), - Map.entry("addGroupMember", Map.entry("void", - Map.entry(List.of("String","String"), - 1)))))); - exprectedStructure.put("Group", Map.entry(Map.ofEntries(Map.entry("messages", "List"), - Map.entry("members", "List")), - Map.ofEntries(Map.entry("getValue", Map.entry("Map", - Map.entry(List.of(), - 1))), - Map.entry("getMessages", Map.entry("List", - Map.entry(List.of(), - 1))), - Map.entry("getMembers", Map.entry("List", - Map.entry(List.of(), - 1))), - Map.entry("postMessage", Map.entry("void", - Map.entry(List.of("String","String"), - 1))), - Map.entry("addGroupMember", Map.entry("void", - Map.entry(List.of("String","String"), - 1))), - Map.entry("Group", Map.entry("void", - Map.entry(List.of("List","List"), - 2)))))); - exprectedStructure.put("Accounts", Map.entry(Map.ofEntries(), - Map.ofEntries(Map.entry("getValue", Map.entry("Map", - Map.entry(List.of(), - 1))), - Map.entry("getAccount", Map.entry("Account", - Map.entry(List.of("String"), - 1))), - Map.entry("signUp", Map.entry("void", - Map.entry(List.of("String"), - 1)))))); - exprectedStructure.put("Groups", Map.entry(Map.ofEntries(), - Map.ofEntries(Map.entry("getValue", Map.entry("Map", - Map.entry(List.of(), - 1))), - Map.entry("getGroup", Map.entry("Group", - Map.entry(List.of("String"), - 1))), - Map.entry("createGroup", Map.entry("void", - Map.entry(List.of("String"), - 1)))))); - exprectedStructure.put("Account", Map.entry(Map.ofEntries(), - Map.ofEntries(Map.entry("getValue", Map.entry("Map", - Map.entry(List.of(), - 1))), - Map.entry("getNotifications", Map.entry("Map", - Map.entry(List.of(), - 1))), - Map.entry("hasRead", Map.entry("void", - Map.entry(List.of("String","String"), - 1))), - Map.entry("Account", Map.entry("void", - Map.entry(List.of("Map"), - 1)))))); - - checkStructure(generatedCode, exprectedStructure); -// generateCheckCode(generatedCode); - - // check PUSH-first - generatedCode = generateCode("models/GroupChat.model", PushPullValue.PUSH); - exprectedStructure.clear(); + exprectedStructure = new HashMap<>(); exprectedStructure.put("Main", Map.entry(Map.ofEntries(Map.entry("groups", "Groups"), Map.entry("accounts", "Accounts")), Map.ofEntries(Map.entry("Main", Map.entry("void", Map.entry(List.of(), 2))), + Map.entry("getAccount", Map.entry("Map", + Map.entry(List.of("String"), + 1))), + Map.entry("getMessages", Map.entry("List", + Map.entry(List.of("String"), + 1))), + Map.entry("postMessage", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), Map.entry("getNotifications", Map.entry("Map", Map.entry(List.of("String"), 1))), Map.entry("hasRead", Map.entry("void", Map.entry(List.of("String","String"), 1))), - Map.entry("getAccount", Map.entry("Map", - Map.entry(List.of("String"), - 1))), - Map.entry("getGroups", Map.entry("Map", - Map.entry(List.of(), - 1))), - Map.entry("createGroup", Map.entry("void", - Map.entry(List.of("String"), + Map.entry("getAccounts", Map.entry("Map", + Map.entry(List.of(), 1))), + Map.entry("signUp", Map.entry("void", + Map.entry(List.of("String"), + 1))), Map.entry("getMembers", Map.entry("List", Map.entry(List.of("String"), 1))), @@ -627,17 +534,14 @@ Map.entry("getGroup", Map.entry("Map", Map.entry(List.of("String"), 1))), - Map.entry("getAccounts", Map.entry("Map", - Map.entry(List.of(), - 1))), - Map.entry("signUp", Map.entry("void", - Map.entry(List.of("String"), - 1))), - Map.entry("getMessages", Map.entry("List", + Map.entry("getMember", Map.entry("String", + Map.entry(List.of("String","int"), + 1))), + Map.entry("getGroups", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("createGroup", Map.entry("void", Map.entry(List.of("String"), - 1))), - Map.entry("postMessage", Map.entry("void", - Map.entry(List.of("String","String"), 1)))))); exprectedStructure.put("Account", Map.entry(Map.ofEntries(), Map.ofEntries(Map.entry("getValue", Map.entry("Map", @@ -646,6 +550,156 @@ Map.entry("getNotifications", Map.entry("Map", Map.entry(List.of(), 1))), + Map.entry("updateNotificationsFromMessages", Map.entry("void", + Map.entry(List.of("String","int","List","String"), + 1))), + Map.entry("hasRead", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("Account", Map.entry("void", + Map.entry(List.of("Map"), + 1)))))); + exprectedStructure.put("Accounts", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getAccount", Map.entry("Account", + Map.entry(List.of("String"), + 1))), + Map.entry("signUp", Map.entry("void", + Map.entry(List.of("String"), + 1)))))); + exprectedStructure.put("Group", Map.entry(Map.ofEntries(Map.entry("members", "List"), + Map.entry("messages", "List"), + Map.entry("account", "Account"), + Map.entry("accounts", "Accounts")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getMessages", Map.entry("List", + Map.entry(List.of(), + 1))), + Map.entry("getMembers", Map.entry("List", + Map.entry(List.of(), + 1))), + Map.entry("getMember", Map.entry("String", + Map.entry(List.of("int"), + 1))), + Map.entry("postMessage", Map.entry("void", + Map.entry(List.of("String","String"), + 6))), + Map.entry("addGroupMember", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("Group", Map.entry("void", + Map.entry(List.of("List","Accounts","List"), + 3)))))); + exprectedStructure.put("Groups", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getGroup", Map.entry("Group", + Map.entry(List.of("String"), + 1))), + Map.entry("createGroup", Map.entry("void", + Map.entry(List.of("String"), + 1)))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + + // check PUSH-first + generatedCode = generateCode("models/GroupChat.model", PushPullValue.PUSH); + exprectedStructure.clear(); + exprectedStructure.put("Main", Map.entry(Map.ofEntries(Map.entry("accounts", "Accounts"), + Map.entry("groups", "Groups")), + Map.ofEntries(Map.entry("Main", Map.entry("void", + Map.entry(List.of(), + 2))), + Map.entry("getMessages", Map.entry("List", + Map.entry(List.of("String"), + 1))), + Map.entry("postMessage", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("getGroup", Map.entry("Map", + Map.entry(List.of("String"), + 1))), + Map.entry("getAccounts", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("signUp", Map.entry("void", + Map.entry(List.of("String"), + 1))), + Map.entry("getAccount", Map.entry("Map", + Map.entry(List.of("String"), + 1))), + Map.entry("getNotifications", Map.entry("Map", + Map.entry(List.of("String"), + 1))), + Map.entry("hasRead", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("getGroups", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("createGroup", Map.entry("void", + Map.entry(List.of("String"), + 1))), + Map.entry("getMembers", Map.entry("List", + Map.entry(List.of("String"), + 1))), + Map.entry("addGroupMember", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("getMember", Map.entry("String", + Map.entry(List.of("String","int"), + 1)))))); + exprectedStructure.put("Group", Map.entry(Map.ofEntries(Map.entry("members", "List"), + Map.entry("messages", "List"), + Map.entry("account", "Account"), + Map.entry("accounts", "Accounts")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getMessages", Map.entry("List", + Map.entry(List.of(), + 1))), + Map.entry("getMembers", Map.entry("List", + Map.entry(List.of(), + 1))), + Map.entry("getMember", Map.entry("String", + Map.entry(List.of("int"), + 1))), + Map.entry("postMessage", Map.entry("void", + Map.entry(List.of("String","String"), + 6))), + Map.entry("addGroupMember", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("Group", Map.entry("void", + Map.entry(List.of("List","Accounts","List"), + 3)))))); + exprectedStructure.put("Accounts", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getAccount", Map.entry("Account", + Map.entry(List.of("String"), + 1))), + Map.entry("signUp", Map.entry("void", + Map.entry(List.of("String"), + 1)))))); + exprectedStructure.put("Account", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getNotifications", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("updateNotificationsFromMessages", Map.entry("void", + Map.entry(List.of("String","int","List","String"), + 1))), Map.entry("hasRead", Map.entry("void", Map.entry(List.of("String","String"), 1))), @@ -662,36 +716,6 @@ Map.entry("createGroup", Map.entry("void", Map.entry(List.of("String"), 1)))))); - exprectedStructure.put("Group", Map.entry(Map.ofEntries(Map.entry("members", "List"), - Map.entry("messages", "List")), - Map.ofEntries(Map.entry("getValue", Map.entry("Map", - Map.entry(List.of(), - 1))), - Map.entry("getMembers", Map.entry("List", - Map.entry(List.of(), - 1))), - Map.entry("getMessages", Map.entry("List", - Map.entry(List.of(), - 1))), - Map.entry("postMessage", Map.entry("void", - Map.entry(List.of("String","String"), - 1))), - Map.entry("addGroupMember", Map.entry("void", - Map.entry(List.of("String","String"), - 1))), - Map.entry("Group", Map.entry("void", - Map.entry(List.of("List","List"), - 2)))))); - exprectedStructure.put("Accounts", Map.entry(Map.ofEntries(), - Map.ofEntries(Map.entry("getValue", Map.entry("Map", - Map.entry(List.of(), - 1))), - Map.entry("getAccount", Map.entry("Account", - Map.entry(List.of("String"), - 1))), - Map.entry("signUp", Map.entry("void", - Map.entry(List.of("String"), - 1)))))); checkStructure(generatedCode, exprectedStructure); // generateCheckCode(generatedCode); @@ -1028,6 +1052,322 @@ } } + + private void testOnlineBattleGame2() { + try { + // check PULL-first + ArrayList generatedCode = generateCode("models/OnlineBattleGame2.model", PushPullValue.PULL); + Map, // field name to type + Map, // arg types + Integer>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Main", Map.entry(Map.ofEntries(Map.entry("accounts", "Accounts"), + Map.entry("rooms", "Rooms")), + Map.ofEntries(Map.entry("Main", Map.entry("void", + Map.entry(List.of(), + 2))), + Map.entry("getAccount", Map.entry("Map", + Map.entry(List.of("String"), + 1))), + Map.entry("getMember", Map.entry("Map", + Map.entry(List.of("String","int"), + 1))), + Map.entry("getMembers", Map.entry("List", + Map.entry(List.of("String"), + 1))), + Map.entry("addRoomMember", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("getAccounts", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("signUp", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("getRoom", Map.entry("Map", + Map.entry(List.of("String"), + 1))), + Map.entry("getId", Map.entry("String", + Map.entry(List.of("String","int"), + 1))), + Map.entry("getName", Map.entry("String", + Map.entry(List.of("String"), + 1))), + Map.entry("changeName", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("getRooms", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("createRoom", Map.entry("void", + Map.entry(List.of("String"), + 1))), +// Map.entry("getName", Map.entry("String", +// Map.entry(List.of("String","int"), +// 1))), + Map.entry("getBattle", Map.entry("boolean", + Map.entry(List.of("String"), + 1))), + Map.entry("battle", Map.entry("void", + Map.entry(List.of("String","boolean"), + 1))), + Map.entry("getPoint", Map.entry("int", + Map.entry(List.of("String"), + 1)))))); + exprectedStructure.put("Account", Map.entry(Map.ofEntries(Map.entry("name", "String"), + Map.entry("point", "int")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getName", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("getPoint", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("updatePointFromBattle", Map.entry("void", + Map.entry(List.of("String","String","int","boolean","String"), + 1))), + Map.entry("changeName", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("Account", Map.entry("void", + Map.entry(List.of("String","int"), + 2)))))); + exprectedStructure.put("Member", Map.entry(Map.ofEntries(Map.entry("id", "String"), + Map.entry("account", "Account"), + Map.entry("accounts", "Accounts")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getId", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("getName", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("Member", Map.entry("void", + Map.entry(List.of("String","Accounts"), + 2)))))); + exprectedStructure.put("Members", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("List", + Map.entry(List.of(), + 1))), + Map.entry("getMember", Map.entry("Member", + Map.entry(List.of("int"), + 1))), + Map.entry("addRoomMember", Map.entry("void", + Map.entry(List.of("String","String"), + 1)))))); + exprectedStructure.put("Accounts", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getAccount", Map.entry("Account", + Map.entry(List.of("String"), + 1))), + Map.entry("signUp", Map.entry("void", + Map.entry(List.of("String","String"), + 1)))))); + exprectedStructure.put("Room", Map.entry(Map.ofEntries(Map.entry("members", "Members"), + Map.entry("battle", "boolean"), + Map.entry("account", "Account"), + Map.entry("accounts", "Accounts")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getMembers", Map.entry("Members", + Map.entry(List.of(), + 1))), + Map.entry("getBattle", Map.entry("boolean", + Map.entry(List.of(), + 1))), + Map.entry("battle", Map.entry("void", + Map.entry(List.of("String","boolean"), + 6))), + Map.entry("Room", Map.entry("void", + Map.entry(List.of("boolean","Accounts"), + 2)))))); + exprectedStructure.put("Rooms", Map.entry(Map.ofEntries(Map.entry("value", "Map"), + Map.entry("accounts", "Accounts")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getRoom", Map.entry("Room", + Map.entry(List.of("String"), + 1))), + Map.entry("createRoom", Map.entry("void", + Map.entry(List.of("String"), + 1))), + Map.entry("Rooms", Map.entry("void", + Map.entry(List.of("Accounts"), + 1)))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + + // check PUSH-first + generatedCode = generateCode("models/OnlineBattleGame2.model", PushPullValue.PUSH); + exprectedStructure.clear(); + exprectedStructure.put("Main", Map.entry(Map.ofEntries(Map.entry("accounts", "Accounts"), + Map.entry("rooms", "Rooms")), + Map.ofEntries(Map.entry("Main", Map.entry("void", + Map.entry(List.of(), + 2))), + Map.entry("getMembers", Map.entry("List", + Map.entry(List.of("String"), + 1))), + Map.entry("addRoomMember", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("getRoom", Map.entry("Map", + Map.entry(List.of("String"), + 1))), + Map.entry("getBattle", Map.entry("boolean", + Map.entry(List.of("String"), + 1))), + Map.entry("battle", Map.entry("void", + Map.entry(List.of("String","boolean"), + 1))), + Map.entry("getName", Map.entry("String", + Map.entry(List.of("String","int"), + 1))), + Map.entry("getId", Map.entry("String", + Map.entry(List.of("String","int"), + 1))), + Map.entry("getAccount", Map.entry("Map", + Map.entry(List.of("String"), + 1))), + Map.entry("getPoint", Map.entry("int", + Map.entry(List.of("String"), + 1))), + Map.entry("getAccounts", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("signUp", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("getRooms", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("createRoom", Map.entry("void", + Map.entry(List.of("String"), + 1))), + Map.entry("getMember", Map.entry("Map", + Map.entry(List.of("String","int"), + 1))), +// Map.entry("getName", Map.entry("String", +// Map.entry(List.of("String"), +// 1))), + Map.entry("changeName", Map.entry("void", + Map.entry(List.of("String","String"), + 1)))))); + exprectedStructure.put("Members", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("List", + Map.entry(List.of(), + 1))), + Map.entry("getMember", Map.entry("Member", + Map.entry(List.of("int"), + 1))), + Map.entry("addRoomMember", Map.entry("void", + Map.entry(List.of("String","String"), + 1)))))); + exprectedStructure.put("Room", Map.entry(Map.ofEntries(Map.entry("members", "Members"), + Map.entry("battle", "boolean"), + Map.entry("account", "Account"), + Map.entry("accounts", "Accounts")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getMembers", Map.entry("Members", + Map.entry(List.of(), + 1))), + Map.entry("getBattle", Map.entry("boolean", + Map.entry(List.of(), + 1))), + Map.entry("battle", Map.entry("void", + Map.entry(List.of("String","boolean"), + 6))), + Map.entry("Room", Map.entry("void", + Map.entry(List.of("boolean","Accounts"), + 2)))))); + exprectedStructure.put("Account", Map.entry(Map.ofEntries(Map.entry("point", "int"), + Map.entry("name", "String")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getPoint", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("getName", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("updatePointFromBattle", Map.entry("void", + Map.entry(List.of("String","String","int","boolean","String"), + 1))), + Map.entry("changeName", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("Account", Map.entry("void", + Map.entry(List.of("int","String"), + 2)))))); + exprectedStructure.put("Accounts", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getAccount", Map.entry("Account", + Map.entry(List.of("String"), + 1))), + Map.entry("signUp", Map.entry("void", + Map.entry(List.of("String","String"), + 1)))))); + exprectedStructure.put("Rooms", Map.entry(Map.ofEntries(Map.entry("value", "Map"), + Map.entry("accounts", "Accounts")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getRoom", Map.entry("Room", + Map.entry(List.of("String"), + 1))), + Map.entry("createRoom", Map.entry("void", + Map.entry(List.of("String"), + 1))), + Map.entry("Rooms", Map.entry("void", + Map.entry(List.of("Accounts"), + 1)))))); + exprectedStructure.put("Member", Map.entry(Map.ofEntries(Map.entry("account", "Account"), + Map.entry("accounts", "Accounts"), + Map.entry("id", "String")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getName", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("getId", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("updateNameFromId", Map.entry("void", + Map.entry(List.of("String","int","String","int","String"), + 2))), + Map.entry("Member", Map.entry("void", + Map.entry(List.of("Accounts","String"), + 2)))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + private void testPOS() { try { // check PULL-first @@ -1235,6 +1575,83 @@ } } + private void testVotingSystem() { + try { + ArrayList generatedCode = generateCode("models/VotingSystem.model", null); + Map, // field name to type + Map, // arg types + Integer>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Main", Map.entry(Map.ofEntries(Map.entry("accounts", "Accounts"), + Map.entry("counts", "Counts")), + Map.ofEntries(Map.entry("Main", Map.entry("void", + Map.entry(List.of(), + 2))), + Map.entry("getAccounts", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("signUp", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("getVote", Map.entry("String", + Map.entry(List.of("String"), + 1))), + Map.entry("cast", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("getCounts", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getAccount", Map.entry("Map", + Map.entry(List.of("String"), + 1)))))); + exprectedStructure.put("Accounts", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getAccount", Map.entry("Account", + Map.entry(List.of("String"), + 1))), + Map.entry("signUp", Map.entry("void", + Map.entry(List.of("String","String"), + 1)))))); + exprectedStructure.put("Counts", Map.entry(Map.ofEntries(Map.entry("value", "Map"), + Map.entry("account", "Account"), + Map.entry("accounts", "Accounts")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 6))), + Map.entry("Counts", Map.entry("void", + Map.entry(List.of("Accounts"), + 1)))))); + exprectedStructure.put("Account", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getVote", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("cast", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("Account", Map.entry("void", + Map.entry(List.of("String"), + 1)))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + private void testWeatherObservationSystem() { try { // check PULL-first