diff --git a/AlgebraicDataflowArchitectureModel/models/CustomerManagement.model b/AlgebraicDataflowArchitectureModel/models/CustomerManagement.model new file mode 100644 index 0000000..5277f32 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/CustomerManagement.model @@ -0,0 +1,21 @@ +channel AddCustomer { + out customers(csDB:Map, addCustomer(uid:Str, org:Str)) = insert(csDB, uid, {"organization": org}) +} + +channel AddCampany { + out companies(cmDB:Map, addCampany(cid:Str, address:Str)) = insert(cmDB, cid, {"address": address}) +} + +channel SetCustomerOrganization(uid:Str) { + out customers.{uid}.organization(prevCid:Str, setOrganization(cid)) = cid +} + +channel SetCompanyAddress(cid:Str) { + out companies.{cid}.address(prevAdd:Str, setAddress(add)) = add +} + +channel UpdateCustomerAddress(uid:Str) { + in customers.{uid}.organization(prevCid, updateCustomerAddress(cid, add)) = cid + in companies.{cid}.address(prevAdd, updateCustomerAddress(cid, add)) = add + out customers.{uid}.address(prevAdd, updateCustomerAddress(cid, add)) = add +} diff --git a/AlgebraicDataflowArchitectureModel/models/CustomerOffice.model b/AlgebraicDataflowArchitectureModel/models/CustomerOffice.model deleted file mode 100644 index 42cdf13..0000000 --- a/AlgebraicDataflowArchitectureModel/models/CustomerOffice.model +++ /dev/null @@ -1,21 +0,0 @@ -channel CIO_AddCustomer { - out customers(db:Map, addCustomer(uid:Str, off:Str)) = insert(db, uid, {"off": off}) -} - -channel CIO_AddCampany { - out companies(db:Map, addCampany(cid:Str, add:Str)) = insert(db, cid, {"add": add}) -} - -channel CIO_SetCustomerOff(uid:Str) { - out customers.{uid}.off(cid:Str, setOff(cid2)) = cid2 -} - -channel CIO_SetCompanyAdd(cid:Str) { - out companies.{cid}.add(a1:Str, setAdd(a2)) = a2 -} - -channel C(uid:Str) { - in customers.{uid}.off(cid, sync(cid2, add2)) = cid2 - in companies.{cid2}.add(a1, sync(cid2, add2)) = add2 - out customers.{uid}.add(a3:Str, sync(cid2, add2)) = add2 -} diff --git a/AlgebraicDataflowArchitectureModel/models/GroupChat.model b/AlgebraicDataflowArchitectureModel/models/GroupChat.model new file mode 100644 index 0000000..1203e1a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/GroupChat.model @@ -0,0 +1,27 @@ +channel Signup { + out accounts(acDB:Map, signUp(aid:Str)) = insert(acDB, aid, {"notifications": nil}) +} + +channel HasRead(aid:Str) { + out accounts.{aid}.notifications(ntMap:Map, hasRead(gid:Str)) = delete(ntMap, gid) +} + +channel CreateGroup { + out groups(grDB:Map, createGroup(gid:Str)) = insert(grDB, gid, {"members": nil, "messages": nil}) +} + +channel AddGroupMember(gid:Str) { + out groups.{gid}.members(memList:List, addGroupMember(aid:Str)) = append(memList, aid) +} + +channel PostMessage(gid:Str) { + out groups.{gid}.messages(mesList:List, postMessage(message:Str)) = append(mesList, message) +} + +channel Notify(gid:Str) { + in groups.{gid}.messages(prevMesList, notify(m)) = mesList + for EachMember(mno:Int) { + ref groups.{gid}.members.{mno}(m.{mno}:Str, notify(m)) + out accounts.{m.{mno}:Str}.notifications(prevNtMap:Map, notify(m)) = insert(prevNtMap, gid, true) + } +} diff --git a/AlgebraicDataflowArchitectureModel/models/OnlineBattleGame.model b/AlgebraicDataflowArchitectureModel/models/OnlineBattleGame.model index 846ffbf..d91b144 100644 --- a/AlgebraicDataflowArchitectureModel/models/OnlineBattleGame.model +++ b/AlgebraicDataflowArchitectureModel/models/OnlineBattleGame.model @@ -1,31 +1,31 @@ -channel CIO_Signup{ - out accounts(adb:Map, signUp(aid:Str, name:Str)) = insert(adb, aid, {"name": name}) +channel Signup { + out accounts(acDB:Map, signUp(aid:Str, name:Str)) = insert(acDB, aid, {"name": name}) } -channel CIO_ChangeName(aid:Str) { - out accounts.{aid}.name(prev_name:Str, changeName(name)) = name +channel ChangeName(aid:Str) { + out accounts.{aid}.name(prevName:Str, changeName(name)) = name } -channel CIO_CreateRoom { - out rooms(rdb:Map, createRoom(rid:Str, blue_id:Str, red_id:Str)) = insert(rdb, rid, {"blue_id": blue_id, "red_id": red_id}) +channel CreateRoom { + out rooms(rmDB:Map, createRoom(rid:Str, blueId:Str, redId:Str)) = insert(rmDB, rid, {"blue_id": blueId, "red_id": redId}) } -channel CIO_ChangeRed_id(rid:Str) { - out rooms.{rid}.red_id(prev_red_id:Str, changeRed_id(red_id)) = red_id +channel ChangeRedId(rid:Str) { + out rooms.{rid}.red_id(prevRedId:Str, changeRedId(redId)) = redId } -channel CIO_ChangeBlue_id(rid:Str) { - out rooms.{rid}.blue_id(prev_blue_id:Str, changeBlue_id(blue_id)) = blue_id +channel ChangeBlueId(rid:Str) { + out rooms.{rid}.blue_id(prevBlueId:Str, changeBlueId(blueId)) = blueId } -channel C_red(rid:Str) { - in rooms.{rid}.red_id(prev_aid:Str, sync(aid, name)) = aid - in accounts.{aid}.name(prev_name:Str, sync(aid, name)) = name - out rooms.{rid}.red_name(prev_name:Str, sync(aid, name)) = name +channel UpdateRedName(rid:Str) { + in rooms.{rid}.red_id(prevAid:Str, updateRedName(aid, name)) = aid + in accounts.{aid}.name(prevName:Str, updateRedName(aid, name)) = name + out rooms.{rid}.red_name(prevName:Str, updateRedName(aid, name)) = name } -channel C_blue(rid:Str) { - in rooms.{rid}.blue_id(prev_aid:Str, sync(aid, name)) = aid - in accounts.{aid}.name(prev_name:Str, sync(aid, name)) = name - out rooms.{rid}.blue_name(prev_name:Str, sync(aid, name)) = name +channel UpdateBlueName(rid:Str) { + in rooms.{rid}.blue_id(prevAid:Str, updateBlueName(aid, name)) = aid + in accounts.{aid}.name(prevName:Str, updateBlueName(aid, name)) = name + out rooms.{rid}.blue_name(prevName:Str, updateBlueName(aid, name)) = name } \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/models/OnlineBattleGame2.model b/AlgebraicDataflowArchitectureModel/models/OnlineBattleGame2.model new file mode 100644 index 0000000..ccd15a7 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/OnlineBattleGame2.model @@ -0,0 +1,33 @@ +channel Signup { + out accounts(acDB:Map, signUp(aid:Str, name:Str)) = insert(acDB, aid, {"name": name, "point": 0}) +} + +channel ChangeName(aid:Str) { + out accounts.{aid}.name(prevName:Str, changeName(name)) = name +} + +channel CreateRoom { + out rooms(rmDB:Map, createRoom(rid:Str)) = insert(rmDB, rid, {"members": nil, "battle": false}) +} + +channel AddRoomMember(rid:Str) { + out rooms.{rid}.members(mList:List, addRoomMember(id:Str)) = append(mList, {"id": id}) +} + +channel Battle(rid:Str) { + out rooms.{rid}.battle(prevHasWon, battle(hasWon:Bool)) = hasWon +} + +channel UpdateName(rid:Str, mno:Int) { + in rooms.{rid}.members.{mno}.id(prevMid:Str, updateName(mid, name)) = mid + in accounts.{mid}.name(prevName:Str, updateName(mid, name)) = name + out rooms.{rid}.members.{mno}.name(prevName:Str, updateName(mid, name)) = name +} + +channel UpdatePoint(rid:Str) { + in rooms.{rid}.battle(prevState, updatePoint(hasWon, mid)) = hasWon + for EachMember(mno:Int) { + ref rooms.{rid}.members.{mno}.id(mid:Str, updatePoint(hasWon, mid)) + out accounts.{mid}.point(prevPoint:Int, updatePoint(hasWon, mid)) = if(hasWon, prevPoint + 1, prevPoint) + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/models/Twitter.model b/AlgebraicDataflowArchitectureModel/models/Twitter.model index 491d904..e5c46a8 100644 --- a/AlgebraicDataflowArchitectureModel/models/Twitter.model +++ b/AlgebraicDataflowArchitectureModel/models/Twitter.model @@ -1,16 +1,20 @@ -channel CIO1(myId:Str) { - out accounts(ac:List, signup(name:Str)) = cons(tuple(name, nil, nil), t1) +channel SignUp { + out accounts(acDB:Map, signup(id:Str, name:Str)) = insert(acDB, id, {"name": name, "tweets": nil, "followees": nil, "timeline": nil}) } -channel CIO2(id:Str) { - out accounts.{id}.tweets(t1:List, tweet(text:Str, time:Long)) = cons(tuple(time, text), t1) +channel Tweet(id:Str) { + out accounts.{id}.tweets(twList:List, tweet(text:Str, time:Long)) = append(twList, {"time": time, "text": text}) } -channel C(myId:Str; m:Json) { +channel AddFollowee(id:Str) { + out accounts.{id}.followees(fwList:List, addFollowee(flwId:Str)) = append(fwList, flwId) +} + +channel UpdateTimeline(myId:Str) { in accounts.{myId}.tweets(t1:List, m) = m.myTweets - sub C2(no:Int; flw:Json := m.followees.{no}) { - in accounts.{myId}.followees.{no}(id:Str, flw) = flw.id - in accounts.{flw.id}.tweets(t2:List, flw) = flw.tweets + for EachFollowee(no:Int) { + in accounts.{myId}.followees.{no}(id:Str, m) = m.flw.{no}.id + in accounts.{m.flw.{no}.id}.tweets(t2:List, m) = m.flw.{no}.tweets } - out accounts.{myId}.timeline(l:List, m) = merge(m.myTweets, m.followees) + out accounts.{myId}.timeline(l:List, m) = merge(m.myTweets, m.flw) } \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/models/VotingSystem.model b/AlgebraicDataflowArchitectureModel/models/VotingSystem.model new file mode 100644 index 0000000..ff425db --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/models/VotingSystem.model @@ -0,0 +1,14 @@ +channel Signup { + out accounts(acDB:Map, signUp(aid:Str, name:Str)) = insert(acDB, aid, {"name": name, "vote": null}) +} + +channel Cast(aid:Str) { + out accounts.{aid}.vote(preV, cast(v:Str)) = v +} + +channel Collect { + for EachAccount(aid:Str) { + in accounts.{aid}.vote(preV:Str, collect(m)) = m.{aid} + } + out counts(preCnts:Json, collect(m)) = m +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Accounts/Account.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Accounts/Account.java new file mode 100644 index 0000000..423e265 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Accounts/Account.java @@ -0,0 +1,19 @@ +import java.util.*; + +public class Account { + private String name; + public Map getValue() { + Map temp_nil1 = new HashMap<>(); + temp_nil1.put("name",this.getName()); + return temp_nil1; + } + public String getName() { + return this.name; + } + public void changeName(int uid, String name) { + this.name = name; + } + public Account(String name) { + this.name = name; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Accounts/Accounts.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Accounts/Accounts.java new file mode 100644 index 0000000..d851471 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Accounts/Accounts.java @@ -0,0 +1,42 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/accounts") +@Component +public class Accounts { + private List value = new ArrayList<>(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public List getValue() { + return new ArrayList<>(value); + } + public Account getAccount(int uid) { + return this.value.get(uid); + } + @Path("/{uid}") + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getAccountValue(@PathParam("uid") int uid) { + return getAccount(uid).getValue(); + } + @Path("/{uid}/name") + @Produces(MediaType.APPLICATION_JSON) + @GET + public String getNameValue(@PathParam("uid") int uid) { + return getAccount(uid).getName(); + } + @POST + public void signup(@FormParam("name") String name) { + this.value.add(new Account(name)); + } + @Path("/{uid}/name") + @PUT + public void changeName(@PathParam("uid") int uid, @FormParam("name") String name) { + getAccount(uid).changeName(uid, name); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PULL-first/Hour.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PULL-first/Hour.java new file mode 100644 index 0000000..a7acf75 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PULL-first/Hour.java @@ -0,0 +1,28 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/hour") +@Component +public class Hour { + private int value = 0; + @Produces(MediaType.APPLICATION_JSON) + @GET + public int getValue() { + return value; + } + @POST + public void updateFromMin(@FormParam("min") int min) { + int temp_if0; + if ((min==0)) { + temp_if0 = ((this.value+1)%24); + } else { + temp_if0 = this.value; + } + this.value = temp_if0; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PULL-first/Hour_ang.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PULL-first/Hour_ang.java new file mode 100644 index 0000000..9c14018 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PULL-first/Hour_ang.java @@ -0,0 +1,19 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/hour_ang") +@Component +public class Hour_ang { + private Client client = ClientBuilder.newClient(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public double getValue() { + int hour = client.target("http://localhost:8080").path("/hour").request().get(int.class); + return ((hour/6)*Math.PI); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PULL-first/Min.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PULL-first/Min.java new file mode 100644 index 0000000..db4cea5 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PULL-first/Min.java @@ -0,0 +1,27 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/min") +@Component +public class Min { + private int value = 0; + private Client client = ClientBuilder.newClient(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public int getValue() { + return value; + } + @POST + public void tick() throws JsonProcessingException { + Form form = new Form(); + form.param("min", Integer.toString(this.value)); + Entity
entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED); + String result = client.target("http://localhost:8080").path("/hour").request().post(entity, String.class); + this.value = ((this.value+1)%60); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PULL-first/Min_ang.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PULL-first/Min_ang.java new file mode 100644 index 0000000..96ae12b --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PULL-first/Min_ang.java @@ -0,0 +1,19 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/min_ang") +@Component +public class Min_ang { + private Client client = ClientBuilder.newClient(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public double getValue() { + int min = client.target("http://localhost:8080").path("/min").request().get(int.class); + return ((min/30)*Math.PI); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PUSH-first/Hour.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PUSH-first/Hour.java new file mode 100644 index 0000000..12f10e1 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PUSH-first/Hour.java @@ -0,0 +1,33 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/hour") +@Component +public class Hour { + private double value = 0.0; + private Client client = ClientBuilder.newClient(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public double getValue() { + return value; + } + @POST + public void updateFromMin(@FormParam("min") double min) throws JsonProcessingException { + double temp_if1; + if ((min==0)) { + temp_if1 = ((this.value+1)%24); + } else { + temp_if1 = this.value; + } + this.value = temp_if1; + Form form = new Form(); + form.param("hour", Double.toString(this.value)); + Entity entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED); + String result = client.target("http://localhost:8080").path("/hour_ang").request().put(entity, String.class); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PUSH-first/Hour_ang.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PUSH-first/Hour_ang.java new file mode 100644 index 0000000..5412a9a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PUSH-first/Hour_ang.java @@ -0,0 +1,22 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/hour_ang") +@Component +public class Hour_ang { + private double value = 0.0; + @Produces(MediaType.APPLICATION_JSON) + @GET + public double getValue() { + return value; + } + @PUT + public void updateFromHour(@FormParam("hour") double hour) { + this.value = ((hour/6)*Math.PI); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PUSH-first/Min.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PUSH-first/Min.java new file mode 100644 index 0000000..f068cb6 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PUSH-first/Min.java @@ -0,0 +1,31 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/min") +@Component +public class Min { + private double value = 0.0; + private Client client = ClientBuilder.newClient(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public double getValue() { + return value; + } + @POST + public void tick() throws JsonProcessingException { + Form form = new Form(); + form.param("min", Double.toString(this.value)); + Entity entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED); + String result = client.target("http://localhost:8080").path("/hour").request().post(entity, String.class); + form = new Form(); + form.param("min", Double.toString(this.value)); + entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED); + result = client.target("http://localhost:8080").path("/min_ang").request().put(entity, String.class); + this.value = ((this.value+1)%60); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PUSH-first/Min_ang.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PUSH-first/Min_ang.java new file mode 100644 index 0000000..b467b97 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/Clock/PUSH-first/Min_ang.java @@ -0,0 +1,22 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/min_ang") +@Component +public class Min_ang { + private double value = 0.0; + @Produces(MediaType.APPLICATION_JSON) + @GET + public double getValue() { + return value; + } + @PUT + public void updateFromMin(@FormParam("min") double min) { + this.value = ((min/30)*Math.PI); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/CustomerManagement/Companies.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/CustomerManagement/Companies.java new file mode 100644 index 0000000..a67aea8 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/CustomerManagement/Companies.java @@ -0,0 +1,42 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/companies") +@Component +public class Companies { + private Map value = new HashMap<>(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getValue() { + return new HashMap<>(value); + } + public Company getCompany(String cid) { + return this.value.get(cid); + } + @Path("/{cid}/address") + @Produces(MediaType.APPLICATION_JSON) + @GET + public String getAddressValue(@PathParam("cid") String cid) { + return getCompany(cid).getAddress(); + } + @Path("/{cid}") + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getCompanyValue(@PathParam("cid") String cid) { + return getCompany(cid).getValue(); + } + @Path("/{cid}/address") + @PUT + public void setAddress(@PathParam("cid") String cid, @FormParam("add") String add) { + getCompany(cid).setAddress(cid, add); + } + @POST + public void addCampany(@FormParam("address") String address, @FormParam("cid") String cid) { + this.value.put(cid,new Company(address)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/CustomerManagement/Company.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/CustomerManagement/Company.java new file mode 100644 index 0000000..24e1769 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/CustomerManagement/Company.java @@ -0,0 +1,19 @@ +import java.util.*; + +public class Company { + private String address; + public Map getValue() { + Map temp_nil2 = new HashMap<>(); + temp_nil2.put("address",this.getAddress()); + return temp_nil2; + } + public String getAddress() { + return this.address; + } + public void setAddress(String cid, String add) { + this.address = add; + } + public Company(String address) { + this.address = address; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/CustomerManagement/Customer.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/CustomerManagement/Customer.java new file mode 100644 index 0000000..96f2373 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/CustomerManagement/Customer.java @@ -0,0 +1,26 @@ +import java.util.*; +import javax.ws.rs.client.*; + +public class Customer { + private Client client = ClientBuilder.newClient(); + private String organization; + public Map getValue() { + Map temp_nil3 = new HashMap<>(); + temp_nil3.put("address",this.getAddress()); + temp_nil3.put("organization",this.getOrganization()); + return temp_nil3; + } + public String getAddress() { + String address = client.target("http://localhost:8080").path("/companies/" + organization + "/address").request().get(String.class); + return address; + } + public String getOrganization() { + return this.organization; + } + public void setOrganization(String uid, String cid) { + this.organization = cid; + } + public Customer(String organization) { + this.organization = organization; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/CustomerManagement/Customers.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/CustomerManagement/Customers.java new file mode 100644 index 0000000..22358f4 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/CustomerManagement/Customers.java @@ -0,0 +1,48 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/customers") +@Component +public class Customers { + private Map value = new HashMap<>(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getValue() { + return new HashMap<>(value); + } + public Customer getCustomer(String uid) { + return this.value.get(uid); + } + @Path("/{uid}") + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getCustomerValue(@PathParam("uid") String uid) { + return getCustomer(uid).getValue(); + } + @Path("/{uid}/address") + @Produces(MediaType.APPLICATION_JSON) + @GET + public String getAddressValue(@PathParam("uid") String uid) { + return getCustomer(uid).getAddress(); + } + @Path("/{uid}/organization") + @Produces(MediaType.APPLICATION_JSON) + @GET + public String getOrganizationValue(@PathParam("uid") String uid) { + return getCustomer(uid).getOrganization(); + } + @POST + public void addCustomer(@FormParam("org") String org, @FormParam("uid") String uid) { + this.value.put(uid,new Customer(org)); + } + @Path("/{uid}/organization") + @PUT + public void setOrganization(@PathParam("uid") String uid, @FormParam("cid") String cid) { + getCustomer(uid).setOrganization(uid, cid); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Account.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Account.java new file mode 100644 index 0000000..5f97d07 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Account.java @@ -0,0 +1,22 @@ +import java.util.*; + +public class Account { + private Map notifications; + public Map getValue() { + Map temp_nil3 = new HashMap<>(); + temp_nil3.put("notifications",this.getNotifications()); + return temp_nil3; + } + public Map getNotifications() { + return notifications; + } + public void updateNotificationsFromMessages(String self, String gid, int mno, List messages, String member) { + this.notifications.put(gid,true); + } + public void hasRead(String aid, String gid) { + this.notifications.remove(gid); + } + public Account(Map notifications) { + this.notifications = notifications; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Accounts.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Accounts.java new file mode 100644 index 0000000..c18d220 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Accounts.java @@ -0,0 +1,47 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/accounts") +@Component +public class Accounts { + private Map value = new HashMap<>(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getValue() { + return new HashMap<>(value); + } + public Account getAccount(String v1) { + return this.value.get(v1); + } + @Path("accounts/{v1}/notifications") + @POST + public void updateNotificationsFromMessages(@PathParam("v1") String v1, @FormParam("gid") String gid, @FormParam("mno") int mno, @FormParam("messages") List messages, @FormParam("member") String member) { + getAccount(v1).updateNotificationsFromMessages(v1, gid, mno, messages, member); + } + @Path("/{v1}") + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getAccountValue(@PathParam("v1") String v1) { + return getAccount(v1).getValue(); + } + @Path("/{aid}/notifications") + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getNotificationsValue(@PathParam("aid") String aid) { + return getAccount(aid).getNotifications(); + } + @POST + public void signUp(@FormParam("aid") String aid) { + this.value.put(aid,new Account(new HashMap<>())); + } + @Path("/{aid}/notifications") + @DELETE + public void hasRead(@PathParam("aid") String aid, @FormParam("gid") String gid) { + getAccount(aid).hasRead(aid, gid); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Group.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Group.java new file mode 100644 index 0000000..bb06954 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Group.java @@ -0,0 +1,45 @@ +import java.util.*; +import javax.ws.rs.client.*; + +public class Group { + private List members; + private List messages; + private Client client = ClientBuilder.newClient(); + public Map getValue() { + Map temp_nil2 = new HashMap<>(); + temp_nil2.put("members",this.getMembers()); + temp_nil2.put("messages",this.getMessages()); + return temp_nil2; + } + public List getMembers() { + return this.members; + } + public String getMember(int mno) { + return this.members.get(mno); + } + public List getMessages() { + return this.messages; + } + public void postMessage(String gid, String message) throws JsonProcessingException { + for (int mno = 0; mno < members.size(); mno++) { + String member = getMember(mno); + Form form = new Form(); + form.param("gid", gid.toString()); + form.param("mno", Integer.toString(mno)); + for (String i: messages) { + form.param("messages", i.toString()); + } + form.param("member", member.toString()); + Entity entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED); + String result = client.target("http://localhost:8080").path("/accounts/"+member+"/notifications").request().post(entity, String.class); + } + this.messages.add(message); + } + public void addGroupMember(String gid, String aid) { + this.members.add(aid); + } + public Group(List members, List messages) { + this.members = members; + this.messages = messages; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Groups.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Groups.java new file mode 100644 index 0000000..19b1f42 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/GroupChat/Groups.java @@ -0,0 +1,59 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/groups") +@Component +public class Groups { + private Map value = new HashMap<>(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getValue() { + return new HashMap<>(value); + } + public Group getGroup(String gid) { + return this.value.get(gid); + } + @Path("/{gid}") + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getGroupValue(@PathParam("gid") String gid) { + return getGroup(gid).getValue(); + } + @Path("/{gid}/members") + @Produces(MediaType.APPLICATION_JSON) + @GET + public List getMembersValue(@PathParam("gid") String gid) { + return getGroup(gid).getMembers(); + } + @Path("/{gid}/messages") + @Produces(MediaType.APPLICATION_JSON) + @GET + public List getMessagesValue(@PathParam("gid") String gid) { + return getGroup(gid).getMessages(); + } + @Path("/{gid}/members/{mno}") + @Produces(MediaType.APPLICATION_JSON) + @GET + public String getMemberValue(@PathParam("gid") String gid, @PathParam("mno") int mno) { + return getGroup(gid).getMember(mno); + } + @Path("/{gid}/members") + @POST + public void addGroupMember(@PathParam("gid") String gid, @FormParam("aid") String aid) { + getGroup(gid).addGroupMember(gid, aid); + } + @Path("/{gid}/messages") + @POST + public void postMessage(@PathParam("gid") String gid, @FormParam("message") String message) throws JsonProcessingException { + getGroup(gid).postMessage(gid, message); + } + @POST + public void createGroup(@FormParam("gid") String gid) { + this.value.put(gid,new Group(new ArrayList<>(), new ArrayList<>())); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/InventoryManagement/Inventory.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/InventoryManagement/Inventory.java new file mode 100644 index 0000000..de0f8fc --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/InventoryManagement/Inventory.java @@ -0,0 +1,42 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/inventory") +@Component +public class Inventory { + private Map value = new HashMap<>(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getValue() { + return new HashMap<>(value); + } + public InventoryElement getInventoryElement(String itemId) { + return this.value.get(itemId); + } + @Path("/{itemId}/count") + @Produces(MediaType.APPLICATION_JSON) + @GET + public int getCountValue(@PathParam("itemId") String itemId) { + return getInventoryElement(itemId).getCount(); + } + @Path("/{itemId}") + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getInventoryElementValue(@PathParam("itemId") String itemId) { + return getInventoryElement(itemId).getValue(); + } + @Path("/{itemId}/count") + @POST + public void receiveOrShip(@PathParam("itemId") String itemId, @FormParam("quantity") int quantity) { + getInventoryElement(itemId).receiveOrShip(itemId, quantity); + } + @POST + public void registerItem(@FormParam("itemName") String itemName, @FormParam("quantity") int quantity, @FormParam("itemId") String itemId) { + this.value.put(itemId,new InventoryElement(quantity)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/InventoryManagement/InventoryElement.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/InventoryManagement/InventoryElement.java new file mode 100644 index 0000000..8928640 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/InventoryManagement/InventoryElement.java @@ -0,0 +1,19 @@ +import java.util.*; + +public class InventoryElement { + private int count; + public Map getValue() { + Map temp_nil16 = new HashMap<>(); + temp_nil16.put("count",this.getCount()); + return temp_nil16; + } + public int getCount() { + return this.count; + } + public void receiveOrShip(String itemId, int quantity) { + this.count = (this.value+quantity); + } + public InventoryElement(int count) { + this.count = count; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame/Account.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame/Account.java new file mode 100644 index 0000000..1f2a118 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame/Account.java @@ -0,0 +1,19 @@ +import java.util.*; + +public class Account { + private String name; + public Map getValue() { + Map temp_nil19 = new HashMap<>(); + temp_nil19.put("name",this.getName()); + return temp_nil19; + } + public String getName() { + return this.name; + } + public void changeName(String aid, String name) { + this.name = name; + } + public Account(String name) { + this.name = name; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame/Accounts.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame/Accounts.java new file mode 100644 index 0000000..e4c2155 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame/Accounts.java @@ -0,0 +1,42 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/accounts") +@Component +public class Accounts { + private Map value = new HashMap<>(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getValue() { + return new HashMap<>(value); + } + public Account getAccount(String aid) { + return this.value.get(aid); + } + @Path("/{aid}/name") + @Produces(MediaType.APPLICATION_JSON) + @GET + public String getNameValue(@PathParam("aid") String aid) { + return getAccount(aid).getName(); + } + @Path("/{aid}") + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getAccountValue(@PathParam("aid") String aid) { + return getAccount(aid).getValue(); + } + @POST + public void signUp(@FormParam("name") String name, @FormParam("aid") String aid) { + this.value.put(aid,new Account(name)); + } + @Path("/{aid}/name") + @PUT + public void changeName(@PathParam("aid") String aid, @FormParam("name") String name) { + getAccount(aid).changeName(aid, name); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame/Room.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame/Room.java new file mode 100644 index 0000000..c5ce166 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame/Room.java @@ -0,0 +1,40 @@ +import java.util.*; +import javax.ws.rs.client.*; + +public class Room { + private Client client = ClientBuilder.newClient(); + private String blue_id; + private String red_id; + public Map getValue() { + Map temp_nil18 = new HashMap<>(); + temp_nil18.put("blue_id",this.getBlue_id()); + temp_nil18.put("red_name",this.getRed_name()); + temp_nil18.put("blue_name",this.getBlue_name()); + temp_nil18.put("red_id",this.getRed_id()); + return temp_nil18; + } + public String getRed_name() { + String name = client.target("http://localhost:8080").path("/accounts/" + red_id + "/name").request().get(String.class); + return name; + } + public String getBlue_id() { + return this.blue_id; + } + public String getBlue_name() { + String name = client.target("http://localhost:8080").path("/accounts/" + blue_id + "/name").request().get(String.class); + return name; + } + public String getRed_id() { + return this.red_id; + } + public void changeRedId(String rid, String redId) { + this.red_id = redId; + } + public void changeBlueId(String rid, String blueId) { + this.blue_id = blueId; + } + public Room(String blue_id, String red_id) { + this.blue_id = blue_id; + this.red_id = red_id; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame/Rooms.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame/Rooms.java new file mode 100644 index 0000000..0124b30 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame/Rooms.java @@ -0,0 +1,65 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/rooms") +@Component +public class Rooms { + private Map value = new HashMap<>(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getValue() { + return new HashMap<>(value); + } + public Room getRoom(String rid) { + return this.value.get(rid); + } + @Path("/{rid}/blue_id") + @Produces(MediaType.APPLICATION_JSON) + @GET + public String getBlue_idValue(@PathParam("rid") String rid) { + return getRoom(rid).getBlue_id(); + } + @Path("/{rid}/red_name") + @Produces(MediaType.APPLICATION_JSON) + @GET + public String getRed_nameValue(@PathParam("rid") String rid) { + return getRoom(rid).getRed_name(); + } + @Path("/{rid}/blue_name") + @Produces(MediaType.APPLICATION_JSON) + @GET + public String getBlue_nameValue(@PathParam("rid") String rid) { + return getRoom(rid).getBlue_name(); + } + @Path("/{rid}") + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getRoomValue(@PathParam("rid") String rid) { + return getRoom(rid).getValue(); + } + @Path("/{rid}/red_id") + @Produces(MediaType.APPLICATION_JSON) + @GET + public String getRed_idValue(@PathParam("rid") String rid) { + return getRoom(rid).getRed_id(); + } + @Path("/{rid}/blue_id") + @PUT + public void changeBlueId(@PathParam("rid") String rid, @FormParam("blueId") String blueId) { + getRoom(rid).changeBlueId(rid, blueId); + } + @Path("/{rid}/red_id") + @PUT + public void changeRedId(@PathParam("rid") String rid, @FormParam("redId") String redId) { + getRoom(rid).changeRedId(rid, redId); + } + @POST + public void createRoom(@FormParam("blueId") String blueId, @FormParam("redId") String redId, @FormParam("rid") String rid) { + this.value.put(rid,new Room(blueId, redId)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Account.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Account.java new file mode 100644 index 0000000..31bc4bd --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Account.java @@ -0,0 +1,33 @@ +import java.util.*; + +public class Account { + private String name; + private int point; + public Map getValue() { + Map temp_nil5 = new HashMap<>(); + temp_nil5.put("point",this.getPoint()); + temp_nil5.put("name",this.getName()); + return temp_nil5; + } + public String getName() { + return this.name; + } + public int getPoint() { + return point; + } + public void updatePointFromBattle(String self, String rid, int mno, boolean battle, String id) { + int temp_if0; + if (battle) { + temp_if0 = (this.point+1); + } else { + temp_if0 = this.point; + }this.point = temp_if0; + } + public void changeName(String aid, String name) { + this.name = name; + } + public Account(String name, int point) { + this.name = name; + this.point = point; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Accounts.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Accounts.java new file mode 100644 index 0000000..fc4d20e --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Accounts.java @@ -0,0 +1,53 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/accounts") +@Component +public class Accounts { + private Map value = new HashMap<>(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getValue() { + return new HashMap<>(value); + } + public Account getAccount(String mid) { + return this.value.get(mid); + } + @Path("accounts/{mid}/point") + @POST + public void updatePointFromBattle(@PathParam("mid") String mid, @FormParam("rid") String rid, @FormParam("mno") int mno, @FormParam("battle") boolean battle, @FormParam("id") String id) { + getAccount(mid).updatePointFromBattle(mid, rid, mno, battle, id); + } + @Path("/{mid}/point") + @Produces(MediaType.APPLICATION_JSON) + @GET + public int getPointValue(@PathParam("mid") String mid) { + return getAccount(mid).getPoint(); + } + @Path("/{mid}") + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getAccountValue(@PathParam("mid") String mid) { + return getAccount(mid).getValue(); + } + @Path("/{mid}/name") + @Produces(MediaType.APPLICATION_JSON) + @GET + public String getNameValue(@PathParam("mid") String mid) { + return getAccount(mid).getName(); + } + @POST + public void signUp(@FormParam("name") String name, @FormParam("aid") String aid) { + this.value.put(aid,new Account(name, 0)); + } + @Path("/{aid}/name") + @PUT + public void changeName(@PathParam("aid") String aid, @FormParam("name") String name) { + getAccount(aid).changeName(aid, name); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Member.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Member.java new file mode 100644 index 0000000..d12c2cf --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Member.java @@ -0,0 +1,23 @@ +import java.util.*; +import javax.ws.rs.client.*; + +public class Member { + private String id; + private Client client = ClientBuilder.newClient(); + public Map getValue() { + Map temp_nil6 = new HashMap<>(); + temp_nil6.put("id",this.getId()); + temp_nil6.put("name",this.getName()); + return temp_nil6; + } + public String getId() { + return this.id; + } + public String getName() { + String name = client.target("http://localhost:8080").path("/accounts/"+id+"/name").request().get(String.class); + return name; + } + public Member(String id) { + this.id = id; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Members.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Members.java new file mode 100644 index 0000000..0dd75c8 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Members.java @@ -0,0 +1,14 @@ +import java.util.*; + +public class Members { + private List value = new ArrayList<>(); + public List getValue() { + return new ArrayList<>(value); + } + public Member getMember(int mno) { + return this.value.get(mno); + } + public void addRoomMember(String rid, String id) { + this.value.add(new Member(id)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Room.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Room.java new file mode 100644 index 0000000..edbdd93 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Room.java @@ -0,0 +1,36 @@ +import java.util.*; +import javax.ws.rs.client.*; + +public class Room { + private Members members = new Members(); + private boolean battle; + private Client client = ClientBuilder.newClient(); + public Map getValue() { + Map temp_nil4 = new HashMap<>(); + temp_nil4.put("battle",this.getBattle()); + temp_nil4.put("members",this.members.getValue()); + return temp_nil4; + } + public Members getMembers() { + return this.members; + } + public boolean getBattle() { + return this.battle; + } + public void battle(String rid, boolean hasWon) throws JsonProcessingException { + for (int mno = 0; mno < members.getValue().size(); mno++) { + String id = getMembers().getMember(mno).getId(); + Form form = new Form(); + form.param("rid", rid.toString()); + form.param("mno", Integer.toString(mno)); + form.param("battle", Boolean.toString(battle)); + form.param("id", id.toString()); + Entity entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED); + String result = client.target("http://localhost:8080").path("/accounts/"+id+"/point").request().post(entity, String.class); + } + this.battle = hasWon; + } + public Room(boolean battle) { + this.battle = battle; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Rooms.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Rooms.java new file mode 100644 index 0000000..818eebd --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/OnlineBattleGame2/Rooms.java @@ -0,0 +1,71 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/rooms") +@Component +public class Rooms { + private Map value = new HashMap<>(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getValue() { + return new HashMap<>(value); + } + public Room getRoom(String rid) { + return this.value.get(rid); + } + @Path("/{rid}") + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getRoomValue(@PathParam("rid") String rid) { + return getRoom(rid).getValue(); + } + @Path("/{rid}/members/{mno}/id") + @Produces(MediaType.APPLICATION_JSON) + @GET + public String getIdValue(@PathParam("rid") String rid, @PathParam("mno") int mno) { + return getRoom(rid).getMembers().getMember(mno).getId(); + } + @Path("/{rid}/members/{mno}") + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getMemberValue(@PathParam("rid") String rid, @PathParam("mno") int mno) { + return getRoom(rid).getMembers().getMember(mno).getValue(); + } + @Path("/{rid}/battle") + @Produces(MediaType.APPLICATION_JSON) + @GET + public boolean getBattleValue(@PathParam("rid") String rid) { + return getRoom(rid).getBattle(); + } + @Path("/{rid}/members/{mno}/name") + @Produces(MediaType.APPLICATION_JSON) + @GET + public String getNameValue(@PathParam("rid") String rid, @PathParam("mno") int mno) { + return getRoom(rid).getMembers().getMember(mno).getName(); + } + @Path("/{rid}/members") + @Produces(MediaType.APPLICATION_JSON) + @GET + public List getMembersValue(@PathParam("rid") String rid) { + return getRoom(rid).getMembers().getValue(); + } + @POST + public void createRoom(@FormParam("rid") String rid) { + this.value.put(rid,new Room(false)); + } + @Path("/{rid}/battle") + @PUT + public void battle(@PathParam("rid") String rid, @FormParam("hasWon") boolean hasWon) throws JsonProcessingException { + getRoom(rid).battle(rid, hasWon); + } + @Path("/{rid}/members") + @POST + public void addRoomMember(@PathParam("rid") String rid, @FormParam("id") String id) { + getRoom(rid).getMembers().addRoomMember(rid, id); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PULL-first/History.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PULL-first/History.java new file mode 100644 index 0000000..92776d5 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PULL-first/History.java @@ -0,0 +1,22 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/history") +@Component +public class History { + private List value = new ArrayList<>(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public List getValue() { + return value; + } + @POST + public void updateFromPayment(@FormParam("payment") int payment) { + this.value.add(0, payment); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PULL-first/Payment.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PULL-first/Payment.java new file mode 100644 index 0000000..a694ab6 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PULL-first/Payment.java @@ -0,0 +1,27 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/payment") +@Component +public class Payment { + private int value = 0; + private Client client = ClientBuilder.newClient(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public int getValue() { + return value; + } + @PUT + public void purchase(@FormParam("x") int x) throws JsonProcessingException { + Form form = new Form(); + form.param("payment", Integer.toString(this.value)); + Entity entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED); + String result = client.target("http://localhost:8080").path("/history").request().post(entity, String.class); + this.value = x; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PULL-first/Points.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PULL-first/Points.java new file mode 100644 index 0000000..f7ac799 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PULL-first/Points.java @@ -0,0 +1,19 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/points") +@Component +public class Points { + private Client client = ClientBuilder.newClient(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public int getValue() { + int payment = client.target("http://localhost:8080").path("/payment").request().get(int.class); + return (int)Math.floor((payment*0.05)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PULL-first/Total.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PULL-first/Total.java new file mode 100644 index 0000000..87b8845 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PULL-first/Total.java @@ -0,0 +1,23 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/total") +@Component +public class Total { + private Client client = ClientBuilder.newClient(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public int getValue() { + List history = client.target("http://localhost:8080").path("/history").request().get(ArrayList.class); + Integer temp_sum1 = 0; + for (Integer x: history) { + temp_sum1 += x; + } + return temp_sum1; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PUSH-first/History.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PUSH-first/History.java new file mode 100644 index 0000000..e6c5c7f --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PUSH-first/History.java @@ -0,0 +1,29 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/history") +@Component +public class History { + private List value = new ArrayList<>(); + private Client client = ClientBuilder.newClient(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public List getValue() { + return value; + } + @POST + public void updateFromPayment(@FormParam("payment") double payment) throws JsonProcessingException { + this.value.add(0, payment); + Form form = new Form(); + for (Double i: this.value) { + form.param("history", i.toString()); + } + Entity entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED); + String result = client.target("http://localhost:8080").path("/total").request().put(entity, String.class); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PUSH-first/Payment.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PUSH-first/Payment.java new file mode 100644 index 0000000..5d65395 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PUSH-first/Payment.java @@ -0,0 +1,31 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/payment") +@Component +public class Payment { + private double value = 0.0; + private Client client = ClientBuilder.newClient(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public double getValue() { + return value; + } + @PUT + public void purchase(@FormParam("x") double x) throws JsonProcessingException { + Form form = new Form(); + form.param("payment", Double.toString(this.value)); + Entity entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED); + String result = client.target("http://localhost:8080").path("/points").request().put(entity, String.class); + form = new Form(); + form.param("payment", Double.toString(this.value)); + entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED); + result = client.target("http://localhost:8080").path("/history").request().post(entity, String.class); + this.value = x; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PUSH-first/Points.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PUSH-first/Points.java new file mode 100644 index 0000000..7071bf0 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PUSH-first/Points.java @@ -0,0 +1,22 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/points") +@Component +public class Points { + private int value = 0; + @Produces(MediaType.APPLICATION_JSON) + @GET + public int getValue() { + return value; + } + @PUT + public void updateFromPayment(@FormParam("payment") double payment) { + this.value = (int)Math.floor((payment*0.05)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PUSH-first/Total.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PUSH-first/Total.java new file mode 100644 index 0000000..0fdd957 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/POS/PUSH-first/Total.java @@ -0,0 +1,26 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/total") +@Component +public class Total { + private int value = 0; + @Produces(MediaType.APPLICATION_JSON) + @GET + public int getValue() { + return value; + } + @PUT + public void updateFromHistory(@FormParam("history") List history) { + Integer temp_sum1 = 0; + for (Integer x: history) { + temp_sum1 += x; + } + this.value = temp_sum1; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/SimpleTwitter/Account.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/SimpleTwitter/Account.java new file mode 100644 index 0000000..fe3d260 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/SimpleTwitter/Account.java @@ -0,0 +1,19 @@ +import java.util.*; + +public class Account { + private List tweets; + public Map getValue() { + Map temp_nil26 = new HashMap<>(); + temp_nil26.put("tweets",this.getTweets()); + return temp_nil26; + } + public List getTweets() { + return this.tweets; + } + public void tweet(String accountId, String contents) { + this.tweets.add(contents); + } + public Account(List tweets) { + this.tweets = tweets; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/SimpleTwitter/Accounts.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/SimpleTwitter/Accounts.java new file mode 100644 index 0000000..8797c5d --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/SimpleTwitter/Accounts.java @@ -0,0 +1,42 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/accounts") +@Component +public class Accounts { + private Map value = new HashMap<>(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getValue() { + return new HashMap<>(value); + } + public Account getAccount(String accountId) { + return this.value.get(accountId); + } + @Path("/{accountId}/tweets") + @Produces(MediaType.APPLICATION_JSON) + @GET + public List getTweetsValue(@PathParam("accountId") String accountId) { + return getAccount(accountId).getTweets(); + } + @Path("/{accountId}") + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getAccountValue(@PathParam("accountId") String accountId) { + return getAccount(accountId).getValue(); + } + @POST + public void signUp(@FormParam("name") String name, @FormParam("accountId") String accountId) { + this.value.put(accountId,new Account(new ArrayList<>())); + } + @Path("/{accountId}/tweets") + @POST + public void tweet(@PathParam("accountId") String accountId, @FormParam("contents") String contents) { + getAccount(accountId).tweet(accountId, contents); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/VotingSystem/Account.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/VotingSystem/Account.java new file mode 100644 index 0000000..5bdc922 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/VotingSystem/Account.java @@ -0,0 +1,19 @@ +import java.util.*; + +public class Account { + private String vote; + public Map getValue() { + Map temp_nil7 = new HashMap<>(); + temp_nil7.put("vote",this.getVote()); + return temp_nil7; + } + public String getVote() { + return this.vote; + } + public void cast(String aid, String v) { + this.vote = v; + } + public Account(String vote) { + this.vote = vote; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/VotingSystem/Accounts.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/VotingSystem/Accounts.java new file mode 100644 index 0000000..ab6b793 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/VotingSystem/Accounts.java @@ -0,0 +1,42 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/accounts") +@Component +public class Accounts { + private Map value = new HashMap<>(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getValue() { + return new HashMap<>(value); + } + public Account getAccount(String aid) { + return this.value.get(aid); + } + @Path("/{aid}/vote") + @Produces(MediaType.APPLICATION_JSON) + @GET + public String getVoteValue(@PathParam("aid") String aid) { + return getAccount(aid).getVote(); + } + @Path("/{aid}") + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getAccountValue(@PathParam("aid") String aid) { + return getAccount(aid).getValue(); + } + @Path("/{aid}/vote") + @PUT + public void cast(@PathParam("aid") String aid, @FormParam("v") String v) { + getAccount(aid).cast(aid, v); + } + @POST + public void signUp(@FormParam("name") String name, @FormParam("aid") String aid) { + this.value.put(aid,new Account(null)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/VotingSystem/Counts.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/VotingSystem/Counts.java new file mode 100644 index 0000000..06026ed --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/VotingSystem/Counts.java @@ -0,0 +1,27 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/counts") +@Component +public class Counts { + private Map value = new HashMap<>(); + private Client client = ClientBuilder.newClient(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public Map getValue() { + Map v0 = new HashMap<>(); + Map> accounts_json = client.target("http://localhost:8080").path("/accounts").request().get(HashMap.class); + Map> accounts = new HashMap<>(); + accounts = accounts_json; + for (String aid: accounts.keySet()) { + String vote = client.target("http://localhost:8080").path("/accounts/"+aid+"/vote").request().get(String.class); + v0.put(aid,vote); + } + return v0; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/WeatherObservationSystem/PULL-first/Highest.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/WeatherObservationSystem/PULL-first/Highest.java new file mode 100644 index 0000000..0b95991 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/WeatherObservationSystem/PULL-first/Highest.java @@ -0,0 +1,32 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/highest") +@Component +public class Highest { + private double value = 0.0; + @Produces(MediaType.APPLICATION_JSON) + @GET + public double getValue() { + return value; + } + @POST + public void updateFromTemp_f(@FormParam("temp_f") double temp_f) { + double temp_if2; + if ((temp_f>=this.value)) { + temp_if2 = temp_f; + } else { + temp_if2 = this.value; + } + this.value = temp_if2; + } + @PUT + public void reset(@FormParam("v") double v) { + this.value = v; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/WeatherObservationSystem/PULL-first/Temp_c.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/WeatherObservationSystem/PULL-first/Temp_c.java new file mode 100644 index 0000000..3492c58 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/WeatherObservationSystem/PULL-first/Temp_c.java @@ -0,0 +1,19 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/temp_c") +@Component +public class Temp_c { + private Client client = ClientBuilder.newClient(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public double getValue() { + double temp_f = client.target("http://localhost:8080").path("/temp_f").request().get(double.class); + return ((temp_f-32)/1.8); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/WeatherObservationSystem/PULL-first/Temp_f.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/WeatherObservationSystem/PULL-first/Temp_f.java new file mode 100644 index 0000000..a23f614 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/WeatherObservationSystem/PULL-first/Temp_f.java @@ -0,0 +1,27 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/temp_f") +@Component +public class Temp_f { + private double value = 0.0; + private Client client = ClientBuilder.newClient(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public double getValue() { + return value; + } + @PUT + public void observe(@FormParam("x") double x) throws JsonProcessingException { + Form form = new Form(); + form.param("temp_f", Double.toString(this.value)); + Entity entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED); + String result = client.target("http://localhost:8080").path("/highest").request().post(entity, String.class); + this.value = x; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/WeatherObservationSystem/PUSH-first/Highest.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/WeatherObservationSystem/PUSH-first/Highest.java new file mode 100644 index 0000000..7196436 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/WeatherObservationSystem/PUSH-first/Highest.java @@ -0,0 +1,32 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/highest") +@Component +public class Highest { + private double value = 0.0; + @Produces(MediaType.APPLICATION_JSON) + @GET + public double getValue() { + return value; + } + @POST + public void updateFromTemp_f(@FormParam("temp_f") double temp_f) { + double temp_if3; + if ((temp_f>=this.value)) { + temp_if3 = temp_f; + } else { + temp_if3 = this.value; + } + this.value = temp_if3; + } + @PUT + public void reset(@FormParam("v") double v) { + this.value = v; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/WeatherObservationSystem/PUSH-first/Temp_c.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/WeatherObservationSystem/PUSH-first/Temp_c.java new file mode 100644 index 0000000..ee3d10a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/WeatherObservationSystem/PUSH-first/Temp_c.java @@ -0,0 +1,22 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/temp_c") +@Component +public class Temp_c { + private double value = 0.0; + @Produces(MediaType.APPLICATION_JSON) + @GET + public double getValue() { + return value; + } + @PUT + public void updateFromTemp_f(@FormParam("temp_f") double temp_f) { + this.value = ((temp_f-32)/1.8); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/WeatherObservationSystem/PUSH-first/Temp_f.java b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/WeatherObservationSystem/PUSH-first/Temp_f.java new file mode 100644 index 0000000..a589b35 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/JAX-RS/WeatherObservationSystem/PUSH-first/Temp_f.java @@ -0,0 +1,31 @@ +import java.util.*; +import javax.ws.rs.*; +import javax.ws.rs.client.*; +import javax.ws.rs.core.*; +import org.springframework.stereotype.Component; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.core.JsonProcessingException; + +@Path("/temp_f") +@Component +public class Temp_f { + private double value = 0.0; + private Client client = ClientBuilder.newClient(); + @Produces(MediaType.APPLICATION_JSON) + @GET + public double getValue() { + return value; + } + @PUT + public void observe(@FormParam("x") double x) throws JsonProcessingException { + Form form = new Form(); + form.param("temp_f", Double.toString(this.value)); + Entity entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED); + String result = client.target("http://localhost:8080").path("/temp_c").request().put(entity, String.class); + form = new Form(); + form.param("temp_f", Double.toString(this.value)); + entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED); + result = client.target("http://localhost:8080").path("/highest").request().post(entity, String.class); + this.value = x; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/Accounts/Account.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/Accounts/Account.java new file mode 100644 index 0000000..40c6dfb --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/Accounts/Account.java @@ -0,0 +1,19 @@ +import java.util.*; + +public class Account { + private String name; + public Map getValue() { + Map temp_nil0 = new HashMap<>(); + temp_nil0.put("name",this.getName()); + return temp_nil0; + } + public String getName() { + return this.name; + } + public void changeName(int uid, String name) { + this.name = name; + } + public Account(String name) { + this.name = name; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/Accounts/Accounts.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/Accounts/Accounts.java new file mode 100644 index 0000000..9053c32 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/Accounts/Accounts.java @@ -0,0 +1,14 @@ +import java.util.*; + +public class Accounts { + private List value = new ArrayList<>(); + public List getValue() { + return new ArrayList<>(value); + } + public Account getAccount(int uid) { + return this.value.get(uid); + } + public void signup(String name) { + this.value.add(new Account(name)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/Accounts/Main.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/Accounts/Main.java new file mode 100644 index 0000000..1340414 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/Accounts/Main.java @@ -0,0 +1,23 @@ +import java.util.*; + +public class Main { + private Accounts accounts; + public Main() { + accounts = new Accounts(); + } + public String getName(int uid) { + return this.accounts.getAccount(uid).getName(); + } + public void changeName(int uid, String name) { + this.accounts.getAccount(uid).changeName(uid, name); + } + public Map getAccount(int uid) { + return this.accounts.getAccount(uid).getValue(); + } + public List getAccounts() { + return this.accounts.getValue(); + } + public void signup(String name) { + this.accounts.signup(name); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PULL-first/Clock.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PULL-first/Clock.java new file mode 100644 index 0000000..a8b488a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PULL-first/Clock.java @@ -0,0 +1,29 @@ +import java.util.*; + +public class Clock { + private Hour hour; + private Hour_ang hour_ang; + private Min min; + private Min_ang min_ang; + public Clock() { + hour = new Hour(); + hour_ang = new Hour_ang(hour); + min = new Min(hour); + min_ang = new Min_ang(min); + } + public double getHour() { + return this.hour.getValue(); + } + public double getHour_ang() { + return this.hour_ang.getValue(); + } + public double getMin() { + return this.min.getValue(); + } + public void tick() { + this.min.tick(); + } + public double getMin_ang() { + return this.min_ang.getValue(); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PULL-first/Hour.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PULL-first/Hour.java new file mode 100644 index 0000000..0e389ac --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PULL-first/Hour.java @@ -0,0 +1,17 @@ +import java.util.*; + +public class Hour { + private double value = 0.0; + public double getValue() { + return value; + } + public void updateFromMin(double min) { + double temp_if1; + if ((min==0)) { + temp_if1 = ((this.value+1)%24); + } else { + temp_if1 = this.value; + } + this.value = temp_if1; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PULL-first/Hour_ang.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PULL-first/Hour_ang.java new file mode 100644 index 0000000..2758d12 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PULL-first/Hour_ang.java @@ -0,0 +1,11 @@ +import java.util.*; + +public class Hour_ang { + private Hour hour; + public double getValue() { + return ((this.hour.getValue()/6)*Math.PI); + } + public Hour_ang(Hour hour) { + this.hour = hour; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PULL-first/Min.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PULL-first/Min.java new file mode 100644 index 0000000..f0d82da --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PULL-first/Min.java @@ -0,0 +1,16 @@ +import java.util.*; + +public class Min { + private double value = 0.0; + private Hour hour; + public double getValue() { + return value; + } + public void tick() { + this.value = ((this.value+1)%60); + this.hour.updateFromMin(value); + } + public Min(Hour hour) { + this.hour = hour; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PULL-first/Min_ang.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PULL-first/Min_ang.java new file mode 100644 index 0000000..d91aefa --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PULL-first/Min_ang.java @@ -0,0 +1,11 @@ +import java.util.*; + +public class Min_ang { + private Min min; + public double getValue() { + return ((this.min.getValue()/30)*Math.PI); + } + public Min_ang(Min min) { + this.min = min; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PUSH-first/Clock.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PUSH-first/Clock.java new file mode 100644 index 0000000..2710bd1 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PUSH-first/Clock.java @@ -0,0 +1,29 @@ +import java.util.*; + +public class Clock { + private Hour_ang hour_ang; + private Min_ang min_ang; + private Hour hour; + private Min min; + public Clock() { + hour_ang = new Hour_ang(); + min_ang = new Min_ang(); + hour = new Hour(hour_ang); + min = new Min(min_ang,hour); + } + public double getHour_ang() { + return this.hour_ang.getValue(); + } + public double getMin_ang() { + return this.min_ang.getValue(); + } + public int getHour() { + return this.hour.getValue(); + } + public int getMin() { + return this.min.getValue(); + } + public void tick() { + this.min.tick(); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PUSH-first/Hour.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PUSH-first/Hour.java new file mode 100644 index 0000000..a305b95 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PUSH-first/Hour.java @@ -0,0 +1,22 @@ +import java.util.*; + +public class Hour { + private int value = 0; + private Hour_ang hour_ang; + public int getValue() { + return value; + } + public void updateFromMin(int min) { + int temp_if0; + if ((min==0)) { + temp_if0 = ((this.value+1)%24); + } else { + temp_if0 = this.value; + } + this.value = temp_if0; + this.hour_ang.updateFromHour(value); + } + public Hour(Hour_ang hour_ang) { + this.hour_ang = hour_ang; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PUSH-first/Hour_ang.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PUSH-first/Hour_ang.java new file mode 100644 index 0000000..8463ad7 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PUSH-first/Hour_ang.java @@ -0,0 +1,11 @@ +import java.util.*; + +public class Hour_ang { + private double value = 0.0; + public double getValue() { + return value; + } + public void updateFromHour(int hour) { + this.value = ((hour/6)*Math.PI); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PUSH-first/Min.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PUSH-first/Min.java new file mode 100644 index 0000000..c0e4de0 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PUSH-first/Min.java @@ -0,0 +1,19 @@ +import java.util.*; + +public class Min { + private int value = 0; + private Min_ang min_ang; + private Hour hour; + public int getValue() { + return value; + } + public void tick() { + this.value = ((this.value+1)%60); + this.min_ang.updateFromMin(value); + this.hour.updateFromMin(value); + } + public Min(Min_ang min_ang, Hour hour) { + this.min_ang = min_ang; + this.hour = hour; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PUSH-first/Min_ang.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PUSH-first/Min_ang.java new file mode 100644 index 0000000..756097f --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/Clock/PUSH-first/Min_ang.java @@ -0,0 +1,11 @@ +import java.util.*; + +public class Min_ang { + private double value = 0.0; + public double getValue() { + return value; + } + public void updateFromMin(int min) { + this.value = ((min/30)*Math.PI); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/CustomerManagement/Companies.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/CustomerManagement/Companies.java new file mode 100644 index 0000000..148543f --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/CustomerManagement/Companies.java @@ -0,0 +1,14 @@ +import java.util.*; + +public class Companies { + private Map value = new HashMap<>(); + public Map getValue() { + return new HashMap<>(value); + } + public Company getCompany(String cid) { + return this.value.get(cid); + } + public void addCampany(String address, String cid) { + this.value.put(cid,new Company(address)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/CustomerManagement/Company.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/CustomerManagement/Company.java new file mode 100644 index 0000000..fd0eaba --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/CustomerManagement/Company.java @@ -0,0 +1,19 @@ +import java.util.*; + +public class Company { + private String address; + public Map getValue() { + Map temp_nil6 = new HashMap<>(); + temp_nil6.put("address",this.getAddress()); + return temp_nil6; + } + public String getAddress() { + return this.address; + } + public void setAddress(String cid, String add) { + this.address = add; + } + public Company(String address) { + this.address = address; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/CustomerManagement/Customer.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/CustomerManagement/Customer.java new file mode 100644 index 0000000..4c19192 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/CustomerManagement/Customer.java @@ -0,0 +1,28 @@ +import java.util.*; + +public class Customer { + private String organization; + private Company company; + private Companies companies; + public Map getValue() { + Map temp_nil7 = new HashMap<>(); + temp_nil7.put("address",this.getAddress()); + temp_nil7.put("organization",this.getOrganization()); + return temp_nil7; + } + public String getOrganization() { + return this.organization; + } + public String getAddress() { + return this.company.getAddress(); + } + public void setOrganization(String uid, String cid) { + this.organization = cid; + this.company = this.companies.getCompany(this.organization); + } + public Customer(String organization, Companies companies) { + this.organization = organization; + this.companies = companies; + this.company = this.companies.getCompany(this.organization); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/CustomerManagement/CustomerManagement.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/CustomerManagement/CustomerManagement.java new file mode 100644 index 0000000..a919176 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/CustomerManagement/CustomerManagement.java @@ -0,0 +1,43 @@ +import java.util.*; + +public class CustomerManagement { + private Companies companies; + private Customers customers; + public CustomerManagement() { + companies = new Companies(); + customers = new Customers(companies); + } + public Map getCompanies() { + return this.companies.getValue(); + } + public void addCampany(String address, String cid) { + this.companies.addCampany(address, cid); + } + public String getAddress(String cid) { + return this.companies.getCompany(cid).getAddress(); + } + public void setAddress(String cid, String add) { + this.companies.getCompany(cid).setAddress(cid, add); + } + public String getOrganization(String uid) { + return this.customers.getCustomer(uid).getOrganization(); + } + public void setOrganization(String uid, String cid) { + this.customers.getCustomer(uid).setOrganization(uid, cid); + } + public String getAddress(String uid) { + return this.customers.getCustomer(uid).getAddress(); + } + public Map getCompany(String cid) { + return this.companies.getCompany(cid).getValue(); + } + public Map getCustomers() { + return this.customers.getValue(); + } + public void addCustomer(String org, String uid) { + this.customers.addCustomer(org, uid); + } + public Map getCustomer(String uid) { + return this.customers.getCustomer(uid).getValue(); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/CustomerManagement/Customers.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/CustomerManagement/Customers.java new file mode 100644 index 0000000..04725d7 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/CustomerManagement/Customers.java @@ -0,0 +1,18 @@ +import java.util.*; + +public class Customers { + private Map value = new HashMap<>(); + private Companies companies; + public Map getValue() { + return new HashMap<>(value); + } + public Customer getCustomer(String uid) { + return this.value.get(uid); + } + public void addCustomer(String org, String uid) { + this.value.put(uid,new Customer(org, companies)); + } + public Customers(Companies companies) { + this.companies = companies; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/Account.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/Account.java new file mode 100644 index 0000000..10f53f1 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/Account.java @@ -0,0 +1,22 @@ +import java.util.*; + +public class Account { + private Map notifications; + public Map getValue() { + Map temp_nil17 = new HashMap<>(); + temp_nil17.put("notifications",this.getNotifications()); + return temp_nil17; + } + public Map getNotifications() { + return new HashMap<>(notifications); + } + public void updateNotificationsFromMessages(String self, String gid, int mno, List messages, String member) { + this.notifications.put(gid,true); + } + public void hasRead(String aid, String gid) { + this.notifications.remove(gid); + } + public Account(Map notifications) { + this.notifications = notifications; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/Accounts.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/Accounts.java new file mode 100644 index 0000000..2e7c4e3 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/Accounts.java @@ -0,0 +1,14 @@ +import java.util.*; + +public class Accounts { + private Map value = new HashMap<>(); + public Map getValue() { + return new HashMap<>(value); + } + public Account getAccount(String v1) { + return this.value.get(v1); + } + public void signUp(String aid) { + this.value.put(aid,new Account(new HashMap<>())); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/Group.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/Group.java new file mode 100644 index 0000000..faa7d19 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/Group.java @@ -0,0 +1,39 @@ +import java.util.*; + +public class Group { + private List messages; + private Account account; + private Accounts accounts; + private List members; + public Map getValue() { + Map temp_nil16 = new HashMap<>(); + temp_nil16.put("members",this.getMembers()); + temp_nil16.put("messages",this.getMessages()); + return temp_nil16; + } + public List getMessages() { + return this.messages; + } + public List getMembers() { + return this.members; + } + public String getMember(int mno) { + return this.members.get(mno); + } + public void postMessage(String gid, String message) { + this.messages.add(message); + for (int mno = 0; mno < this.members.size(); mno++) { + String member = getMember(mno); + this.account = accounts.getAccount(member); + this.account.updateNotificationsFromMessages(member, gid, mno, messages, member); + } + } + public void addGroupMember(String gid, String aid) { + this.members.add(aid); + } + public Group(List messages, Accounts accounts, List members) { + this.messages = messages; + this.accounts = accounts; + this.members = members; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/GroupChat.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/GroupChat.java new file mode 100644 index 0000000..6ee5abf --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/GroupChat.java @@ -0,0 +1,49 @@ +import java.util.*; + +public class GroupChat { + private Accounts accounts; + private Groups groups; + public GroupChat() { + accounts = new Accounts(); + groups = new Groups(accounts); + } + public Map getGroup(String gid) { + return this.groups.getGroup(gid).getValue(); + } + public Map getAccount(String v1) { + return this.accounts.getAccount(v1).getValue(); + } + public List getMessages(String gid) { + return this.groups.getGroup(gid).getMessages(); + } + public void postMessage(String gid, String message) { + this.groups.getGroup(gid).postMessage(gid, message); + } + public List getMembers(String gid) { + return this.groups.getGroup(gid).getMembers(); + } + public void addGroupMember(String gid, String aid) { + this.groups.getGroup(gid).addGroupMember(gid, aid); + } + public Map getNotifications(String v1) { + return this.accounts.getAccount(v1).getNotifications(); + } + public void hasRead(String aid, String gid) { + this.accounts.getAccount(aid).hasRead(aid, gid); + } + public String getMember(String gid, int mno) { + return this.groups.getGroup(gid).getMember(mno); + } + public Map getAccounts() { + return this.accounts.getValue(); + } + public void signUp(String aid) { + this.accounts.signUp(aid); + } + public Map getGroups() { + return this.groups.getValue(); + } + public void createGroup(String gid) { + this.groups.createGroup(gid); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/Groups.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/Groups.java new file mode 100644 index 0000000..ada656a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/GroupChat/Groups.java @@ -0,0 +1,18 @@ +import java.util.*; + +public class Groups { + private Map value = new HashMap<>(); + private Accounts accounts; + public Map getValue() { + return new HashMap<>(value); + } + public Group getGroup(String gid) { + return this.value.get(gid); + } + public void createGroup(String gid) { + this.value.put(gid,new Group(new ArrayList<>(), accounts, new ArrayList<>())); + } + public Groups(Accounts accounts) { + this.accounts = accounts; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/InventoryManagement/Inventory.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/InventoryManagement/Inventory.java new file mode 100644 index 0000000..da9dc38 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/InventoryManagement/Inventory.java @@ -0,0 +1,14 @@ +import java.util.*; + +public class Inventory { + private Map value = new HashMap<>(); + public Map getValue() { + return new HashMap<>(value); + } + public InventoryElement getInventoryElement(String itemId) { + return this.value.get(itemId); + } + public void registerItem(String itemName, int quantity, String itemId) { + this.value.put(itemId,new InventoryElement(quantity)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/InventoryManagement/InventoryElement.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/InventoryManagement/InventoryElement.java new file mode 100644 index 0000000..366444a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/InventoryManagement/InventoryElement.java @@ -0,0 +1,19 @@ +import java.util.*; + +public class InventoryElement { + private int count; + public Map getValue() { + Map temp_nil17 = new HashMap<>(); + temp_nil17.put("count",this.getCount()); + return temp_nil17; + } + public int getCount() { + return this.count; + } + public void receiveOrShip(String itemId, int quantity) { + this.count = (this.value+quantity); + } + public InventoryElement(int count) { + this.count = count; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/InventoryManagement/InventoryManagement.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/InventoryManagement/InventoryManagement.java new file mode 100644 index 0000000..3f3d2fc --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/InventoryManagement/InventoryManagement.java @@ -0,0 +1,23 @@ +import java.util.*; + +public class InventoryManagement { + private Inventory inventory; + public InventoryManagement() { + inventory = new Inventory(); + } + public Map getInventory() { + return this.inventory.getValue(); + } + public void registerItem(String itemName, int quantity, String itemId) { + this.inventory.registerItem(itemName, quantity, itemId); + } + public int getCount(String itemId) { + return this.inventory.getInventoryElement(itemId).getCount(); + } + public void receiveOrShip(String itemId, int quantity) { + this.inventory.getInventoryElement(itemId).receiveOrShip(itemId, quantity); + } + public Map getInventoryElement(String itemId) { + return this.inventory.getInventoryElement(itemId).getValue(); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PULL-first/Account.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PULL-first/Account.java new file mode 100644 index 0000000..5838e95 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PULL-first/Account.java @@ -0,0 +1,19 @@ +import java.util.*; + +public class Account { + private String name; + public Map getValue() { + Map temp_nil23 = new HashMap<>(); + temp_nil23.put("name",this.getName()); + return temp_nil23; + } + public String getName() { + return this.name; + } + public void changeName(String aid, String name) { + this.name = name; + } + public Account(String name) { + this.name = name; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PULL-first/Accounts.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PULL-first/Accounts.java new file mode 100644 index 0000000..feb415f --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PULL-first/Accounts.java @@ -0,0 +1,14 @@ +import java.util.*; + +public class Accounts { + private Map value = new HashMap<>(); + public Map getValue() { + return new HashMap<>(value); + } + public Account getAccount(String aid) { + return this.value.get(aid); + } + public void signUp(String name, String aid) { + this.value.put(aid,new Account(name)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PULL-first/OnlineBattleGame.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PULL-first/OnlineBattleGame.java new file mode 100644 index 0000000..5339f4c --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PULL-first/OnlineBattleGame.java @@ -0,0 +1,52 @@ +import java.util.*; + +public class OnlineBattleGame { + private Accounts accounts; + private Rooms rooms; + public OnlineBattleGame() { + accounts = new Accounts(); + rooms = new Rooms(accounts); + } + public String getBlue_id(String rid) { + return this.rooms.getRoom(rid).getBlue_id(); + } + public void changeBlueId(String rid, String blueId) { + this.rooms.getRoom(rid).changeBlueId(rid, blueId); + } + public Map getAccount(String aid) { + return this.accounts.getAccount(aid).getValue(); + } + public String getName(String aid) { + return this.accounts.getAccount(aid).getName(); + } + public void changeName(String aid, String name) { + this.accounts.getAccount(aid).changeName(aid, name); + } + public String getBlue_name(String rid) { + return this.rooms.getRoom(rid).getBlue_name(); + } + public Map getRoom(String rid) { + return this.rooms.getRoom(rid).getValue(); + } + public String getRed_id(String rid) { + return this.rooms.getRoom(rid).getRed_id(); + } + public void changeRedId(String rid, String redId) { + this.rooms.getRoom(rid).changeRedId(rid, redId); + } + public Map getAccounts() { + return this.accounts.getValue(); + } + public void signUp(String name, String aid) { + this.accounts.signUp(name, aid); + } + public Map getRooms() { + return this.rooms.getValue(); + } + public void createRoom(String blueId, String redId, String rid) { + this.rooms.createRoom(blueId, redId, rid); + } + public String getRed_name(String rid) { + return this.rooms.getRoom(rid).getRed_name(); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PULL-first/Room.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PULL-first/Room.java new file mode 100644 index 0000000..399506e --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PULL-first/Room.java @@ -0,0 +1,43 @@ +import java.util.*; + +public class Room { + private String blue_id; + private Account account; + private Accounts accounts; + private String red_id; + public Map getValue() { + Map temp_nil22 = new HashMap<>(); + temp_nil22.put("blue_id",this.getBlue_id()); + temp_nil22.put("red_name",this.getRed_name()); + temp_nil22.put("blue_name",this.getBlue_name()); + temp_nil22.put("red_id",this.getRed_id()); + return temp_nil22; + } + public String getBlue_id() { + return this.blue_id; + } + public String getBlue_name() { + return this.account.getName(); + } + public String getRed_id() { + return this.red_id; + } + public String getRed_name() { + return this.account.getName(); + } + public void changeRedId(String rid, String redId) { + this.red_id = redId; + this.account = this.accounts.getAccount(this.red_id); + } + public void changeBlueId(String rid, String blueId) { + this.blue_id = blueId; + this.account = this.accounts.getAccount(this.blue_id); + } + public Room(String blue_id, Accounts accounts, String red_id) { + this.blue_id = blue_id; + this.accounts = accounts; + this.red_id = red_id; + this.account = this.accounts.getAccount(this.blue_id); + this.account = this.accounts.getAccount(this.red_id); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PULL-first/Rooms.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PULL-first/Rooms.java new file mode 100644 index 0000000..2016c96 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PULL-first/Rooms.java @@ -0,0 +1,18 @@ +import java.util.*; + +public class Rooms { + private Map value = new HashMap<>(); + private Accounts accounts; + public Map getValue() { + return new HashMap<>(value); + } + public Room getRoom(String rid) { + return this.value.get(rid); + } + public void createRoom(String blueId, String redId, String rid) { + this.value.put(rid,new Room(blueId, accounts, redId)); + } + public Rooms(Accounts accounts) { + this.accounts = accounts; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PUSH-first/Account.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PUSH-first/Account.java new file mode 100644 index 0000000..63f2dd3 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PUSH-first/Account.java @@ -0,0 +1,19 @@ +import java.util.*; + +public class Account { + private String name; + public Map getValue() { + Map temp_nil21 = new HashMap<>(); + temp_nil21.put("name",this.getName()); + return temp_nil21; + } + public String getName() { + return this.name; + } + public void changeName(String aid, String name) { + this.name = name; + } + public Account(String name) { + this.name = name; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PUSH-first/Accounts.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PUSH-first/Accounts.java new file mode 100644 index 0000000..feb415f --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PUSH-first/Accounts.java @@ -0,0 +1,14 @@ +import java.util.*; + +public class Accounts { + private Map value = new HashMap<>(); + public Map getValue() { + return new HashMap<>(value); + } + public Account getAccount(String aid) { + return this.value.get(aid); + } + public void signUp(String name, String aid) { + this.value.put(aid,new Account(name)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PUSH-first/OnlineBattleGame.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PUSH-first/OnlineBattleGame.java new file mode 100644 index 0000000..02e2ca7 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PUSH-first/OnlineBattleGame.java @@ -0,0 +1,52 @@ +import java.util.*; + +public class OnlineBattleGame { + private Accounts accounts; + private Rooms rooms; + public OnlineBattleGame() { + accounts = new Accounts(); + rooms = new Rooms(accounts); + } + public Map getAccount(String aid) { + return this.accounts.getAccount(aid).getValue(); + } + public String getBlue_name(String rid) { + return this.rooms.getRoom(rid).getBlue_name(); + } + public String getBlue_id(String rid) { + return this.rooms.getRoom(rid).getBlue_id(); + } + public void changeBlueId(String rid, String blueId) { + this.rooms.getRoom(rid).changeBlueId(rid, blueId); + } + public String getRed_name(String rid) { + return this.rooms.getRoom(rid).getRed_name(); + } + public String getName(String aid) { + return this.accounts.getAccount(aid).getName(); + } + public void changeName(String aid, String name) { + this.accounts.getAccount(aid).changeName(aid, name); + } + public Map getRoom(String rid) { + return this.rooms.getRoom(rid).getValue(); + } + public String getRed_id(String rid) { + return this.rooms.getRoom(rid).getRed_id(); + } + public void changeRedId(String rid, String redId) { + this.rooms.getRoom(rid).changeRedId(rid, redId); + } + public Map getAccounts() { + return this.accounts.getValue(); + } + public void signUp(String name, String aid) { + this.accounts.signUp(name, aid); + } + public Map getRooms() { + return this.rooms.getValue(); + } + public void createRoom(String blueId, String redId, String rid) { + this.rooms.createRoom(blueId, redId, rid); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PUSH-first/Room.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PUSH-first/Room.java new file mode 100644 index 0000000..251aacf --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PUSH-first/Room.java @@ -0,0 +1,53 @@ +import java.util.*; + +public class Room { + private Account account; + private Accounts accounts; + private String blue_id; + private String red_id; + public Map getValue() { + Map temp_nil20 = new HashMap<>(); + temp_nil20.put("blue_id",this.getBlue_id()); + temp_nil20.put("red_name",this.getRed_name()); + temp_nil20.put("blue_name",this.getBlue_name()); + temp_nil20.put("red_id",this.getRed_id()); + return temp_nil20; + } + public String getBlue_name() { + return name; + } + public String getBlue_id() { + return this.blue_id; + } + public String getRed_name() { + return name; + } + public String getRed_id() { + return this.red_id; + } + public void updateBlue_nameFromBlue_id(String self, String rid, String blue_id) { + this.blue_name = name; + this.blue_id = blue_id; + } + public void updateRed_nameFromRed_id(String self, String rid, String red_id) { + this.red_name = name; + this.red_id = red_id; + } + public void changeRedId(String rid, String redId) { + this.red_id = redId; + this.account = this.accounts.getAccount(this.red_id); + this.updateRed_nameFromRed_id(rid, rid, red_id); + } + public void changeBlueId(String rid, String blueId) { + this.blue_id = blueId; + this.account = this.accounts.getAccount(this.blue_id); + this.updateBlue_nameFromBlue_id(rid, rid, blue_id); + } + public Room(Accounts accounts, String blue_id, String red_id) { + this.accounts = accounts; + this.blue_id = blue_id; + this.red_id = red_id; + this.account = this.accounts.getAccount(this.blue_id); + this.account = this.accounts.getAccount(this.red_id); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PUSH-first/Rooms.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PUSH-first/Rooms.java new file mode 100644 index 0000000..f674087 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame/PUSH-first/Rooms.java @@ -0,0 +1,18 @@ +import java.util.*; + +public class Rooms { + private Map value = new HashMap<>(); + private Accounts accounts; + public Map getValue() { + return new HashMap<>(value); + } + public Room getRoom(String rid) { + return this.value.get(rid); + } + public void createRoom(String blueId, String redId, String rid) { + this.value.put(rid,new Room(accounts, blueId, redId)); + } + public Rooms(Accounts accounts) { + this.accounts = accounts; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/Account.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/Account.java new file mode 100644 index 0000000..f95e5e6 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/Account.java @@ -0,0 +1,33 @@ +import java.util.*; + +public class Account { + private String name; + private int point; + public Map getValue() { + Map temp_nil1 = new HashMap<>(); + temp_nil1.put("name",this.getName()); + temp_nil1.put("point",this.getPoint()); + return temp_nil1; + } + public String getName() { + return this.name; + } + public int getPoint() { + return point; + } + public void updatePointFromBattle(String self, String rid, int mno, boolean battle, String id) { + int temp_if0; + if (battle) { + temp_if0 = (this.point+1); + } else { + temp_if0 = this.point; + }this.point = temp_if0; + } + public void changeName(String aid, String name) { + this.name = name; + } + public Account(String name, int point) { + this.name = name; + this.point = point; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/Accounts.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/Accounts.java new file mode 100644 index 0000000..6d92c89 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/Accounts.java @@ -0,0 +1,14 @@ +import java.util.*; + +public class Accounts { + private Map value = new HashMap<>(); + public Map getValue() { + return new HashMap<>(value); + } + public Account getAccount(String mid) { + return this.value.get(mid); + } + public void signUp(String name, String aid) { + this.value.put(aid,new Account(name, 0)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/Member.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/Member.java new file mode 100644 index 0000000..aac4c47 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/Member.java @@ -0,0 +1,23 @@ +import java.util.*; + +public class Member { + private String id; + private Account account; + private Accounts accounts; + public Map getValue() { + Map temp_nil2 = new HashMap<>(); + temp_nil2.put("name",this.getName()); + temp_nil2.put("id",this.getId()); + return temp_nil2; + } + public String getId() { + return this.id; + } + public String getName() { + return this.account.getName(); + } + public Member(String id, Accounts accounts) { + this.id = id; + this.accounts = accounts; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/Members.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/Members.java new file mode 100644 index 0000000..091b561 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/Members.java @@ -0,0 +1,14 @@ +import java.util.*; + +public class Members { + private List value = new ArrayList<>(); + public List getValue() { + return new ArrayList<>(value); + } + public Member getMember(int mno) { + return this.value.get(mno); + } + public void addRoomMember(String rid, String id) { + this.value.add(new Member(id, accounts)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/OnlineBattleGame2.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/OnlineBattleGame2.java new file mode 100644 index 0000000..cc758ed --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/OnlineBattleGame2.java @@ -0,0 +1,58 @@ +import java.util.*; + +public class OnlineBattleGame2 { + private Accounts accounts; + private Rooms rooms; + public OnlineBattleGame2() { + accounts = new Accounts(); + rooms = new Rooms(accounts); + } + public boolean getBattle(String rid) { + return this.rooms.getRoom(rid).getBattle(); + } + public void battle(String rid, boolean hasWon) { + this.rooms.getRoom(rid).battle(rid, hasWon); + } + public Map getRoom(String rid) { + return this.rooms.getRoom(rid).getValue(); + } + public String getName(String mid) { + return this.accounts.getAccount(mid).getName(); + } + public void changeName(String aid, String name) { + this.accounts.getAccount(aid).changeName(aid, name); + } + public Map getAccounts() { + return this.accounts.getValue(); + } + public void signUp(String name, String aid) { + this.accounts.signUp(name, aid); + } + public List getMembers(String rid) { + return this.rooms.getRoom(rid).getMembers().getValue(); + } + public void addRoomMember(String rid, String id) { + this.rooms.getRoom(rid).getMembers().addRoomMember(rid, id); + } + public int getPoint(String mid) { + return this.accounts.getAccount(mid).getPoint(); + } + public Map getRooms() { + return this.rooms.getValue(); + } + public void createRoom(String rid) { + this.rooms.createRoom(rid); + } + public Map getAccount(String mid) { + return this.accounts.getAccount(mid).getValue(); + } + public Map getMember(String rid, int mno) { + return this.rooms.getRoom(rid).getMembers().getMember(mno).getValue(); + } + public String getId(String rid, int mno) { + return this.rooms.getRoom(rid).getMembers().getMember(mno).getId(); + } + public String getName(String rid, int mno) { + return this.rooms.getRoom(rid).getMembers().getMember(mno).getName(); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/Room.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/Room.java new file mode 100644 index 0000000..43129a4 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/Room.java @@ -0,0 +1,32 @@ +import java.util.*; + +public class Room { + private Members members = new Members(); + private boolean battle; + private Account account; + private Accounts accounts; + public Map getValue() { + Map temp_nil0 = new HashMap<>(); + temp_nil0.put("members",this.members.getValue()); + temp_nil0.put("battle",this.getBattle()); + return temp_nil0; + } + public Members getMembers() { + return this.members; + } + public boolean getBattle() { + return this.battle; + } + public void battle(String rid, boolean hasWon) { + this.battle = hasWon; + for (int mno = 0; mno < this.members.getValue().size(); mno++) { + String id = this.members.getMember(mno).getId(); + this.account = accounts.getAccount(id); + this.account.updatePointFromBattle(id, rid, mno, battle, id); + } + } + public Room(boolean battle, Accounts accounts) { + this.battle = battle; + this.accounts = accounts; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/Rooms.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/Rooms.java new file mode 100644 index 0000000..d4230fa --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/OnlineBattleGame2/Rooms.java @@ -0,0 +1,18 @@ +import java.util.*; + +public class Rooms { + private Map value = new HashMap<>(); + private Accounts accounts; + public Map getValue() { + return new HashMap<>(value); + } + public Room getRoom(String rid) { + return this.value.get(rid); + } + public void createRoom(String rid) { + this.value.put(rid,new Room(false, accounts)); + } + public Rooms(Accounts accounts) { + this.accounts = accounts; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PULL-first/History.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PULL-first/History.java new file mode 100644 index 0000000..75373bd --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PULL-first/History.java @@ -0,0 +1,11 @@ +import java.util.*; + +public class History { + private List value = new ArrayList<>(); + public List getValue() { + return new ArrayList<>(value); + } + public void updateFromPayment(int payment) { + this.value.add(0, payment); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PULL-first/POS.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PULL-first/POS.java new file mode 100644 index 0000000..55d7d08 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PULL-first/POS.java @@ -0,0 +1,29 @@ +import java.util.*; + +public class POS { + private History history; + private Total total; + private Payment payment; + private Points points; + public POS() { + history = new History(); + total = new Total(history); + payment = new Payment(history); + points = new Points(payment); + } + public List getHistory() { + return this.history.getValue(); + } + public int getTotal() { + return this.total.getValue(); + } + public int getPayment() { + return this.payment.getValue(); + } + public void purchase(int x) { + this.payment.purchase(x); + } + public int getPoints() { + return this.points.getValue(); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PULL-first/Payment.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PULL-first/Payment.java new file mode 100644 index 0000000..23c1088 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PULL-first/Payment.java @@ -0,0 +1,16 @@ +import java.util.*; + +public class Payment { + private int value = 0; + private History history; + public int getValue() { + return value; + } + public void purchase(int x) { + this.value = x; + this.history.updateFromPayment(value); + } + public Payment(History history) { + this.history = history; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PULL-first/Points.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PULL-first/Points.java new file mode 100644 index 0000000..2b11644 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PULL-first/Points.java @@ -0,0 +1,11 @@ +import java.util.*; + +public class Points { + private Payment payment; + public int getValue() { + return (int)Math.floor((this.payment.getValue()*0.05)); + } + public Points(Payment payment) { + this.payment = payment; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PULL-first/Total.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PULL-first/Total.java new file mode 100644 index 0000000..62564ff --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PULL-first/Total.java @@ -0,0 +1,15 @@ +import java.util.*; + +public class Total { + private History history; + public int getValue() { + Integer temp_sum1 = 0; + for (Integer x: this.history.getValue()) { + temp_sum1 += x; + } + return temp_sum1; + } + public Total(History history) { + this.history = history; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PUSH-first/History.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PUSH-first/History.java new file mode 100644 index 0000000..6aa27b8 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PUSH-first/History.java @@ -0,0 +1,17 @@ +import java.util.*; + +public class History { + private List value = new ArrayList<>(); + private Total total; + public List getValue() { + return new ArrayList<>(value); + } + public void updateFromPayment(double payment) { + this.value.add(0, payment); + + this.total.updateFromHistory(value); + } + public History(Total total) { + this.total = total; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PUSH-first/POS.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PUSH-first/POS.java new file mode 100644 index 0000000..2d3e544 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PUSH-first/POS.java @@ -0,0 +1,29 @@ +import java.util.*; + +public class POS { + private Points points; + private Total total; + private History history; + private Payment payment; + public POS() { + points = new Points(); + total = new Total(); + history = new History(total); + payment = new Payment(points,history); + } + public int getPoints() { + return this.points.getValue(); + } + public int getTotal() { + return this.total.getValue(); + } + public List getHistory() { + return this.history.getValue(); + } + public double getPayment() { + return this.payment.getValue(); + } + public void purchase(double x) { + this.payment.purchase(x); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PUSH-first/Payment.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PUSH-first/Payment.java new file mode 100644 index 0000000..ebf56ac --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PUSH-first/Payment.java @@ -0,0 +1,19 @@ +import java.util.*; + +public class Payment { + private double value = 0.0; + private Points points; + private History history; + public double getValue() { + return value; + } + public void purchase(double x) { + this.value = x; + this.points.updateFromPayment(value); + this.history.updateFromPayment(value); + } + public Payment(Points points, History history) { + this.points = points; + this.history = history; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PUSH-first/Points.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PUSH-first/Points.java new file mode 100644 index 0000000..55cb552 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PUSH-first/Points.java @@ -0,0 +1,11 @@ +import java.util.*; + +public class Points { + private int value = 0; + public int getValue() { + return value; + } + public void updateFromPayment(double payment) { + this.value = (int)Math.floor((payment*0.05)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PUSH-first/Total.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PUSH-first/Total.java new file mode 100644 index 0000000..2a3c00e --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/POS/PUSH-first/Total.java @@ -0,0 +1,15 @@ +import java.util.*; + +public class Total { + private int value = 0; + public int getValue() { + return value; + } + public void updateFromHistory(List history) { + Integer temp_sum1 = 0; + for (Integer x: history) { + temp_sum1 += x; + } + this.value = temp_sum1; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/SimpleTwitter/Account.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/SimpleTwitter/Account.java new file mode 100644 index 0000000..f007a58 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/SimpleTwitter/Account.java @@ -0,0 +1,19 @@ +import java.util.*; + +public class Account { + private List tweets; + public Map getValue() { + Map temp_nil27 = new HashMap<>(); + temp_nil27.put("tweets",this.getTweets()); + return temp_nil27; + } + public List getTweets() { + return this.tweets; + } + public void tweet(String accountId, String contents) { + this.tweets.add(contents); + } + public Account(List tweets) { + this.tweets = tweets; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/SimpleTwitter/Accounts.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/SimpleTwitter/Accounts.java new file mode 100644 index 0000000..7f2a17b --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/SimpleTwitter/Accounts.java @@ -0,0 +1,14 @@ +import java.util.*; + +public class Accounts { + private Map value = new HashMap<>(); + public Map getValue() { + return new HashMap<>(value); + } + public Account getAccount(String accountId) { + return this.value.get(accountId); + } + public void signUp(String name, String accountId) { + this.value.put(accountId,new Account(new ArrayList<>())); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/SimpleTwitter/SimpleTwitter.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/SimpleTwitter/SimpleTwitter.java new file mode 100644 index 0000000..36aaf77 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/SimpleTwitter/SimpleTwitter.java @@ -0,0 +1,23 @@ +import java.util.*; + +public class SimpleTwitter { + private Accounts accounts; + public SimpleTwitter() { + accounts = new Accounts(); + } + public List getTweets(String accountId) { + return this.accounts.getAccount(accountId).getTweets(); + } + public void tweet(String accountId, String contents) { + this.accounts.getAccount(accountId).tweet(accountId, contents); + } + public Map getAccount(String accountId) { + return this.accounts.getAccount(accountId).getValue(); + } + public Map getAccounts() { + return this.accounts.getValue(); + } + public void signUp(String name, String accountId) { + this.accounts.signUp(name, accountId); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/Account.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/Account.java new file mode 100644 index 0000000..605d6c1 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/Account.java @@ -0,0 +1,19 @@ +import java.util.*; + +public class Account { + private String vote; + public Map getValue() { + Map temp_nil4 = new HashMap<>(); + temp_nil4.put("vote",this.getVote()); + return temp_nil4; + } + public String getVote() { + return this.vote; + } + public void cast(String aid, String v) { + this.vote = v; + } + public Account(String vote) { + this.vote = vote; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/Accounts.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/Accounts.java new file mode 100644 index 0000000..d0b8bb3 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/Accounts.java @@ -0,0 +1,14 @@ +import java.util.*; + +public class Accounts { + private Map value = new HashMap<>(); + public Map getValue() { + return new HashMap<>(value); + } + public Account getAccount(String aid) { + return this.value.get(aid); + } + public void signUp(String name, String aid) { + this.value.put(aid,new Account(null)); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/Counts.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/Counts.java new file mode 100644 index 0000000..a0d5b9f --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/Counts.java @@ -0,0 +1,18 @@ +import java.util.*; + +public class Counts { + private Map value = new HashMap<>(); + private Account account; + private Accounts accounts; + public Map getValue() { + Map v0 = new HashMap<>(); + for (String aid: this.accounts.getValue().keySet()) { + String vote = this.accounts.getAccount(aid).getVote(); + v0.put(aid,vote); + } + return v0; + } + public Counts(Accounts accounts) { + this.accounts = accounts; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/VotingSystem.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/VotingSystem.java new file mode 100644 index 0000000..27950ce --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/VotingSystem/VotingSystem.java @@ -0,0 +1,28 @@ +import java.util.*; + +public class VotingSystem { + private Accounts accounts; + private Counts counts; + public VotingSystem() { + accounts = new Accounts(); + counts = new Counts(accounts); + } + public Map getAccount(String aid) { + return this.accounts.getAccount(aid).getValue(); + } + public Map getAccounts() { + return this.accounts.getValue(); + } + public void signUp(String name, String aid) { + this.accounts.signUp(name, aid); + } + public Map getCounts() { + return this.counts.getValue(); + } + public String getVote(String aid) { + return this.accounts.getAccount(aid).getVote(); + } + public void cast(String aid, String v) { + this.accounts.getAccount(aid).cast(aid, v); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PULL-first/Highest.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PULL-first/Highest.java new file mode 100644 index 0000000..407263c --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PULL-first/Highest.java @@ -0,0 +1,20 @@ +import java.util.*; + +public class Highest { + private double value = 0.0; + public double getValue() { + return value; + } + public void updateFromTemp_f(double temp_f) { + double temp_if5; + if ((temp_f>=this.value)) { + temp_if5 = temp_f; + } else { + temp_if5 = this.value; + } + this.value = temp_if5; + } + public void reset(double v) { + this.value = v; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PULL-first/Temp_c.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PULL-first/Temp_c.java new file mode 100644 index 0000000..7eb9df6 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PULL-first/Temp_c.java @@ -0,0 +1,11 @@ +import java.util.*; + +public class Temp_c { + private Temp_f temp_f; + public double getValue() { + return ((this.temp_f.getValue()-32)/1.8); + } + public Temp_c(Temp_f temp_f) { + this.temp_f = temp_f; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PULL-first/Temp_f.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PULL-first/Temp_f.java new file mode 100644 index 0000000..2b57682 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PULL-first/Temp_f.java @@ -0,0 +1,16 @@ +import java.util.*; + +public class Temp_f { + private double value = 0.0; + private Highest highest; + public double getValue() { + return value; + } + public void observe(double x) { + this.value = x; + this.highest.updateFromTemp_f(value); + } + public Temp_f(Highest highest) { + this.highest = highest; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PULL-first/WeatherObservationSystem.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PULL-first/WeatherObservationSystem.java new file mode 100644 index 0000000..940b5a7 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PULL-first/WeatherObservationSystem.java @@ -0,0 +1,27 @@ +import java.util.*; + +public class WeatherObservationSystem { + private Highest highest; + private Temp_f temp_f; + private Temp_c temp_c; + public WeatherObservationSystem() { + highest = new Highest(); + temp_f = new Temp_f(highest); + temp_c = new Temp_c(temp_f); + } + public double getHighest() { + return this.highest.getValue(); + } + public void reset(double v) { + this.highest.reset(v); + } + public double getTemp_f() { + return this.temp_f.getValue(); + } + public void observe(double x) { + this.temp_f.observe(x); + } + public double getTemp_c() { + return this.temp_c.getValue(); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PUSH-first/Highest.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PUSH-first/Highest.java new file mode 100644 index 0000000..be085e2 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PUSH-first/Highest.java @@ -0,0 +1,20 @@ +import java.util.*; + +public class Highest { + private double value = 0.0; + public double getValue() { + return value; + } + public void updateFromTemp_f(double temp_f) { + double temp_if6; + if ((temp_f>=this.value)) { + temp_if6 = temp_f; + } else { + temp_if6 = this.value; + } + this.value = temp_if6; + } + public void reset(double v) { + this.value = v; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PUSH-first/Temp_c.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PUSH-first/Temp_c.java new file mode 100644 index 0000000..8aea709 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PUSH-first/Temp_c.java @@ -0,0 +1,11 @@ +import java.util.*; + +public class Temp_c { + private double value = 0.0; + public double getValue() { + return value; + } + public void updateFromTemp_f(double temp_f) { + this.value = ((temp_f-32)/1.8); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PUSH-first/Temp_f.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PUSH-first/Temp_f.java new file mode 100644 index 0000000..3a63072 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PUSH-first/Temp_f.java @@ -0,0 +1,19 @@ +import java.util.*; + +public class Temp_f { + private double value = 0.0; + private Temp_c temp_c; + private Highest highest; + public double getValue() { + return value; + } + public void observe(double x) { + this.value = x; + this.temp_c.updateFromTemp_f(value); + this.highest.updateFromTemp_f(value); + } + public Temp_f(Temp_c temp_c, Highest highest) { + this.temp_c = temp_c; + this.highest = highest; + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PUSH-first/WeatherObservationSystem.java b/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PUSH-first/WeatherObservationSystem.java new file mode 100644 index 0000000..9d43e68 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/prototypes/Java/WeatherObservationSystem/PUSH-first/WeatherObservationSystem.java @@ -0,0 +1,27 @@ +import java.util.*; + +public class WeatherObservationSystem { + private Temp_c temp_c; + private Highest highest; + private Temp_f temp_f; + public WeatherObservationSystem() { + temp_c = new Temp_c(); + highest = new Highest(); + temp_f = new Temp_f(temp_c,highest); + } + public double getTemp_c() { + return this.temp_c.getValue(); + } + public double getHighest() { + return this.highest.getValue(); + } + public void reset(double v) { + this.highest.reset(v); + } + public double getTemp_f() { + return this.temp_f.getValue(); + } + public void observe(double x) { + this.temp_f.observe(x); + } +} \ No newline at end of file diff --git a/AlgebraicDataflowArchitectureModel/src/algorithms/DataTransferModelAnalyzer.java b/AlgebraicDataflowArchitectureModel/src/algorithms/DataTransferModelAnalyzer.java index f54aea4..a392b11 100644 --- a/AlgebraicDataflowArchitectureModel/src/algorithms/DataTransferModelAnalyzer.java +++ b/AlgebraicDataflowArchitectureModel/src/algorithms/DataTransferModelAnalyzer.java @@ -4,6 +4,7 @@ import java.util.Collection; import java.util.HashSet; import java.util.List; +import java.util.Set; import models.*; import models.algebra.*; @@ -75,12 +76,55 @@ * @return annotated data flow graph */ static public DataFlowGraph annotateWithSelectableDataTransferAttiribute(DataFlowGraph graph) { - List resNodes = new ArrayList<>(graph.getResourceNodes()); - // set push only attributes + HashSet unvisitedNodes = new HashSet<>(graph.getResourceNodes()); + // Turn push only for (Node resNode: graph.getResourceNodes()) { - if (resNodes.contains(resNode) && ((StoreAttribute) ((ResourceNode) resNode).getAttribute()).isNeeded()) { - resNodes.remove(resNode); - trackEdges(resNode, resNodes); + if (unvisitedNodes.contains(resNode) && ((StoreAttribute) ((ResourceNode) resNode).getAttribute()).isNeeded()) { + unvisitedNodes.remove(resNode); + trackEdgesBackwardForPush(resNode, unvisitedNodes); + } + } + // Turn push/pull only with respect to channel hierarchies. + while (!unvisitedNodes.isEmpty()) { + Node resNode = unvisitedNodes.iterator().next(); + unvisitedNodes.remove(resNode); + for (Edge chToRes : ((ResourceNode) resNode).getInEdges()) { + ChannelNode chNode = (ChannelNode) chToRes.getSource(); + // Should take into account the channel hierarchy. + boolean pullContained = false; + Set ancestorChannels = chNode.getAncestors(); + for (ChannelNode ancestorCh: ancestorChannels) { + for (Edge resToCh: ancestorCh.getInEdges()) { + ResourceNode srcResNode = (ResourceNode) resToCh.getSource(); + DataTransferChannel ch = (DataTransferChannel) ancestorCh.getChannel(); + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.isOutside()) { + PushPullAttribute ppat = new PushPullAttribute(); + ppat.addOption(PushPullValue.PULL); // To refer to outside resource. + ((DataFlowEdge) resToCh).setAttribute(ppat); + pullContained = true; + } else { + PushPullAttribute ppat = new PushPullAttribute(); + ppat.addOption(PushPullValue.PUSH); // For broadcasting transfer. + ((DataFlowEdge) resToCh).setAttribute(ppat); + unvisitedNodes.remove(srcResNode); + trackEdgesBackwardForPush(srcResNode, unvisitedNodes); + } + } + } + } + Set descendantChannels = chNode.getDescendants(); + for (ChannelNode descendantCh: descendantChannels) { + for (Edge resToCh: descendantCh.getInEdges()) { + PushPullAttribute ppat = new PushPullAttribute(); + ppat.addOption(PushPullValue.PULL); // For collecting transfer. + ((DataFlowEdge) resToCh).setAttribute(ppat); + pullContained = true; + } + } + if (pullContained) { + trackEdgesForwardForPull(resNode, unvisitedNodes); + } } } // set push/pull attributes to the remaining edges @@ -96,18 +140,57 @@ return graph; } - static private void trackEdges(Node resNode, List resNodes) { - // recursively set push only attributes to input side edges + static private void trackEdgesBackwardForPush(Node resNode, HashSet unvisitedNodes) { + // recursively turn push only backward in data-flow. for (Edge chToRes : ((ResourceNode) resNode).getInEdges()) { - Node chNode = chToRes.getSource(); - for (Edge resToCh : ((ChannelNode) chNode).getInEdges()) { + ChannelNode chNode = (ChannelNode) chToRes.getSource(); + // Should take into account the channel hierarchy. + Set ancestorChannels = chNode.getAncestors(); + Set descendantChannels = chNode.getDescendants(); + Set inEdges = new HashSet<>(); + inEdges.addAll(chNode.getInEdges()); + for (ChannelNode ancestorCh: ancestorChannels) { + inEdges.addAll(ancestorCh.getInEdges()); + } + for (ChannelNode descendantCh: descendantChannels) { + inEdges.addAll(descendantCh.getInEdges()); + } + for (Edge resToCh: inEdges) { PushPullAttribute ppat = new PushPullAttribute(); ppat.addOption(PushPullValue.PUSH); ((DataFlowEdge) resToCh).setAttribute(ppat); Node resNode2 = resToCh.getSource(); - if (resNodes.contains(resNode2)) { - resNodes.remove(resNode2); - trackEdges(resNode2, resNodes); + if (unvisitedNodes.contains(resNode2)) { + unvisitedNodes.remove(resNode2); + trackEdgesBackwardForPush(resNode2, unvisitedNodes); + } + } + } + } + + private static void trackEdgesForwardForPull(Node resNode, HashSet unvisitedNodes) { + // recursively turn pull only forward in data-flow. + for (Edge resToCh : ((ResourceNode) resNode).getOutEdges()) { + PushPullAttribute ppat = new PushPullAttribute(); + ppat.addOption(PushPullValue.PULL); + ((DataFlowEdge) resToCh).setAttribute(ppat); + ChannelNode chNode = (ChannelNode) resToCh.getDestination(); + // Should take into account the channel hierarchy. + Set ancestorChannels = chNode.getAncestors(); + Set descendantChannels = chNode.getDescendants(); + Set outEdges = new HashSet<>(); + outEdges.addAll(chNode.getOutEdges()); + for (ChannelNode ancestorCh: ancestorChannels) { + outEdges.addAll(ancestorCh.getOutEdges()); + } + for (ChannelNode descendantCh: descendantChannels) { + outEdges.addAll(descendantCh.getOutEdges()); + } + for (Edge chToRes: outEdges) { + Node resNode2 = chToRes.getDestination(); + if (unvisitedNodes.contains(resNode2)) { + unvisitedNodes.remove(resNode2); + trackEdgesForwardForPull(resNode2, unvisitedNodes); } } } diff --git a/AlgebraicDataflowArchitectureModel/src/algorithms/TypeInference.java b/AlgebraicDataflowArchitectureModel/src/algorithms/TypeInference.java index 45a82b9..f57b895 100644 --- a/AlgebraicDataflowArchitectureModel/src/algorithms/TypeInference.java +++ b/AlgebraicDataflowArchitectureModel/src/algorithms/TypeInference.java @@ -148,1058 +148,1106 @@ // 1. Collect type information from the architecture model. Collection channels = new HashSet<>(model.getInputChannels()); channels.addAll(model.getChannels()); - for (Channel c : channels) { - for (ChannelMember cm : c.getChannelMembers()) { - StateTransition st = cm.getStateTransition(); - ResourceHierarchy res = cm.getResource().getResourceHierarchy(); - - // 1.1 Group expressions by resources. - List identicalResources = resources.get(res); - if (identicalResources == null) { - identicalResources = new ArrayList<>(); - resources.put(res, identicalResources); - } - identicalResources.add(st.getCurStateExpression()); - expToResource.put(System.identityHashCode(st.getCurStateExpression()), identicalResources); - if (st.getNextStateExpression() != null) { - identicalResources.add(st.getNextStateExpression()); - expToResource.put(System.identityHashCode(st.getNextStateExpression()), identicalResources); - } - Map updatedExps = getUpdateSet(updateFromResource, identicalResources); - Type resType = res.getResourceStateType(); - Expression exp = st.getCurStateExpression(); - Type expType = getExpTypeIfUpdatable(resType, exp); - if (expType != null) { - res.setResourceStateType(expType); - for (Expression resExp : identicalResources) { - if (resExp != exp) { - if (resExp instanceof Variable && compareTypes(((Variable) resExp).getType(), expType)) { - ((Variable) resExp).setType(expType); - updatedExps.put(System.identityHashCode(resExp), resExp); - } else if (resExp instanceof Term && compareTypes(((Term) resExp).getType(), expType)) { - ((Term) resExp).setType(expType); - updatedExps.put(System.identityHashCode(resExp), resExp); - } + for (Channel ch : channels) { + // 1.1 Group expressions by resources. + IGroupExpressionsByResource groupExpressionsByResource = new IGroupExpressionsByResource() { + public void groupForChannel(Channel ch) { + for (ChannelMember cm : ch.getChannelMembers()) { + StateTransition st = cm.getStateTransition(); + ResourceHierarchy res = cm.getResource().getResourceHierarchy(); + List identicalResources = resources.get(res); + if (identicalResources == null) { + identicalResources = new ArrayList<>(); + resources.put(res, identicalResources); } - } - } else if (exp instanceof Variable) { - if (compareTypes(((Variable) exp).getType(), resType)) { - ((Variable) exp).setType(resType); - updatedExps.put(System.identityHashCode(exp), exp); - } - } else if (exp instanceof Term) { - if (compareTypes(((Term) exp).getType(), resType)) { - ((Term) exp).setType(resType); - updatedExps.put(System.identityHashCode(exp), exp); - } - } - resType = res.getResourceStateType(); - exp = st.getNextStateExpression(); - if (exp != null) { - expType = getExpTypeIfUpdatable(resType, exp); - if (expType != null) { - res.setResourceStateType(expType); - for (Expression resExp : identicalResources) { - if (resExp != exp) { - if (resExp instanceof Variable && compareTypes(((Variable) resExp).getType(), expType)) { - ((Variable) resExp).setType(expType); - updatedExps.put(System.identityHashCode(resExp), resExp); - } else if (resExp instanceof Term && compareTypes(((Term) resExp).getType(), expType)) { - ((Term) resExp).setType(expType); - updatedExps.put(System.identityHashCode(resExp), resExp); - } - } + identicalResources.add(st.getCurStateExpression()); + expToResource.put(System.identityHashCode(st.getCurStateExpression()), identicalResources); + if (st.getNextStateExpression() != null) { + identicalResources.add(st.getNextStateExpression()); + expToResource.put(System.identityHashCode(st.getNextStateExpression()), identicalResources); } - } else if (exp instanceof Variable) { - if (compareTypes(((Variable) exp).getType(), resType)) { - ((Variable) exp).setType(resType); - updatedExps.put(System.identityHashCode(exp), exp); - } - } else if (exp instanceof Term) { - if (compareTypes(((Term) exp).getType(), resType)) { - ((Term) exp).setType(resType); - updatedExps.put(System.identityHashCode(exp), exp); - } - } - } - - // 1.2 Group expressions by variable. - Map> locals = new HashMap<>(); - Map localTypes = new HashMap<>(); - List allVariables = new ArrayList<>(); - allVariables.addAll(st.getCurStateExpression().getVariables().values()); - allVariables.addAll(st.getMessageExpression().getVariables().values()); - if (st.getNextStateExpression() != null) { - allVariables.addAll(st.getNextStateExpression().getVariables().values()); - } - for (Selector s: c.getSelectors()) { // add channel selectors - if (s.getExpression() instanceof Variable) { - allVariables.add((Variable) s.getExpression()); - } - } - ResourcePath resPath = cm.getResource(); - for (Expression param: resPath.getPathParams()) { // add path parameters - if (param instanceof Variable) { - allVariables.add((Variable) param); - } else if (param instanceof Term) { - allVariables.addAll(((Term) param).getVariables().values()); - } - } - for (Variable var : allVariables) { - List sameVariable = locals.get(var.getName()); - if (sameVariable == null) { - sameVariable = new ArrayList<>(); - sameVariable.add(var); - expToVariable.put(System.identityHashCode(var), sameVariable); - locals.put(var.getName(), sameVariable); - localTypes.put(var.getName(), var.getType()); - } else { - sameVariable.add(var); - expToVariable.put(System.identityHashCode(var), sameVariable); - Type varType = localTypes.get(var.getName()); - Map updatedVars = getUpdateSet(updateFromVariable, sameVariable); - if (compareTypes(varType, var.getType())) { - localTypes.put(var.getName(), var.getType()); - for (Expression v : sameVariable) { - if (v != var) { - if (compareTypes(((Variable) v).getType(), var.getType())) { - ((Variable) v).setType(var.getType()); - updatedVars.put(System.identityHashCode(v), v); + Map updatedExps = getUpdateSet(updateFromResource, identicalResources); + Type resType = res.getResourceStateType(); + Expression exp = st.getCurStateExpression(); + Type expType = getExpTypeIfUpdatable(resType, exp); + if (expType != null) { + res.setResourceStateType(expType); + for (Expression resExp : identicalResources) { + if (resExp != exp) { + if (resExp instanceof Variable && compareTypes(((Variable) resExp).getType(), expType)) { + ((Variable) resExp).setType(expType); + updatedExps.put(System.identityHashCode(resExp), resExp); + } else if (resExp instanceof Term && compareTypes(((Term) resExp).getType(), expType)) { + ((Term) resExp).setType(expType); + updatedExps.put(System.identityHashCode(resExp), resExp); } } } - } else if (compareTypes(var.getType(), varType)) { - var.setType(varType); - updatedVars.put(System.identityHashCode(var), var); - } - } - } - for (String varName : locals.keySet()) { - variables.put(System.identityHashCode(locals.get(varName)), localTypes.get(varName)); - } - - // 1.3 Group expressions by message. - Expression message = st.getMessageExpression(); - if (message instanceof Variable) { - Type msgType = ((Variable) message).getType(); - Map, Type>> msgTypeMap = messages.get(c); - if (msgTypeMap == null) { - msgTypeMap = new HashMap<>(); - messages.put(c, msgTypeMap); - } - Map.Entry, Type> typeAndExps = msgTypeMap.get(0); - if (typeAndExps == null) { - List exps = new ArrayList<>(); - exps.add(message); - typeAndExps = new AbstractMap.SimpleEntry<>(exps, msgType); - msgTypeMap.put(0, typeAndExps); - expToMessage.put(System.identityHashCode(message), exps); - } else { - typeAndExps.getKey().add(message); - expToMessage.put(System.identityHashCode(message), typeAndExps.getKey()); - Map updateExps = getUpdateSet(updateFromMessage, typeAndExps.getKey()); - if (compareTypes(typeAndExps.getValue(), msgType)) { - typeAndExps.setValue(msgType); - for (Expression e : typeAndExps.getKey()) { - if (e != message) { - if (e instanceof Variable) { - ((Variable) e).setType(msgType); - updateExps.put(System.identityHashCode(e), e); - } - } + } else if (exp instanceof Variable) { + if (compareTypes(((Variable) exp).getType(), resType)) { + ((Variable) exp).setType(resType); + updatedExps.put(System.identityHashCode(exp), exp); } - } else if (compareTypes(msgType, typeAndExps.getValue())) { - ((Variable) message).setType(typeAndExps.getValue()); - updateExps.put(System.identityHashCode(message), message); + } else if (exp instanceof Term) { + if (compareTypes(((Term) exp).getType(), resType)) { + ((Term) exp).setType(resType); + updatedExps.put(System.identityHashCode(exp), exp); + } } - } - } else if (message instanceof Term) { - Map, Type>> msgTypeMap = messages.get(c); - if (msgTypeMap == null) { - msgTypeMap = new HashMap<>(); - messages.put(c, msgTypeMap); - } - for (int i = 0; i < ((Term) message).getArity(); i++) { - Expression arg = ((Term) message).getChild(i); - Type argType = null; - if (arg instanceof Variable) { - argType = ((Variable) arg).getType(); - } else if (arg instanceof Term) { - argType = ((Term) arg).getType(); - } else { - continue; - } - Map.Entry, Type> typeAndExps = msgTypeMap.get(i); - if (typeAndExps == null) { - List exps = new ArrayList<>(); - exps.add(arg); - typeAndExps = new AbstractMap.SimpleEntry<>(exps, argType); - msgTypeMap.put(i, typeAndExps); - expToMessage.put(System.identityHashCode(arg), exps); - } else { - typeAndExps.getKey().add(arg); - expToMessage.put(System.identityHashCode(arg), typeAndExps.getKey()); - Map updateExps = getUpdateSet(updateFromMessage, typeAndExps.getKey()); - if (compareTypes(typeAndExps.getValue(), argType)) { - typeAndExps.setValue(argType); - for (Expression e : typeAndExps.getKey()) { - if (e != arg) { - if (e instanceof Variable) { - ((Variable) e).setType(argType); - updateExps.put(System.identityHashCode(e), e); + resType = res.getResourceStateType(); + exp = st.getNextStateExpression(); + if (exp != null) { + expType = getExpTypeIfUpdatable(resType, exp); + if (expType != null) { + res.setResourceStateType(expType); + for (Expression resExp : identicalResources) { + if (resExp != exp) { + if (resExp instanceof Variable && compareTypes(((Variable) resExp).getType(), expType)) { + ((Variable) resExp).setType(expType); + updatedExps.put(System.identityHashCode(resExp), resExp); + } else if (resExp instanceof Term && compareTypes(((Term) resExp).getType(), expType)) { + ((Term) resExp).setType(expType); + updatedExps.put(System.identityHashCode(resExp), resExp); } } } - } else if (compareTypes(argType, typeAndExps.getValue())) { - if (arg instanceof Variable) { - ((Variable) arg).setType(typeAndExps.getValue()); - updateExps.put(System.identityHashCode(arg), arg); - } else if (arg instanceof Term) { - ((Term) arg).setType(typeAndExps.getValue()); - updateExps.put(System.identityHashCode(arg), arg); + } else if (exp instanceof Variable) { + if (compareTypes(((Variable) exp).getType(), resType)) { + ((Variable) exp).setType(resType); + updatedExps.put(System.identityHashCode(exp), exp); + } + } else if (exp instanceof Term) { + if (compareTypes(((Term) exp).getType(), resType)) { + ((Term) exp).setType(resType); + updatedExps.put(System.identityHashCode(exp), exp); } } } } - } - - // 1.4 Extract constraints on expressions in each term. - List terms = new ArrayList<>(); - if (st.getCurStateExpression() instanceof Term) { - Map subTerms = ((Term) st.getCurStateExpression()).getSubTerms(Term.class); - terms.addAll(subTerms.values()); - } - if (st.getMessageExpression() instanceof Term) { - Map subTerms = ((Term) st.getMessageExpression()).getSubTerms(Term.class); - terms.addAll(subTerms.values()); - } - if (st.getNextStateExpression() != null && st.getNextStateExpression() instanceof Term) { - Map subTerms = ((Term) st.getNextStateExpression()).getSubTerms(Term.class); - terms.addAll(subTerms.values()); - } - for (Term t : terms) { - Symbol symbol = t.getSymbol(); - if (symbol.equals(DataConstraintModel.cons) || symbol.equals(DataConstraintModel.set) || symbol.equals(DataConstraintModel.append)) { - // If the root symbol of the term is cons or set. - List consExps = new ArrayList<>(); - consExps.add(t); // list term - updateExpressionBelonging(expToConsOrSet, t, consExps); - if (symbol.equals(DataConstraintModel.cons)) { - // If the root symbol of the term is cons. - for (Expression e : t.getChildren()) { - consExps.add(e); - updateExpressionBelonging(expToConsOrSet, e, consExps); - } - } else if (symbol.equals(DataConstraintModel.append)) { - // If the root symbol of the term is append. - Expression e = t.getChildren().get(1); - consExps.add(e); // list element - updateExpressionBelonging(expToConsOrSet, e, consExps); - e = t.getChildren().get(0); - consExps.add(e); // list argument - updateExpressionBelonging(expToConsOrSet, e, consExps); - } else { - // If the root symbol of the term is set. - Expression e = t.getChildren().get(2); - consExps.add(e); // list element - updateExpressionBelonging(expToConsOrSet, e, consExps); - e = t.getChildren().get(0); - consExps.add(e); // list argument - updateExpressionBelonging(expToConsOrSet, e, consExps); - } - Type newType = getExpTypeIfUpdatable(t.getType(), consExps.get(2)); - if (newType != null) { - // If the type of the 2nd argument of cons (1st argument of set/append) is more concrete than the type of the term. - t.setType(newType); - Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); - updateCons.put(System.identityHashCode(t), t); - } else { - Type arg2Type = null; - if (consExps.get(2) != null && consExps.get(2) instanceof Variable) { - arg2Type = ((Variable) consExps.get(2)).getType(); - if (compareTypes(arg2Type, t.getType())) { - // If the type of the term is more concrete than the type of the 2nd argument of cons (1st argument of set/append). - ((Variable) consExps.get(2)).setType(t.getType()); - Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); - updateCons.put(System.identityHashCode(consExps.get(2)), consExps.get(2)); - } - } else if (consExps.get(2) != null && consExps.get(2) instanceof Term) { - arg2Type = ((Term) consExps.get(2)).getType(); - if (compareTypes(arg2Type, t.getType())) { - // If the type of the term is more concrete than the type of the 2nd argument of cons (1st argument of set/append). - ((Term) consExps.get(2)).setType(t.getType()); - Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); - updateCons.put(System.identityHashCode(consExps.get(2)), consExps.get(2)); - } - } - } - Type newCompType = getExpTypeIfUpdatable(listComponentTypes.get(t.getType()), consExps.get(1)); - if (newCompType != null) { - // If the type of the 1st argument of cons (3rd argument of set) is more concrete than the type of list component. - Type newListType = listTypes.get(newCompType); - if (newListType == null) { - // Create new list type. - newListType = createNewListType(newCompType, DataConstraintModel.typeList); - } - t.setType(newListType); - Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); - updateCons.put(System.identityHashCode(t), t); - if (consExps.get(2) != null && consExps.get(2) instanceof Variable) { - ((Variable) consExps.get(2)).setType(newListType); - updateCons.put(System.identityHashCode(consExps.get(2)), consExps.get(2)); - } else if (consExps.get(2) != null && consExps.get(2) instanceof Term) { - ((Term) consExps.get(2)).setType(newListType); - updateCons.put(System.identityHashCode(consExps.get(2)), consExps.get(2)); - } - } - consOrSet.put(System.identityHashCode(consExps), t.getType()); - } else if (symbol.equals(DataConstraintModel.head) || symbol.equals(DataConstraintModel.get)) { - // If the root symbol of the term is head or get. - List consExps = new ArrayList<>(); - Expression e = t.getChildren().get(0); - consExps.add(e); // list argument - updateExpressionBelonging(expToConsOrSet, e, consExps); - consExps.add(t); // list's component - updateExpressionBelonging(expToConsOrSet, t, consExps); - consExps.add(null); - Type listType = listTypes.get(t.getType()); - if (listType == null && t.getType() != null) { - // Create a new list type. - listType = createNewListType(t.getType(), DataConstraintModel.typeList); - } - Type newListType = getExpTypeIfUpdatable(listType, consExps.get(0)); - if (newListType != null) { - // If the type of the component of the 1st argument is more concrete than the type of the term. - Type newCompType = listComponentTypes.get(newListType); - if (newCompType != null) { - t.setType(newCompType); - Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); - updateCons.put(System.identityHashCode(t), t); - } - consOrSet.put(System.identityHashCode(consExps), newListType); - } else { - // If the type of the term is more concrete than the type of the component of the 1st argument. - if (consExps.get(0) != null && consExps.get(0) instanceof Variable) { - ((Variable) consExps.get(0)).setType(listType); - Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); - updateCons.put(System.identityHashCode(consExps.get(0)), consExps.get(0)); - } else if (consExps.get(0) != null && consExps.get(0) instanceof Term) { - ((Term) consExps.get(0)).setType(listType); - Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); - updateCons.put(System.identityHashCode(consExps.get(0)), consExps.get(0)); - } - consOrSet.put(System.identityHashCode(consExps), listType); - } - } else if (symbol.equals(DataConstraintModel.tail)) { - // If the root symbol of the term is tail. - List consExps = new ArrayList<>(); - consExps.add(t); // list term - updateExpressionBelonging(expToConsOrSet, t, consExps); - consExps.add(null); // list's component - Expression e = t.getChildren().get(0); - consExps.add(e); // list argument - updateExpressionBelonging(expToConsOrSet, e, consExps); - Type newType = getExpTypeIfUpdatable(t.getType(), consExps.get(2)); - if (newType != null) { - // If the type of the argument is more concrete than the type of the term. - t.setType(newType); - Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); - updateCons.put(System.identityHashCode(t), t); - } else { - Type argType = null; - if (consExps.get(2) != null && consExps.get(2) instanceof Variable) { - argType = ((Variable) consExps.get(2)).getType(); - if (compareTypes(argType, t.getType())) { - // If the type of the term is more concrete than the type of the argument. - ((Variable) consExps.get(2)).setType(t.getType()); - Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); - updateCons.put(System.identityHashCode(consExps.get(2)), consExps.get(2)); - } - } else if (consExps.get(2) != null && consExps.get(2) instanceof Term) { - argType = ((Term) consExps.get(2)).getType(); - if (compareTypes(argType, t.getType())) { - // If the type of the term is more concrete than the type of the argument. - ((Term) consExps.get(2)).setType(t.getType()); - Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); - updateCons.put(System.identityHashCode(consExps.get(2)), consExps.get(2)); - } - } - } - consOrSet.put(System.identityHashCode(consExps), t.getType()); - } else if (symbol.equals(DataConstraintModel.tuple)) { - // If the root symbol of the term is tuple. - List tupleExps = new ArrayList<>(); - List newArgTypesList = new ArrayList<>(); - tupleExps.add(t); // tuple term - updateExpressionBelonging(expToTuple, t, tupleExps); - for (Expression e : t.getChildren()) { - tupleExps.add(e); // tuple's component - updateExpressionBelonging(expToTuple, e, tupleExps); - if (e instanceof Variable) { - newArgTypesList.add(((Variable) e).getType()); - } else if (e instanceof Term) { - newArgTypesList.add(((Term) e).getType()); - } else { - newArgTypesList.add(null); - } - } - if (t.getType() == DataConstraintModel.typeTuple) { - Type newTupleType = tupleTypes.get(newArgTypesList); - if (newTupleType == null) { - // Create new tuple type; - newTupleType = createNewTupleType(newArgTypesList, DataConstraintModel.typeTuple); - } - // Update the type of the tuple term and record the updated expression. - t.setType(newTupleType); - Map updateExps = getUpdateSet(updateFromTuple, tupleExps); - updateExps.put(System.identityHashCode(t), t); - } - tuple.put(System.identityHashCode(tupleExps), t.getType()); - } else if (symbol.equals(DataConstraintModel.pair)) { - // If the root symbol of the term is pair. - List pairExps = new ArrayList<>(); - pairExps.add(t); // pair - updateExpressionBelonging(expToPair, t, pairExps); - if (t.getType() == DataConstraintModel.typePair) { - for (Expression e : t.getChildren()) { - pairExps.add(e); // left/right - updateExpressionBelonging(expToPair, e, pairExps); - Type newArgType = null; - if (e instanceof Variable) { - newArgType = (((Variable) e).getType()); - - } else if (e instanceof Term) { - newArgType = (((Term) e).getType()); - } - - if (newArgType != null) { - Type newPairType = pairTypes.get(newArgType); - if (newPairType != null) { - t.setType(newPairType); - Map updateExps = getUpdateSet(updateFromPair, pairExps); - updateExps.put(System.identityHashCode(t), t); - } - } - } - pair.put(System.identityHashCode(pairExps), t.getType()); - - } - } else if (symbol.equals(DataConstraintModel.fst)) { - // If the root symbol of the term is fst. - List tupleExps = new ArrayList<>(); - Expression arg = t.getChildren().get(0); - tupleExps.add(arg); // tuple argument - updateExpressionBelonging(expToTuple, arg, tupleExps); - tupleExps.add(t); // first component - updateExpressionBelonging(expToTuple, t, tupleExps); - tupleExps.add(null); // second component - Type argType = null; - if (arg instanceof Variable) { - argType = ((Variable) arg).getType(); - } else if (arg instanceof Term) { - argType = ((Term) arg).getType(); - } - Type newTupleType = DataConstraintModel.typeTuple; - if (argType == DataConstraintModel.typeTuple && t.getType() != null) { - List newCompTypeList = new ArrayList<>(); - newCompTypeList.add(t.getType()); - newCompTypeList.add(null); - newTupleType = tupleTypes.get(newCompTypeList); - if (newTupleType == null) { - // Create new tuple type; - newTupleType = createNewTupleType(newCompTypeList, DataConstraintModel.typeTuple); - } - } - if (argType != newTupleType && newTupleType != null) { - // Update the type of the tuple argument and record the updated expression. - if (arg instanceof Variable) { - ((Variable) arg).setType(newTupleType); - argType = newTupleType; - } else if (arg instanceof Term) { - ((Term) arg).setType(newTupleType); - argType = newTupleType; - } - Map updateExps = getUpdateSet(updateFromTuple, tupleExps); - updateExps.put(System.identityHashCode(arg), arg); - } - tuple.put(System.identityHashCode(tupleExps), argType); - } else if (symbol.equals(DataConstraintModel.snd)) { - // If the root symbol of the term is snd. - List tupleExps = new ArrayList<>(); - Expression arg = t.getChildren().get(0); - tupleExps.add(arg); // tuple argument - updateExpressionBelonging(expToTuple, arg, tupleExps); - tupleExps.add(null); // first component - tupleExps.add(t); // second component - updateExpressionBelonging(expToTuple, t, tupleExps); - Type argType = null; - if (arg instanceof Variable) { - argType = ((Variable) arg).getType(); - } else if (arg instanceof Term) { - argType = ((Term) arg).getType(); - } - Type newTupleType = DataConstraintModel.typeTuple; - if (argType == DataConstraintModel.typeTuple && t.getType() != null) { - List newCompTypeList = new ArrayList<>(); - newCompTypeList.add(null); - if (DataConstraintModel.typeTuple.isAncestorOf(t.getType())) { - List sndTypes = tupleComponentTypes.get(t.getType()); - if (sndTypes != null) { - for (Type t2: sndTypes) { - newCompTypeList.add(t2); - } - } else { - newCompTypeList.add(t.getType()); - } - } else { - newCompTypeList.add(t.getType()); - } - newTupleType = tupleTypes.get(newCompTypeList); - if (newTupleType == null) { - // Create new tuple type; - newTupleType = createNewTupleType(newCompTypeList, DataConstraintModel.typeTuple); - } - } - if (argType != newTupleType && newTupleType != null) { - // Update the type of the tuple argument and record the updated expression. - if (arg instanceof Variable) { - ((Variable) arg).setType(newTupleType); - argType = newTupleType; - } else if (arg instanceof Term) { - ((Term) arg).setType(newTupleType); - argType = newTupleType; - } - Map updateExps = getUpdateSet(updateFromTuple, tupleExps); - updateExps.put(System.identityHashCode(arg), arg); - } - tuple.put(System.identityHashCode(tupleExps), argType); - } else if (symbol.equals(DataConstraintModel.left)) { - // If the root symbol of the term is left. - List pairExps = new ArrayList<>(); - Expression arg = t.getChildren().get(0); - pairExps.add(arg); // pair - updateExpressionBelonging(expToPair, arg, pairExps); - pairExps.add(t); // left - updateExpressionBelonging(expToPair, t, pairExps); - pairExps.add(null); // right - Type argType = null; - if (arg instanceof Variable) { - argType = ((Variable) arg).getType(); - } else if (arg instanceof Term) { - argType = ((Term) arg).getType(); - } - Type newPairType = DataConstraintModel.typePair; - if (argType == DataConstraintModel.typePair && t.getType() != null) { - List newCompTypeList = new ArrayList<>(); - newCompTypeList.add(t.getType()); - newCompTypeList.add(null); - newPairType = pairTypes.get(newCompTypeList); - if (newPairType == null) { - // Create new tuple type; - newPairType = createNewTupleType(newCompTypeList, DataConstraintModel.typePair); - } - } - if (argType != newPairType && newPairType != null) { - if (arg instanceof Variable) { - ((Variable) arg).setType(newPairType); - argType = newPairType; - } else if (arg instanceof Term) { - ((Term) arg).setType(newPairType); - argType = newPairType; - } - Map updateExps = getUpdateSet(updateFromPair, pairExps); - updateExps.put(System.identityHashCode(arg), arg); - } - pair.put(System.identityHashCode(pairExps), argType); - } else if (symbol.equals(DataConstraintModel.right)) { - // If the root symbol of the term is right. - List pairExps = new ArrayList<>(); - Expression arg = t.getChildren().get(0); - pairExps.add(arg); // pair - updateExpressionBelonging(expToPair, arg, pairExps); - pairExps.add(null); // left - pairExps.add(t); // right - updateExpressionBelonging(expToPair, t, pairExps); - Type argType = null; - if (arg instanceof Variable) { - argType = ((Variable) arg).getType(); - } else if (arg instanceof Term) { - argType = ((Term) arg).getType(); - } - Type newPairType = DataConstraintModel.typePair; - if (argType == DataConstraintModel.typePair && t.getType() != null) { - List newCompTypeList = new ArrayList<>(); - newCompTypeList.add(null); - newCompTypeList.add(t.getType()); - newPairType = pairTypes.get(newCompTypeList); - if (newPairType == null) { - // Create new tuple type; - newPairType = createNewTupleType(newCompTypeList, DataConstraintModel.typePair); - } - } - if (argType != newPairType && newPairType != null) { - if (arg instanceof Variable) { - ((Variable) arg).setType(newPairType); - argType = newPairType; - } else if (arg instanceof Term) { - ((Term) arg).setType(newPairType); - argType = newPairType; - } - Map updateExps = getUpdateSet(updateFromPair, pairExps); - updateExps.put(System.identityHashCode(arg), arg); - } - pair.put(System.identityHashCode(pairExps), argType); - } else if (symbol.equals(DataConstraintModel.lookup)) { - // If the root symbol of the term is lookup. - List mapExps = new ArrayList<>(); - Expression arg1 = t.getChildren().get(0); // map - mapExps.add(arg1); - updateExpressionBelonging(expToMap, arg1, mapExps); - Expression arg2 = t.getChildren().get(1); // key - mapExps.add(arg2); - updateExpressionBelonging(expToMap, arg2, mapExps); - mapExps.add(t); // value - updateExpressionBelonging(expToMap, t, mapExps); - Type arg1Type = null; - if (arg1 instanceof Variable) { - arg1Type = ((Variable) arg1).getType(); - } else if (arg1 instanceof Term) { - arg1Type = ((Term) arg1).getType(); - } - List newCompTypeList = new ArrayList<>(); - if (arg2 instanceof Variable) { - newCompTypeList.add(((Variable) arg2).getType()); - } else if (arg2 instanceof Term) { - newCompTypeList.add(((Term) arg2).getType()); - } else { - newCompTypeList.add(null); - } - newCompTypeList.add(t.getType()); - if (arg1Type == DataConstraintModel.typeMap || arg1Type == null) { - Type newMapType = mapTypes.get(newCompTypeList); - if (newMapType == null) { - // Create new tuple type; - newMapType = createNewMapType(newCompTypeList, DataConstraintModel.typeMap); - } - // Update the type of the map argument and record the updated expression. - if (arg1 instanceof Variable) { - ((Variable) arg1).setType(newMapType); - arg1Type = newMapType; - } else if (arg1 instanceof Term) { - ((Term) arg1).setType(newMapType); - arg1Type = newMapType; - } - Map updateExps = getUpdateSet(updateFromMap, mapExps); - updateExps.put(System.identityHashCode(arg1), arg1); - } - map.put(System.identityHashCode(mapExps), arg1Type); - } else if (symbol.equals(DataConstraintModel.insert)) { - // If the root symbol of the term is insert. - List mapExps = new ArrayList<>(); - mapExps.add(t); // map - updateExpressionBelonging(expToMap, t, mapExps); - Expression arg1 = t.getChildren().get(1); // key - mapExps.add(arg1); - updateExpressionBelonging(expToMap, arg1, mapExps); - Expression arg2 = t.getChildren().get(2); // value - mapExps.add(arg2); - updateExpressionBelonging(expToMap, arg2, mapExps); - Expression arg0 = t.getChildren().get(0); // map - mapExps.add(arg0); - updateExpressionBelonging(expToMap, arg0, mapExps); - Type termType = t.getType(); - List newCompTypeList = new ArrayList<>(); - if (arg1 instanceof Variable) { - newCompTypeList.add(((Variable) arg1).getType()); - } else if (arg1 instanceof Term) { - newCompTypeList.add(((Term) arg1).getType()); - } else { - newCompTypeList.add(null); - } - if (arg2 instanceof Variable) { - newCompTypeList.add(((Variable) arg2).getType()); - } else if (arg2 instanceof Term) { - newCompTypeList.add(((Term) arg2).getType()); - } else { - newCompTypeList.add(null); - } - Type newTermType = getExpTypeIfUpdatable(termType, mapExps.get(3)); - if (newTermType != null) { - // If the type of the 1st argument of insert is more concrete than the type of the term. - t.setType(newTermType); - termType = newTermType; - Map updateExps = getUpdateSet(updateFromMap, mapExps); - updateExps.put(System.identityHashCode(t), t); - } else { - Type arg3Type = null; - if (mapExps.get(3) != null && mapExps.get(3) instanceof Variable) { - arg3Type = ((Variable) mapExps.get(3)).getType(); - if (compareTypes(arg3Type, t.getType())) { - // If the type of the term is more concrete than the type of the 1st argument of insert. - ((Variable) mapExps.get(3)).setType(t.getType()); - Map updateExps = getUpdateSet(updateFromMap, mapExps); - updateExps.put(System.identityHashCode(mapExps.get(3)), mapExps.get(3)); - } - } else if (mapExps.get(3) != null && mapExps.get(3) instanceof Term) { - arg3Type = ((Term) mapExps.get(3)).getType(); - if (compareTypes(arg3Type, t.getType())) { - // If the type of the term is more concrete than the type of the 1st argument of insert. - ((Term) mapExps.get(3)).setType(t.getType()); - Map updateExps = getUpdateSet(updateFromMap, mapExps); - updateExps.put(System.identityHashCode(mapExps.get(3)), mapExps.get(3)); - } - } - } - if (termType == DataConstraintModel.typeMap || termType == null) { - Type newMapType = mapTypes.get(newCompTypeList); - if (newMapType == null) { - // Create new tuple type; - newMapType = createNewMapType(newCompTypeList, DataConstraintModel.typeMap); - } - // Update the type of the map term and record the updated expression. - t.setType(newMapType); - Map updateExps = getUpdateSet(updateFromMap, mapExps); - updateExps.put(System.identityHashCode(t), t); - if (mapExps.get(3) != null && mapExps.get(3) instanceof Variable) { - ((Variable) mapExps.get(3)).setType(newMapType); - updateExps.put(System.identityHashCode(mapExps.get(3)), mapExps.get(3)); - } else if (mapExps.get(3) != null && mapExps.get(3) instanceof Term) { - ((Term) mapExps.get(3)).setType(newMapType); - updateExps.put(System.identityHashCode(mapExps.get(3)), mapExps.get(3)); - } - termType = newMapType; - } - map.put(System.identityHashCode(mapExps), termType); - } else if (symbol.equals(DataConstraintModel.addMember)) { - // If the root symbol of the term is addMember (addMember(json, key, value)). - List dotExps = new ArrayList<>(); - Expression jsonArg = t.getChildren().get(0); - Expression keyArg = t.getChildren().get(1); - Expression valueArg = t.getChildren().get(2); - dotExps.add(t); // json - updateExpressionBelonging(expToJson, t, dotExps); - dotExps.add(keyArg); // key - updateExpressionBelonging(expToJson, keyArg, dotExps); - dotExps.add(valueArg); // value - updateExpressionBelonging(expToJson, valueArg, dotExps); - dotExps.add(jsonArg); // json - updateExpressionBelonging(expToJson, jsonArg, dotExps); - Type jsonType = t.getType(); - Type valueType = null; - if (valueArg instanceof Variable) { - valueType = ((Variable) valueArg).getType(); - } else if (valueArg instanceof Term) { - valueType = ((Term) valueArg).getType(); - } - String keyName = null; - if (keyArg instanceof Constant) { - keyName = ((Constant) keyArg).getSymbol().getName(); - } - Type jsonArgType = null; - if (jsonArg instanceof Variable) { - jsonArgType = ((Variable) jsonArg).getType(); - } else if (jsonArg instanceof Term) { - jsonArgType = ((Term) jsonArg).getType(); - } - Type newJsonType = DataConstraintModel.typeJson; - if (jsonType == DataConstraintModel.typeJson && jsonArgType != null && keyName != null) { - Map newMemberTypes = new HashMap<>(((JsonType) jsonArgType).getMemberTypes()); - newMemberTypes.put(keyName, valueType); - newJsonType = jsonTypes.get(newMemberTypes); - if (newJsonType == null) { - // Create new json type; - newJsonType = createNewJsonType(newMemberTypes, DataConstraintModel.typeJson); - } - } - if (jsonType != newJsonType && newJsonType != null && !newJsonType.isAncestorOf(jsonType)) { - // Update the type of the json term and record the updated expression. - t.setType(newJsonType); - jsonType = newJsonType; - Map updateExps = getUpdateSet(updateFromJson, dotExps); - updateExps.put(System.identityHashCode(t), t); - } - json.put(System.identityHashCode(dotExps), jsonType); - } else if (symbol.equals(DataConstraintModel.dot)) { - // If the root symbol of the term is dot (json.property). - List dotExps = new ArrayList<>(); - Expression jsonArg = t.getChildren().get(0); - Expression keyArg = t.getChildren().get(1); - dotExps.add(jsonArg); // json - updateExpressionBelonging(expToJson, jsonArg, dotExps); - dotExps.add(keyArg); // key - updateExpressionBelonging(expToJson, keyArg, dotExps); - dotExps.add(t); // value - updateExpressionBelonging(expToJson, t, dotExps); - dotExps.add(null); // json - Type jsonType = null; - if (jsonArg instanceof Variable) { - jsonType = ((Variable) jsonArg).getType(); - } else if (jsonArg instanceof Term) { - jsonType = ((Term) jsonArg).getType(); - } - String keyName = null; - if (keyArg instanceof Constant) { - keyName = ((Constant) keyArg).getSymbol().getName(); - } - Type newJsonType = DataConstraintModel.typeJson; - if (jsonType == DataConstraintModel.typeJson && t.getType() != null && keyName != null) { - Map newMemberTypes = new HashMap<>(); - newMemberTypes.put(keyName, t.getType()); - newJsonType = jsonTypes.get(newMemberTypes); - if (newJsonType == null) { - // Create new json type; - newJsonType = createNewJsonType(newMemberTypes, DataConstraintModel.typeJson); - } - } - if (jsonType != newJsonType && newJsonType != null && !newJsonType.isAncestorOf(jsonType)) { - // Update the type of the json argument and record the updated expression. - if (jsonArg instanceof Variable) { - ((Variable) jsonArg).setType(newJsonType); - jsonType = newJsonType; - } else if (jsonArg instanceof Term) { - ((Term) jsonArg).setType(newJsonType); - jsonType = newJsonType; - } - Map updateExps = getUpdateSet(updateFromJson, dotExps); - updateExps.put(System.identityHashCode(jsonArg), jsonArg); - } - json.put(System.identityHashCode(dotExps), jsonType); - } else if (symbol.equals(DataConstraintModel.dotParam)) { - // If the root symbol of the term is dot (json.{param}). - List dotExps = new ArrayList<>(); - Expression jsonArg = t.getChildren().get(0); - Expression keyArg = t.getChildren().get(1); - dotExps.add(jsonArg); // json (list/map) - updateExpressionBelonging(expToJson, jsonArg, dotExps); - dotExps.add(null); // key - dotExps.add(t); // value - updateExpressionBelonging(expToJson, t, dotExps); - dotExps.add(null); // json - Type jsonType = null; - if (jsonArg instanceof Variable) { - jsonType = ((Variable) jsonArg).getType(); - } else if (jsonArg instanceof Term) { - jsonType = ((Term) jsonArg).getType(); - } - Type keyType = null; - if (keyArg instanceof Variable) { - keyType = ((Variable) keyArg).getType(); - } else if (keyArg instanceof Term) { - keyType = ((Term) keyArg).getType(); - } - Type newJsonType = null; - if (keyType == DataConstraintModel.typeInt) { - newJsonType = DataConstraintModel.typeList; - } else if (keyType == DataConstraintModel.typeString) { - newJsonType = DataConstraintModel.typeMap; - } - if (t.getType() != null) { - if ((jsonType == DataConstraintModel.typeList)) { - newJsonType = listTypes.get(t.getType()); - if (newJsonType == null) { - // Create new list type; - newJsonType = createNewListType(t.getType(), DataConstraintModel.typeList); - } - } else if (jsonType == DataConstraintModel.typeMap) { - List keyValueTypes = Arrays.asList(new Type[] {DataConstraintModel.typeString, t.getType()}); - newJsonType = mapTypes.get(keyValueTypes); - if (newJsonType == null) { - // Create new map type; - newJsonType = createNewMapType(keyValueTypes, DataConstraintModel.typeMap); - } - } - } - if (jsonType != newJsonType && newJsonType != null && !newJsonType.isAncestorOf(jsonType)) { - // Update the type of the json argument and record the updated expression. - if (jsonArg instanceof Variable) { - ((Variable) jsonArg).setType(newJsonType); - jsonType = newJsonType; - } else if (jsonArg instanceof Term) { - ((Term) jsonArg).setType(newJsonType); - jsonType = newJsonType; - } - Map updateExps = getUpdateSet(updateFromJson, dotExps); - updateExps.put(System.identityHashCode(jsonArg), jsonArg); - } - json.put(System.identityHashCode(dotExps), jsonType); - } else if (symbol.equals(DataConstraintModel.cond)) { - // If the root symbol of the term is if function. - Expression c1 = t.getChild(1); - Expression c2 = t.getChild(2); - List condTerms = new ArrayList<>(); - condTerms.add(t); - condTerms.add(c1); - condTerms.add(c2); - expToVariable.put(System.identityHashCode(t), condTerms); - expToVariable.put(System.identityHashCode(c1), condTerms); - expToVariable.put(System.identityHashCode(c2), condTerms); - Type condType = t.getType(); - Map updatedVars = getUpdateSet(updateFromVariable, condTerms); - Type child1Type = getExpTypeIfUpdatable(condType, c1); - if (child1Type != null) { - condType = child1Type; - t.setType(child1Type); - updatedVars.put(System.identityHashCode(t), t); - } else { - if (c1 instanceof Variable && compareTypes(((Variable) c1).getType(), condType)) { - ((Variable) c1).setType(condType); - updatedVars.put(System.identityHashCode(c1), c1); - } else if (c1 instanceof Term && compareTypes(((Term) c1).getType(), condType)) { - ((Term) c1).setType(condType); - updatedVars.put(System.identityHashCode(c1), c1); - } - } - Type child2Type = getExpTypeIfUpdatable(condType, c2); - if (child2Type != null) { - condType = child2Type; - t.setType(child2Type); - updatedVars.put(System.identityHashCode(t), t); - if (c1 instanceof Variable) { - ((Variable) c1).setType(child2Type); - updatedVars.put(System.identityHashCode(c1), c1); - } else if (c1 instanceof Term) { - ((Term) c1).setType(child2Type); - updatedVars.put(System.identityHashCode(c1), c1); - } - } else { - if (c2 instanceof Variable && compareTypes(((Variable) c2).getType(), condType)) { - ((Variable) c2).setType(condType); - updatedVars.put(System.identityHashCode(c2), c2); - } else if (c2 instanceof Term && compareTypes(((Term) c2).getType(), condType)) { - ((Term) c2).setType(condType); - updatedVars.put(System.identityHashCode(c2), c2); - } - } - variables.put(System.identityHashCode(condTerms), condType); - } else if (symbol.equals(DataConstraintModel.add) || symbol.equals(DataConstraintModel.sub) - || symbol.equals(DataConstraintModel.mul) || symbol.equals(DataConstraintModel.div)) { - // If the root symbol of the term is arithmetic operators. - Expression c1 = t.getChild(0); - Expression c2 = t.getChild(1); - List operands = new ArrayList<>(); - operands.add(t); - operands.add(c1); - operands.add(c2); - expToVariable.put(System.identityHashCode(t), operands); - expToVariable.put(System.identityHashCode(c1), operands); - expToVariable.put(System.identityHashCode(c2), operands); - Type opType = t.getType(); - Map updatedVars = getUpdateSet(updateFromVariable, operands); - Type child1Type = getExpTypeIfUpdatable(opType, c1); - if (child1Type != null) { - opType = child1Type; - t.setType(child1Type); - updatedVars.put(System.identityHashCode(t), t); - } else { - if (c1 instanceof Variable && compareTypes(((Variable) c1).getType(), opType)) { - ((Variable) c1).setType(opType); - updatedVars.put(System.identityHashCode(c1), c1); - } else if (c1 instanceof Term && compareTypes(((Term) c1).getType(), opType)) { - ((Term) c1).setType(opType); - updatedVars.put(System.identityHashCode(c1), c1); - } - } - Type child2Type = getExpTypeIfUpdatable(opType, c2); - if (child2Type != null) { - opType = child2Type; - t.setType(child2Type); - updatedVars.put(System.identityHashCode(t), t); - if (c1 instanceof Variable) { - ((Variable) c1).setType(child2Type); - updatedVars.put(System.identityHashCode(c1), c1); - } else if (c1 instanceof Term) { - ((Term) c1).setType(child2Type); - updatedVars.put(System.identityHashCode(c1), c1); - } - } else { - if (c2 instanceof Variable && compareTypes(((Variable) c2).getType(), opType)) { - ((Variable) c2).setType(opType); - updatedVars.put(System.identityHashCode(c2), c2); - } else if (c2 instanceof Term && compareTypes(((Term) c2).getType(), opType)) { - ((Term) c2).setType(opType); - updatedVars.put(System.identityHashCode(c2), c2); - } - } - variables.put(System.identityHashCode(operands), opType); - } else if (symbol.getSignature() != null - && symbol.getSignature()[0] == DataConstraintModel.typeList) { - // If the root symbol of the term is the list type (except for the cons - // function). - List consExps = new ArrayList<>(); - consExps.add(t); - expToVariable.put(System.identityHashCode(t), consExps); - Type condType = t.getType(); - Map updatedVars = getUpdateSet(updateFromVariable, consExps); - for (int i = 1; i < symbol.getSignature().length; i++) { - Type tc = symbol.getSignature()[i]; - if (tc == DataConstraintModel.typeList) { - Expression e = t.getChildren().get(i - 1); - Type newType = getExpTypeIfUpdatable(condType, e); - if (newType != null) { - condType = newType; - for (Expression e2 : consExps) { - if (e2 instanceof Variable) { - ((Variable) e2).setType(newType); - updatedVars.put(System.identityHashCode(e2), e2); - } else if (e2 instanceof Term) { - ((Term) e2).setType(newType); - updatedVars.put(System.identityHashCode(e2), e2); - } - } - } else { - if (e instanceof Variable && compareTypes(((Variable) e).getType(), condType)) { - ((Variable) e).setType(condType); - updatedVars.put(System.identityHashCode(e), e); - } else if (e instanceof Term && compareTypes(((Term) e).getType(), condType)) { - ((Term) e).setType(condType); - updatedVars.put(System.identityHashCode(e), e); - } - } - consExps.add(e); - expToVariable.put(System.identityHashCode(e), consExps); - } - } - variables.put(System.identityHashCode(consExps), condType); + for (Channel childCh: ch.getChildren()) { + groupForChannel(childCh); } } + }; + groupExpressionsByResource.groupForChannel(ch); - // 1.5 Extract constraints on path parameters and resources. - ResourcePath rPath = cm.getResource(); - while (rPath != null) { - Expression param = rPath.getLastParam(); - if (param != null) { - ResourceHierarchy parent = rPath.getResourceHierarchy().getParent(); - if (parent != null) { - List pathParams = resourcePathParams.get(parent); - if (pathParams == null) { - pathParams = new ArrayList<>(); - resourcePathParams.put(parent, pathParams); + // 1.2 Group expressions by variable. + IGroupExpressionsByVariable groupExpressionsByVariable = new IGroupExpressionsByVariable() { + public void groupForChannel(Channel ch) { + for (ChannelMember cm : ch.getChannelMembers()) { + StateTransition st = cm.getStateTransition(); + Map> locals = new HashMap<>(); + Map localTypes = new HashMap<>(); + List allVariables = new ArrayList<>(); + allVariables.addAll(st.getCurStateExpression().getVariables().values()); + allVariables.addAll(st.getMessageExpression().getVariables().values()); + if (st.getNextStateExpression() != null) { + allVariables.addAll(st.getNextStateExpression().getVariables().values()); + } + for (Selector s: ch.getAllSelectors()) { // add channel selectors + if (s.getExpression() instanceof Variable) { + allVariables.add((Variable) s.getExpression()); } - pathParams.add(param); - expToPathParams.put(System.identityHashCode(param), pathParams); - Type parentType = parent.getResourceStateType(); - Type paramType = null; + } + ResourcePath resPath = cm.getResource(); + for (Expression param: resPath.getPathParams()) { // add path parameters if (param instanceof Variable) { - paramType = ((Variable) param).getType(); + allVariables.add((Variable) param); } else if (param instanceof Term) { - paramType = ((Term) param).getType(); + allVariables.addAll(((Term) param).getVariables().values()); } - if (paramType != null && parentType == null) { - if (paramType.equals(DataConstraintModel.typeString)) { - parentType = DataConstraintModel.typeMap; - } else if (paramType.equals(DataConstraintModel.typeInt)) { - parentType = DataConstraintModel.typeList; + } + for (Variable var : allVariables) { + List sameVariable = locals.get(var.getName()); + if (sameVariable == null) { + sameVariable = new ArrayList<>(); + sameVariable.add(var); + expToVariable.put(System.identityHashCode(var), sameVariable); + locals.put(var.getName(), sameVariable); + localTypes.put(var.getName(), var.getType()); + } else { + sameVariable.add(var); + expToVariable.put(System.identityHashCode(var), sameVariable); + Type varType = localTypes.get(var.getName()); + Map updatedVars = getUpdateSet(updateFromVariable, sameVariable); + if (compareTypes(varType, var.getType())) { + localTypes.put(var.getName(), var.getType()); + for (Expression v : sameVariable) { + if (v != var) { + if (compareTypes(((Variable) v).getType(), var.getType())) { + ((Variable) v).setType(var.getType()); + updatedVars.put(System.identityHashCode(v), v); + } + } + } + } else if (compareTypes(var.getType(), varType)) { + var.setType(varType); + updatedVars.put(System.identityHashCode(var), var); } - if (parentType != null) { - parent.setResourceStateType(parentType); - updateFromResourceOwnership.add(parent); + } + } + for (String varName : locals.keySet()) { + variables.put(System.identityHashCode(locals.get(varName)), localTypes.get(varName)); + } + } + for (Channel childCh: ch.getChildren()) { + groupForChannel(childCh); + } + } + }; + groupExpressionsByVariable.groupForChannel(ch); + + // 1.3 Group expressions by message. + IGroupExpressionsByMessage groupExpressionsByMessage = new IGroupExpressionsByMessage() { + public void groupForChannel(Channel rootCh, Channel ch) { + for (ChannelMember cm : ch.getChannelMembers()) { + Expression message = cm.getStateTransition().getMessageExpression(); + if (message instanceof Variable) { + Type msgType = ((Variable) message).getType(); + Map, Type>> msgTypeMap = messages.get(rootCh); + if (msgTypeMap == null) { + msgTypeMap = new HashMap<>(); + messages.put(rootCh, msgTypeMap); + } + Map.Entry, Type> typeAndExps = msgTypeMap.get(0); + if (typeAndExps == null) { + List exps = new ArrayList<>(); + exps.add(message); + typeAndExps = new AbstractMap.SimpleEntry<>(exps, msgType); + msgTypeMap.put(0, typeAndExps); + expToMessage.put(System.identityHashCode(message), exps); + } else { + typeAndExps.getKey().add(message); + expToMessage.put(System.identityHashCode(message), typeAndExps.getKey()); + Map updateExps = getUpdateSet(updateFromMessage, typeAndExps.getKey()); + if (compareTypes(typeAndExps.getValue(), msgType)) { + typeAndExps.setValue(msgType); + for (Expression e : typeAndExps.getKey()) { + if (e != message) { + if (e instanceof Variable) { + ((Variable) e).setType(msgType); + updateExps.put(System.identityHashCode(e), e); + } + } + } + } else if (compareTypes(msgType, typeAndExps.getValue())) { + ((Variable) message).setType(typeAndExps.getValue()); + updateExps.put(System.identityHashCode(message), message); + } + } + } else if (message instanceof Term) { + Map, Type>> msgTypeMap = messages.get(rootCh); + if (msgTypeMap == null) { + msgTypeMap = new HashMap<>(); + messages.put(rootCh, msgTypeMap); + } + for (int i = 0; i < ((Term) message).getArity(); i++) { + Expression arg = ((Term) message).getChild(i); + Type argType = null; + if (arg instanceof Variable) { + argType = ((Variable) arg).getType(); + } else if (arg instanceof Term) { + argType = ((Term) arg).getType(); + } else { + continue; + } + Map.Entry, Type> typeAndExps = msgTypeMap.get(i); + if (typeAndExps == null) { + List exps = new ArrayList<>(); + exps.add(arg); + typeAndExps = new AbstractMap.SimpleEntry<>(exps, argType); + msgTypeMap.put(i, typeAndExps); + expToMessage.put(System.identityHashCode(arg), exps); + } else { + typeAndExps.getKey().add(arg); + expToMessage.put(System.identityHashCode(arg), typeAndExps.getKey()); + Map updateExps = getUpdateSet(updateFromMessage, typeAndExps.getKey()); + if (compareTypes(typeAndExps.getValue(), argType)) { + typeAndExps.setValue(argType); + for (Expression e : typeAndExps.getKey()) { + if (e != arg) { + if (e instanceof Variable) { + ((Variable) e).setType(argType); + updateExps.put(System.identityHashCode(e), e); + } + } + } + } else if (compareTypes(argType, typeAndExps.getValue())) { + if (arg instanceof Variable) { + ((Variable) arg).setType(typeAndExps.getValue()); + updateExps.put(System.identityHashCode(arg), arg); + } else if (arg instanceof Term) { + ((Term) arg).setType(typeAndExps.getValue()); + updateExps.put(System.identityHashCode(arg), arg); + } + } } } } } - rPath = rPath.getParent(); + for (Channel childCh: ch.getChildren()) { + groupForChannel(rootCh, childCh); + } } - } + }; + groupExpressionsByMessage.groupForChannel(ch, ch); + + // 1.4 Extract constraints on expressions in each term. + IExtractConstraintsOnExpressionsInTerm extractConstraintsOnExpressionsInTerm = new IExtractConstraintsOnExpressionsInTerm() { + public void extractForChannel(Channel ch) { + for (ChannelMember cm : ch.getChannelMembers()) { + StateTransition st = cm.getStateTransition(); + List terms = new ArrayList<>(); + if (st.getCurStateExpression() instanceof Term) { + Map subTerms = ((Term) st.getCurStateExpression()).getSubTerms(Term.class); + terms.addAll(subTerms.values()); + } + if (st.getMessageExpression() instanceof Term) { + Map subTerms = ((Term) st.getMessageExpression()).getSubTerms(Term.class); + terms.addAll(subTerms.values()); + } + if (st.getNextStateExpression() != null && st.getNextStateExpression() instanceof Term) { + Map subTerms = ((Term) st.getNextStateExpression()).getSubTerms(Term.class); + terms.addAll(subTerms.values()); + } + for (Term t : terms) { + Symbol symbol = t.getSymbol(); + if (symbol.equals(DataConstraintModel.cons) || symbol.equals(DataConstraintModel.set) || symbol.equals(DataConstraintModel.append)) { + // If the root symbol of the term is cons or set. + List consExps = new ArrayList<>(); + consExps.add(t); // list term + updateExpressionBelonging(expToConsOrSet, t, consExps); + if (symbol.equals(DataConstraintModel.cons)) { + // If the root symbol of the term is cons. + for (Expression e : t.getChildren()) { + consExps.add(e); + updateExpressionBelonging(expToConsOrSet, e, consExps); + } + } else if (symbol.equals(DataConstraintModel.append)) { + // If the root symbol of the term is append. + Expression e = t.getChildren().get(1); + consExps.add(e); // list element + updateExpressionBelonging(expToConsOrSet, e, consExps); + e = t.getChildren().get(0); + consExps.add(e); // list argument + updateExpressionBelonging(expToConsOrSet, e, consExps); + } else { + // If the root symbol of the term is set. + Expression e = t.getChildren().get(2); + consExps.add(e); // list element + updateExpressionBelonging(expToConsOrSet, e, consExps); + e = t.getChildren().get(0); + consExps.add(e); // list argument + updateExpressionBelonging(expToConsOrSet, e, consExps); + } + Type newType = getExpTypeIfUpdatable(t.getType(), consExps.get(2)); + if (newType != null) { + // If the type of the 2nd argument of cons (1st argument of set/append) is more concrete than the type of the term. + t.setType(newType); + Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); + updateCons.put(System.identityHashCode(t), t); + } else { + Type arg2Type = null; + if (consExps.get(2) != null && consExps.get(2) instanceof Variable) { + arg2Type = ((Variable) consExps.get(2)).getType(); + if (compareTypes(arg2Type, t.getType())) { + // If the type of the term is more concrete than the type of the 2nd argument of cons (1st argument of set/append). + ((Variable) consExps.get(2)).setType(t.getType()); + Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); + updateCons.put(System.identityHashCode(consExps.get(2)), consExps.get(2)); + } + } else if (consExps.get(2) != null && consExps.get(2) instanceof Term) { + arg2Type = ((Term) consExps.get(2)).getType(); + if (compareTypes(arg2Type, t.getType())) { + // If the type of the term is more concrete than the type of the 2nd argument of cons (1st argument of set/append). + ((Term) consExps.get(2)).setType(t.getType()); + Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); + updateCons.put(System.identityHashCode(consExps.get(2)), consExps.get(2)); + } + } + } + Type newCompType = getExpTypeIfUpdatable(listComponentTypes.get(t.getType()), consExps.get(1)); + if (newCompType != null) { + // If the type of the 1st argument of cons (3rd argument of set) is more concrete than the type of list component. + Type newListType = listTypes.get(newCompType); + if (newListType == null) { + // Create new list type. + newListType = createNewListType(newCompType, DataConstraintModel.typeList); + } + t.setType(newListType); + Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); + updateCons.put(System.identityHashCode(t), t); + if (consExps.get(2) != null && consExps.get(2) instanceof Variable) { + ((Variable) consExps.get(2)).setType(newListType); + updateCons.put(System.identityHashCode(consExps.get(2)), consExps.get(2)); + } else if (consExps.get(2) != null && consExps.get(2) instanceof Term) { + ((Term) consExps.get(2)).setType(newListType); + updateCons.put(System.identityHashCode(consExps.get(2)), consExps.get(2)); + } + } + consOrSet.put(System.identityHashCode(consExps), t.getType()); + } else if (symbol.equals(DataConstraintModel.head) || symbol.equals(DataConstraintModel.get)) { + // If the root symbol of the term is head or get. + List consExps = new ArrayList<>(); + Expression e = t.getChildren().get(0); + consExps.add(e); // list argument + updateExpressionBelonging(expToConsOrSet, e, consExps); + consExps.add(t); // list's component + updateExpressionBelonging(expToConsOrSet, t, consExps); + consExps.add(null); + Type listType = listTypes.get(t.getType()); + if (listType == null && t.getType() != null) { + // Create a new list type. + listType = createNewListType(t.getType(), DataConstraintModel.typeList); + } + Type newListType = getExpTypeIfUpdatable(listType, consExps.get(0)); + if (newListType != null) { + // If the type of the component of the 1st argument is more concrete than the type of the term. + Type newCompType = listComponentTypes.get(newListType); + if (newCompType != null) { + t.setType(newCompType); + Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); + updateCons.put(System.identityHashCode(t), t); + } + consOrSet.put(System.identityHashCode(consExps), newListType); + } else { + // If the type of the term is more concrete than the type of the component of the 1st argument. + if (consExps.get(0) != null && consExps.get(0) instanceof Variable) { + ((Variable) consExps.get(0)).setType(listType); + Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); + updateCons.put(System.identityHashCode(consExps.get(0)), consExps.get(0)); + } else if (consExps.get(0) != null && consExps.get(0) instanceof Term) { + ((Term) consExps.get(0)).setType(listType); + Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); + updateCons.put(System.identityHashCode(consExps.get(0)), consExps.get(0)); + } + consOrSet.put(System.identityHashCode(consExps), listType); + } + } else if (symbol.equals(DataConstraintModel.tail)) { + // If the root symbol of the term is tail. + List consExps = new ArrayList<>(); + consExps.add(t); // list term + updateExpressionBelonging(expToConsOrSet, t, consExps); + consExps.add(null); // list's component + Expression e = t.getChildren().get(0); + consExps.add(e); // list argument + updateExpressionBelonging(expToConsOrSet, e, consExps); + Type newType = getExpTypeIfUpdatable(t.getType(), consExps.get(2)); + if (newType != null) { + // If the type of the argument is more concrete than the type of the term. + t.setType(newType); + Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); + updateCons.put(System.identityHashCode(t), t); + } else { + Type argType = null; + if (consExps.get(2) != null && consExps.get(2) instanceof Variable) { + argType = ((Variable) consExps.get(2)).getType(); + if (compareTypes(argType, t.getType())) { + // If the type of the term is more concrete than the type of the argument. + ((Variable) consExps.get(2)).setType(t.getType()); + Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); + updateCons.put(System.identityHashCode(consExps.get(2)), consExps.get(2)); + } + } else if (consExps.get(2) != null && consExps.get(2) instanceof Term) { + argType = ((Term) consExps.get(2)).getType(); + if (compareTypes(argType, t.getType())) { + // If the type of the term is more concrete than the type of the argument. + ((Term) consExps.get(2)).setType(t.getType()); + Map updateCons = getUpdateSet(updateFromConsOrSet, consExps); + updateCons.put(System.identityHashCode(consExps.get(2)), consExps.get(2)); + } + } + } + consOrSet.put(System.identityHashCode(consExps), t.getType()); + } else if (symbol.equals(DataConstraintModel.tuple)) { + // If the root symbol of the term is tuple. + List tupleExps = new ArrayList<>(); + List newArgTypesList = new ArrayList<>(); + tupleExps.add(t); // tuple term + updateExpressionBelonging(expToTuple, t, tupleExps); + for (Expression e : t.getChildren()) { + tupleExps.add(e); // tuple's component + updateExpressionBelonging(expToTuple, e, tupleExps); + if (e instanceof Variable) { + newArgTypesList.add(((Variable) e).getType()); + } else if (e instanceof Term) { + newArgTypesList.add(((Term) e).getType()); + } else { + newArgTypesList.add(null); + } + } + if (t.getType() == DataConstraintModel.typeTuple) { + Type newTupleType = tupleTypes.get(newArgTypesList); + if (newTupleType == null) { + // Create new tuple type; + newTupleType = createNewTupleType(newArgTypesList, DataConstraintModel.typeTuple); + } + // Update the type of the tuple term and record the updated expression. + t.setType(newTupleType); + Map updateExps = getUpdateSet(updateFromTuple, tupleExps); + updateExps.put(System.identityHashCode(t), t); + } + tuple.put(System.identityHashCode(tupleExps), t.getType()); + } else if (symbol.equals(DataConstraintModel.pair)) { + // If the root symbol of the term is pair. + List pairExps = new ArrayList<>(); + pairExps.add(t); // pair + updateExpressionBelonging(expToPair, t, pairExps); + if (t.getType() == DataConstraintModel.typePair) { + for (Expression e : t.getChildren()) { + pairExps.add(e); // left/right + updateExpressionBelonging(expToPair, e, pairExps); + Type newArgType = null; + if (e instanceof Variable) { + newArgType = (((Variable) e).getType()); + + } else if (e instanceof Term) { + newArgType = (((Term) e).getType()); + } + + if (newArgType != null) { + Type newPairType = pairTypes.get(newArgType); + if (newPairType != null) { + t.setType(newPairType); + Map updateExps = getUpdateSet(updateFromPair, pairExps); + updateExps.put(System.identityHashCode(t), t); + } + } + } + pair.put(System.identityHashCode(pairExps), t.getType()); + + } + } else if (symbol.equals(DataConstraintModel.fst)) { + // If the root symbol of the term is fst. + List tupleExps = new ArrayList<>(); + Expression arg = t.getChildren().get(0); + tupleExps.add(arg); // tuple argument + updateExpressionBelonging(expToTuple, arg, tupleExps); + tupleExps.add(t); // first component + updateExpressionBelonging(expToTuple, t, tupleExps); + tupleExps.add(null); // second component + Type argType = null; + if (arg instanceof Variable) { + argType = ((Variable) arg).getType(); + } else if (arg instanceof Term) { + argType = ((Term) arg).getType(); + } + Type newTupleType = DataConstraintModel.typeTuple; + if (argType == DataConstraintModel.typeTuple && t.getType() != null) { + List newCompTypeList = new ArrayList<>(); + newCompTypeList.add(t.getType()); + newCompTypeList.add(null); + newTupleType = tupleTypes.get(newCompTypeList); + if (newTupleType == null) { + // Create new tuple type; + newTupleType = createNewTupleType(newCompTypeList, DataConstraintModel.typeTuple); + } + } + if (argType != newTupleType && newTupleType != null) { + // Update the type of the tuple argument and record the updated expression. + if (arg instanceof Variable) { + ((Variable) arg).setType(newTupleType); + argType = newTupleType; + } else if (arg instanceof Term) { + ((Term) arg).setType(newTupleType); + argType = newTupleType; + } + Map updateExps = getUpdateSet(updateFromTuple, tupleExps); + updateExps.put(System.identityHashCode(arg), arg); + } + tuple.put(System.identityHashCode(tupleExps), argType); + } else if (symbol.equals(DataConstraintModel.snd)) { + // If the root symbol of the term is snd. + List tupleExps = new ArrayList<>(); + Expression arg = t.getChildren().get(0); + tupleExps.add(arg); // tuple argument + updateExpressionBelonging(expToTuple, arg, tupleExps); + tupleExps.add(null); // first component + tupleExps.add(t); // second component + updateExpressionBelonging(expToTuple, t, tupleExps); + Type argType = null; + if (arg instanceof Variable) { + argType = ((Variable) arg).getType(); + } else if (arg instanceof Term) { + argType = ((Term) arg).getType(); + } + Type newTupleType = DataConstraintModel.typeTuple; + if (argType == DataConstraintModel.typeTuple && t.getType() != null) { + List newCompTypeList = new ArrayList<>(); + newCompTypeList.add(null); + if (DataConstraintModel.typeTuple.isAncestorOf(t.getType())) { + List sndTypes = tupleComponentTypes.get(t.getType()); + if (sndTypes != null) { + for (Type t2: sndTypes) { + newCompTypeList.add(t2); + } + } else { + newCompTypeList.add(t.getType()); + } + } else { + newCompTypeList.add(t.getType()); + } + newTupleType = tupleTypes.get(newCompTypeList); + if (newTupleType == null) { + // Create new tuple type; + newTupleType = createNewTupleType(newCompTypeList, DataConstraintModel.typeTuple); + } + } + if (argType != newTupleType && newTupleType != null) { + // Update the type of the tuple argument and record the updated expression. + if (arg instanceof Variable) { + ((Variable) arg).setType(newTupleType); + argType = newTupleType; + } else if (arg instanceof Term) { + ((Term) arg).setType(newTupleType); + argType = newTupleType; + } + Map updateExps = getUpdateSet(updateFromTuple, tupleExps); + updateExps.put(System.identityHashCode(arg), arg); + } + tuple.put(System.identityHashCode(tupleExps), argType); + } else if (symbol.equals(DataConstraintModel.left)) { + // If the root symbol of the term is left. + List pairExps = new ArrayList<>(); + Expression arg = t.getChildren().get(0); + pairExps.add(arg); // pair + updateExpressionBelonging(expToPair, arg, pairExps); + pairExps.add(t); // left + updateExpressionBelonging(expToPair, t, pairExps); + pairExps.add(null); // right + Type argType = null; + if (arg instanceof Variable) { + argType = ((Variable) arg).getType(); + } else if (arg instanceof Term) { + argType = ((Term) arg).getType(); + } + Type newPairType = DataConstraintModel.typePair; + if (argType == DataConstraintModel.typePair && t.getType() != null) { + List newCompTypeList = new ArrayList<>(); + newCompTypeList.add(t.getType()); + newCompTypeList.add(null); + newPairType = pairTypes.get(newCompTypeList); + if (newPairType == null) { + // Create new tuple type; + newPairType = createNewTupleType(newCompTypeList, DataConstraintModel.typePair); + } + } + if (argType != newPairType && newPairType != null) { + if (arg instanceof Variable) { + ((Variable) arg).setType(newPairType); + argType = newPairType; + } else if (arg instanceof Term) { + ((Term) arg).setType(newPairType); + argType = newPairType; + } + Map updateExps = getUpdateSet(updateFromPair, pairExps); + updateExps.put(System.identityHashCode(arg), arg); + } + pair.put(System.identityHashCode(pairExps), argType); + } else if (symbol.equals(DataConstraintModel.right)) { + // If the root symbol of the term is right. + List pairExps = new ArrayList<>(); + Expression arg = t.getChildren().get(0); + pairExps.add(arg); // pair + updateExpressionBelonging(expToPair, arg, pairExps); + pairExps.add(null); // left + pairExps.add(t); // right + updateExpressionBelonging(expToPair, t, pairExps); + Type argType = null; + if (arg instanceof Variable) { + argType = ((Variable) arg).getType(); + } else if (arg instanceof Term) { + argType = ((Term) arg).getType(); + } + Type newPairType = DataConstraintModel.typePair; + if (argType == DataConstraintModel.typePair && t.getType() != null) { + List newCompTypeList = new ArrayList<>(); + newCompTypeList.add(null); + newCompTypeList.add(t.getType()); + newPairType = pairTypes.get(newCompTypeList); + if (newPairType == null) { + // Create new tuple type; + newPairType = createNewTupleType(newCompTypeList, DataConstraintModel.typePair); + } + } + if (argType != newPairType && newPairType != null) { + if (arg instanceof Variable) { + ((Variable) arg).setType(newPairType); + argType = newPairType; + } else if (arg instanceof Term) { + ((Term) arg).setType(newPairType); + argType = newPairType; + } + Map updateExps = getUpdateSet(updateFromPair, pairExps); + updateExps.put(System.identityHashCode(arg), arg); + } + pair.put(System.identityHashCode(pairExps), argType); + } else if (symbol.equals(DataConstraintModel.lookup)) { + // If the root symbol of the term is lookup. + List mapExps = new ArrayList<>(); + Expression arg1 = t.getChildren().get(0); // map + mapExps.add(arg1); + updateExpressionBelonging(expToMap, arg1, mapExps); + Expression arg2 = t.getChildren().get(1); // key + mapExps.add(arg2); + updateExpressionBelonging(expToMap, arg2, mapExps); + mapExps.add(t); // value + updateExpressionBelonging(expToMap, t, mapExps); + Type arg1Type = null; + if (arg1 instanceof Variable) { + arg1Type = ((Variable) arg1).getType(); + } else if (arg1 instanceof Term) { + arg1Type = ((Term) arg1).getType(); + } + List newCompTypeList = new ArrayList<>(); + if (arg2 instanceof Variable) { + newCompTypeList.add(((Variable) arg2).getType()); + } else if (arg2 instanceof Term) { + newCompTypeList.add(((Term) arg2).getType()); + } else { + newCompTypeList.add(null); + } + newCompTypeList.add(t.getType()); + if (arg1Type == DataConstraintModel.typeMap || arg1Type == null) { + Type newMapType = mapTypes.get(newCompTypeList); + if (newMapType == null) { + // Create new tuple type; + newMapType = createNewMapType(newCompTypeList, DataConstraintModel.typeMap); + } + // Update the type of the map argument and record the updated expression. + if (arg1 instanceof Variable) { + ((Variable) arg1).setType(newMapType); + arg1Type = newMapType; + } else if (arg1 instanceof Term) { + ((Term) arg1).setType(newMapType); + arg1Type = newMapType; + } + Map updateExps = getUpdateSet(updateFromMap, mapExps); + updateExps.put(System.identityHashCode(arg1), arg1); + } + map.put(System.identityHashCode(mapExps), arg1Type); + } else if (symbol.equals(DataConstraintModel.insert)) { + // If the root symbol of the term is insert. + List mapExps = new ArrayList<>(); + mapExps.add(t); // map + updateExpressionBelonging(expToMap, t, mapExps); + Expression arg1 = t.getChildren().get(1); // key + mapExps.add(arg1); + updateExpressionBelonging(expToMap, arg1, mapExps); + Expression arg2 = t.getChildren().get(2); // value + mapExps.add(arg2); + updateExpressionBelonging(expToMap, arg2, mapExps); + Expression arg0 = t.getChildren().get(0); // map + mapExps.add(arg0); + updateExpressionBelonging(expToMap, arg0, mapExps); + Type termType = t.getType(); + List newCompTypeList = new ArrayList<>(); + if (arg1 instanceof Variable) { + newCompTypeList.add(((Variable) arg1).getType()); + } else if (arg1 instanceof Term) { + newCompTypeList.add(((Term) arg1).getType()); + } else { + newCompTypeList.add(null); + } + if (arg2 instanceof Variable) { + newCompTypeList.add(((Variable) arg2).getType()); + } else if (arg2 instanceof Term) { + newCompTypeList.add(((Term) arg2).getType()); + } else { + newCompTypeList.add(null); + } + Type newTermType = getExpTypeIfUpdatable(termType, mapExps.get(3)); + if (newTermType != null) { + // If the type of the 1st argument of insert is more concrete than the type of the term. + t.setType(newTermType); + termType = newTermType; + Map updateExps = getUpdateSet(updateFromMap, mapExps); + updateExps.put(System.identityHashCode(t), t); + } else { + Type arg3Type = null; + if (mapExps.get(3) != null && mapExps.get(3) instanceof Variable) { + arg3Type = ((Variable) mapExps.get(3)).getType(); + if (compareTypes(arg3Type, t.getType())) { + // If the type of the term is more concrete than the type of the 1st argument of insert. + ((Variable) mapExps.get(3)).setType(t.getType()); + Map updateExps = getUpdateSet(updateFromMap, mapExps); + updateExps.put(System.identityHashCode(mapExps.get(3)), mapExps.get(3)); + } + } else if (mapExps.get(3) != null && mapExps.get(3) instanceof Term) { + arg3Type = ((Term) mapExps.get(3)).getType(); + if (compareTypes(arg3Type, t.getType())) { + // If the type of the term is more concrete than the type of the 1st argument of insert. + ((Term) mapExps.get(3)).setType(t.getType()); + Map updateExps = getUpdateSet(updateFromMap, mapExps); + updateExps.put(System.identityHashCode(mapExps.get(3)), mapExps.get(3)); + } + } + } + if (termType == DataConstraintModel.typeMap || termType == null) { + Type newMapType = mapTypes.get(newCompTypeList); + if (newMapType == null) { + // Create new tuple type; + newMapType = createNewMapType(newCompTypeList, DataConstraintModel.typeMap); + } + // Update the type of the map term and record the updated expression. + t.setType(newMapType); + Map updateExps = getUpdateSet(updateFromMap, mapExps); + updateExps.put(System.identityHashCode(t), t); + if (mapExps.get(3) != null && mapExps.get(3) instanceof Variable) { + ((Variable) mapExps.get(3)).setType(newMapType); + updateExps.put(System.identityHashCode(mapExps.get(3)), mapExps.get(3)); + } else if (mapExps.get(3) != null && mapExps.get(3) instanceof Term) { + ((Term) mapExps.get(3)).setType(newMapType); + updateExps.put(System.identityHashCode(mapExps.get(3)), mapExps.get(3)); + } + termType = newMapType; + } + map.put(System.identityHashCode(mapExps), termType); + } else if (symbol.equals(DataConstraintModel.addMember)) { + // If the root symbol of the term is addMember (addMember(json, key, value)). + List dotExps = new ArrayList<>(); + Expression jsonArg = t.getChildren().get(0); + Expression keyArg = t.getChildren().get(1); + Expression valueArg = t.getChildren().get(2); + dotExps.add(t); // json + updateExpressionBelonging(expToJson, t, dotExps); + dotExps.add(keyArg); // key + updateExpressionBelonging(expToJson, keyArg, dotExps); + dotExps.add(valueArg); // value + updateExpressionBelonging(expToJson, valueArg, dotExps); + dotExps.add(jsonArg); // json + updateExpressionBelonging(expToJson, jsonArg, dotExps); + Type jsonType = t.getType(); + Type valueType = null; + if (valueArg instanceof Variable) { + valueType = ((Variable) valueArg).getType(); + } else if (valueArg instanceof Term) { + valueType = ((Term) valueArg).getType(); + } + String keyName = null; + if (keyArg instanceof Constant) { + keyName = ((Constant) keyArg).getSymbol().getName(); + } + Type jsonArgType = null; + if (jsonArg instanceof Variable) { + jsonArgType = ((Variable) jsonArg).getType(); + } else if (jsonArg instanceof Term) { + jsonArgType = ((Term) jsonArg).getType(); + } + Type newJsonType = DataConstraintModel.typeJson; + if (jsonType == DataConstraintModel.typeJson && jsonArgType != null && keyName != null) { + Map newMemberTypes = new HashMap<>(((JsonType) jsonArgType).getMemberTypes()); + newMemberTypes.put(keyName, valueType); + newJsonType = jsonTypes.get(newMemberTypes); + if (newJsonType == null) { + // Create new json type; + newJsonType = createNewJsonType(newMemberTypes, DataConstraintModel.typeJson); + } + } + if (jsonType != newJsonType && newJsonType != null && !newJsonType.isAncestorOf(jsonType)) { + // Update the type of the json term and record the updated expression. + t.setType(newJsonType); + jsonType = newJsonType; + Map updateExps = getUpdateSet(updateFromJson, dotExps); + updateExps.put(System.identityHashCode(t), t); + } + json.put(System.identityHashCode(dotExps), jsonType); + } else if (symbol.equals(DataConstraintModel.dot)) { + // If the root symbol of the term is dot (json.property). + List dotExps = new ArrayList<>(); + Expression jsonArg = t.getChildren().get(0); + Expression keyArg = t.getChildren().get(1); + dotExps.add(jsonArg); // json + updateExpressionBelonging(expToJson, jsonArg, dotExps); + dotExps.add(keyArg); // key + updateExpressionBelonging(expToJson, keyArg, dotExps); + dotExps.add(t); // value + updateExpressionBelonging(expToJson, t, dotExps); + dotExps.add(null); // json + Type jsonType = null; + if (jsonArg instanceof Variable) { + jsonType = ((Variable) jsonArg).getType(); + } else if (jsonArg instanceof Term) { + jsonType = ((Term) jsonArg).getType(); + } + String keyName = null; + if (keyArg instanceof Constant) { + keyName = ((Constant) keyArg).getSymbol().getName(); + } + Type newJsonType = DataConstraintModel.typeJson; + if (jsonType == DataConstraintModel.typeJson && t.getType() != null && keyName != null) { + Map newMemberTypes = new HashMap<>(); + newMemberTypes.put(keyName, t.getType()); + newJsonType = jsonTypes.get(newMemberTypes); + if (newJsonType == null) { + // Create new json type; + newJsonType = createNewJsonType(newMemberTypes, DataConstraintModel.typeJson); + } + } + if (jsonType != newJsonType && newJsonType != null && !newJsonType.isAncestorOf(jsonType)) { + // Update the type of the json argument and record the updated expression. + if (jsonArg instanceof Variable) { + ((Variable) jsonArg).setType(newJsonType); + jsonType = newJsonType; + } else if (jsonArg instanceof Term) { + ((Term) jsonArg).setType(newJsonType); + jsonType = newJsonType; + } + Map updateExps = getUpdateSet(updateFromJson, dotExps); + updateExps.put(System.identityHashCode(jsonArg), jsonArg); + } + json.put(System.identityHashCode(dotExps), jsonType); + } else if (symbol.equals(DataConstraintModel.dotParam)) { + // If the root symbol of the term is dot (json.{param}). + List dotExps = new ArrayList<>(); + Expression jsonArg = t.getChildren().get(0); + Expression keyArg = t.getChildren().get(1); + dotExps.add(jsonArg); // json (list/map) + updateExpressionBelonging(expToJson, jsonArg, dotExps); + dotExps.add(null); // key + dotExps.add(t); // value + updateExpressionBelonging(expToJson, t, dotExps); + dotExps.add(null); // json + Type jsonType = null; + if (jsonArg instanceof Variable) { + jsonType = ((Variable) jsonArg).getType(); + } else if (jsonArg instanceof Term) { + jsonType = ((Term) jsonArg).getType(); + } + Type keyType = null; + if (keyArg instanceof Variable) { + keyType = ((Variable) keyArg).getType(); + } else if (keyArg instanceof Term) { + keyType = ((Term) keyArg).getType(); + } + Type newJsonType = null; + if (keyType == DataConstraintModel.typeInt) { + newJsonType = DataConstraintModel.typeList; + } else if (keyType == DataConstraintModel.typeString) { + newJsonType = DataConstraintModel.typeMap; + } + if (t.getType() != null) { + if ((jsonType == DataConstraintModel.typeList)) { + newJsonType = listTypes.get(t.getType()); + if (newJsonType == null) { + // Create new list type; + newJsonType = createNewListType(t.getType(), DataConstraintModel.typeList); + } + } else if (jsonType == DataConstraintModel.typeMap) { + List keyValueTypes = Arrays.asList(new Type[] {DataConstraintModel.typeString, t.getType()}); + newJsonType = mapTypes.get(keyValueTypes); + if (newJsonType == null) { + // Create new map type; + newJsonType = createNewMapType(keyValueTypes, DataConstraintModel.typeMap); + } + } + } + if (jsonType != newJsonType && newJsonType != null && !newJsonType.isAncestorOf(jsonType)) { + // Update the type of the json argument and record the updated expression. + if (jsonArg instanceof Variable) { + ((Variable) jsonArg).setType(newJsonType); + jsonType = newJsonType; + } else if (jsonArg instanceof Term) { + ((Term) jsonArg).setType(newJsonType); + jsonType = newJsonType; + } + Map updateExps = getUpdateSet(updateFromJson, dotExps); + updateExps.put(System.identityHashCode(jsonArg), jsonArg); + } + json.put(System.identityHashCode(dotExps), jsonType); + } else if (symbol.equals(DataConstraintModel.cond)) { + // If the root symbol of the term is if function. + Expression c1 = t.getChild(1); + Expression c2 = t.getChild(2); + List condTerms = new ArrayList<>(); + condTerms.add(t); + condTerms.add(c1); + condTerms.add(c2); + expToVariable.put(System.identityHashCode(t), condTerms); + expToVariable.put(System.identityHashCode(c1), condTerms); + expToVariable.put(System.identityHashCode(c2), condTerms); + Type condType = t.getType(); + Map updatedVars = getUpdateSet(updateFromVariable, condTerms); + Type child1Type = getExpTypeIfUpdatable(condType, c1); + if (child1Type != null) { + condType = child1Type; + t.setType(child1Type); + updatedVars.put(System.identityHashCode(t), t); + } else { + if (c1 instanceof Variable && compareTypes(((Variable) c1).getType(), condType)) { + ((Variable) c1).setType(condType); + updatedVars.put(System.identityHashCode(c1), c1); + } else if (c1 instanceof Term && compareTypes(((Term) c1).getType(), condType)) { + ((Term) c1).setType(condType); + updatedVars.put(System.identityHashCode(c1), c1); + } + } + Type child2Type = getExpTypeIfUpdatable(condType, c2); + if (child2Type != null) { + condType = child2Type; + t.setType(child2Type); + updatedVars.put(System.identityHashCode(t), t); + if (c1 instanceof Variable) { + ((Variable) c1).setType(child2Type); + updatedVars.put(System.identityHashCode(c1), c1); + } else if (c1 instanceof Term) { + ((Term) c1).setType(child2Type); + updatedVars.put(System.identityHashCode(c1), c1); + } + } else { + if (c2 instanceof Variable && compareTypes(((Variable) c2).getType(), condType)) { + ((Variable) c2).setType(condType); + updatedVars.put(System.identityHashCode(c2), c2); + } else if (c2 instanceof Term && compareTypes(((Term) c2).getType(), condType)) { + ((Term) c2).setType(condType); + updatedVars.put(System.identityHashCode(c2), c2); + } + } + variables.put(System.identityHashCode(condTerms), condType); + } else if (symbol.equals(DataConstraintModel.add) || symbol.equals(DataConstraintModel.sub) + || symbol.equals(DataConstraintModel.mul) || symbol.equals(DataConstraintModel.div)) { + // If the root symbol of the term is arithmetic operators. + Expression c1 = t.getChild(0); + Expression c2 = t.getChild(1); + List operands = new ArrayList<>(); + operands.add(t); + operands.add(c1); + operands.add(c2); + expToVariable.put(System.identityHashCode(t), operands); + expToVariable.put(System.identityHashCode(c1), operands); + expToVariable.put(System.identityHashCode(c2), operands); + Type opType = t.getType(); + Map updatedVars = getUpdateSet(updateFromVariable, operands); + Type child1Type = getExpTypeIfUpdatable(opType, c1); + if (child1Type != null) { + opType = child1Type; + t.setType(child1Type); + updatedVars.put(System.identityHashCode(t), t); + } else { + if (c1 instanceof Variable && compareTypes(((Variable) c1).getType(), opType)) { + ((Variable) c1).setType(opType); + updatedVars.put(System.identityHashCode(c1), c1); + } else if (c1 instanceof Term && compareTypes(((Term) c1).getType(), opType)) { + ((Term) c1).setType(opType); + updatedVars.put(System.identityHashCode(c1), c1); + } + } + Type child2Type = getExpTypeIfUpdatable(opType, c2); + if (child2Type != null) { + opType = child2Type; + t.setType(child2Type); + updatedVars.put(System.identityHashCode(t), t); + if (c1 instanceof Variable) { + ((Variable) c1).setType(child2Type); + updatedVars.put(System.identityHashCode(c1), c1); + } else if (c1 instanceof Term) { + ((Term) c1).setType(child2Type); + updatedVars.put(System.identityHashCode(c1), c1); + } + } else { + if (c2 instanceof Variable && compareTypes(((Variable) c2).getType(), opType)) { + ((Variable) c2).setType(opType); + updatedVars.put(System.identityHashCode(c2), c2); + } else if (c2 instanceof Term && compareTypes(((Term) c2).getType(), opType)) { + ((Term) c2).setType(opType); + updatedVars.put(System.identityHashCode(c2), c2); + } + } + variables.put(System.identityHashCode(operands), opType); + } else if (symbol.getSignature() != null + && symbol.getSignature()[0] == DataConstraintModel.typeList) { + // If the root symbol of the term is the list type (except for the cons function). + List consExps = new ArrayList<>(); + consExps.add(t); + expToVariable.put(System.identityHashCode(t), consExps); + Type condType = t.getType(); + Map updatedVars = getUpdateSet(updateFromVariable, consExps); + for (int i = 1; i < symbol.getSignature().length; i++) { + Type tc = symbol.getSignature()[i]; + if (tc == DataConstraintModel.typeList) { + Expression e = t.getChildren().get(i - 1); + Type newType = getExpTypeIfUpdatable(condType, e); + if (newType != null) { + condType = newType; + for (Expression e2 : consExps) { + if (e2 instanceof Variable) { + ((Variable) e2).setType(newType); + updatedVars.put(System.identityHashCode(e2), e2); + } else if (e2 instanceof Term) { + ((Term) e2).setType(newType); + updatedVars.put(System.identityHashCode(e2), e2); + } + } + } else { + if (e instanceof Variable && compareTypes(((Variable) e).getType(), condType)) { + ((Variable) e).setType(condType); + updatedVars.put(System.identityHashCode(e), e); + } else if (e instanceof Term && compareTypes(((Term) e).getType(), condType)) { + ((Term) e).setType(condType); + updatedVars.put(System.identityHashCode(e), e); + } + } + consExps.add(e); + expToVariable.put(System.identityHashCode(e), consExps); + } + } + variables.put(System.identityHashCode(consExps), condType); + } + } + } + for (Channel childCh: ch.getChildren()) { + extractForChannel(childCh); + } + } + }; + extractConstraintsOnExpressionsInTerm.extractForChannel(ch); + + // 1.5 Extract constraints on path parameters and resources. + IExtractConstraintsOnPathParametersAndResources extractConstraintsOnPathParametersAndResources = new IExtractConstraintsOnPathParametersAndResources() { + public void extractForChannel(Channel ch) { + for (ChannelMember cm : ch.getChannelMembers()) { + ResourcePath rPath = cm.getResource(); + while (rPath != null) { + Expression param = rPath.getLastParam(); + if (param != null) { + ResourceHierarchy parent = rPath.getResourceHierarchy().getParent(); + if (parent != null) { + List pathParams = resourcePathParams.get(parent); + if (pathParams == null) { + pathParams = new ArrayList<>(); + resourcePathParams.put(parent, pathParams); + } + pathParams.add(param); + expToPathParams.put(System.identityHashCode(param), pathParams); + Type parentType = parent.getResourceStateType(); + Type paramType = null; + if (param instanceof Variable) { + paramType = ((Variable) param).getType(); + } else if (param instanceof Term) { + paramType = ((Term) param).getType(); + } + if (paramType != null && parentType == null) { + if (paramType.equals(DataConstraintModel.typeString)) { + parentType = DataConstraintModel.typeMap; + } else if (paramType.equals(DataConstraintModel.typeInt)) { + parentType = DataConstraintModel.typeList; + } + if (parentType != null) { + parent.setResourceStateType(parentType); + updateFromResourceOwnership.add(parent); + } + } + } + } + rPath = rPath.getParent(); + } + } + for (Channel childCh: ch.getChildren()) { + extractForChannel(childCh); + } + } + }; + extractConstraintsOnPathParametersAndResources.extractForChannel(ch); } // 1.6 Extract constraints on resource hierarchies. @@ -2331,4 +2379,24 @@ } return false; } + + private static interface IGroupExpressionsByResource { + public void groupForChannel(Channel ch); + } + + private static interface IGroupExpressionsByVariable { + public void groupForChannel(Channel ch); + } + + private static interface IGroupExpressionsByMessage { + public void groupForChannel(Channel rootCh, Channel ch); + } + + private static interface IExtractConstraintsOnExpressionsInTerm { + public void extractForChannel(Channel ch); + } + + private static interface IExtractConstraintsOnPathParametersAndResources { + public void extractForChannel(Channel ch); + } } diff --git a/AlgebraicDataflowArchitectureModel/src/application/ApplicationMenuBar.java b/AlgebraicDataflowArchitectureModel/src/application/ApplicationMenuBar.java index dfbbe9f..8ee36b5 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/ApplicationMenuBar.java +++ b/AlgebraicDataflowArchitectureModel/src/application/ApplicationMenuBar.java @@ -12,7 +12,7 @@ private NewResourceAction newResourceAction = null; private NewChannelAction newChannelAction = null; - private NewIOChannelAction newIOChannelAction = null; + private NewEventChannelAction newIOChannelAction = null; private NewFormulaChannelAction newFormulaChannelAction = null; private DeleteAction deleteAction = null; private JavaPrototypeGenerateAction javaPrototypeGenerateAction = null; @@ -32,7 +32,7 @@ newMenu.add(newResourceAction = new NewResourceAction(applicationWindow.getEditor())); newMenu.add(newChannelAction = new NewChannelAction(applicationWindow.getEditor())); - newMenu.add(newIOChannelAction = new NewIOChannelAction(applicationWindow.getEditor())); + newMenu.add(newIOChannelAction = new NewEventChannelAction(applicationWindow.getEditor())); newMenu.add(newFormulaChannelAction = new NewFormulaChannelAction(applicationWindow.getEditor())); JMenu menu = null; diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/JavaPrototypeGenerateAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/JavaPrototypeGenerateAction.java index 4083dce..846c273 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/actions/JavaPrototypeGenerateAction.java +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/JavaPrototypeGenerateAction.java @@ -18,6 +18,7 @@ import generators.JavaCodeGenerator; import generators.JavaMethodBodyGenerator; import generators.JavaSpecific; +import generators.StandaloneSpecific; import models.dataConstraintModel.ResourceHierarchy; import models.dataConstraintModel.ResourcePath; import models.dataFlowModel.DataTransferModel; @@ -59,8 +60,8 @@ } else { JavaCodeGenerator.resetMainTypeName(); // use the default main type's name. } - editor.setCodes(JavaMethodBodyGenerator.doGenerate(graph, model, JavaCodeGenerator.doGenerate(graph, model))); -// editor.setCodes(new CodeGeneratorFromDataFlowGraph().generateCode(model, graph, new JavaSpecific())); +// editor.setCodes(JavaMethodBodyGenerator.doGenerate(graph, model, JavaCodeGenerator.doGenerate(graph, model))); + editor.setCodes(new CodeGeneratorFromDataFlowGraph().generateCode(model, graph, new StandaloneSpecific(), new JavaSpecific())); ModelExtension.recoverModel(model); for (CompilationUnit file : editor.getCodes()) { System.out.println(file); diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/JerseyPrototypeGenerateAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/JerseyPrototypeGenerateAction.java index dbf75fd..da1a3df 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/actions/JerseyPrototypeGenerateAction.java +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/JerseyPrototypeGenerateAction.java @@ -1,10 +1,8 @@ package application.actions; import java.awt.event.ActionEvent; -import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; -import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; @@ -13,12 +11,13 @@ import algorithms.*; import application.editor.Editor; import code.ast.*; +import generators.CodeGeneratorFromDataFlowGraph; import generators.DataTransferMethodAnalyzer; +import generators.JavaSpecific; import generators.JerseyCodeGenerator; import generators.JerseyMethodBodyGenerator; -import models.algebra.Type; +import generators.JerseySpecific; import models.dataConstraintModel.ResourceHierarchy; -import models.dataConstraintModel.ResourcePath; import models.dataFlowModel.DataTransferModel; import models.dataFlowModel.ModelExtension; import models.dataFlowModel.DataFlowGraph; @@ -58,7 +57,8 @@ } else { JerseyCodeGenerator.resetMainTypeName(); // use the default main type's name. } - editor.setCodes(JerseyMethodBodyGenerator.doGenerate(graph, model, JerseyCodeGenerator.doGenerate(graph, model))); +// editor.setCodes(JerseyMethodBodyGenerator.doGenerate(graph, model, JerseyCodeGenerator.doGenerate(graph, model))); + editor.setCodes(new CodeGeneratorFromDataFlowGraph().generateCode(model, graph, new JerseySpecific(), new JavaSpecific())); ModelExtension.recoverModel(model); for (CompilationUnit file : editor.getCodes()) { System.out.println(file); diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/NewChannelAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/NewChannelAction.java index 6ee664c..1006055 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/actions/NewChannelAction.java +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/NewChannelAction.java @@ -22,7 +22,7 @@ public void actionPerformed(ActionEvent e) { String channelName = JOptionPane.showInputDialog("Channel Name:"); if (channelName == null) return; - editor.addChannel(new DataTransferChannel(channelName)); + editor.addChannel(channelName); } } diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/NewEventChannelAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/NewEventChannelAction.java new file mode 100644 index 0000000..df10657 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/NewEventChannelAction.java @@ -0,0 +1,28 @@ +package application.actions; + +import java.awt.event.ActionEvent; + +import javax.swing.JOptionPane; + +import application.editor.Editor; +import models.dataFlowModel.DataTransferChannel; + +public class NewEventChannelAction extends AbstractEditorAction { + + /** + * + */ + private static final long serialVersionUID = -1657072017390171313L; + + public NewEventChannelAction(Editor editor) { + super("Event Channel...", editor); + } + + @Override + public void actionPerformed(ActionEvent e) { + String channelName = JOptionPane.showInputDialog("Event Channel Name:"); + if (channelName == null) return; + editor.addEventChannel(channelName); + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/NewFormulaChannelAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/NewFormulaChannelAction.java index 068c758..245be58 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/actions/NewFormulaChannelAction.java +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/NewFormulaChannelAction.java @@ -56,7 +56,7 @@ String channelName = channelText.getText(); String symbol = symbolText.getText(); if(r == JOptionPane.OK_OPTION) { - editor.addFormulaChannel(new FormulaChannel(channelName, editor.getModel().getSymbol(symbol))); + editor.addFormulaChannel(channelName, editor.getModel().getSymbol(symbol)); } } } diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/NewIOChannelAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/NewIOChannelAction.java deleted file mode 100644 index 85e48de..0000000 --- a/AlgebraicDataflowArchitectureModel/src/application/actions/NewIOChannelAction.java +++ /dev/null @@ -1,28 +0,0 @@ -package application.actions; - -import java.awt.event.ActionEvent; - -import javax.swing.JOptionPane; - -import application.editor.Editor; -import models.dataFlowModel.DataTransferChannel; - -public class NewIOChannelAction extends AbstractEditorAction { - - /** - * - */ - private static final long serialVersionUID = -1657072017390171313L; - - public NewIOChannelAction(Editor editor) { - super("I/O Channel", editor); - } - - @Override - public void actionPerformed(ActionEvent e) { - String channelName = JOptionPane.showInputDialog("I/O Channel Name:"); - if (channelName == null) return; - editor.addIOChannel(new DataTransferChannel(channelName)); - } - -} diff --git a/AlgebraicDataflowArchitectureModel/src/application/actions/NewResourceAction.java b/AlgebraicDataflowArchitectureModel/src/application/actions/NewResourceAction.java index bd9aad2..3fb3f38 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/actions/NewResourceAction.java +++ b/AlgebraicDataflowArchitectureModel/src/application/actions/NewResourceAction.java @@ -1,9 +1,12 @@ package application.actions; import application.editor.Editor; +import models.dataConstraintModel.ResourcePath; +import models.dataFlowModel.ResourceNode; import javax.swing.*; import java.awt.event.ActionEvent; +import java.util.List; public class NewResourceAction extends AbstractEditorAction { @@ -15,10 +18,25 @@ @Override public void actionPerformed(ActionEvent e) { - String resName = JOptionPane.showInputDialog("Resource Name:"); + List selectedResNodes = editor.getSelectedResourceNodes(); + String initialName = ""; + if (selectedResNodes != null && selectedResNodes.size() == 1) { + initialName = selectedResNodes.get(0).getPrimaryResourcePath().toString(); + } + String resName = JOptionPane.showInputDialog("Resource Name:", initialName); if (resName == null) { return; } - editor.addResourcePath(null, resName); + if (selectedResNodes == null || selectedResNodes.size() == 0) { + editor.addResourceNode(null, resName); + } else if (selectedResNodes.size() == 1) { + if (initialName.length() > 0 && resName.startsWith(initialName)) { + resName = resName.substring(initialName.length()); + if (resName.startsWith(".")) { + resName = resName.substring(1); + } + } + editor.addResourceNode(selectedResNodes.get(0), resName); + } } } diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/Editor.java b/AlgebraicDataflowArchitectureModel/src/application/editor/Editor.java index ed5bfa9..22c8ff0 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/editor/Editor.java +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/Editor.java @@ -14,11 +14,15 @@ import com.mxgraph.view.mxGraph; import com.mxgraph.view.mxGraphView; import models.EdgeAttribute; +import models.algebra.Expression; +import models.algebra.Symbol; +import models.algebra.Variable; import models.dataConstraintModel.Channel; import models.dataConstraintModel.ResourcePath; import models.dataFlowModel.DataFlowGraph; import models.dataFlowModel.DataTransferChannel; import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.ResourceNode; import models.visualModel.FormulaChannel; import parser.Parser; import parser.ParserDTRAM; @@ -171,9 +175,6 @@ // Force to change PushPullSelectionStage to construct mxGraph boolean stageChanged = changeStage(STAGE_PUSH_PULL_SELECTION); - if (stageChanged && curStage instanceof PushPullSelectionStage) { - ((PushPullSelectionStage) curStage).constructGraph(); - } // Set layout setDAGLayout(); @@ -207,9 +208,6 @@ // Force to change PushPullSelectionStage to construct mxGraph boolean stageChanged = changeStage(STAGE_PUSH_PULL_SELECTION); - if (stageChanged && curStage instanceof PushPullSelectionStage) { - ((PushPullSelectionStage) curStage).constructGraph(); - } // Restore the geometry parser.doParseGeometry(graph); @@ -313,14 +311,6 @@ return fileString.toString(); } - public void delete() { - boolean stageChanged = changeStage(STAGE_DATA_FLOW_MODELING); - if (!stageChanged) { - return; - } - ((DataFlowModelingStage) curStage).delete(); - } - public void setDAGLayout() { mxCell root = (mxCell) graph.getDefaultParent(); @@ -363,44 +353,142 @@ graph.getModel().endUpdate(); } } + + public List getSelectedResourceNodes() { + Object[] sels = graph.getSelectionCells(); + List resNodes = new ArrayList<>(); + if (curStage instanceof DataFlowModelingStage) { + for (Object sel: sels) { + if (sel instanceof mxCell && ((mxCell) sel).isVertex()) { + mxCell cell = ((mxCell) sel); + ResourceNode resNode = ((DataFlowModelingStage) curStage).getResourceNode(cell); + if (resNode != null) { + resNodes.add(resNode); + } + } + } + } + return resNodes; + } + + public List getSelectedChannels() { + Object[] sels = graph.getSelectionCells(); + List channels = new ArrayList<>(); + for (Object sel: sels) { + if (sel instanceof mxCell && ((mxCell) sel).isVertex()) { + mxCell cell = ((mxCell) sel); + Channel channel = model.getChannel((String) cell.getValue()); + if (channel != null) { + channels.add(channel); + } else { + channel = model.getInputChannel((String) cell.getValue()); + if (channel != null) { + channels.add(channel); + } + } + } + } + return channels; + } - public void addResourcePath(ResourcePath parentPath, String resName) { + public void delete() { + boolean stageChanged = changeStage(STAGE_DATA_FLOW_MODELING); + if (!stageChanged) { + return; + } + ((DataFlowModelingStage) curStage).delete(); + } + + public void addResourceNode(ResourceNode parentNode, String resName) { // Force to change to data-flow modeling stage boolean stageChanged = changeStage(STAGE_DATA_FLOW_MODELING); if (!stageChanged) { return; } - ((DataFlowModelingStage) curStage).addResourcePath(parentPath, resName); + ((DataFlowModelingStage) curStage).addResourceNode(parentNode, resName); model = curStage.getModel(); } - public void addChannel(DataTransferChannel channelGen) { + public void addChannel(String channelName) { // Force to change to data-flow modeling stage boolean stageChanged = changeStage(STAGE_DATA_FLOW_MODELING); if (!stageChanged) { return; } - ((DataFlowModelingStage) curStage).addChannel(channelGen); + DataTransferChannel channel = null; + if (channelName.contains(Parser.LEFT_BRACKET) && channelName.contains(Parser.RIGHT_BRACKET)) { + channel = new DataTransferChannel(channelName.substring(0, channelName.indexOf(Parser.LEFT_BRACKET))); + Parser.TokenStream stream = new Parser.TokenStream(); + Parser parser = new Parser(stream); + stream.addLine(channelName.substring(channelName.indexOf(Parser.LEFT_BRACKET), channelName.length())); + try { + String leftBracket = stream.next(); + if (leftBracket.equals(Parser.LEFT_BRACKET)) { + // has selectors + String rightBracket = null; + do { + String selector = stream.next(); + Variable var = parser.parseVariable(stream, model, selector); + channel.addSelector(var); + rightBracket = stream.next(); + } while (rightBracket.equals(Parser.COMMA)); + if (!rightBracket.equals(Parser.RIGHT_BRACKET)) throw new ExpectedRightBracket(stream.getLine()); + leftBracket = stream.next(); + } + } catch (ExpectedRightBracket e) { + e.printStackTrace(); + } + } else { + channel = new DataTransferChannel(channelName); + } + ((DataFlowModelingStage) curStage).addChannel(channel); model = curStage.getModel(); } - public void addIOChannel(DataTransferChannel ioChannelGen) { + public void addEventChannel(String channelName) { // Force to change to data-flow modeling stage boolean stageChanged = changeStage(STAGE_DATA_FLOW_MODELING); if (!stageChanged) { return; } - ((DataFlowModelingStage) curStage).addIOChannel(ioChannelGen); + DataTransferChannel eventChannel = null; + if (channelName.contains(Parser.LEFT_BRACKET) && channelName.contains(Parser.RIGHT_BRACKET)) { + eventChannel = new DataTransferChannel(channelName.substring(0, channelName.indexOf(Parser.LEFT_BRACKET))); + Parser.TokenStream stream = new Parser.TokenStream(); + Parser parser = new Parser(stream); + stream.addLine(channelName.substring(channelName.indexOf(Parser.LEFT_BRACKET), channelName.length())); + try { + String leftBracket = stream.next(); + if (leftBracket.equals(Parser.LEFT_BRACKET)) { + // has selectors + String rightBracket = null; + do { + String selector = stream.next(); + Variable var = parser.parseVariable(stream, model, selector); + eventChannel.addSelector(var); + rightBracket = stream.next(); + } while (rightBracket.equals(Parser.COMMA)); + if (!rightBracket.equals(Parser.RIGHT_BRACKET)) throw new ExpectedRightBracket(stream.getLine()); + leftBracket = stream.next(); + } + } catch (ExpectedRightBracket e) { + e.printStackTrace(); + } + } else { + eventChannel = new DataTransferChannel(channelName); + } + ((DataFlowModelingStage) curStage).addEventChannel(eventChannel); model = curStage.getModel(); } - public void addFormulaChannel(FormulaChannel formulaChannelGen) { + public void addFormulaChannel(String channelName, Symbol op) { // Force to change to data-flow modeling stage boolean stageChanged = changeStage(STAGE_DATA_FLOW_MODELING); if (!stageChanged) { return; } - ((DataFlowModelingStage) curStage).addFormulaChannel(formulaChannelGen); + FormulaChannel formulaChannel = new FormulaChannel(channelName, op); + ((DataFlowModelingStage) curStage).addFormulaChannel(formulaChannel); model = curStage.getModel(); } diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DataFlowCellEditor.java b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DataFlowCellEditor.java index c0709b6..f8e85fc 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DataFlowCellEditor.java +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DataFlowCellEditor.java @@ -34,14 +34,10 @@ if (graphComponent.getGraph().getModel().isEdge(cellObj)) { return; } - DataTransferModel model = stage.getModel(); - DataTransferChannel ch = (DataTransferChannel) model.getChannel((String) ((mxCell) cellObj).getValue()); + DataTransferChannel ch = (DataTransferChannel) ((DataFlowModelingStage) stage).getChannel((mxCell) cellObj); if (ch == null) { - ch = (DataTransferChannel) model.getInputChannel((String) ((mxCell) cellObj).getValue()); - if (ch == null) { - // selected cell is a resource - return; - } + // selected cell is a resource + return; } JPanel panel = new JPanel(); if (ch instanceof FormulaChannel) { @@ -91,7 +87,9 @@ } } } else { - JTextArea textArea = new JTextArea(ch.getSourceText(), 10, 20); + JTextArea textArea = new JTextArea(ch.getSourceText(), 15, 50); + textArea.setLineWrap(true); + textArea.setTabSize(4); panel.add(textArea); int option = JOptionPane.showConfirmDialog(null, panel, "Channel Code", JOptionPane.OK_CANCEL_OPTION); diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DataFlowModelingStage.java b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DataFlowModelingStage.java index d4d0006..6c97196 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DataFlowModelingStage.java +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/DataFlowModelingStage.java @@ -15,17 +15,25 @@ import models.dataConstraintModel.Channel; import models.dataConstraintModel.ChannelMember; import models.dataConstraintModel.ResourcePath; +import models.dataConstraintModel.Selector; +import models.dataFlowModel.DataFlowGraph; import models.dataFlowModel.DataTransferChannel; import models.dataFlowModel.DataTransferModel; +import models.dataFlowModel.ResourceNode; import models.visualModel.FormulaChannel; import parser.Parser; import parser.exceptions.*; import java.awt.event.MouseListener; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; public class DataFlowModelingStage extends Stage { + private HashMap resNodeToCell = new HashMap<>(); + private HashMap channelToCell = new HashMap<>(); + private HashMap cellToResNode = new HashMap<>(); + private HashMap cellToChannel = new HashMap<>(); public DataFlowModelingStage(mxGraphComponent graphComponent) { super(graphComponent); @@ -33,6 +41,20 @@ @Override public void init(Stage prevStage) { + if (prevStage instanceof PushPullSelectionStage) { + if (((PushPullSelectionStage) prevStage).getResNodeToCell() != null) { + resNodeToCell = ((PushPullSelectionStage) prevStage).getResNodeToCell(); + } + if (((PushPullSelectionStage) prevStage).getChannelToCell() != null) { + channelToCell = ((PushPullSelectionStage) prevStage).getChannelToCell(); + } + for (ResourceNode resNode: resNodeToCell.keySet()) { + cellToResNode.put(resNodeToCell.get(resNode), resNode); + } + for (DataTransferChannel ch: channelToCell.keySet()) { + cellToChannel.put(channelToCell.get(ch), ch); + } + } } @Override @@ -98,13 +120,24 @@ } return Validation.checkUpdateConflict(model); } + + public ResourceNode getResourceNode(mxCell cell) { + return cellToResNode.get(cell); + } + + public Channel getChannel(mxCell cell) { + return cellToChannel.get(cell); + } - public void addResourcePath(ResourcePath parentPath, String resName) { - ResourcePath resourcePath; - if (parentPath == null) { - resourcePath = new ResourcePath(resName); + public void addResourceNode(ResourceNode parentNode, String resName) { + ResourceNode resourceNode = null; + ResourcePath resourcePath = null; + if (parentNode == null) { + resourcePath = new ResourcePath(resName); + resourceNode = new ResourceNode(null, resourcePath); getModel().addResourcePath(resourcePath); } else { + ResourcePath parentPath = parentNode.getPrimaryResourcePath(); if (resName.startsWith(Parser.LEFT_CURLY_BRACKET) && resName.endsWith(Parser.RIGHT_CURLY_BRACKET)) { Parser.TokenStream stream = new Parser.TokenStream(); Parser parser = new Parser(stream); @@ -112,12 +145,14 @@ try { Expression exp = parser.parseTerm(stream, getModel()); resourcePath = new ResourcePath(parentPath, exp); + resourceNode = new ResourceNode(parentNode, resourcePath); getModel().addResourcePath(resourcePath); } catch (ExpectedRightBracket | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { e.printStackTrace(); } } else { resourcePath = new ResourcePath(parentPath, resName); + resourceNode = new ResourceNode(parentNode, resourcePath); getModel().addResourcePath(resourcePath); } } @@ -125,14 +160,101 @@ graph.getModel().beginUpdate(); mxCell root = (mxCell) graph.getDefaultParent(); try { - graph.insertVertex(root, null, resName, 20, 20, 80, 30, "shape=ellipse;perimeter=ellipsePerimeter"); // insert a resource as a vertex + if (parentNode == null) { + mxCell resCell = (mxCell) graph.insertVertex(root, null, resourceNode.getPrimaryResourcePath().getName(), 20, 20, 80, 30, "shape=ellipse;perimeter=ellipsePerimeter;verticalAlign=top"); // insert a resource as a vertex + resNodeToCell.put(resourceNode, resCell); + cellToResNode.put(resCell, resourceNode); + } else { + mxCell parentCell = resNodeToCell.get(parentNode); + double pw = parentCell.getGeometry().getWidth(); + double ph = parentCell.getGeometry().getHeight(); + mxCell resCell = (mxCell) graph.insertVertex(parentCell, null, resourceNode.getPrimaryResourcePath().getName(), 20, 20, pw * 0.8, ph * 0.8, "shape=ellipse;perimeter=ellipsePerimeter;verticalAlign=top"); // insert a resource as a vertex + resNodeToCell.put(resourceNode, resCell); + cellToResNode.put(resCell, resourceNode); + } } finally { graph.getModel().endUpdate(); } } - public void addChannel(DataTransferChannel channelGen) { - getModel().addChannel(channelGen); + public void addChannel(DataTransferChannel channel) { + getModel().addChannel(channel); + + graph.getModel().beginUpdate(); + mxCell root = (mxCell) graph.getDefaultParent(); + try { + mxGeometry geo1 = new mxGeometry(0, 0.5, PORT_DIAMETER, PORT_DIAMETER); + geo1.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS)); + geo1.setRelative(true); + + mxGeometry geo2 = new mxGeometry(1.0, 0.5, PORT_DIAMETER, PORT_DIAMETER); + geo2.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS)); + geo2.setRelative(true); + + String channelName = channel.getChannelName(); + if (channel.getSelectors().size() > 0) { + channelName += "("; + String delimiter = ""; + for (Selector s: channel.getSelectors()) { + Expression exp = s.getExpression(); + String selectorName = exp.toString(); + channelName += delimiter + selectorName; + delimiter = ", "; + } + channelName += ")"; + } + Object chCell = graph.insertVertex(root, null, channelName, 150, 20, 30, 30, "verticalAlign=top"); // insert a channel as a vertex + mxCell port_in = new mxCell(null, geo1, "shape=ellipse;perimter=ellipsePerimeter"); + port_in.setVertex(true); + graph.addCell(port_in, chCell); // insert the input port of a channel + mxCell port_out = new mxCell(null, geo2, "shape=ellipse;perimter=ellipsePerimeter"); + port_out.setVertex(true); + graph.addCell(port_out, chCell); // insert the output port of a channel + channelToCell.put(channel, (mxCell) chCell); + cellToChannel.put((mxCell) chCell, channel); + cellToChannel.put(port_in, channel); + cellToChannel.put(port_out, channel); + } finally { + graph.getModel().endUpdate(); + } + } + + public void addEventChannel(DataTransferChannel eventChannel) { + getModel().addInputChannel(eventChannel); + + graph.getModel().beginUpdate(); + mxCell root = (mxCell) graph.getDefaultParent(); + try { + mxGeometry geo2 = new mxGeometry(1.0, 0.5, PORT_DIAMETER, PORT_DIAMETER); + geo2.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS)); + geo2.setRelative(true); + + String channelName = eventChannel.getChannelName(); + if (eventChannel.getSelectors().size() > 0) { + channelName += "("; + String delimiter = ""; + for (Selector s: eventChannel.getSelectors()) { + Expression exp = s.getExpression(); + String selectorName = exp.toString(); + channelName += delimiter + selectorName; + delimiter = ", "; + } + channelName += ")"; + } + Object chCell = graph.insertVertex(root, null, channelName, 150, 20, 30, 30, "verticalAlign=top"); // insert an I/O channel as a vertex + mxCell port_out = new mxCell(null, geo2, "shape=ellipse;perimter=ellipsePerimeter"); + port_out.setVertex(true); + graph.addCell(port_out, chCell); // insert the output port of a channel + channelToCell.put(eventChannel, (mxCell) chCell); + cellToChannel.put((mxCell) chCell, eventChannel); + cellToChannel.put(port_out, eventChannel); + } finally { + graph.getModel().endUpdate(); + } + } + + public void addFormulaChannel(FormulaChannel formulaChannel) { + getModel().addChannel(formulaChannel); graph.getModel().beginUpdate(); mxCell root = (mxCell) graph.getDefaultParent(); @@ -145,88 +267,69 @@ geo2.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS)); geo2.setRelative(true); - Object channel = graph.insertVertex(root, null, channelGen.getChannelName(), 150, 20, 30, 30); // insert a channel as a vertex + String channelName = formulaChannel.getChannelName(); + if (formulaChannel.getSelectors().size() > 0) { + channelName += "("; + String delimiter = ""; + for (Selector s: formulaChannel.getSelectors()) { + Expression exp = s.getExpression(); + String selectorName = exp.toString(); + channelName += delimiter + selectorName; + delimiter = ", "; + } + channelName += ")"; + } + Object chCell = graph.insertVertex(root, null, channelName, 150, 20, 30, 30, "verticalAlign=top"); // insert a channel as a vertex mxCell port_in = new mxCell(null, geo1, "shape=ellipse;perimter=ellipsePerimeter"); port_in.setVertex(true); - graph.addCell(port_in, channel); // insert the input port of a channel + graph.addCell(port_in, chCell); // insert the input port of a channel mxCell port_out = new mxCell(null, geo2, "shape=ellipse;perimter=ellipsePerimeter"); port_out.setVertex(true); - graph.addCell(port_out, channel); // insert the output port of a channel - } finally { - graph.getModel().endUpdate(); - } - } - - public void addIOChannel(DataTransferChannel ioChannelGen) { - getModel().addInputChannel(ioChannelGen); - - graph.getModel().beginUpdate(); - mxCell root = (mxCell) graph.getDefaultParent(); - try { - mxGeometry geo2 = new mxGeometry(1.0, 0.5, PORT_DIAMETER, PORT_DIAMETER); - geo2.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS)); - geo2.setRelative(true); - - Object channel = graph.insertVertex(root, null, ioChannelGen.getChannelName(), 150, 20, 30, 30); // insert an I/O channel as a vertex - mxCell port_out = new mxCell(null, geo2, "shape=ellipse;perimter=ellipsePerimeter"); - port_out.setVertex(true); - graph.addCell(port_out, channel); // insert the output port of a channel - } finally { - graph.getModel().endUpdate(); - } - } - - public void addFormulaChannel(FormulaChannel formulaChannelGen) { - getModel().addChannel(formulaChannelGen); - - graph.getModel().beginUpdate(); - mxCell root = (mxCell) graph.getDefaultParent(); - try { - mxGeometry geo1 = new mxGeometry(0, 0.5, PORT_DIAMETER, PORT_DIAMETER); - geo1.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS)); - geo1.setRelative(true); - - mxGeometry geo2 = new mxGeometry(1.0, 0.5, PORT_DIAMETER, PORT_DIAMETER); - geo2.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS)); - geo2.setRelative(true); - - Object channel = graph.insertVertex(root, null, formulaChannelGen.getChannelName(), 150, 20, 30, 30); // insert a channel as a vertex - mxCell port_in = new mxCell(null, geo1, "shape=ellipse;perimter=ellipsePerimeter"); - port_in.setVertex(true); - graph.addCell(port_in, channel); // insert the input port of a channel - mxCell port_out = new mxCell(null, geo2, "shape=ellipse;perimter=ellipsePerimeter"); - port_out.setVertex(true); - graph.addCell(port_out, channel); // insert the output port of a channel + graph.addCell(port_out, chCell); // insert the output port of a channel + channelToCell.put(formulaChannel, (mxCell) chCell); + cellToChannel.put((mxCell) chCell, formulaChannel); + cellToChannel.put(port_in, formulaChannel); + cellToChannel.put(port_out, formulaChannel); } finally { graph.getModel().endUpdate(); } } public boolean connectEdge(mxCell edge, mxCell src, mxCell dst) { - DataTransferModel model = getModel(); - Channel srcCh = model.getChannel((String) src.getValue()); + DataTransferChannel srcCh = cellToChannel.get(src); if (srcCh == null) { - srcCh = model.getInputChannel((String) src.getValue()); - if (srcCh == null) { - ResourcePath srcRes = model.getResourcePath((String) src.getValue()); - Channel dstCh = model.getChannel((String) dst.getValue()); - if (srcRes == null || dstCh == null) { - return false; - } - // resource to channel edge - ChannelMember srcCm = new ChannelMember(srcRes); - ((DataTransferChannel) dstCh).addChannelMemberAsInput(srcCm); - edge.setValue(new Editor.SrcDstAttribute(srcRes, dstCh)); - return true; + ResourceNode srcResNode = cellToResNode.get(src); + DataTransferChannel dstCh = cellToChannel.get(dst); + if (srcResNode == null || dstCh == null) { + return false; } + // resource to channel edge + ResourcePath srcRes = srcResNode.getPrimaryResourcePath(); + if (srcResNode.getIndegree() + srcResNode.getOutdegree() == 0) { + srcResNode.addOutSideResource(dstCh, srcRes); + } else { + srcRes = new ResourcePath(srcRes.getName(), srcRes.getResourceHierarchy()); + model.addResourcePath(srcRes); + } + ChannelMember srcCm = new ChannelMember(srcRes); + dstCh.addChannelMemberAsInput(srcCm); + edge.setValue(new Editor.SrcDstAttribute(srcRes, dstCh)); + return true; } - ResourcePath dstRes = model.getResourcePath((String) dst.getValue()); - if (dstRes == null) { + ResourceNode dstResNode = cellToResNode.get(dst); + if (dstResNode == null) { return false; } // channel to resource edge + ResourcePath dstRes = dstResNode.getPrimaryResourcePath(); + if (dstResNode.getIndegree() + dstResNode.getOutdegree() == 0) { + dstResNode.addInSideResource(srcCh, dstRes); + } else { + dstRes = new ResourcePath(dstRes.getName(), dstRes.getResourceHierarchy()); + model.addResourcePath(dstRes); + } ChannelMember dstCm = new ChannelMember(dstRes); - ((DataTransferChannel) srcCh).addChannelMemberAsOutput(dstCm); + srcCh.addChannelMemberAsOutput(dstCm); edge.setValue(new Editor.SrcDstAttribute(srcCh, dstRes)); return true; } @@ -235,28 +338,32 @@ for (Object obj : graph.getSelectionCells()) { mxCell cell = (mxCell) obj; if (cell.isEdge()) { - String srcName = (String) cell.getSource().getValue(); - String dstName = (String) cell.getTarget().getValue(); - if (model.getResourcePath(srcName) != null) { + mxCell srcCell = (mxCell) cell.getSource(); + mxCell dstCell = (mxCell) cell.getTarget(); + if (cellToResNode.get(srcCell) != null) { // resource to channel edge - Channel ch = model.getChannel(dstName); - ch.removeChannelMember(model.getResourcePath(srcName)); - } else if (model.getResourcePath(dstName) != null) { + DataTransferChannel ch = cellToChannel.get(dstCell); + ch.removeChannelMember(cellToResNode.get(srcCell).getOutSideResource(ch)); + } else if (cellToResNode.get(dstCell) != null) { // channel to resource edge - Channel ch = model.getChannel(srcName); - if (ch == null) { - ch = model.getInputChannel(srcName); - } - ch.removeChannelMember(model.getResourcePath(dstName)); + DataTransferChannel ch = cellToChannel.get(srcCell); + ch.removeChannelMember(cellToResNode.get(dstCell).getInSideResource(ch)); } } else if (cell.isVertex()) { - String name = (String) cell.getValue(); - if (model.getChannel(name) != null) { - model.removeChannel(name); - } else if (model.getInputChannel(name) != null) { - model.removeInputChannel(name); - } else if (model.getResourcePath(name) != null) { - model.removeResourcePath(name); + if (cellToChannel.get(cell) != null) { + DataTransferChannel ch = cellToChannel.get(cell); + if (ch.getInputChannelMembers().size() == 0) { + model.removeInputChannel(cellToChannel.get(cell).getChannelName()); + } else { + model.removeChannel(cellToChannel.get(cell).getChannelName()); + } + } else if (cellToResNode.get(cell) != null) { + for (ResourcePath resPath: cellToResNode.get(cell).getInSideResources()) { + model.removeResourcePath(resPath); + } + for (ResourcePath resPath: cellToResNode.get(cell).getOutSideResources()) { + model.removeResourcePath(resPath); + } } } } diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/PushPullSelectionCellEditor.java b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/PushPullSelectionCellEditor.java index 7d6d99b..8ca07ef 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/PushPullSelectionCellEditor.java +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/PushPullSelectionCellEditor.java @@ -82,8 +82,7 @@ } } if (selected != null) { - options.remove(selected); - options.add(0, selected); + attr.selectOption(selected); } graphComponent.labelChanged(cell, attr, trig); } diff --git a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/PushPullSelectionStage.java b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/PushPullSelectionStage.java index 5f4ae42..b1e32e7 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/editor/stages/PushPullSelectionStage.java +++ b/AlgebraicDataflowArchitectureModel/src/application/editor/stages/PushPullSelectionStage.java @@ -25,6 +25,8 @@ public class PushPullSelectionStage extends Stage { private DataFlowGraph dataFlowGraph = null; + private HashMap resNodeToCell; + private HashMap channelToCell; public PushPullSelectionStage(mxGraphComponent graphComponent) { super(graphComponent); @@ -79,8 +81,12 @@ private DataFlowGraph analyzeDataTransferModel(DataTransferModel model) { DataFlowGraph flowGraph = DataTransferModelAnalyzer.createDataFlowGraphWithStateStoringAttribute(model); - DataFlowGraph dataFlowGraph = DataTransferModelAnalyzer.annotateWithSelectableDataTransferAttiribute(flowGraph); - updateEdgeAttributes(dataFlowGraph); + dataFlowGraph = DataTransferModelAnalyzer.annotateWithSelectableDataTransferAttiribute(flowGraph); + mxCell parent = (mxCell) graph.getDefaultParent(); + if (parent.getChildCount() == 0) { + constructGraph(); // Construct data-flow graph (on file open action) + } + updateEdgeAttributes(dataFlowGraph); // Update push/pull selection pull-downs return dataFlowGraph; } @@ -125,9 +131,10 @@ graph.getModel().beginUpdate(); try { - Map channelsIn = new HashMap<>(); - Map channelsOut = new HashMap<>(); - Map resources = new HashMap<>(); + Map channelInToCell = new HashMap<>(); + Map channelOutToCell = new HashMap<>(); + channelToCell = new HashMap<>(); + resNodeToCell = new HashMap<>(); mxGeometry geoPortIn = new mxGeometry(0, 0.5, PORT_DIAMETER, PORT_DIAMETER); geoPortIn.setOffset(new mxPoint(-PORT_RADIUS, -PORT_RADIUS)); @@ -142,9 +149,9 @@ int w = 80; int h = 30; ResourcePath resourcePath = resourceNode.getPrimaryResourcePath(); - Object resource = graph.insertVertex(parent, null, resourcePath.getLeafResourceName(), 20, 20, w, h, "shape=ellipse;perimeter=ellipsePerimeter"); // insert a resource as a vertex - resources.put(resourceNode, resource); - createChildResourceVertices(resource, resourceNode, resources, w, h); + mxCell resourceCell = (mxCell) graph.insertVertex(parent, null, resourcePath.getLeafResourceName(), 20, 20, w, h, "shape=ellipse;perimeter=ellipsePerimeter;verticalAlign=top"); // insert a resource as a vertex + resNodeToCell.put(resourceNode, resourceCell); + createChildResourceVertices(resourceCell, resourceNode, w, h); } // create channel vertices @@ -152,72 +159,68 @@ DataTransferChannel channel = channelNode.getChannel(); if (!channel.getInputResources().isEmpty()) { // Normal channel - if (channelsIn.get(channel) == null || channelsOut.get(channel) == null) { - if (Objects.equals(channel.getSelectors().toString(), "[]")) { - Object channelCell = graph.insertVertex(parent, null, channel.getChannelName(), 150, 20, 30, 30); // insert a channel as a vertex + if (channelInToCell.get(channel) == null || channelOutToCell.get(channel) == null) { + String channelName = channel.getChannelName(); + if (channel.getSelectors().size() > 0) { + channelName += "("; + String delimiter = ""; + for (Selector s: channel.getSelectors()) { + Expression exp = s.getExpression(); + String selectorName = exp.toString(); + channelName += delimiter + selectorName; + delimiter = ", "; + } + channelName += ")"; + } + int w = 60; + int h = 30; + if (channelNode.getChildren().size() > 0) { + w *= 2; + h *= 2; + } + mxCell channelCell = (mxCell) graph.insertVertex(parent, null, channelName, 150, 20, w, h, "verticalAlign=top"); // insert a channel as a vertex + channelToCell.put(channel, channelCell); - mxCell portIn = new mxCell(null, geoPortIn, "shape=ellipse;perimter=ellipsePerimeter"); - portIn.setVertex(true); - graph.addCell(portIn, channelCell); // insert the input port of a channel - channelsIn.put(channel, portIn); + mxCell portIn = new mxCell(null, geoPortIn, "shape=ellipse;perimter=ellipsePerimeter"); + portIn.setVertex(true); + graph.addCell(portIn, channelCell); // insert the input port of a channel + channelInToCell.put(channel, portIn); - mxCell port_out = new mxCell(null, geoPortOut, "shape=ellipse;perimter=ellipsePerimeter"); - port_out.setVertex(true); - graph.addCell(port_out, channelCell); // insert the output port of a channel - channelsOut.put(channel, port_out); - } else { - for (Selector s : channel.getSelectors()) { - Expression exp = s.getExpression(); - String selectorName = exp.toString(); - String channelName = channel.getChannelName(); - String cellName = channelName + " (" + selectorName + ")"; - - Object channelCell = graph.insertVertex(parent, null, cellName, 150, 20, 60, 30); // insert a channel as a vertex - - mxCell portIn = new mxCell(null, geoPortIn, "shape=ellipse;perimter=ellipsePerimeter"); - portIn.setVertex(true); - graph.addCell(portIn, channelCell); // insert the input port of a channel - channelsIn.put(channel, portIn); - - mxCell portOut = new mxCell(null, geoPortOut, "shape=ellipse;perimter=ellipsePerimeter"); - portOut.setVertex(true); - graph.addCell(portOut, channelCell); // insert the output port of a channel - channelsOut.put(channel, portOut); - } - } + mxCell portOut = new mxCell(null, geoPortOut, "shape=ellipse;perimter=ellipsePerimeter"); + portOut.setVertex(true); + graph.addCell(portOut, channelCell); // insert the output port of a channel + channelOutToCell.put(channel, portOut); + createChildChannelVertices(channelCell, channelNode, channelInToCell, channelOutToCell, geoPortIn, geoPortOut, w, h); } } else { - // I/O channel - if (channelsOut.get(channel) == null) { - if (Objects.equals(channel.getSelectors().toString(), "[]")) { - Object chCell = graph.insertVertex(parent, null, channel.getChannelName(), 150, 20, 30, 30); // insert a channel as a vertex - mxCell port_in = new mxCell(null, geoPortIn, "shape=ellipse;perimter=ellipsePerimeter"); - port_in.setVertex(true); - graph.addCell(port_in, chCell); // insert the input port of a channel - mxCell port_out = new mxCell(null, geoPortOut, "shape=ellipse;perimter=ellipsePerimeter"); - port_out.setVertex(true); - graph.addCell(port_out, chCell); // insert the output port of a channel - channelsIn.put(channel, port_in); - channelsOut.put(channel, port_out); - } else { - for (Selector s : channel.getSelectors()) { - Expression exp = s.getExpression(); - String selectorName = exp.toString(); - String channelName = channel.getChannelName(); - String cellName = channelName + " (" + selectorName + ")"; - Object chCell = graph.insertVertex(parent, null, cellName, 150, 20, 60, 30); // insert a channel as a vertex - - mxCell port_in = new mxCell(null, geoPortIn, "shape=ellipse;perimter=ellipsePerimeter"); - port_in.setVertex(true); - graph.addCell(port_in, chCell); // insert the input port of a channel - channelsIn.put(channel, port_in); - - mxCell port_out = new mxCell(null, geoPortOut, "shape=ellipse;perimter=ellipsePerimeter"); - port_out.setVertex(true); - graph.addCell(port_out, chCell); // insert the output port of a channel - channelsOut.put(channel, port_out); - } - } + // Event channel + if (channelOutToCell.get(channel) == null) { + String channelName = channel.getChannelName(); + if (channel.getSelectors().size() > 0) { + channelName += "("; + String delimiter = ""; + for (Selector s: channel.getSelectors()) { + Expression exp = s.getExpression(); + String selectorName = exp.toString(); + channelName += delimiter + selectorName; + delimiter = ", "; + } + channelName += ")"; + } + int w = 40; + int h = 30; + if (channelNode.getChildren().size() > 0) { + w *= 2; + h *= 2; + } + mxCell channelCell = (mxCell) graph.insertVertex(parent, null, channelName, 150, 20, w, h, "verticalAlign=top"); // insert a channel as a vertex + channelToCell.put(channel, channelCell); + + mxCell portOut = new mxCell(null, geoPortOut, "shape=ellipse;perimter=ellipsePerimeter"); + portOut.setVertex(true); + graph.addCell(portOut, channelCell); // insert the output port of a channel + channelOutToCell.put(channel, portOut); + createChildChannelVertices(channelCell, channelNode, channelInToCell, channelOutToCell, geoPortIn, geoPortOut, w, h); } } } @@ -229,17 +232,17 @@ // output edge DataTransferChannel channel = ((ChannelNode) dataFlowEdge.getSource()).getChannel(); ResourcePath dstRes = ((ResourceNode) dataFlowEdge.getDestination()).getInSideResource(channel); - graph.insertEdge(parent, null, new Editor.SrcDstAttribute(channel, dstRes), channelsOut.get(channel), resources.get((ResourceNode) dataFlowEdge.getDestination()), "movable=false"); + graph.insertEdge(parent, null, new Editor.SrcDstAttribute(channel, dstRes), channelOutToCell.get(channel), resNodeToCell.get((ResourceNode) dataFlowEdge.getDestination()), "movable=false"); } else { // input edge DataTransferChannel channel = ((ChannelNode) dataFlowEdge.getDestination()).getChannel(); ResourcePath srcRes = ((ResourceNode) dataFlowEdge.getSource()).getOutSideResource(channel); Set> toRes = getResourceDependencyForChannel(channel, dataFlowGraph); for (Map.Entry RtoR : toRes) { - graph.insertEdge(parent, null, null, resources.get(RtoR.getValue()), resources.get(RtoR.getKey()), "dashed=true;movable=false"); + graph.insertEdge(parent, null, null, resNodeToCell.get(RtoR.getValue()), resNodeToCell.get(RtoR.getKey()), "dashed=true;movable=false"); } - graph.insertEdge(parent, null, new Editor.SrcDstAttribute(srcRes, channel), resources.get((ResourceNode) dataFlowEdge.getSource()), channelsIn.get(channel), "movable=false"); + graph.insertEdge(parent, null, new Editor.SrcDstAttribute(srcRes, channel), resNodeToCell.get((ResourceNode) dataFlowEdge.getSource()), channelInToCell.get(channel), "movable=false"); } } @@ -247,7 +250,7 @@ // reference edges DataTransferChannel channel = (DataTransferChannel) ch; for (ResourcePath refRes : channel.getReferenceResources()) { - graph.insertEdge(parent, null, null, resources.get(dataFlowGraph.getResourceNode(refRes)), channelsIn.get(channel), "dashed=true;movable=false"); + graph.insertEdge(parent, null, null, resNodeToCell.get(dataFlowGraph.getResourceNode(refRes)), channelInToCell.get(channel), "dashed=true;movable=false"); } } } finally { @@ -255,14 +258,75 @@ } } - private void createChildResourceVertices(Object resource, ResourceNode resNode, Map resources, int w, int h) { - for (ResourceNode childNode : resNode.getChildren()) { - ResourcePath childRes = childNode.getPrimaryResourcePath(); - Object childResource = graph.insertVertex(resource, null, childRes.getName(), 0, 0, w, h, "shape=ellipse;perimeter=ellipsePerimeter"); // insert a resource as a vertex - resources.put(childNode, childResource); - createChildResourceVertices(childResource, childNode, resources, w, h); + private void createChildResourceVertices(mxCell parentCell, ResourceNode parentResNode, int w, int h) { + for (ResourceNode resNode: parentResNode.getChildren()) { + ResourcePath resPath = resNode.getPrimaryResourcePath(); + mxCell resourceCell = (mxCell) graph.insertVertex(parentCell, null, resPath.getName(), 0, 0, w, h, "shape=ellipse;perimeter=ellipsePerimeter;verticalAlign=top"); // insert a resource as a vertex + resNodeToCell.put(resNode, resourceCell); + createChildResourceVertices(resourceCell, resNode, w, h); } } + + private void createChildChannelVertices(mxCell parentCell, ChannelNode parentChannelNode, + Map channelInToCell, Map channelOutToCell, mxGeometry geoPortIn, mxGeometry geoPortOut, int w, int h) { + for (ChannelNode channelNode: parentChannelNode.getChildren()) { + DataTransferChannel channel = channelNode.getChannel(); + if (!channel.getInputResources().isEmpty()) { + // Normal channel + if (channelInToCell.get(channel) == null || channelOutToCell.get(channel) == null) { + String channelName = channel.getChannelName(); + if (channel.getSelectors().size() > 0) { + channelName += "("; + String delimiter = ""; + for (Selector s: channel.getSelectors()) { + Expression exp = s.getExpression(); + String selectorName = exp.toString(); + channelName += delimiter + selectorName; + delimiter = ", "; + } + channelName += ")"; + } + mxCell channelCell = (mxCell) graph.insertVertex(parentCell, null, channelName, w / 4, h / 4, w / 2, h / 2, "verticalAlign=top"); // insert a channel as a vertex + channelToCell.put(channel, channelCell); + + mxCell portIn = new mxCell(null, geoPortIn, "shape=ellipse;perimter=ellipsePerimeter"); + portIn.setVertex(true); + graph.addCell(portIn, channelCell); // insert the input port of a channel + channelInToCell.put(channel, portIn); + + mxCell portOut = new mxCell(null, geoPortOut, "shape=ellipse;perimter=ellipsePerimeter"); + portOut.setVertex(true); + graph.addCell(portOut, channelCell); // insert the output port of a channel + channelOutToCell.put(channel, portOut); + createChildChannelVertices(channelCell, channelNode, channelInToCell, channelOutToCell, geoPortIn, geoPortOut, w / 2, h / 2); + } + } else { + // Event channel + if (channelOutToCell.get(channel) == null) { + String channelName = channel.getChannelName(); + if (channel.getSelectors().size() > 0) { + channelName += "("; + String delimiter = ""; + for (Selector s: channel.getSelectors()) { + Expression exp = s.getExpression(); + String selectorName = exp.toString(); + channelName += delimiter + selectorName; + delimiter = ", "; + } + channelName += ")"; + } + mxCell channelCell = (mxCell) graph.insertVertex(parentCell, null, channelName, w / 4, h / 4, w / 2, h / 2, "verticalAlign=top"); // insert a channel as a vertex + channelToCell.put(channel, channelCell); + + mxCell portOut = new mxCell(null, geoPortOut, "shape=ellipse;perimter=ellipsePerimeter"); + portOut.setVertex(true); + graph.addCell(portOut, channelCell); // insert the output port of a channel + channelOutToCell.put(channel, portOut); + createChildChannelVertices(channelCell, channelNode, channelInToCell, channelOutToCell, geoPortIn, geoPortOut, w / 2, h / 2); + } + } + } + } private Set> getResourceDependencyForChannel(DataTransferChannel ch, DataFlowGraph dataFlowGraph) { Set> resourceDependency = new HashSet<>(); @@ -292,4 +356,12 @@ public DataFlowGraph getDataFlowGraph() { return dataFlowGraph; } + + public HashMap getResNodeToCell() { + return resNodeToCell; + } + + public HashMap getChannelToCell() { + return channelToCell; + } } diff --git a/AlgebraicDataflowArchitectureModel/src/application/simulator/InputEventCellEditor.java b/AlgebraicDataflowArchitectureModel/src/application/simulator/InputEventCellEditor.java index 82f6fe3..569fe28 100644 --- a/AlgebraicDataflowArchitectureModel/src/application/simulator/InputEventCellEditor.java +++ b/AlgebraicDataflowArchitectureModel/src/application/simulator/InputEventCellEditor.java @@ -105,24 +105,24 @@ if (!graphComponent.getGraph().getModel().isEdge(cell)) { Resource res = simulator.getCurState().getResource((String) ((mxCell) cell).getValue()); - ResourceIdentifier resId = res.getResourceIdentifier();//resource - ArrayList eventChs = new ArrayList<>();//iOchannelList - ArrayList messages = new ArrayList<>();//ADLmessage - ArrayList eventMessages = new ArrayList<>();//messageList + ResourceIdentifier resId = res.getResourceIdentifier(); // clicked resource + ArrayList eventChs = new ArrayList<>(); // eventchannelList + ArrayList messages = new ArrayList<>(); // ADLmessage + ArrayList eventMessages = new ArrayList<>(); // messageList ResourcePath eventResPath = null; - for (Channel ch: simulator.getModel().getInputChannels()) {//all channel + for (Channel ch: simulator.getModel().getInputChannels()) { // all channels eventResPath = getSelectableMessages(ch, resId, eventChs, messages, eventMessages, eventResPath); } - if(messages.isEmpty()) { + if (messages.isEmpty()) { return; } String[] eventList = messages.toArray(new String[messages.size()]); JComboBox event = new JComboBox(eventList); JPanel eventChoice = new JPanel(); - eventChoice.add(event);//FirstEventChoice + eventChoice.add(event); // FirstEventChoice int ret = JOptionPane.showConfirmDialog(window, eventChoice, "Event Choice", JOptionPane.OK_CANCEL_OPTION); if (ret == JOptionPane.OK_OPTION) { @@ -131,14 +131,14 @@ int i , eventNum; i = eventNum = 0; - for(String eventString : eventList) { + for (String eventString : eventList) { if(eventString.equals(event.getSelectedItem().toString())) { eventNum = i; } i++; } - JTextArea textArea = new JTextArea(eventMessages.get(eventNum).toString(), 10, 30);//EventInput + JTextArea textArea = new JTextArea(eventMessages.get(eventNum).toString(), 10, 30); // EventInput inputEvent.add(textArea); int approve = JOptionPane.showConfirmDialog(window, inputEvent, "Event Code", JOptionPane.OK_CANCEL_OPTION); @@ -148,14 +148,11 @@ TokenStream stream = new Parser.TokenStream(); Parser parser = new Parser(stream); stream.addLine(textArea.getText()); - Expression eventMessage = parser.parseTerm(stream,simulator.getModel()); + Expression eventMessage = parser.parseTerm(stream, simulator.getModel()); Event newEvent = new Event(eventChs.get(eventNum), eventMessage, eventResPath, simulator.getCurState().getResource(resId)); simulator.transition(newEvent); -// SimulationLayout layout = new SimulationLayout(simulator.getCurState()); -// layout.constructSimulateGraph(graph, simulator); - graphComponent.setCellEditor(new InputEventCellEditor(window, graphComponent, simulator, this.editor)); } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork @@ -164,8 +161,8 @@ } } } - //resource - return; + // resource + return; } mxCellState state = graphComponent.getGraph().getView().getState(cell); @@ -191,21 +188,32 @@ private ResourcePath getSelectableMessages(Channel ch, ResourceIdentifier resId, ArrayList eventChs, ArrayList messages, ArrayList eventMessages, ResourcePath eventResPath) { - if (((DataTransferChannel)ch).getInputResources().size() == 0) {//ioch Or normalch - for (ChannelMember out: ((DataTransferChannel)ch).getOutputChannelMembers()) { + if (((DataTransferChannel) ch).getInputResources().size() == 0) { // event ch or normal ch + for (ChannelMember out: ((DataTransferChannel) ch).getOutputChannelMembers()) { ResourcePath resPath = out.getResource(); - if (!out.isOutside() && resId.isInstanceOf(resPath)) {//account.uid == acounts.123 + if (!out.isOutside() && resId.isInstanceOf(resPath)) { // account.uid == acounts.123 eventResPath = resPath; - eventChs.add(((DataTransferChannel)ch)); + eventChs.add(((DataTransferChannel) ch)); String message = null; - Expression mesExp = out.getStateTransition().getMessageExpression();//sync(a,b) - eventMessages.add(mesExp); + Expression mesExp = out.getStateTransition().getMessageExpression(); if (mesExp instanceof Term) { - message = ((Term) mesExp).getSymbol().toString();//sync + List pathParams = resPath.getPathParams(); + List children = ((Term) mesExp).getChildren(); + mesExp = new Term(((Term) mesExp).getSymbol()); + for (Expression child: children) { + if (!pathParams.contains(child)) { + ((Term) mesExp).addChild(child); + } else { + int idx = pathParams.indexOf(child); + ((Term) mesExp).addChild(resId.getPathParams().get(idx)); + } + } + message = ((Term) mesExp).getSymbol().toString(); } else if(mesExp instanceof Variable) { - message = ((Variable) mesExp).getName();//x,y,z.. + message = ((Variable) mesExp).getName(); } - messages.add(message);//pulldown + eventMessages.add(mesExp); + messages.add(message); // for the pull-down menu } } for (Channel childCh: ch.getChildren()) { @@ -215,57 +223,6 @@ return eventResPath; } -// private mxGraph constructNextSimulateGraph(Set simulateRes, DataTransferModel model,DataFlowGraph dataFlowGraph, mxGraph nextGraph) { -// bReflectingArchitectureModel = true; -// ((mxGraphModel) nextGraph.getModel()).clear(); -// Object parent = nextGraph.getDefaultParent(); -// nextGraph.getModel().beginUpdate(); -// -// try { -// Map resources = new HashMap<>(); -// -// // create resource vertices -// for (Resource resNode: simulateRes) { -// int w = 400; -// int h = 150; -// ResourcePath res = resNode.getResourceIdentifier(); -// Object resource = nextGraph.insertVertex(parent, null, -// res.toString(), x, y, w, h, -// "shape=ellipse;perimeter=ellipsePerimeter;verticalAlign=top;"); // insert a resource as a vertex -// resources.put(resNode, resource); -// -// createNextChildSimulateResourceVerticies(resource, resNode, resources, w, h); -// x+=400; -// } -// -// } finally { -// nextGraph.getModel().endUpdate(); -// } -// -// bReflectingArchitectureModel = false; -// return nextGraph; -// } -// -// private void createNextChildSimulateResourceVerticies(Object resource, Resource resNode, Map resources, int w, int h) { //sample -// -// if(resNode.getChildren() != null) { -// for (Resource childNode: resNode.getChildren()) { -// int i = 1; -// ResourcePath childRes = childNode.getResourceIdentifier(); -// Object childResource = graph.insertVertex(resource, null, -// childRes.toString(), x/i, 0, w/(i+1), h/(i+2), -// "shape=ellipse;perimeter=ellipsePerimeter;verticalAlign=top;"); // insert a resource as a vertex -// resources.put(childNode, childResource); -// i++; -// createNextChildSimulateResourceVerticies(childResource, childNode, resources, w, h); -// } -// } -// if (resNode.getChildren() == null || resNode.getChildren().size() == 0) { -// Object state = graph.insertVertex(resource, null, -// resNode.getState().getValue().toString(), 0.5, 0.5, 0.25, 0.25, "opacity=0;verticalAlign=down;", true); // insert a state label as a vertex -// } -// } - @Override public void stopEditing(boolean cancel) { if (editingCell != null) { @@ -287,8 +244,7 @@ } } if (selected != null) { - options.remove(selected); - options.add(0, selected); + attr.selectOption(selected); } graphComponent.labelChanged(cell, attr, trig); } diff --git a/AlgebraicDataflowArchitectureModel/src/code/ast/CodeUtil.java b/AlgebraicDataflowArchitectureModel/src/code/ast/CodeUtil.java index 99f1561..fd57c66 100644 --- a/AlgebraicDataflowArchitectureModel/src/code/ast/CodeUtil.java +++ b/AlgebraicDataflowArchitectureModel/src/code/ast/CodeUtil.java @@ -10,34 +10,4 @@ } return newString; } - - public static String getToStringExp(String typeName, String rawExp) { - if (typeName.equals("int")) { - return "Integer.toString(" + rawExp + ")"; - } else if (typeName.equals("float")) { - return "Float.toString(" + rawExp + ")"; - } else if (typeName.equals("double")) { - return "Double.toString(" + rawExp + ")"; - } else if (typeName.equals("boolean")) { - return "Boolean.toString(" + rawExp + ")"; - } else { - return rawExp + ".toString()"; - } - } - - public static String getToValueExp(String typeName, String strExp) { - if (typeName.equals("int")) { - return "Integer.parseInt(" + strExp + ")"; - } else if (typeName.equals("float")) { - return "Float.parseFloat(" + strExp + ")"; - } else if (typeName.equals("double")) { - return "Double.parseDouble(" + strExp + ")"; - } else if (typeName.equals("boolean")) { - return "Boolean.parseBoolean(" + strExp + ")"; - } else if (typeName.startsWith("ArrayList") || typeName.startsWith("List")) { - return "Arrays.asList(" + strExp + ".replace(\"[\",\"\").replace(\"]\",\"\").split(\",\",0))"; - } else { - return strExp; - } - } } diff --git a/AlgebraicDataflowArchitectureModel/src/generators/CodeGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/CodeGenerator.java index 2a7db3b..b23a6a3 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/CodeGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/CodeGenerator.java @@ -10,6 +10,7 @@ import java.util.Set; import java.util.Stack; +import algorithms.TypeInference; import code.ast.Block; import code.ast.CompilationUnit; import code.ast.FieldDeclaration; @@ -50,13 +51,15 @@ */ public abstract class CodeGenerator { public static final String fieldOfResourceState = "value"; + public static final String methoNameOfResourceState = "Value"; public static final String getterPrefix = "get"; - public static final String getterOfResourceState = "getValue"; + public static final String getterOfResourceState = getterPrefix + methoNameOfResourceState; // "getValue" public static final String updateMethodPrefix = "update"; public static final String from = "From"; public static final String _for = "For"; private static String mainTypeName = null; private static ILanguageSpecific langSpec = null; + private static IPlatformSpecific platformSpec = null; public static String getMainTypeName() { return mainTypeName; @@ -120,6 +123,8 @@ if (res.getParent() == null) return true; if (res.getChildren() == null || res.getChildren().size() == 0) return false; if (res.getNumParameters() > 0 && res.getChildren().size() == 1 && res.getChildren().iterator().next().getNumParameters() > 0) return false; + if (res.getChildren().size() == 1 && res.getChildren().iterator().next().getNumParameters() > 0 + && (res.getChildren().iterator().next().getChildren() == null || res.getChildren().iterator().next().getChildren().size() == 0)) return false; return true; // return res.getParent() == null || !(res.getChildren() == null || res.getChildren().size() == 0); } @@ -132,47 +137,41 @@ * @param langSpec specified language * @return source codes */ - public ArrayList generateCode(DataTransferModel model, DataFlowGraph flowGraph, ILanguageSpecific langSpec) { + public ArrayList generateCode(DataTransferModel model, DataFlowGraph flowGraph, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { CodeGenerator.langSpec = langSpec; + CodeGenerator.platformSpec = platformSpec; ArrayList codes = new ArrayList<>(); - // Get the dependency among root nodes. - Map> dependedRootComponentGraph = getDependedRootComponentGraph(model); - - // Sort the all components. - ArrayList components = determineComponentOrder(flowGraph, dependedRootComponentGraph); - - // Add the main component. - if (mainTypeName == null) { - mainTypeName = langSpec.getMainComponentName(); + Map> dependedRootComponentGraph = null; + Collection components = null; + if (platformSpec.isMonolithic()) { + // To build monolithic application, the dependency of the components should be resolved in advance. + + // Get the dependency among root nodes. + dependedRootComponentGraph = getDependedRootComponentGraph(model); + + // Sort the all components. + components = determineComponentOrder(flowGraph, dependedRootComponentGraph); + } else { + // Get the all components. + components = flowGraph.getResourceNodes(); } - TypeDeclaration mainComponent = langSpec.newTypeDeclaration(mainTypeName); - MethodDeclaration mainConstructor = mainComponent.createConstructor(); - CompilationUnit mainCU = langSpec.newCompilationUnit(mainComponent); - codes.add(mainCU); // Generate the other components. - generateCodeFromFlowGraph(model, flowGraph, components, dependedRootComponentGraph, mainComponent, mainConstructor, codes, langSpec); + generateCodeFromFlowGraph(model, flowGraph, components, codes, dependedRootComponentGraph, platformSpec, langSpec); return codes; } - public abstract void generateCodeFromFlowGraph(DataTransferModel model, DataFlowGraph flowGraph, ArrayList components, Map> dependedRootComponentGraph, - TypeDeclaration mainComponent, MethodDeclaration mainConstructor, ArrayList codes, ILanguageSpecific langSpec); + public abstract void generateCodeFromFlowGraph(DataTransferModel model, DataFlowGraph flowGraph, Collection components, ArrayList codes, + Map> dependedRootComponentGraph, IPlatformSpecific platformSpec, ILanguageSpecific langSpec); private static Map> getDependedRootComponentGraph(DataTransferModel model) { Map> dependedComponentGraph = new HashMap<>(); for (Channel ch: model.getChannels()) { - DataTransferChannel dtCh = (DataTransferChannel) ch; Set inRes = new HashSet<>(); Set outRes = new HashSet<>(); - for (ChannelMember cm: dtCh.getChannelMembers()) { - if (cm.isOutside()) { - outRes.add(cm.getResource().getResourceHierarchy()); - } else { - inRes.add(cm.getResource().getResourceHierarchy()); - } - } + getDependedRootComponentGraphSub(ch, inRes, outRes, true); if (outRes.size() > 0 && inRes.size() > 0) { for (ResourceHierarchy out: outRes) { for (ResourceHierarchy in: inRes) { @@ -181,13 +180,32 @@ dependings = new HashSet<>(); dependedComponentGraph.put(out.getRoot(), dependings); } - dependings.add(in.getRoot()); + if (!out.getRoot().equals(in.getRoot())) { + dependings.add(in.getRoot()); + } } } } } return dependedComponentGraph; } + + private static void getDependedRootComponentGraphSub(Channel ch, Set inRes, Set outRes, boolean isRoot) { + DataTransferChannel dtCh = (DataTransferChannel) ch; + for (ChannelMember cm: dtCh.getChannelMembers()) { + if (!isRoot && !cm.isOutside()) { + outRes.add(cm.getResource().getResourceHierarchy()); // dependency to a descendant channel resource. + } + if (cm.isOutside()) { + outRes.add(cm.getResource().getResourceHierarchy()); // dependency to an outside resource. + } else { + inRes.add(cm.getResource().getResourceHierarchy()); // dependency from an inside resource. + } + } + for (Channel childCh: ch.getChildren()) { + getDependedRootComponentGraphSub(childCh, inRes, outRes, false); + } + } private static ArrayList determineComponentOrder(DataFlowGraph graph, Map> dependedRootComponentGraph) { ArrayList objects = new ArrayList<>(); @@ -207,7 +225,7 @@ // For each incoming PUSH transfer. for (Edge chToRes: curResNode.getInEdges()) { for (Edge resToCh: chToRes.getSource().getInEdges()) { - if (!(resToCh instanceof DataFlowEdge) || ((PushPullAttribute)((DataFlowEdge) resToCh).getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { + if (!(resToCh instanceof DataFlowEdge) || ((PushPullAttribute)((DataFlowEdge) resToCh).getAttribute()).getSelectedOption() == PushPullValue.PUSH) { topologicalSort((ResourceNode) resToCh.getSource(), allNodes, dependedRootComponentGraph, visited, orderedList); } } @@ -216,7 +234,7 @@ if (curResNode instanceof ResourceNode) { for (Edge resToCh: curResNode.getOutEdges()) { DataFlowEdge de = (DataFlowEdge) resToCh; - if (((PushPullAttribute) de.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { + if (((PushPullAttribute) de.getAttribute()).getSelectedOption() != PushPullValue.PUSH) { for (Edge chToRes : resToCh.getDestination().getOutEdges()) { topologicalSort((ResourceNode) chToRes.getDestination(), allNodes, dependedRootComponentGraph, visited, orderedList); } @@ -282,7 +300,9 @@ } if (constructor.getParameters() != null) { for (VariableDeclaration var: constructor.getParameters()) { - parameters.add(var.getName()); + if (!parameters.contains(var.getName())) { + parameters.add(var.getName()); + } } } @@ -312,26 +332,36 @@ return dstRes; } - protected void fillStateGetterMethod(MethodDeclaration stateGetter, ResourceHierarchy resourceHierarchy, Type resStateType, ILanguageSpecific langSpec) { + protected void fillStateGetterMethod(MethodDeclaration stateGetter, ResourceHierarchy resourceHierarchy, Type resStateType, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { // returns the state field when all incoming data-flow edges are PUSH-style. if (langSpec.isValueType(resStateType)) { stateGetter.addStatement(langSpec.getReturnStatement(langSpec.getFieldAccessor(fieldOfResourceState)) + langSpec.getStatementDelimiter()); // return value; } else { if (resourceHierarchy.getChildren() != null && resourceHierarchy.getChildren().size() == 1 && resourceHierarchy.getChildren().iterator().next().getNumParameters() > 0) { // list or map - String implTypeName = resStateType.getImplementationTypeName(); - // copy the current state to be returned as a 'value' - List parameters = new ArrayList<>(); - parameters.add(langSpec.getFieldAccessor(fieldOfResourceState)); - stateGetter.addStatement(langSpec.getReturnStatement(langSpec.getConstructorInvocation(implTypeName, parameters)) + langSpec.getStatementDelimiter()); // return new Resource(value); - } else { - if (resourceHierarchy.getChildren() == null || resourceHierarchy.getChildren().size() == 0) { - // a leaf resource +// if (!platformSpec.isMonolithic()) { +// // For REST API +// stateGetter.addStatement(langSpec.getReturnStatement(langSpec.getFieldAccessor(fieldOfResourceState)) + langSpec.getStatementDelimiter()); // return value; +// } else { String implTypeName = resStateType.getImplementationTypeName(); // copy the current state to be returned as a 'value' List parameters = new ArrayList<>(); parameters.add(langSpec.getFieldAccessor(fieldOfResourceState)); stateGetter.addStatement(langSpec.getReturnStatement(langSpec.getConstructorInvocation(implTypeName, parameters)) + langSpec.getStatementDelimiter()); // return new Resource(value); +// } + } else { + if (resourceHierarchy.getChildren() == null || resourceHierarchy.getChildren().size() == 0) { +// // a leaf resource +// if (!platformSpec.isMonolithic()) { +// // For REST API +// stateGetter.addStatement(langSpec.getReturnStatement(langSpec.getFieldAccessor(fieldOfResourceState)) + langSpec.getStatementDelimiter()); // return value; +// } else { + String implTypeName = resStateType.getImplementationTypeName(); + // copy the current state to be returned as a 'value' + List parameters = new ArrayList<>(); + parameters.add(langSpec.getFieldAccessor(fieldOfResourceState)); + stateGetter.addStatement(langSpec.getReturnStatement(langSpec.getConstructorInvocation(implTypeName, parameters)) + langSpec.getStatementDelimiter()); // return new Resource(value); +// } } else { Term composer = null; Term composerSub = new Constant(DataConstraintModel.nil); @@ -340,10 +370,10 @@ String childTypeName = getComponentName(child, langSpec); String fieldName = langSpec.toVariableName(childTypeName); Term childGetter = null; - if ((child.getChildren() == null || child.getChildren().size() == 0) && child.getNumParameters() == 0) { + if (!generatesComponent(child)) { // the child is not a class - childGetter = new Term(new Symbol("get" + childTypeName, 1, Symbol.Type.METHOD)); - childGetter.addChild(new Constant("this")); + childGetter = new Term(new Symbol(getterPrefix + childTypeName, 1, Symbol.Type.METHOD)); + childGetter.addChild(new Constant(langSpec.getSelfExp())); } else { // the child is a class childGetter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); @@ -369,14 +399,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) { @@ -402,7 +434,7 @@ } } - protected void declareAccessorInMainComponent(TypeDeclaration mainComponent, ResourceNode accessRes, MethodDeclaration stateGetter, ILanguageSpecific langSpec) { + protected void declareAccessorInMainComponent(TypeDeclaration mainComponent, ResourceNode accessRes, MethodDeclaration stateGetter, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { List mainGetterParams = new ArrayList<>(); int v = 1; for (Expression param: accessRes.getPrimaryResourcePath().getPathParams()) { @@ -415,6 +447,11 @@ } v++; } + ResourcePath accessResPath = new ResourcePath(accessRes.getPrimaryResourcePath()); + for (int i = 0; i < mainGetterParams.size(); i++) { + Parameter pathParam = new Parameter(mainGetterParams.get(i).getName()); + accessResPath.replacePathParam(i, pathParam, null); + } MethodDeclaration accessor = null; if (mainGetterParams.size() == 0) { accessor = langSpec.newMethodDeclaration(getterPrefix + getComponentName(accessRes.getResourceHierarchy(), langSpec), getImplStateType(accessRes.getResourceHierarchy(), langSpec)); @@ -422,7 +459,7 @@ accessor = langSpec.newMethodDeclaration(getterPrefix + getComponentName(accessRes.getResourceHierarchy(), langSpec), false, getImplStateType(accessRes.getResourceHierarchy(), langSpec), mainGetterParams); } Block block = new Block(); - Expression getState = getPullAccessor().getDirectStateAccessorFor(accessRes.getPrimaryResourcePath(), null); + Expression getState = getPullAccessor(platformSpec).getDirectStateAccessorFor(accessResPath, null); block.addStatement(langSpec.getReturnStatement(getState.toImplementation(new String[] {null})) + langSpec.getStatementDelimiter()); // if (stateGetter.getParameters() == null || stateGetter.getParameters().size() == 0) { // block.addStatement(langSpec.getReturnStatement(langSpec.getMethodInvocation(accessRes.getResourceName(), stateGetter.getName())) + langSpec.getStatementDelimiter()); @@ -499,134 +536,484 @@ } return input; } + + + protected MethodDeclaration getMethod(TypeDeclaration component, String methodName, List params) { + for (MethodDeclaration m: component.getMethods()) { + if (m.getName().equals(methodName)) { + if (m.getParameters() == null && (params == null || params.size() == 0)) return m; + if (m.getParameters() != null && params != null && m.getParameters().size() == params.size()) { + boolean matchParams = true; + for (int i = 0; i < m.getParameters().size(); i++) { + if (!m.getParameters().get(i).getType().equals(params.get(i).getType())) { + matchParams = false; + break; + } + } + if (matchParams) return m; + } + } + } + return null; + } protected MethodDeclaration getMethod(TypeDeclaration component, String methodName) { for (MethodDeclaration m: component.getMethods()) { if (m.getName().equals(methodName)) return m; } return null; + } + + protected boolean isPut(ChannelMember cm) { + return cm.getStateTransition().isRightUnary(); } - protected IResourceStateAccessor getPushAccessor() { - return new IResourceStateAccessor() { - @Override - public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { - ResourcePath targetRes= target.getResource(); - ResourcePath fromRes= from.getResource(); - if (targetRes.equals(fromRes)) { - return new Field(fieldOfResourceState, - targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() - : DataConstraintModel.typeInt); + protected boolean isDelete(ChannelMember cm) { + boolean isDelete = false; + Expression nextExp = cm.getStateTransition().getNextStateExpression(); + if (nextExp instanceof Term) { + Symbol rootSymbol = ((Term) nextExp).getSymbol(); + if (rootSymbol.equals(DataConstraintModel.delete) || rootSymbol.equals(DataConstraintModel.remove)) { + isDelete = true; + } else if (rootSymbol.equals(DataConstraintModel.cond)) { + Expression childExp = ((Term) nextExp).getChild(1); + if (childExp instanceof Term) { + rootSymbol = ((Term) childExp).getSymbol(); + if (rootSymbol.equals(DataConstraintModel.delete) || rootSymbol.equals(DataConstraintModel.remove)) { + isDelete = true; + } } - // use the cached value as the current state - return new Field(targetRes.getLeafResourceName(), - targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() - : DataConstraintModel.typeInt); - } - - @Override - public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { - ResourcePath targerRes= target.getResource(); - return new Parameter(targerRes.getLeafResourceName(), - targerRes.getResourceStateType() != null ? targerRes.getResourceStateType() - : DataConstraintModel.typeInt); - } - - @Override - public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { - if (fromRes != null && targetRes.equals(fromRes)) { - return new Field(fieldOfResourceState, - targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() - : DataConstraintModel.typeInt); + childExp = ((Term) nextExp).getChild(2); + if (childExp instanceof Term) { + rootSymbol = ((Term) childExp).getSymbol(); + if (rootSymbol.equals(DataConstraintModel.delete) || rootSymbol.equals(DataConstraintModel.remove)) { + isDelete = true; + } } - // for reference channel member - return new Parameter(targetRes.getLeafResourceName(), - targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() - : DataConstraintModel.typeInt); } - }; + } + return isDelete; } - protected IResourceStateAccessor getPullAccessor() { - return new IResourceStateAccessor() { - @Override - public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { - ResourcePath targetRes= target.getResource(); - if (from != null) { - ResourcePath fromRes= from.getResource(); - if (!target.isOutside()) { - return getDirectStateAccessorFor(targetRes, fromRes); - } - Term getter = null; - String targetComponentName = getComponentName(targetRes.getResourceHierarchy(), langSpec); - if (generatesComponent(targetRes.getResourceHierarchy())) { - getter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); - getter.addChild(new Field(langSpec.toVariableName(targetComponentName), targetRes.getResourceStateType())); - } else { - String parentName = langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy().getParent(), langSpec)); - Type parentType = targetRes.getResourceHierarchy().getParent().getResourceStateType(); - getter = new Term(new Symbol(getterPrefix + targetComponentName, 1, Symbol.Type.METHOD)); - getter.addChild(new Field(parentName, parentType)); - } - return getter; + protected String getGetterResourcePathAndPathParams(ResourcePath resPath, List pathParams, + IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { + int v = 1; + List params = new ArrayList<>(); + for (Expression pathParam: resPath.getPathParams()) { + if (pathParam instanceof Variable) { + Variable var = (Variable) pathParam; + String paramName = var.getName(); + params.add("{" + paramName + "}"); + VariableDeclaration param = langSpec.newVariableDeclaration(var.getType(), paramName); + if (!platformSpec.isMonolithic()) ((RestApiSpecific) platformSpec).addPathParamAnnotation(param, paramName); + pathParams.add(param); + } else if (pathParam instanceof Term) { + Term var = (Term) pathParam; + String paramName = "v" + v; + params.add("{" + paramName + "}"); + VariableDeclaration param = langSpec.newVariableDeclaration(var.getType(), paramName); + if (!platformSpec.isMonolithic()) ((RestApiSpecific) platformSpec).addPathParamAnnotation(param, paramName); + pathParams.add(param); + } + } + return resPath.getResourceHierarchy().toResourcePath(params); + } + + protected String getUpdateResourcePathAndPathParams(ResourcePath resPath, ArrayList pathParams, boolean isRestAPI, + IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { + int v = 1; + List params = new ArrayList<>(); + for (Expression pathParam: resPath.getPathParams()) { + if (pathParam instanceof Variable) { + Variable var = (Variable) pathParam; + String paramName = null; + if (isRestAPI) { + paramName = var.getName(); } else { - return getDirectStateAccessorFor(targetRes, null); + paramName = "self" + (v > 1 ? v : ""); + } + params.add("{" + paramName + "}"); + VariableDeclaration param = langSpec.newVariableDeclaration(var.getType(), paramName); + if (isRestAPI) ((RestApiSpecific) platformSpec).addPathParamAnnotation(param, paramName); + pathParams.add(param); + } else if (pathParam instanceof Term) { + Term var = (Term) pathParam; + String paramName = null; + if (isRestAPI) { + paramName = "v" + v; + } else { + paramName = "self" + (v > 1 ? v : ""); + } + params.add("{" + paramName + "}"); + VariableDeclaration param = langSpec.newVariableDeclaration(var.getType(), paramName); + if (isRestAPI) ((RestApiSpecific) platformSpec).addPathParamAnnotation(param, paramName); + pathParams.add(param); + } + v++; + } + return resPath.getResourceHierarchy().toResourcePath(params); + } + + protected String getInputMethodResourcePathAndPathParams(ResourcePath resPath, ArrayList rootInputParams, + IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { + int v = 1; + List params = new ArrayList<>(); + if (resPath.getLastParam() != null) { + Expression pathParam = resPath.getLastParam(); + if (pathParam instanceof Variable) { + Variable var = (Variable) pathParam; + String paramName = var.getName(); + params.add("{" + paramName + "}"); + VariableDeclaration param = langSpec.newVariableDeclaration(var.getType(), paramName); + if (!platformSpec.isMonolithic()) ((RestApiSpecific) platformSpec).addPathParamAnnotation(param, paramName); + rootInputParams.add(param); + } else if (pathParam instanceof Term) { + Term var = (Term) pathParam; + String paramName = "v" + v; + params.add("{" + paramName + "}"); + VariableDeclaration param = langSpec.newVariableDeclaration(var.getType(), paramName); + if (!platformSpec.isMonolithic()) ((RestApiSpecific) platformSpec).addPathParamAnnotation(param, paramName); + rootInputParams.add(param); + } + v++; + } + if (resPath.getParent() != null) { + for (Expression pathParam: resPath.getParent().getPathParams()) { + if (pathParam instanceof Variable) { + Variable var = (Variable) pathParam; + String paramName = var.getName(); + params.add("{" + paramName + "}"); + VariableDeclaration param = langSpec.newVariableDeclaration(var.getType(), paramName); + if (!platformSpec.isMonolithic()) ((RestApiSpecific) platformSpec).addPathParamAnnotation(param, paramName); + rootInputParams.add(param); + } else if (pathParam instanceof Term) { + Term var = (Term) pathParam; + String paramName = "v" + v; + params.add("{" + paramName + "}"); + VariableDeclaration param = langSpec.newVariableDeclaration(var.getType(), paramName); + if (!platformSpec.isMonolithic()) ((RestApiSpecific) platformSpec).addPathParamAnnotation(param, paramName); + rootInputParams.add(param); + } + v++; + } + } + return resPath.getResourceHierarchy().toResourcePath(params); + } + + protected void generatePullDataTransfer(MethodDeclaration methodBody, String fromResourceName, String fromResourcePath, Type fromResourceType, boolean doesAddFirst, + IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { + RestApiSpecific restApiSpec = (RestApiSpecific) platformSpec; + String varName = new String(fromResourceName); + String respTypeName = fromResourceType.getInterfaceTypeName(); + String respImplTypeName = fromResourceType.getImplementationTypeName(); + String respConverter = ""; + if (DataConstraintModel.typeList.isAncestorOf(fromResourceType) && fromResourceType != DataConstraintModel.typeList) { + Type compType = TypeInference.getListComponentType(fromResourceType); + if (DataConstraintModel.typeTuple.isAncestorOf(compType)) { + varName += "_json"; + String mapTypeName = convertFromEntryToMapType(compType, langSpec); + respTypeName = langSpec.newListType(mapTypeName).getInterfaceTypeName(); + respConverter += langSpec.getVariableDeclaration(fromResourceType.getInterfaceTypeName(), fromResourceName) + langSpec.getAssignment() + langSpec.getConstructorInvocation(fromResourceType.getImplementationTypeName(), null) + langSpec.getStatementDelimiter() + "\n"; + respConverter += langSpec.getForStatementForCollection("i", mapTypeName, varName) + "\n"; + respConverter += "\t" + langSpec.getMethodInvocation(fromResourceName, DataConstraintModel.append.getImplName(), List.of(getCodeForConversionFromMapToTuple(compType, "i", langSpec))) + langSpec.getStatementDelimiter() + "\n"; + respConverter += langSpec.getEndForStatement("i") ; + restApiSpec.addJsonException(methodBody); + } else if (DataConstraintModel.typeMap.isAncestorOf(compType)) { + // To do. + } + } else if (DataConstraintModel.typeTuple.isAncestorOf(fromResourceType)) { + varName += "_json"; + respTypeName = convertFromEntryToMapType(fromResourceType, langSpec); + respConverter += langSpec.getVariableDeclaration(fromResourceType.getInterfaceTypeName(), fromResourceName) + langSpec.getAssignment() + getCodeForConversionFromMapToTuple(fromResourceType, varName, langSpec) + langSpec.getStatementDelimiter(); + respImplTypeName = "HashMap"; + } else if (DataConstraintModel.typePair.isAncestorOf(fromResourceType)) { + varName += "_json"; + respTypeName = convertFromEntryToMapType(fromResourceType, langSpec); + respConverter += langSpec.getVariableDeclaration(fromResourceType.getInterfaceTypeName(), fromResourceName) + langSpec.getAssignment() + getCodeForConversionFromMapToPair(fromResourceType, varName, langSpec) + langSpec.getStatementDelimiter(); + respImplTypeName = "HashMap"; + } else if (DataConstraintModel.typeMap.isAncestorOf(fromResourceType)) { + varName += "_json"; + respTypeName = convertFromEntryToMapType(fromResourceType, langSpec); + respConverter += langSpec.getVariableDeclaration(fromResourceType.getInterfaceTypeName(), fromResourceName) + langSpec.getAssignment() + langSpec.getConstructorInvocation(fromResourceType.getImplementationTypeName(), null) + langSpec.getStatementDelimiter() + "\n"; + respConverter += getCodeForConversionFromMapToMap(fromResourceType, varName, fromResourceName, langSpec); + respImplTypeName = "HashMap"; + } + if (doesAddFirst) { + if (respConverter.length() > 0) { + methodBody.addFirstStatement(respConverter); + } + methodBody.addFirstStatement(langSpec.getVariableDeclaration(respTypeName, varName) + langSpec.getAssignment() + restApiSpec.getHttpMethodCallWithResponseStatement(restApiSpec.getBaseURL(), fromResourcePath, getterPrefix, respImplTypeName)); + } else { + methodBody.addStatement(langSpec.getVariableDeclaration(respTypeName, varName) + langSpec.getAssignment() + restApiSpec.getHttpMethodCallWithResponseStatement(restApiSpec.getBaseURL(), fromResourcePath, getterPrefix, respImplTypeName)); + if (respConverter.length() > 0) { + methodBody.addStatement(respConverter); + } + } + } + + protected String convertFromEntryToMapType(Type type, ILanguageSpecific langSpec) { + String mapTypeName = null; + if (DataConstraintModel.typePair.isAncestorOf(type)) { + Type compType = TypeInference.getPairComponentType(type); + String wrapperType = DataConstraintModel.getWrapperType(compType); + if (wrapperType != null) { + mapTypeName = langSpec.newMapType(DataConstraintModel.typeString, wrapperType).getInterfaceTypeName(); + } else { + mapTypeName = langSpec.newMapType(DataConstraintModel.typeString, compType.getInterfaceTypeName()).getInterfaceTypeName(); + } + } else if (DataConstraintModel.typeMap.isAncestorOf(type)) { + List compTypes = TypeInference.getMapComponentTypes(type); + String wrapperType = DataConstraintModel.getWrapperType(compTypes.get(1)); + if (wrapperType != null) { + mapTypeName = langSpec.newMapType(DataConstraintModel.typeString, wrapperType).getInterfaceTypeName(); + } else { + mapTypeName = langSpec.newMapType(DataConstraintModel.typeString, compTypes.get(1).getInterfaceTypeName()).getInterfaceTypeName(); + } + } else { + mapTypeName = type.getInterfaceTypeName(); + mapTypeName = mapTypeName.replace(DataConstraintModel.typeTuple.getInterfaceTypeName(), DataConstraintModel.typeMap.getInterfaceTypeName()); + for (int idx = mapTypeName.indexOf("<", 0); idx >= 0; idx = mapTypeName.indexOf("<", idx + 1)) { // Java specific + int to = mapTypeName.indexOf(",", idx); // Java specific + if (to > idx) { + mapTypeName = mapTypeName.substring(0, idx + 1) + DataConstraintModel.typeString.getInterfaceTypeName() + mapTypeName.substring(to); // All elements except for the last one have the string type. } } + } + return mapTypeName; + } - @Override - public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { - ResourcePath targetRes= target.getResource(); - if (from != null) { - ResourcePath fromRes= from.getResource(); - if (!target.isOutside()) { - return getDirectStateAccessorFor(targetRes, fromRes); - } - Term getter = null; - String targetComponentName = getComponentName(targetRes.getResourceHierarchy(), langSpec); - if (generatesComponent(targetRes.getResourceHierarchy())) { - getter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); - getter.addChild(new Field(langSpec.toVariableName(targetComponentName), targetRes.getResourceStateType())); - } else { - String parentName = langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy().getParent(), langSpec)); - Type parentType = targetRes.getResourceHierarchy().getParent().getResourceStateType(); - getter = new Term(new Symbol(getterPrefix + targetComponentName, 1, Symbol.Type.METHOD)); - getter.addChild(new Field(parentName, parentType)); - } - return getter; - } else { - return getDirectStateAccessorFor(targetRes, null); - } + protected String getCodeForConversionFromMapToTuple(Type tupleType, String mapVar, ILanguageSpecific langSpec) { + String decoded = "$x"; + List elementsTypes = TypeInference.getTupleComponentTypes(tupleType); + String elementBase = mapVar; + for (Type elmType: elementsTypes.subList(0, elementsTypes.size() - 1)) { + elementBase = langSpec.getFirstEntryFromMapExp(elementBase); // elementBase.entrySet().iterator().next() + if (elmType == DataConstraintModel.typeBoolean + || elmType == DataConstraintModel.typeInt + || elmType == DataConstraintModel.typeLong + || elmType == DataConstraintModel.typeFloat + || elmType == DataConstraintModel.typeDouble) { + String getKey = langSpec.getMethodInvocation(elementBase, DataConstraintModel.fst.getImplName()); // elementBase.getKey() + String elmVal = langSpec.getStringToValueExp(elmType.getImplementationTypeName(), getKey); // Integer.parseInt(elementBase.getKey()) + decoded = decoded.replace("$x", langSpec.getPairExp(elmVal, "$x")); // new AbstractMap.SimpleEntry<>(Integer.parseInt(elementBase.getKey()), $x) + } else if (elmType == DataConstraintModel.typeString) { + String getKey = langSpec.getMethodInvocation(elementBase, DataConstraintModel.fst.getImplName()); // elementBase.getKey() + decoded = decoded.replace("$x", langSpec.getPairExp(getKey, "$x")); // new AbstractMap.SimpleEntry<>(elementBase.getKey(), $x) + } else { + // To do. } - - @Override - public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { - if (fromRes != null) { + elementBase = langSpec.getMethodInvocation(elementBase, DataConstraintModel.snd.getImplName()); // elementBase.getValue() + } + decoded = decoded.replace("$x", elementBase); + return decoded; + } + + protected String getCodeForConversionFromMapToPair(Type pairType, String mapVar, ILanguageSpecific langSpec) { + String decoded = "$x"; + decoded = decoded.replace("$x", "new Pair<>(" + mapVar + ".get(\"left\"), $x)"); + decoded = decoded.replace("$x", mapVar + ".get(\"right\")"); + return decoded; + } + + protected String getCodeForConversionFromMapToMap(Type mapType, String mapVal, String mapVar, ILanguageSpecific langSpec) { + List elementsTypes = TypeInference.getMapComponentTypes(mapType); + Type keyType = elementsTypes.get(0); +// Type valType = elementsTypes.get(1); + String decoded = ""; + if (keyType == DataConstraintModel.typeBoolean + || keyType == DataConstraintModel.typeInt + || keyType == DataConstraintModel.typeLong + || keyType == DataConstraintModel.typeFloat + || keyType == DataConstraintModel.typeDouble) { + String keyVal = langSpec.getStringToValueExp(keyType.getImplementationTypeName(), "k"); + String getInvocation = langSpec.getMethodInvocation(mapVal, DataConstraintModel.lookup.getImplName(), List.of(keyVal)); + decoded += langSpec.getForStatementForMap("k", DataConstraintModel.typeString.getInterfaceTypeName(), mapVal) + "\n"; + decoded += "\t" + langSpec.getMethodInvocation(mapVar, DataConstraintModel.insert.getImplName(), List.of(keyVal, getInvocation)) + langSpec.getStatementDelimiter() + "\n"; + decoded += langSpec.getEndForStatement("k"); + } else if (keyType == DataConstraintModel.typeString) { + decoded += mapVar + langSpec.getAssignment() + mapVal + langSpec.getStatementDelimiter(); + } + return decoded; + } + + protected IResourceStateAccessor getPushAccessor(IPlatformSpecific platformSpec) { + if (platformSpec.isMonolithic()) { + return new IResourceStateAccessor() { + @Override + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes= target.getResource(); + ResourcePath fromRes= from.getResource(); if (targetRes.equals(fromRes)) { return new Field(fieldOfResourceState, targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } + // use the cached value as the current state + return new Field(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes= target.getResource(); + return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { + if (fromRes != null && targetRes.equals(fromRes)) { + return new Field(fieldOfResourceState, + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } // for reference channel member - Term getter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); - getter.addChild(new Field(targetRes.getLeafResourceName(), targetRes.getResourceStateType())); - return getter; - } else { - // access from the outside of the hierarchy - Stack pathStack = new Stack<>(); - ResourcePath curPath = targetRes; - do { - pathStack.push(curPath); - curPath = curPath.getParent(); - } while (curPath != null); - // iterate from the root resource + return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + }; + } else { + return new IResourceStateAccessor() { + @Override + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + ResourcePath fromRes = from.getResource(); + if (targetRes.equals(fromRes)) { + return new Field(fieldOfResourceState, + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + // use the cached value as the current state + return new Field(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { + if (fromRes != null && targetRes.equals(fromRes)) { + return new Field(fieldOfResourceState, + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + return null; + } + }; + } + } + + protected IResourceStateAccessor getPullAccessor(IPlatformSpecific platformSpec) { + if (platformSpec.isMonolithic()) { + return new IResourceStateAccessor() { + @Override + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes= target.getResource(); + if (from != null) { + ResourcePath fromRes= from.getResource(); + if (!target.isOutside()) { + return getDirectStateAccessorFor(targetRes, fromRes); + } + Term getter = null; + String targetComponentName = getComponentName(targetRes.getResourceHierarchy(), langSpec); + if (generatesComponent(targetRes.getResourceHierarchy())) { + getter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); + getter.addChild(new Field(langSpec.toVariableName(targetComponentName), targetRes.getResourceStateType())); + } else { + String parentName = langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy().getParent(), langSpec)); + Type parentType = targetRes.getResourceHierarchy().getParent().getResourceStateType(); + getter = new Term(new Symbol(getterPrefix + targetComponentName, 1, Symbol.Type.METHOD)); + getter.addChild(new Field(parentName, parentType)); + } + return getter; + } else { + return getDirectStateAccessorFor(targetRes, null); + } + } + + @Override + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes= target.getResource(); + if (from != null) { + ResourcePath fromRes= from.getResource(); + if (!target.isOutside()) { + return getDirectStateAccessorFor(targetRes, fromRes); + } + Term getter = null; + String targetComponentName = getComponentName(targetRes.getResourceHierarchy(), langSpec); + if (generatesComponent(targetRes.getResourceHierarchy())) { + getter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); + getter.addChild(new Field(langSpec.toVariableName(targetComponentName), targetRes.getResourceStateType())); + } else { + String parentName = langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy().getParent(), langSpec)); + Type parentType = targetRes.getResourceHierarchy().getParent().getResourceStateType(); + getter = new Term(new Symbol(getterPrefix + targetComponentName, 1, Symbol.Type.METHOD)); + getter.addChild(new Field(parentName, parentType)); + } + return getter; + } else { + return getDirectStateAccessorFor(targetRes, null); + } + } + + @Override + public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { + if (fromRes != null) { + if (targetRes.equals(fromRes)) { + return new Field(fieldOfResourceState, + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + if (fromRes.isAncestorOf(targetRes)) { + Stack pathStack = new Stack<>(); + ResourcePath curPath = targetRes; + do { + pathStack.push(curPath); + curPath = curPath.getParent(); + } while (!curPath.equals(fromRes)); + // iterate from the fromRes resource + return getRelativePath(targetRes, pathStack); + } + if (generatesComponent(targetRes.getResourceHierarchy())) { + Term getter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); + getter.addChild(new Field(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), targetRes.getResourceStateType())); + return getter; + } else { + return new Field(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), targetRes.getResourceStateType()); + } + } else { + // (#3) access from the outside of the hierarchy (must be kept consistent with #4) + Stack pathStack = new Stack<>(); + ResourcePath curPath = targetRes; + do { + pathStack.push(curPath); + curPath = curPath.getParent(); + } while (curPath != null); + // iterate from the root resource + return getRelativePath(targetRes, pathStack); + } + } + + private Expression getRelativePath(ResourcePath targetRes, Stack pathStack) { + ResourcePath curPath; Term getter = null; - int v = 1; int arity = 2; + boolean doesChainInvocations = true; while (!pathStack.empty()) { curPath = pathStack.pop(); String typeName = getComponentName(curPath.getResourceHierarchy(), langSpec); - if (getter == null) { + if (getter == null && generatesComponent(curPath.getResourceHierarchy())) { // root resource String fieldName = langSpec.toVariableName(typeName); getter = new Field(fieldName, new Type(typeName, typeName)); @@ -636,58 +1023,43 @@ 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); + 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(getterPrefix + 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(getterPrefix + 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++; } } } @@ -700,47 +1072,204 @@ } return getter; } - } - }; + }; + } else { + return new IResourceStateAccessor() { + @Override + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + if (from != null && !target.isOutside()) { + ResourcePath fromRes = from.getResource(); + if (targetRes.getCommonPrefix(fromRes) != null) { + return getDirectStateAccessorFor(targetRes, fromRes); + } + } + // for reference channel member + return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + if (from != null && !target.isOutside()) { + ResourcePath fromRes = from.getResource(); + if (targetRes.getCommonPrefix(fromRes) != null) { + return getDirectStateAccessorFor(targetRes, fromRes); + } + } + return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { + if (fromRes != null && !fromRes.getResourceHierarchy().isAncestorOf(targetRes.getResourceHierarchy())) { + if (targetRes.equals(fromRes)) { + return new Field(fieldOfResourceState, + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } else { + // (#3) access from an ancestor or outside of the hierarchy (must be kept consistent with #4) + Stack pathStack = new Stack<>(); + ResourcePath curPath = targetRes; + do { + if (fromRes != null && curPath.equals(fromRes)) break; + pathStack.push(curPath); + curPath = curPath.getParent(); + } while (curPath != null); + // iterate from the `from' resource + Term getter = null; + int arity = 2; + boolean doesChainInvocations = true; + while (!pathStack.empty()) { + curPath = pathStack.pop(); + String typeName = getComponentName(curPath.getResourceHierarchy(), langSpec); + if (getter == null && fromRes == null) { + // root resource + String fieldName = langSpec.toVariableName(typeName); + getter = new Field(fieldName, new Type(typeName, typeName)); + } else { + if (generatesComponent(curPath.getResourceHierarchy())) { + if (arity == 2) { + Term newGetter = new Term(new Symbol(getterPrefix + typeName, -1, Symbol.Type.METHOD)); + newGetter.addChild(getter); + if (curPath.getResourceHierarchy().getNumParameters() > 0) { + Expression param = curPath.getLastParam(); + if (param != null) { + newGetter.addChild(param); + newGetter.getSymbol().setArity(2); + } + } + getter = newGetter; + } else { + // add the last path parameter. + if (curPath.getResourceHierarchy().getNumParameters() > 0) { + Expression param = curPath.getLastParam(); + if (param != null) { + getter.getSymbol().setArity(arity); + getter.addChild(param); + } + } + } + arity = 2; + doesChainInvocations = true; + } else { + // to get a descendant resource directly. (e.g, .todos.{year}.{month}.{day}.{id} ==> .getTodos().getTodo(year, month, day, id)) + if (doesChainInvocations) { + Term newGetter = new Term(new Symbol(getterPrefix + typeName, -1, Symbol.Type.METHOD)); + newGetter.addChild(getter); + getter = newGetter; + doesChainInvocations = false; + } + if (curPath.getResourceHierarchy().getNumParameters() > 0) { + // may change the symbol name + getter.getSymbol().changeName(getterPrefix + typeName); + // add a path parameter. + Expression param = curPath.getLastParam(); + if (param != null) { + getter.getSymbol().setArity(arity); + getter.addChild(param); + arity++; + } + } + } + } + } + + if (generatesComponent(targetRes.getResourceHierarchy())) { + Term newGetter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); + newGetter.addChild(getter); + getter = newGetter; + } + return getter; + } + } + }; + } } - protected IResourceStateAccessor getRefAccessor() { - return new IResourceStateAccessor() { - @Override - public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { - ResourcePath targetRes= target.getResource(); - ResourcePath fromRes= from.getResource(); - if (targetRes.equals(fromRes)) { - return new Field(fieldOfResourceState, + protected IResourceStateAccessor getRefAccessor(IPlatformSpecific platformSpec) { + if (platformSpec.isMonolithic()) { + return new IResourceStateAccessor() { + @Override + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes= target.getResource(); + ResourcePath fromRes= from.getResource(); + if (targetRes.equals(fromRes)) { + return new Field(fieldOfResourceState, + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + // use the cached value as the current state + return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } - // use the cached value as the current state - return new Parameter(targetRes.getLeafResourceName(), - targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() - : DataConstraintModel.typeInt); - } - - @Override - public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { - ResourcePath targerRes= target.getResource(); - return new Parameter(targerRes.getLeafResourceName(), - targerRes.getResourceStateType() != null ? targerRes.getResourceStateType() - : DataConstraintModel.typeInt); - } - - @Override - public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { - if (fromRes != null && targetRes.equals(fromRes)) { - return new Field(fieldOfResourceState, + + @Override + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes= target.getResource(); + return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } - // for reference channel member - return new Parameter(targetRes.getLeafResourceName(), - targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() - : DataConstraintModel.typeInt); - } - }; + + @Override + public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { + if (fromRes != null && targetRes.equals(fromRes)) { + return new Field(fieldOfResourceState, + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + }; + } else { + return new IResourceStateAccessor() { + @Override + public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + ResourcePath fromRes = from.getResource(); + if (targetRes.equals(fromRes)) { + return new Field(fieldOfResourceState, + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + // for reference channel member + return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getNextStateAccessorFor(ChannelMember target, ChannelMember from) { + ResourcePath targetRes = target.getResource(); + return new Parameter(langSpec.toVariableName(getComponentName(targetRes.getResourceHierarchy(), langSpec)), + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + + @Override + public Expression getDirectStateAccessorFor(ResourcePath targetRes, ResourcePath fromRes) { + if (fromRes != null && targetRes.equals(fromRes)) { + return new Field(fieldOfResourceState, + targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() + : DataConstraintModel.typeInt); + } + return null; + } + }; + } } } diff --git a/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java b/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java index f8ecbc6..a4b6fa6 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/CodeGeneratorFromDataFlowGraph.java @@ -2,17 +2,22 @@ import java.util.AbstractMap; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; + +import algorithms.TypeInference; + import java.util.Set; import java.util.Stack; -import java.util.AbstractMap.SimpleEntry; +import code.ast.Annotation; import code.ast.Block; +import code.ast.CodeUtil; import code.ast.CompilationUnit; import code.ast.FieldDeclaration; import code.ast.MethodDeclaration; @@ -27,6 +32,7 @@ import models.algebra.Parameter; import models.algebra.ParameterizedIdentifierIsFutureWork; import models.algebra.Position; +import models.algebra.Symbol; import models.algebra.Term; import models.algebra.Type; import models.algebra.UnificationFailed; @@ -54,15 +60,28 @@ public class CodeGeneratorFromDataFlowGraph extends CodeGenerator { - public void generateCodeFromFlowGraph(DataTransferModel model, DataFlowGraph flowGraph, ArrayList components, Map> dependedRootComponentGraph, - TypeDeclaration mainComponent, MethodDeclaration mainConstructor, ArrayList codes, ILanguageSpecific langSpec) { + public void generateCodeFromFlowGraph(DataTransferModel model, DataFlowGraph flowGraph, Collection components, ArrayList codes, + Map> dependedRootComponentGraph, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { Map resourceComponents = new HashMap<>(); Map resourceConstructors = new HashMap<>(); - List> constructorParams = new ArrayList<>(); + Map> constructorParams = new HashMap<>(); List> constructorStatements = new ArrayList<>(); Map>> updateStatements = new HashMap<>(); Map> descendantGetters = new HashMap<>(); + TypeDeclaration mainComponent = null; + MethodDeclaration mainConstructor = null; + if (platformSpec.hasMain()) { + // Add the main component. + if (getMainTypeName() == null) { + setMainTypeName(langSpec.getMainComponentName()); + } + mainComponent = langSpec.newTypeDeclaration(getMainTypeName()); + mainConstructor = mainComponent.createConstructor(); + CompilationUnit mainCU = langSpec.newCompilationUnit(mainComponent); + codes.add(mainCU); + } + // For each components (1st pass). for (Node componentNode: components) { ResourceNode resourceNode = (ResourceNode) componentNode; @@ -76,26 +95,38 @@ if (component == null) { // Add compilation unit for this component. component = langSpec.newTypeDeclaration(resourceName); + if (!platformSpec.isMonolithic() && resourceNode.getResourceHierarchy().getParent() == null) { + // For each root node, add component annotations. + ((RestApiSpecific) platformSpec).addComponentAnnotations(component, resourceNode.getResourceName()); + } resourceComponents.put(resourceNode.getResourceHierarchy(), component); CompilationUnit cu = langSpec.newCompilationUnit(component); + if (!platformSpec.isMonolithic() && resourceNode.getResourceHierarchy().getParent() == null) { + // For each root node, add platform specific imports. + ((RestApiSpecific) platformSpec).addPlatformSpecificImports(cu); + } codes.add(cu); - // Declare the constructor. - MethodDeclaration constructor = declareConstructor(resourceNode, component, dependedRootComponentGraph, depends, langSpec); - - if (resourceNode.getResourceHierarchy().getParent() == null) { - // For each root resource - // Update the main component for this component. - updateMainComponent(mainComponent, mainConstructor, componentNode, constructor, depends, langSpec); - } - - // Declare the fields to refer to reference resources. - declareFieldsToReferenceResources(model, resourceNode, component, constructor, depends, langSpec); - - if (constructor.getParameters() == null || constructor.getParameters().size() == 0) { - component.removeMethod(constructor); - } else { - resourceConstructors.put(resourceNode.getResourceHierarchy(), constructor); + if (platformSpec.isMonolithic()) { + // For monolithic applications (components are tightly coupled and must be built together). + + // Declare the constructor. + MethodDeclaration constructor = declareConstructor(resourceNode, component, dependedRootComponentGraph, depends, langSpec); + + if (platformSpec.hasMain() && resourceNode.getResourceHierarchy().getParent() == null) { + // For each root resource + // Update the main component for this component. + updateMainComponent(mainComponent, mainConstructor, componentNode, constructor, depends, langSpec); + } + + // Declare the fields to refer to reference resources. + declareFieldsToReferenceResources(model, resourceNode, component, constructor, depends, langSpec); + + if (constructor.getParameters() == null || constructor.getParameters().size() == 0) { + component.removeMethod(constructor); + } else { + resourceConstructors.put(resourceNode.getResourceHierarchy(), constructor); + } } // Declare the field to store the state in this resource. @@ -104,34 +135,41 @@ } // Declare the getter method in this resource to obtain the state. - MethodDeclaration stateGetter = declareStateGetterMethod(resourceNode, component, resStateType, langSpec); + MethodDeclaration stateGetter = declareStateGetterMethod(resourceNode, component, resStateType, platformSpec, langSpec); // Declare the accessor method in the main component to call the getter method. - declareAccessorInMainComponent(mainComponent, resourceNode, stateGetter, langSpec); + if (platformSpec.hasMain()) { + declareAccessorInMainComponent(mainComponent, resourceNode, stateGetter, platformSpec, 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); } } } // For each components (2nd pass). + Map priorMemberForInputChannel = new HashMap<>(); + Set generatedResources = new HashSet<>(); for (Node componentNode: components) { // Declare this resource. ResourceNode resourceNode = (ResourceNode) componentNode; Type resStateType = getImplStateType(resourceNode.getResourceHierarchy(), langSpec); TypeDeclaration component = null; TypeDeclaration parentComponent = null; + TypeDeclaration rootComponent = null; if (generatesComponent(resourceNode.getResourceHierarchy())) { component = resourceComponents.get(resourceNode.getResourceHierarchy()); } if (resourceNode.getResourceHierarchy().getParent() != null) { parentComponent = resourceComponents.get(resourceNode.getResourceHierarchy().getParent()); } + rootComponent = resourceComponents.get(resourceNode.getResourceHierarchy().getRoot()); - // Declare cache fields and update methods in this resource. - Map.Entry, Map>>> initStatementsAndUpdateUpdates = declareCacheFieldsAndUpdateMethods(resourceNode, component, parentComponent, langSpec); + // Declare cache fields and update methods in this resource, and an update accessor method in the type of root resource. + Map.Entry, Map>>> initStatementsAndUpdateUpdates + = declareCacheFieldsAndUpdateMethods(resourceNode, component, parentComponent, rootComponent, platformSpec, langSpec); if (component == null) { // Constructor statements were not added to any component because no component had been generated. for (String statement: initStatementsAndUpdateUpdates.getKey()) { @@ -142,30 +180,40 @@ updateStatements.put(entry.getKey(), entry.getValue()); } - // Declare the fields to refer to other resources in the parent/this component, and the state field in the parent component. - declareFieldsToReferToOtherResourcesAndStateFieldInParentComponent(resourceNode, component, parentComponent, constructorParams, langSpec); + // Declare the fields to refer to other resources for push/pull transfer in the parent/this component, and the state field in the parent component. + declareFieldsToReferToOtherResourcesAndStateFieldInParentComponent(resourceNode, component, parentComponent, constructorParams, platformSpec, langSpec); - // 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); + MethodDeclaration stateGetter = declareStateGetterMethodInAncestor(resourceNode, resourceComponents, resStateType, platformSpec, langSpec); - if (stateGetter != null) { + if (stateGetter != null && platformSpec.hasMain()) { // Declare the accessor method in the main component to call the getter method. - declareAccessorInMainComponent(mainComponent, resourceNode, stateGetter, langSpec); + declareAccessorInMainComponent(mainComponent, resourceNode, stateGetter, platformSpec, langSpec); } } + if (!platformSpec.isMonolithic() && !generatedResources.contains(resourceNode.getResourceHierarchy())) { + // Declare the getter accessor in the root resource. + declareGetterAccessorInTheRootResource(resourceNode, rootComponent, platformSpec, langSpec); + } + // Declare input methods in this component and the main component. - Map.Entry, Map>>> initStatementsAndInputUpdates = declareInputMethodsInThisAndMainComponents(resourceNode, component, parentComponent, mainComponent, model, langSpec); - if (component == null) { - // Constructor statements were not added to any component because no component had been generated. - for (String statement: initStatementsAndInputUpdates.getKey()) { - constructorStatements.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy().getParent(), statement)); + if (!generatedResources.contains(resourceNode.getResourceHierarchy())) { + Map.Entry, Map>>> initStatementsAndInputUpdates + = declareInputMethodsInThisAndMainComponents(resourceNode, component, parentComponent, mainComponent, rootComponent, model, priorMemberForInputChannel, platformSpec, langSpec); + if (component == null) { + // Constructor statements were not added to any component because no component had been generated. + for (String statement: initStatementsAndInputUpdates.getKey()) { + constructorStatements.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy().getParent(), statement)); + } + } + for (Map.Entry>> entry: initStatementsAndInputUpdates.getValue().entrySet()) { + updateStatements.put(entry.getKey(), entry.getValue()); } } - for (Map.Entry>> entry: initStatementsAndInputUpdates.getValue().entrySet()) { - updateStatements.put(entry.getKey(), entry.getValue()); - } + + generatedResources.add(resourceNode.getResourceHierarchy()); } // Add constructor parameters to the ancestor components. @@ -189,14 +237,14 @@ } private static List addConstructorParameters(ResourceHierarchy resource, Map resourceComponents, - Map resourceConstructors, List> constructorParams, ILanguageSpecific langSpec) { + Map resourceConstructors, Map> constructorParams, ILanguageSpecific langSpec) { List params = new ArrayList<>(); for (ResourceHierarchy child: resource.getChildren()) { params.addAll(addConstructorParameters(child, resourceComponents, resourceConstructors, constructorParams, langSpec)); } - for (Entry paramEnt: constructorParams) { - if (paramEnt.getKey().equals(resource)) { - params.add(paramEnt.getValue()); + if (constructorParams.get(resource) != null) { + for (VariableDeclaration param: constructorParams.get(resource).values()) { + params.add(param); } } if (params.size() > 0) { @@ -234,6 +282,7 @@ } private void addUpdateStatementWithConstructorInvocationToMethod(MethodDeclaration method, Expression exp, ResourceHierarchy resource, ResourceHierarchy descendantRes, TypeDeclaration descendantComponent, ILanguageSpecific langSpec) { + // Replace each json term in exp with the corresponding constructor invocation. Type replacedJsonType = descendantRes.getResourceStateType(); String replacingClassName = getComponentName(descendantRes, langSpec); Type descendantType = new Type(replacingClassName, replacingClassName); @@ -247,24 +296,27 @@ 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()) { + // Extract the argument of each constructor parameter from jsonTerm. + JsonAccessor jsonMember = new JsonAccessor(DataConstraintModel.dot); + jsonMember.addChild(jsonTerm); + jsonMember.addChild(new Constant(var.getName(), DataConstraintModel.typeString)); + Expression param = jsonMember.reduce(); // Reduce {"name": "foo", age: 25}.name => "foo" + if (param != null) { + if (param instanceof Term) { + if (((Term) param).getType() == null) { + ((Term) param).setType(var.getType()); + } + } else if (param instanceof Variable) { + if (((Variable) param).getType() == null) { + ((Variable) param).setType(var.getType()); + } } - } 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 +354,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(); } @@ -338,7 +393,7 @@ break; } } - if ((((PushPullAttribute) ((DataFlowEdge) resToCh).getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) { + if ((((PushPullAttribute) ((DataFlowEdge) resToCh).getAttribute()).getSelectedOption() == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) { // for PUSH transfer // ResourceHierarchy dstRes = addReference(component, constructor, ((ResourceNode) chToRes.getDestination()).getOutSideResource().getResourceHierarchy(), langSpec); // if (outsideOutputResource) { @@ -375,7 +430,7 @@ break; } } - if ((((PushPullAttribute) ((DataFlowEdge) resToCh).getAttribute()).getOptions().get(0) != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { + if ((((PushPullAttribute) ((DataFlowEdge) resToCh).getAttribute()).getSelectedOption() != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { // for PULL transfer // srcRes = addReference(component, constructor, ((ResourceNode) resToCh.getSource()).getOutSideResource().getResourceHierarchy(), langSpec); // if (outsideInputResource) { @@ -406,12 +461,22 @@ return constructor; } - private void declareStateField(ResourceNode resourceNode, String resourceName, TypeDeclaration component, Type resStateType, List> constructorParams, ILanguageSpecific langSpec) { + private void declareStateField(ResourceNode resourceNode, String resourceName, TypeDeclaration component, Type resStateType, Map> constructorParams, ILanguageSpecific langSpec) { Set children = resourceNode.getResourceHierarchy().getChildren(); if (children == null || children.size() == 0) { // leaf resource. FieldDeclaration stateField = langSpec.newFieldDeclaration(resStateType, fieldOfResourceState, langSpec.getFieldInitializer(resStateType, resourceNode.getResourceHierarchy().getInitialValue())); component.addField(stateField); + // Add a parameter to initialize the state field to the constructor. +// Map nameToParam = constructorParams.get(resourceNode.getResourceHierarchy()); +// if (nameToParam == null) { +// nameToParam = new HashMap<>(); +// constructorParams.put(resourceNode.getResourceHierarchy(), nameToParam); +// } +// String varName = fieldOfResourceState; +// if (nameToParam.get(varName) == null) { +// nameToParam.put(varName, langSpec.newVariableDeclaration(resStateType, varName)); +// } } else { ResourceHierarchy child = children.iterator().next(); if (children.size() == 1 && child.getNumParameters() > 0) { @@ -435,90 +500,156 @@ } } - private void declareFieldsToReferToOtherResourcesAndStateFieldInParentComponent(ResourceNode resourceNode, TypeDeclaration component, TypeDeclaration parentComponent, List> constructorParams, ILanguageSpecific langSpec) { + private void declareFieldsToReferToOtherResourcesAndStateFieldInParentComponent(ResourceNode resourceNode, TypeDeclaration component, TypeDeclaration parentComponent, + Map> constructorParams, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { // Declare reference fields for push data transfer. boolean noPullTransfer = true; for (Edge resToCh : resourceNode.getOutEdges()) { DataFlowEdge re = (DataFlowEdge) resToCh; - 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; + ChannelMember out = null; for (ChannelMember cm: ch.getOutputChannelMembers()) { - if (((ResourceNode) chToRes.getDestination()).getInSideResources().contains(cm.getResource()) && cm.isOutside()) { - outsideOutputResource = true; // Regarded as push transfer. - break; + if (((ResourceNode) chToRes.getDestination()).getInSideResources().contains(cm.getResource())) { + out = cm; + if (cm.isOutside()) { + outsideOutputResource = true; // Regarded as push transfer. + break; + } } } - if ((((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) { - // Declare a field in the parent component to refer to the destination resource of push transfer. + ResourcePath dstResPath = out.getResource(); + // Also take into account the channel hierarchy to determine push/pull transfer. + if (descendantDstChannels.contains(chNode)) { + outsideOutputResource = true; // Regarded as (broadcasting) push transfer. + } + if (ancestorDstChannels.contains(chNode)) { + outsideInputResource = true; // Regarded as (collecting) pull transfer. + } + if ((((PushPullAttribute) re.getAttribute()).getSelectedOption() == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) { + // Declare a field in the parent or this component to refer to the destination resource of push transfer. if (!generatesComponent(dstRes)) { dstRes = dstRes.getParent(); } String dstResName = getComponentName(dstRes, langSpec); FieldDeclaration refFieldForPush = langSpec.newFieldDeclaration(new Type(dstResName, dstResName), langSpec.toVariableName(dstResName)); VariableDeclaration refVarForPush = langSpec.newVariableDeclaration(new Type(dstResName, dstResName), langSpec.toVariableName(dstResName)); - if (component != null) { - // A component is created for this resource. - if (resourceNode.getResourceHierarchy() != dstRes) { - component.addField(refFieldForPush); - if (!outsideOutputResource) { - constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy(), refVarForPush)); - } + if (!platformSpec.isMonolithic() + && (outsideOutputResource || (resourceNode.getOutSideResource(ch).getCommonPrefix(dstResPath) == null && platformSpec.isDifferentTreesAsDifferentServices()))) { + // Inter-service (for REST API) + if (parentComponent != null && + !((RestApiSpecific) platformSpec).hasHttpClientFieldDeclaration(parentComponent)) { + // Declare a client field to connect to the destination resource of push transfer. + ((RestApiSpecific) platformSpec).addHttpClientFieldDeclaration(parentComponent); } } else { - // No component is created for this resource. - if (resourceNode.getParent().getResourceHierarchy() != dstRes) { - parentComponent.addField(refFieldForPush); - if (!outsideOutputResource) { - constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), refVarForPush)); + // Monolithic or Inner-service + if (component != null) { + // A component is created for this resource. + if (resourceNode.getResourceHierarchy() != dstRes) { + component.addField(refFieldForPush); + if (!outsideOutputResource) { + Map nameToParam = constructorParams.get(resourceNode.getResourceHierarchy()); + if (nameToParam == null) { + nameToParam = new HashMap<>(); + constructorParams.put(resourceNode.getResourceHierarchy(), nameToParam); + } + if (nameToParam.get(dstResName) == null) { + nameToParam.put(dstResName, refVarForPush); + } + } + } + } else { + // No component is created for this resource. + if (resourceNode.getParent().getResourceHierarchy() != dstRes && parentComponent != null) { + parentComponent.addField(refFieldForPush); + if (!outsideOutputResource) { + Map nameToParam = constructorParams.get(resourceNode.getParent().getResourceHierarchy()); + if (nameToParam == null) { + nameToParam = new HashMap<>(); + constructorParams.put(resourceNode.getParent().getResourceHierarchy(), nameToParam); + } + if (nameToParam.get(dstResName) == null) { + nameToParam.put(dstResName, refVarForPush); + } + } } } - } - if (outsideOutputResource) { - // When the reference to the destination resource can vary. - if (dstRes.getParent() != null) { - // Reference to its root resource. - String dstRootResName = getComponentName(dstRes.getRoot(), langSpec); - Type dstRootResType = new Type(dstRootResName, dstRootResName); - dstRootResName = langSpec.toVariableName(dstRootResName); - FieldDeclaration refRootFieldForPush = langSpec.newFieldDeclaration(dstRootResType, dstRootResName); - VariableDeclaration refRootVarForPush = langSpec.newVariableDeclaration(dstRootResType, dstRootResName); - if (component != null) { - // A component is created for this resource. - boolean existsField = false; - for (FieldDeclaration field: component.getFields()) { - if (dstRootResName.equals(field.getName())) { - existsField = true; - break; + if (outsideOutputResource) { + // When the reference to the destination resource can vary. + if (dstRes.getParent() != null) { + // Reference to its root resource. + String dstRootResName = getComponentName(dstRes.getRoot(), langSpec); + Type dstRootResType = new Type(dstRootResName, dstRootResName); + dstRootResName = langSpec.toVariableName(dstRootResName); + FieldDeclaration refRootFieldForPush = langSpec.newFieldDeclaration(dstRootResType, dstRootResName); + VariableDeclaration refRootVarForPush = langSpec.newVariableDeclaration(dstRootResType, dstRootResName); + if (component != null) { + // A component is created for this resource. + boolean existsField = false; + for (FieldDeclaration field: component.getFields()) { + if (dstRootResName.equals(field.getName())) { + existsField = true; + break; + } } - } - if (!existsField) { - component.addField(refRootFieldForPush); - constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy(), refRootVarForPush)); - } - } else { - // No component is created for this resource. - boolean existsField = false; - for (FieldDeclaration field: parentComponent.getFields()) { - if (dstRootResName.equals(field.getName())) { - existsField = true; - break; + if (!existsField) { + component.addField(refRootFieldForPush); + Map nameToParam = constructorParams.get(resourceNode.getResourceHierarchy()); + if (nameToParam == null) { + nameToParam = new HashMap<>(); + constructorParams.put(resourceNode.getResourceHierarchy(), nameToParam); + } + if (nameToParam.get(dstRootResName) == null) { + nameToParam.put(dstRootResName, refRootVarForPush); + } } - } - if (!existsField) { - parentComponent.addField(refRootFieldForPush); - constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), refRootVarForPush)); + } else { + // No component is created for this resource. + boolean existsField = false; + for (FieldDeclaration field: parentComponent.getFields()) { + if (dstRootResName.equals(field.getName())) { + existsField = true; + break; + } + } + if (!existsField) { + parentComponent.addField(refRootFieldForPush); + Map nameToParam = constructorParams.get(resourceNode.getParent().getResourceHierarchy()); + if (nameToParam == null) { + nameToParam = new HashMap<>(); + constructorParams.put(resourceNode.getParent().getResourceHierarchy(), nameToParam); + } + if (nameToParam.get(dstRootResName) == null) { + nameToParam.put(dstRootResName, refRootVarForPush); + } + } } } } @@ -529,9 +660,23 @@ } // Declare reference fields for pull data transfer. for (Edge chToRes : resourceNode.getInEdges()) { - for (Edge resToCh: chToRes.getSource().getInEdges()) { + ChannelNode directSrcChNode = (ChannelNode) chToRes.getSource(); + DataTransferChannel directSrcCh = directSrcChNode.getChannel(); + // Should take into account the channel hierarchy. + Set ancestorSrcChannels = directSrcChNode.getAncestors(); + Set descendantSrcChannels = directSrcChNode.getDescendants(); + Set inEdges = new HashSet<>(); + inEdges.addAll(directSrcChNode.getInEdges()); + for (ChannelNode ancestorSrc: ancestorSrcChannels) { + inEdges.addAll(ancestorSrc.getInEdges()); + } + for (ChannelNode descendantSrc: descendantSrcChannels) { + inEdges.addAll(descendantSrc.getInEdges()); + } + for (Edge resToCh: inEdges) { DataFlowEdge re = (DataFlowEdge) resToCh; - DataTransferChannel ch = ((ChannelNode) re.getDestination()).getChannel(); + ChannelNode chNode = (ChannelNode) re.getDestination(); + DataTransferChannel ch = chNode.getChannel(); ResourcePath srcRes = ((ResourceNode) re.getSource()).getOutSideResource(ch); // Check if the source resource is outside of the channel scope. boolean outsideInputResource = false; @@ -549,7 +694,14 @@ break; } } - if ((((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { + // Also take into account the channel hierarchy to determine push/pull transfer. + if (descendantSrcChannels.contains(chNode)) { + outsideInputResource = true; // Regarded as (collecting) pull transfer. + } + if (ancestorSrcChannels.contains(chNode)) { + outsideOutputResource = true; // Regarded as (broadcasting) push transfer. + } + if ((((PushPullAttribute) re.getAttribute()).getSelectedOption() != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { noPullTransfer = false; // Declare a field in the parent/this component to refer to the source resource of pull transfer. if (!generatesComponent(srcRes.getResourceHierarchy())) { @@ -558,57 +710,97 @@ String srcResName = getComponentName(srcRes.getResourceHierarchy(), langSpec); FieldDeclaration refFieldForPull = langSpec.newFieldDeclaration(new Type(srcResName, srcResName), langSpec.toVariableName(srcResName)); VariableDeclaration refVarForPull = langSpec.newVariableDeclaration(new Type(srcResName, srcResName), langSpec.toVariableName(srcResName)); - if (component != null) { - // A component is created for this resource. - if (resourceNode.getResourceHierarchy() != srcRes.getResourceHierarchy()) { - component.addField(refFieldForPull); - if (!outsideInputResource) { - constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy(), refVarForPull)); - } + if (!platformSpec.isMonolithic() + && (outsideInputResource || (resourceNode.getInSideResource(ch).getCommonPrefix(srcRes) == null && platformSpec.isDifferentTreesAsDifferentServices()))) { + // Inter-service (for REST API) + if (parentComponent != null + && !((RestApiSpecific) platformSpec).hasHttpClientFieldDeclaration(parentComponent)) { + // Declare a client field to connect to the destination resource of push transfer. + ((RestApiSpecific) platformSpec).addHttpClientFieldDeclaration(parentComponent); } } else { - // No component is created for this resource. - if (resourceNode.getParent().getResourceHierarchy() != srcRes.getResourceHierarchy()) { - parentComponent.addField(refFieldForPull); - if (!outsideInputResource) { - constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), refVarForPull)); + // Monolithic or Inner-service (for REST API) + // Declare a field to directly refer to the source resource of pull transfer. + if (component != null) { + // A component is created for this resource. + if (resourceNode.getResourceHierarchy() != srcRes.getResourceHierarchy()) { + component.addField(refFieldForPull); + if (!outsideInputResource) { + Map nameToParam = constructorParams.get(resourceNode.getResourceHierarchy()); + if (nameToParam == null) { + nameToParam = new HashMap<>(); + constructorParams.put(resourceNode.getResourceHierarchy(), nameToParam); + } + if (nameToParam.get(srcResName) == null) { + nameToParam.put(srcResName, refVarForPull); + } + } + } + } else { + // No component is created for this resource. + if (resourceNode.getParent().getResourceHierarchy() != srcRes.getResourceHierarchy() && parentComponent != null) { + parentComponent.addField(refFieldForPull); + if (!outsideInputResource) { + Map nameToParam = constructorParams.get(resourceNode.getParent().getResourceHierarchy()); + if (nameToParam == null) { + nameToParam = new HashMap<>(); + constructorParams.put(resourceNode.getParent().getResourceHierarchy(), nameToParam); + } + if (nameToParam.get(srcResName) == null) { + nameToParam.put(srcResName, refVarForPull); + } + } } } - } - if (outsideInputResource) { - // When the reference to the source resource can vary. - if (srcRes.getParent() != null) { - // Reference to its root resource. - String srcRootResName = getComponentName(srcRes.getRoot().getResourceHierarchy(), langSpec); - Type srcRootResType = new Type(srcRootResName, srcRootResName); - srcRootResName = langSpec.toVariableName(srcRootResName); - FieldDeclaration refRootFieldForPull = langSpec.newFieldDeclaration(srcRootResType, srcRootResName); - VariableDeclaration refRootVarForPull = langSpec.newVariableDeclaration(srcRootResType, srcRootResName); - if (component != null) { - // A component is created for this resource. - boolean existsField = false; - for (FieldDeclaration field: component.getFields()) { - if (srcRootResName.equals(field.getName())) { - existsField = true; - break; + if (outsideInputResource) { + // When the reference to the source resource can vary. + if (srcRes.getParent() != null) { + // Reference to its root resource. + String srcRootResName = getComponentName(srcRes.getRoot().getResourceHierarchy(), langSpec); + Type srcRootResType = new Type(srcRootResName, srcRootResName); + srcRootResName = langSpec.toVariableName(srcRootResName); + FieldDeclaration refRootFieldForPull = langSpec.newFieldDeclaration(srcRootResType, srcRootResName); + VariableDeclaration refRootVarForPull = langSpec.newVariableDeclaration(srcRootResType, srcRootResName); + if (component != null) { + // A component is created for this resource. + boolean existsField = false; + for (FieldDeclaration field: component.getFields()) { + if (srcRootResName.equals(field.getName())) { + existsField = true; + break; + } } - } - if (!existsField) { - component.addField(refRootFieldForPull); - constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy(), refRootVarForPull)); - } - } else { - // No component is created for this resource. - boolean existsField = false; - for (FieldDeclaration field: parentComponent.getFields()) { - if (srcRootResName.equals(field.getName())) { - existsField = true; - break; + if (!existsField) { + component.addField(refRootFieldForPull); + Map nameToParam = constructorParams.get(resourceNode.getResourceHierarchy()); + if (nameToParam == null) { + nameToParam = new HashMap<>(); + constructorParams.put(resourceNode.getResourceHierarchy(), nameToParam); + } + if (nameToParam.get(srcRootResName) == null) { + nameToParam.put(srcRootResName, refRootVarForPull); + } } - } - if (!existsField) { - parentComponent.addField(refRootFieldForPull); - constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), refRootVarForPull)); + } else { + // No component is created for this resource. + boolean existsField = false; + for (FieldDeclaration field: parentComponent.getFields()) { + if (srcRootResName.equals(field.getName())) { + existsField = true; + break; + } + } + if (!existsField) { + parentComponent.addField(refRootFieldForPull); + Map nameToParam = constructorParams.get(resourceNode.getParent().getResourceHierarchy()); + if (nameToParam == null) { + nameToParam = new HashMap<>(); + constructorParams.put(resourceNode.getParent().getResourceHierarchy(), nameToParam); + } + if (nameToParam.get(srcRootResName) == null) { + nameToParam.put(srcRootResName, refRootVarForPull); + } + } } } } @@ -631,34 +823,55 @@ if (!existsField) { FieldDeclaration stateField = langSpec.newFieldDeclaration(res.getResourceStateType(), resName); parentComponent.addField(stateField); - constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getParent().getResourceHierarchy(), langSpec.newVariableDeclaration(res.getResourceStateType(), resName))); + Map nameToParam = constructorParams.get(resourceNode.getParent().getResourceHierarchy()); + if (nameToParam == null) { + nameToParam = new HashMap<>(); + constructorParams.put(resourceNode.getParent().getResourceHierarchy(), nameToParam); + } + if (nameToParam.get(resName) == null) { + nameToParam.put(resName, langSpec.newVariableDeclaration(res.getResourceStateType(), resName)); + } } } } } - private MethodDeclaration declareStateGetterMethod(ResourceNode resourceNode, TypeDeclaration component, Type resStateType, ILanguageSpecific langSpec) { + private MethodDeclaration declareStateGetterMethod(ResourceNode resourceNode, TypeDeclaration component, Type resStateType, + IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { // Declare the getter method of the resource state. MethodDeclaration stateGetter = langSpec.newMethodDeclaration(getterOfResourceState, resStateType); + if (!platformSpec.isMonolithic() && resourceNode.getResourceHierarchy().getParent() == null) { + // Since this getter is also an accessor. + ((RestApiSpecific) platformSpec).addGetAnnotations(stateGetter); + } component.addMethod(stateGetter); - if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { - fillStateGetterMethod(stateGetter, resourceNode.getResourceHierarchy(), resStateType, langSpec); + boolean hasDescendantIn = hasDescendantInput(resourceNode); + if (((StoreAttribute) resourceNode.getAttribute()).isStored() && !hasDescendantIn) { + fillStateGetterMethod(stateGetter, resourceNode.getResourceHierarchy(), resStateType, platformSpec, langSpec); } else { // invocations to other getter methods when at least one incoming data-flow edges is PULL-style. - addOtherGetterInvocationsToStateGatter(stateGetter, resourceNode, langSpec); + if (addOtherGetterInvocationsToStateGatter(stateGetter, resourceNode, platformSpec, langSpec)) { + // Declare a client field to connect to the destination resource of push transfer. + if (!((RestApiSpecific) platformSpec).hasHttpClientFieldDeclaration(component)) { + ((RestApiSpecific) platformSpec).addHttpClientFieldDeclaration(component); + } + } } return stateGetter; } - private MethodDeclaration declareStateGetterMethodInAncestor(ResourceNode resourceNode, Map resourceComponents, Type resStateType, ILanguageSpecific langSpec) { + private MethodDeclaration declareStateGetterMethodInAncestor(ResourceNode resourceNode, Map resourceComponents, Type resStateType, + IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { // Search an ancestor in which the getter method is declared. ResourceNode ancestorNode = resourceNode; + ResourceNode childNode = null; Stack ancestors = new Stack<>(); do { ancestors.push(ancestorNode); + childNode = ancestorNode; ancestorNode = ancestorNode.getParent(); } while (!generatesComponent(ancestorNode.getResourceHierarchy())); TypeDeclaration ancestorComponent = resourceComponents.get(ancestorNode.getResourceHierarchy()); @@ -694,68 +907,331 @@ 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); + if (addOtherGetterInvocationsToStateGatter(stateGetter, resourceNode, platformSpec, langSpec)) { + // Declare a client field to connect to the destination resource of push transfer. + if (ancestorComponent != null && !((RestApiSpecific) platformSpec).hasHttpClientFieldDeclaration(ancestorComponent)) { + ((RestApiSpecific) platformSpec).addHttpClientFieldDeclaration(ancestorComponent); + } + } } return stateGetter; } - private void addOtherGetterInvocationsToStateGatter(MethodDeclaration stateGetter, ResourceNode resourceNode, - ILanguageSpecific langSpec) { - boolean isContainedPush = false; - DataTransferChannel ch = null; - HashMap inputResourceToStateAccessor = new HashMap<>(); - for (Edge chToRes: resourceNode.getInEdges()) { - DataTransferChannel ch2 = ((ChannelNode) chToRes.getSource()).getChannel(); - for (Edge resToCh: chToRes.getSource().getInEdges()) { - DataFlowEdge dIn = (DataFlowEdge) resToCh; - ChannelMember in = null; - for (ChannelMember cm: ch2.getInputChannelMembers()) { - if (((ResourceNode) dIn.getSource()).getOutSideResources().contains(cm.getResource())) { - in = cm; - break; - } - } - if (((PushPullAttribute) dIn.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { - // PUSH transfer - isContainedPush = true; - inputResourceToStateAccessor.put(in, getPushAccessor()); - } else { - // PULL transfer - inputResourceToStateAccessor.put(in, getPullAccessor()); - ch = ((ChannelNode) resToCh.getDestination()).getChannel(); // pull containing input side channel is always one. + 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 boolean addOtherGetterInvocationsToStateGatter(MethodDeclaration stateGetter, ResourceNode resourceNode, + IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { + boolean bDeclareClientField = false; 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(platformSpec)); + } else { + // PULL transfer + inputResourceToStateAccessor.put(in, getPullAccessor(platformSpec)); + ch = ((ChannelNode) resToCh.getDestination()).getChannel(); // pull containing input side channel is at most one. + if (!platformSpec.isMonolithic() + && !in.isOutside() + && in.getResource().getCommonPrefix(resourceNode.getInSideResource(ch2)) == null + && platformSpec.isDifferentTreesAsDifferentServices()) { + // for REST API + ResourcePath srcResPath = in.getResource(); + String srcResourceName = langSpec.toVariableName(getComponentName(srcResPath.getResourceHierarchy(), langSpec)); + Type srcResourceType = srcResPath.getResourceStateType(); + List pathParams = new ArrayList<>(); + for (Expression pathExp: srcResPath.getPathParams()) { + String[] sideEffects = new String[] {""}; + pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); + } + generatePullDataTransfer(stateGetter, srcResourceName, + srcResPath.getResourceHierarchy().toResourcePath(pathParams), srcResourceType, + true, platformSpec, langSpec); + bDeclareClientField = true; + } + } + } + } + if (ch == null) { + ch = ch2; + } + ChannelMember out = null; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (resourceNode.getInSideResources().contains(cm.getResource())) { + out = cm; break; } } + + // for reference channel members. + ResourcePath dstResPath = resourceNode.getInSideResource(ch); + for (ChannelMember rc: ch.getReferenceChannelMembers()) { + inputResourceToStateAccessor.put(rc, getPullAccessor(platformSpec)); // by pull data transfer + ResourcePath refResPath = rc.getResource(); + if (!platformSpec.isMonolithic() + && (rc.isOutside() + || (refResPath.getCommonPrefix(dstResPath) == null && platformSpec.isDifferentTreesAsDifferentServices()))) { + // for REST API + String refResourceName = langSpec.toVariableName(getComponentName(refResPath.getResourceHierarchy(), langSpec)); + Type refResourceType = refResPath.getResourceStateType(); + List pathParams = new ArrayList<>(); + for (Expression pathExp: refResPath.getPathParams()) { + String[] sideEffects = new String[] {""}; + pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); + } + generatePullDataTransfer(stateGetter, refResourceName, + refResPath.getResourceHierarchy().toResourcePath(pathParams), refResourceType, + true, platformSpec, langSpec); + bDeclareClientField = true; + } + } + + // Construct the base message. + Map.Entry>>, Term> resourcePathsAndMessage; + if (!isContainedPush) { + // All incoming edges are in PULL-style. + resourcePathsAndMessage = ch.fillOutsideResourcePaths(out, getPullAccessor(platformSpec), null); + } else { + // At least one incoming edge is in PUSH-style. + resourcePathsAndMessage = ch.fillOutsideResourcePaths(out, getPullAccessor(platformSpec), inputResourceToStateAccessor); + } + Map>> resourcePaths = resourcePathsAndMessage.getKey(); + Term messageTerm = resourcePathsAndMessage.getValue(); + + // Data transfer from path depending resource. + for (Entry>> pathEnt: resourcePaths.entrySet()) { + ChannelMember cm = pathEnt.getKey(); + ResourcePath srcResPath = pathEnt.getValue().getKey(); + // get outside srcResPath resource state by pull data transfer. + if (!platformSpec.isMonolithic() + && (cm.isOutside() + || (srcResPath.getCommonPrefix(dstResPath)) == null && platformSpec.isDifferentTreesAsDifferentServices())) { + // for REST API + // Data transfer from an outside input resource is regarded as PULL transfer. + List pathParams = new ArrayList<>(); + for (Expression pathExp: srcResPath.getPathParams()) { + String[] sideEffects = new String[] {""}; + pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); + } + // generate a pull data transfer from a depending in/ref resource. + Type srcResourceType = srcResPath.getResourceStateType(); + String srcResName2 = langSpec.toVariableName(getComponentName(srcResPath.getResourceHierarchy(), langSpec)); + String srcPath2 = srcResPath.toResourcePath().replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + generatePullDataTransfer(stateGetter, srcResName2, srcPath2, srcResourceType, false, platformSpec, langSpec); + bDeclareClientField = true; + } + } + + // Data transfer from the descendant channel hierarchies. + Stack> channelItrStack = new Stack<>(); + DataTransferChannel curChannel = ch; + if (curChannel.getChildren() != null && curChannel.getChildren().size() > 0) { + // retrieve descendant channels recursively. + Iterator chItr = curChannel.getChildren().iterator(); + do { + if (!chItr.hasNext()) { + chItr = channelItrStack.pop(); + } else { + curChannel = (DataTransferChannel) chItr.next(); + // generate pull data transfers. + Set chMems = new HashSet<>(curChannel.getInputChannelMembers()); + chMems.addAll(curChannel.getReferenceChannelMembers()); + for (ChannelMember cm2: chMems) { + if (resourcePaths == null || !resourcePaths.keySet().contains(cm2)) { + // not a depending channel member. + ResourcePath src2 = cm2.getResource(); + Type srcResType2 = src2.getResourceStateType(); + String srcResName2 = langSpec.toVariableName(getComponentName(src2.getResourceHierarchy(), langSpec)); + if (platformSpec.isMonolithic()) { + String srcGetter = getPullAccessor(platformSpec).getDirectStateAccessorFor(src2, resourceNode.getInSideResource(curChannel)).toImplementation(new String[] {}); + stateGetter.addStatement(langSpec.getVariableDeclaration(srcResType2.getInterfaceTypeName(), srcResName2) + + langSpec.getAssignment() + srcGetter + langSpec.getStatementDelimiter()); + } else { + String srcPath2 = src2.toResourcePath().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + generatePullDataTransfer(stateGetter, srcResName2, srcPath2, srcResType2, false, platformSpec, langSpec); + bDeclareClientField = true; + } + } else { + // a depending channel member. + ResourcePath src2 = resourcePaths.get(cm2).getKey(); + // get outside src2 resource state by pull data transfer. + if (cm2.isOutside() || src2.getCommonPrefix(resourceNode.getInSideResource(curChannel)) == null) { + // generate a pull data transfer from a depending in/ref resource. + Type srcResType2 = src2.getResourceStateType(); + String srcResName2 = langSpec.toVariableName(getComponentName(src2.getResourceHierarchy(), langSpec)); + if (platformSpec.isMonolithic()) { + String dependingGetter = getPullAccessor(platformSpec).getDirectStateAccessorFor(src2, resourceNode.getInSideResource(curChannel)).toImplementation(new String[] {}); + stateGetter.addStatement(langSpec.getVariableDeclaration(srcResType2.getInterfaceTypeName(), srcResName2) + + langSpec.getAssignment() + dependingGetter + langSpec.getStatementDelimiter()); + } else { + String srcPath2 = src2.toResourcePath().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + generatePullDataTransfer(stateGetter, srcResName2, srcPath2, srcResType2, false, platformSpec, langSpec); + bDeclareClientField = true; + } + } + } + } + // collect the message constraints by a descendant channel. + List varsForSideEffects = new ArrayList<>(); + int v = 0; + resourcePathsAndMessage = curChannel.fillOutsideResourcePaths(out, getPullAccessor(platformSpec), null); + if (resourcePathsAndMessage != null) { + resourcePaths = resourcePathsAndMessage.getKey(); + Term messageTermSub = resourcePathsAndMessage.getValue(); + for (Entry fieldEnt: ((Term) messageTermSub).getSubTerms(Field.class).entrySet()) { + Position pos = fieldEnt.getKey(); + Field field = fieldEnt.getValue(); + Variable var = new Variable(field.getSymbol().getName(), field.getType()); + ((Term) messageTermSub).replaceSubTerm(pos, var); + } + for (Map.Entry subTermEnt: messageTermSub.getSubTerms(Term.class).entrySet()) { + Term subTerm = subTermEnt.getValue(); + if (!(subTerm instanceof Constant) && subTerm.getSymbol().isImplWithSideEffect()) { + Variable var = new Variable("v" + v, subTerm.getType()); + varsForSideEffects.add(var); + v++; + // Add a side effect statement within the loop + Position pos = new Position(); + pos.addHeadOrder(0); + subTerm.replaceSubTerm(pos, var); + String[] sideEffects = new String[] {""}; + String curState = messageTermSub.toImplementation(sideEffects); + stateGetter.addStatement(sideEffects[0].replaceAll("\n", "")); + // Cancel the side effects in the return value. + pos = subTermEnt.getKey(); + messageTermSub.replaceSubTerm(pos, var); + } + } + if (messageTerm == null) { + messageTerm = messageTermSub; + } else { + messageTerm = (Term) messageTerm.unify(messageTermSub); + } + if (messageTerm == null) { + throw new UnificationFailed(); + } + } + // enclosed by a for loop (for data collecting pull transfer) + Expression selExp = curChannel.getSelectors().get(0).getExpression(); + Type selType = null; + String forVarName = null; + if (selExp instanceof Variable) { + selType = ((Variable) selExp).getType(); + forVarName = ((Variable) selExp).getName(); + ChannelMember insideChMem = null; + for (ChannelMember cm2 :curChannel.getInputChannelMembers()) { + if (!cm2.isOutside()) { + insideChMem = cm2; + break; + } + } + if (insideChMem == null) { + for (ChannelMember cm2 :curChannel.getReferenceChannelMembers()) { + if (!cm2.isOutside()) { + insideChMem = cm2; + break; + } + } + } + ResourcePath insideResPath = insideChMem.getResource(); + while (insideResPath.getParent() != null && (insideResPath.getLastParam() == null || !insideResPath.getLastParam().equals(selExp))) { + insideResPath = insideResPath.getParent(); + } + insideResPath = insideResPath.getParent(); + if (insideResPath != null) { + String parent = null; + if (platformSpec.isMonolithic() + || insideResPath.getCommonPrefix(dstResPath) != null + || !platformSpec.isDifferentTreesAsDifferentServices()) { + if (!platformSpec.isMonolithic() && generatesComponent(insideResPath.getResourceHierarchy())) { + Expression parentGetter = getPullAccessor(platformSpec).getDirectStateAccessorFor(insideResPath, dstResPath); + Term valueGetter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); + valueGetter.addChild(parentGetter); + parent = valueGetter.toImplementation(new String[] {}); + } else { + parent = getPullAccessor(platformSpec).getDirectStateAccessorFor(insideResPath, dstResPath).toImplementation(new String[] {}); + } + } else { + // for REST API + parent = langSpec.toVariableName(getComponentName(insideResPath.getResourceHierarchy(), langSpec)); + } + if (selType.equals(DataConstraintModel.typeInt)) { + // make a for loop (for a list) for data collecting. + stateGetter.addFirstStatement(langSpec.getForStatementForList(forVarName, parent)); + } else if (selType.equals(DataConstraintModel.typeString)) { + // make a for loop (for a map) for data collecting. + stateGetter.addFirstStatement(langSpec.getForStatementForMap(forVarName, DataConstraintModel.typeString.getInterfaceTypeName(), parent)); + } + if (!platformSpec.isMonolithic() + && insideResPath.getCommonPrefix(dstResPath) == null + && platformSpec.isDifferentTreesAsDifferentServices()) { + // for REST API + Type parentResType = insideResPath.getResourceStateType(); + String parentResName = langSpec.toVariableName(getComponentName(insideResPath.getResourceHierarchy(), langSpec)); + String parentResPath = insideResPath.toResourcePath().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + generatePullDataTransfer(stateGetter, parentResName, parentResPath, parentResType, true, platformSpec, langSpec); + bDeclareClientField = true; + } + } + } + // initialize the variables to hold side effects within the loop + for (Variable var: varsForSideEffects) { + stateGetter.addFirstStatement(langSpec.getVariableDeclaration(var.getType().getInterfaceTypeName(), var.getName()) + + langSpec.getAssignment() + langSpec.getConstructorInvocation(var.getType().getImplementationTypeName(), null) + langSpec.getStatementDelimiter()); + } + // end of the loop + stateGetter.addStatement("}"); + if (curChannel.getChildren() != null && curChannel.getChildren().size() > 0) { + channelItrStack.push(chItr); + chItr = curChannel.getChildren().iterator(); + } + } + } while (!channelItrStack.isEmpty()); + } + + // generate a return statement. + String[] sideEffects = new String[] {""}; + String curState = ch.deriveUpdateExpressionOf(out, messageTerm, getPullAccessor(platformSpec)).toImplementation(sideEffects); + stateGetter.addStatement(sideEffects[0] + langSpec.getReturnStatement(curState) + langSpec.getStatementDelimiter()); } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage | UnificationFailed | ValueUndefined e) { e.printStackTrace(); } + return bDeclareClientField; } private void declareDescendantGetterMethods(ResourceNode resourceNode, TypeDeclaration component, Map> descendantGetters, ILanguageSpecific langSpec) { @@ -796,7 +1272,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; @@ -806,21 +1282,37 @@ } } - private Map.Entry, Map>>> declareCacheFieldsAndUpdateMethods(ResourceNode resourceNode, TypeDeclaration component, TypeDeclaration parentComponent, ILanguageSpecific langSpec) { + private Map.Entry, Map>>> declareCacheFieldsAndUpdateMethods(ResourceNode resourceNode, + TypeDeclaration component, TypeDeclaration parentComponent, TypeDeclaration rootComponent, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { // Declare cash fields and update methods in the component. String resComponentName = getComponentName(resourceNode.getResourceHierarchy(), langSpec); List constructorStatements = new ArrayList<>(); Map>> updateStatements = new HashMap<>(); for (Edge chToRes: resourceNode.getInEdges()) { - 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; @@ -828,9 +1320,11 @@ } // Check if the output resource is outside of the channel scope. boolean outsideOutputResource = false; + ChannelMember out = null; ResourcePath dstResPath = null; for (ChannelMember cm: ch.getOutputChannelMembers()) { if (resourceNode.getInSideResources().contains(cm.getResource())) { + out = cm; dstResPath = cm.getResource(); if (cm.isOutside()) { outsideOutputResource = true; // Regarded as push transfer. @@ -838,34 +1332,52 @@ } } } - if ((((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) { - // for push data transfer + // Also take into account the channel hierarchy to determine push/pull transfer. + if (ancestorSrcChannels.contains(indirectSrcChNode)) { + outsideOutputResource = true; // Regarded as (broadcasting) push transfer. + } + if (descendantSrcChannels.contains(indirectSrcChNode)) { + outsideInputResource = true; // Regarded as (collecting) pull transfer. + } + if ((((PushPullAttribute) re.getAttribute()).getSelectedOption() == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) { + // For push data transfer + boolean hasRestAPI = false; + boolean isRestAPI = false; + if (!platformSpec.isMonolithic() + && (outsideOutputResource || (resourceNode.getInSideResource(ch).getCommonPrefix(srcResPath) == null && platformSpec.isDifferentTreesAsDifferentServices()))) { + // Inter-service + hasRestAPI = true; + if (component != null && !((RestApiSpecific) platformSpec).hasHttpClientFieldDeclaration(component)) { + // Declare a client field to connect to the destination resource of push transfer. + ((RestApiSpecific) platformSpec).addHttpClientFieldDeclaration(component); + } + if (resourceNode.getParent() == null) { + // A root resource + isRestAPI = true; + } + } // Declare an update method in the type of the destination resource. ArrayList parameters = new ArrayList<>(); - int v = 1; - for (Expression exp: dstResPath.getPathParams()) { - if (exp instanceof Variable) { - Variable pathVar = (Variable) exp; - String varName = "self" + (v > 1 ? v : ""); -// String varName = pathVar.getName(); - VariableDeclaration pathParam = langSpec.newVariableDeclaration(pathVar.getType(), varName); - parameters.add(pathParam); // A path parameter to identify the self resource. - } - v++; - } + getUpdateResourcePathAndPathParams(dstResPath, parameters, isRestAPI, platformSpec, langSpec); // Path parameters to identify the self resource. for (Selector selector: ch.getAllSelectors()) { if (selector.getExpression() instanceof Variable) { Variable selVar = (Variable) selector.getExpression(); VariableDeclaration chParam = langSpec.newVariableDeclaration(selVar.getType(), selVar.getName()); + if (isRestAPI) ((RestApiSpecific) platformSpec).addFormParamAnnotation(chParam, selVar.getName()); parameters.add(chParam); // A channel parameter to specify the context of the collaboration. } } - parameters.add(langSpec.newVariableDeclaration(srcResPath.getResourceStateType(), srcResPath.getLeafResourceName())); // The state of the source resource to carry the data-flow. + VariableDeclaration param = langSpec.newVariableDeclaration(srcResPath.getResourceStateType(), srcResName); + if (isRestAPI) ((RestApiSpecific) platformSpec).addFormParamAnnotation(param, srcResName); + parameters.add(param); // The state of the source resource to carry the data-flow. // For the refs. for (ResourcePath ref: ch.getReferenceResources()) { if (!resourceNode.getInSideResources().contains(ref)) { - parameters.add(langSpec.newVariableDeclaration(ref.getResourceStateType(), ref.getLeafResourceName())); + String refName = langSpec.toVariableName(getComponentName(ref.getResourceHierarchy(), langSpec)); + param = langSpec.newVariableDeclaration(ref.getResourceStateType(), refName); + if (isRestAPI) ((RestApiSpecific) platformSpec).addFormParamAnnotation(param, refName); + parameters.add(param); } } MethodDeclaration update = null; @@ -878,7 +1390,6 @@ } if (update == null) { update = langSpec.newMethodDeclaration(updateMethodPrefix + from + srcResComponentName, false, null, parameters); - component.addMethod(update); } } else if (parentComponent != null) { for (MethodDeclaration method: parentComponent.getMethods()) { @@ -889,126 +1400,273 @@ } if (update == null) { update = langSpec.newMethodDeclaration(updateMethodPrefix + resComponentName + from + srcResComponentName, false, null, parameters); + } + } + // Calculate in-degree (PUSH transfer) of the destination resource. + int inDegree = 0; + for (Edge resToCh2: inEdges) { + DataFlowEdge df =(DataFlowEdge) resToCh2; + if (((PushPullAttribute) df.getAttribute()).getSelectedOption() == PushPullValue.PUSH) { + inDegree++; + } + } + if (isRestAPI) { + // Determine whether the update method is put or post or delete. + if (isPut(out)) { + ((RestApiSpecific) platformSpec).addPutAnnotations(update); + } else { + if (!isDelete(out)) { + ((RestApiSpecific) platformSpec).addPostAnnotations(update); + } else { + ((RestApiSpecific) platformSpec).addDeleteAnnotations(update); + } + } + if (inDegree > 1) { + // If incoming edges are multiple, then a child resource for each source resource is defined in the destination resource so that its state can be updated separately. + if (isRestAPI) ((RestApiSpecific) platformSpec).addPathAnnotation(update, "/" + srcResName); + } + } + if (update != null) { + if (component != null) { + // A component is created for this resource. + component.addMethod(update); + } else if (parentComponent != null) { + // No component is created for this resource. parentComponent.addMethod(update); } } + // For a post/put REST API. + if (hasRestAPI) { + if (!isRestAPI) { + // If not a root resource. + // Declare an update accessor method in the type of root resource. + declareUpdateAccessorInTheRootResource(resourceNode, update.getName(), ch, out, srcResPath, dstResPath, + rootComponent, inDegree, platformSpec, langSpec); + } + // to convert a json param to a tuple, pair or map object. + for (VariableDeclaration jsonParam: update.getParameters()) { + Type paramType = jsonParam.getType(); + String paramName = jsonParam.getName(); + String paramTypeName = paramType.getInterfaceTypeName(); + String strTypeName = DataConstraintModel.typeString.getInterfaceTypeName(); + String paramConverter = ""; + if (DataConstraintModel.typeList.isAncestorOf(paramType) && paramType != DataConstraintModel.typeList) { + Type compType = TypeInference.getListComponentType(paramType); + if (DataConstraintModel.typeTuple.isAncestorOf(compType)) { + jsonParam.setType(DataConstraintModel.typeListStr); + jsonParam.setName(paramName + "_json"); + paramConverter += langSpec.getVariableDeclaration(paramTypeName, paramName) + langSpec.getAssignment() + langSpec.getConstructorInvocation(paramType.getImplementationTypeName(), null) + langSpec.getStatementDelimiter() + "\n"; + paramConverter += langSpec.getForStatementForCollection("str", strTypeName, jsonParam.getName()) + "\n"; + String mapTypeName = convertFromEntryToMapType(compType, langSpec); + paramConverter += "\t" + ((RestApiSpecific) platformSpec).getConversionFromJsonString("str", "i", mapTypeName) + langSpec.getStatementDelimiter() + "\n"; + paramConverter += "\t" + langSpec.getMethodInvocation(paramName, DataConstraintModel.append.getImplName(), List.of(getCodeForConversionFromMapToTuple(compType, "i", langSpec))) + langSpec.getStatementDelimiter() + "\n"; + paramConverter += langSpec.getEndForStatement("str"); + ((RestApiSpecific) platformSpec).addJsonException(update); + } else if (DataConstraintModel.typePair.isAncestorOf(compType)) { + jsonParam.setType(DataConstraintModel.typeListStr); + jsonParam.setName(paramName + "_json"); + paramConverter += langSpec.getVariableDeclaration(paramTypeName, paramName) + langSpec.getAssignment() + langSpec.getConstructorInvocation(paramType.getImplementationTypeName(), null) + langSpec.getStatementDelimiter() + "\n"; + paramConverter += langSpec.getForStatementForCollection("str", strTypeName, jsonParam.getName()) + "\n"; + String mapTypeName = convertFromEntryToMapType(compType, langSpec); + paramConverter += "\t" + ((RestApiSpecific) platformSpec).getConversionFromJsonString("str", "i", mapTypeName) + langSpec.getStatementDelimiter() + "\n"; + paramConverter += "\t" + langSpec.getMethodInvocation(paramName, DataConstraintModel.append.getImplName(), List.of(getCodeForConversionFromMapToPair(compType, "i", langSpec))) + langSpec.getStatementDelimiter() + "\n"; + paramConverter += langSpec.getEndForStatement("str"); + ((RestApiSpecific) platformSpec).addJsonException(update); + } else if (DataConstraintModel.typeMap.isAncestorOf(compType)) { + jsonParam.setType(DataConstraintModel.typeListStr); + // To do. + } + } else if (DataConstraintModel.typeTuple.isAncestorOf(paramType)) { + jsonParam.setType(DataConstraintModel.typeString); + jsonParam.setName(paramName + "_json"); + paramConverter += langSpec.getVariableDeclaration(paramTypeName, paramName) + langSpec.getStatementDelimiter() + "\n"; + paramConverter += langSpec.getOpeningScoreDelimiter() + "\n"; + String mapTypeName = convertFromEntryToMapType(paramType, langSpec); + paramConverter += "\t" + ((RestApiSpecific) platformSpec).getConversionFromJsonString(paramName + "_json", "i", mapTypeName) + langSpec.getStatementDelimiter() + "\n"; + paramConverter += "\t" + paramName + langSpec.getAssignment() + getCodeForConversionFromMapToTuple(paramType, "i", langSpec) + langSpec.getStatementDelimiter() + "\n"; + paramConverter += langSpec.getClosingScoreDelimiter(); + ((RestApiSpecific) platformSpec).addJsonException(update); + } else if (DataConstraintModel.typePair.isAncestorOf(paramType)) { + jsonParam.setType(DataConstraintModel.typeString); + jsonParam.setName(paramName + "_json"); + paramConverter += langSpec.getVariableDeclaration(paramTypeName, paramName) + langSpec.getStatementDelimiter() + "\n"; + paramConverter += langSpec.getOpeningScoreDelimiter() + "\n"; + String mapTypeName = convertFromEntryToMapType(paramType, langSpec); + paramConverter += "\t" + ((RestApiSpecific) platformSpec).getConversionFromJsonString(paramName + "_json", "i", mapTypeName) + langSpec.getStatementDelimiter() + "\n"; + paramConverter += "\t" + paramName + langSpec.getAssignment() + getCodeForConversionFromMapToPair(paramType, "i", langSpec) + langSpec.getStatementDelimiter() + "\n"; + paramConverter += langSpec.getClosingScoreDelimiter(); + ((RestApiSpecific) platformSpec).addJsonException(update); + } else if (DataConstraintModel.typeMap.isAncestorOf(paramType)) { + jsonParam.setType(DataConstraintModel.typeString); + jsonParam.setName(paramName + "_json"); + paramConverter += langSpec.getVariableDeclaration(paramTypeName, paramName) + langSpec.getAssignment() + langSpec.getConstructorInvocation(paramType.getImplementationTypeName(), null) + langSpec.getStatementDelimiter() + "\n"; + paramConverter += langSpec.getOpeningScoreDelimiter() + "\n"; + String mapTypeName = convertFromEntryToMapType(paramType, langSpec); + paramConverter += "\t" + ((RestApiSpecific) platformSpec).getConversionFromJsonString(paramName + "_json", "i", mapTypeName) + langSpec.getStatementDelimiter() + "\n"; + paramConverter += "\t" + getCodeForConversionFromMapToMap(paramType, "i", paramName, langSpec) + "\n"; + paramConverter += langSpec.getClosingScoreDelimiter(); + ((RestApiSpecific) platformSpec).addJsonException(update); + } + if (paramConverter.length() > 0 && !update.getBody().getStatements().contains(paramConverter)) { + update.addFirstStatement(paramConverter); + } + } + } // Add a statement to update the state field if (((StoreAttribute) resourceNode.getAttribute()).isStored()) { try { - for (ChannelMember out: ch.getOutputChannelMembers()) { - if (resourceNode.getInSideResources().contains(out.getResource())) { - Expression updateExp = null; - if (ch.getReferenceChannelMembers().size() == 0) { - updateExp = ch.deriveUpdateExpressionOf(out, getPushAccessor()).getKey(); + if (resourceNode.getInSideResources().contains(out.getResource())) { + Term unifiedMassage = null; + for (ChannelNode srcChNode: ancestorSrcChannels) { + DataTransferChannel abcestorSrcCh = (DataTransferChannel) srcChNode.getChannel(); + Term message = abcestorSrcCh.fillOutsideResourcePaths(out, getPushAccessor(platformSpec), null).getValue(); + if (unifiedMassage == null) { + unifiedMassage = message; } else { - // if there exists one or more reference channel member. - HashMap inputResourceToStateAccessor = new HashMap<>(); - for (ChannelMember in: ch.getInputChannelMembers()) { - inputResourceToStateAccessor.put(in, getPushAccessor()); - } - for (ChannelMember c: ch.getReferenceChannelMembers()) { - inputResourceToStateAccessor.put(c, getRefAccessor()); - } - updateExp = ch.deriveUpdateExpressionOf(out, getPushAccessor(), inputResourceToStateAccessor).getKey(); + unifiedMassage = (Term) unifiedMassage.unify(message); } - // Replace Json constructor with a constructor of the child resource. - ResourceHierarchy outRes = out.getResource().getResourceHierarchy(); - if (outRes.getChildren().size() == 1 && outRes.getChildren().iterator().next().getNumParameters() > 0) { - ResourceHierarchy descendantRes = outRes; - Set children = descendantRes.getChildren(); - do { - descendantRes = children.iterator().next(); - if (generatesComponent(descendantRes)) { - updateStatements.put(update, new AbstractMap.SimpleEntry<>(updateExp, new AbstractMap.SimpleEntry<>(outRes, descendantRes))); - updateExp = null; - break; - } - children = descendantRes.getChildren(); - } while (children != null && children.size() == 1); + } + Expression updateExp = null; + if (ch.getReferenceChannelMembers().size() == 0) { + Term message = ch.fillOutsideResourcePaths(out, getPushAccessor(platformSpec), null).getValue(); + if (unifiedMassage == null) { + unifiedMassage = message; + } else { + unifiedMassage = (Term) unifiedMassage.unify(message); } - // Add statements to the update method. - String[] sideEffects = new String[] {""}; - String newState = updateExp.toImplementation(sideEffects); - int numOfOutResourcesWithTheSameHierarchy = 0; - for (ResourcePath outResPath: ch.getOutputResources()) { - if (outResPath.getResourceHierarchy().equals(outRes)) { - numOfOutResourcesWithTheSameHierarchy++; + updateExp = ch.deriveUpdateExpressionOf(out, unifiedMassage, getPushAccessor(platformSpec)); + } else { + // if there exists one or more reference channel member. + HashMap inputResourceToStateAccessor = new HashMap<>(); + for (ChannelMember in: ch.getInputChannelMembers()) { + inputResourceToStateAccessor.put(in, getPushAccessor(platformSpec)); + } + for (ChannelMember ref: ch.getReferenceChannelMembers()) { + inputResourceToStateAccessor.put(ref, getRefAccessor(platformSpec)); + } + Term message = ch.fillOutsideResourcePaths(out, getPushAccessor(platformSpec), inputResourceToStateAccessor).getValue(); + if (unifiedMassage == null) { + unifiedMassage = message; + } else { + unifiedMassage = (Term) unifiedMassage.unify(message); + } + updateExp = ch.deriveUpdateExpressionOf(out, unifiedMassage, getPushAccessor(platformSpec)); + } + // Replace Json constructor with a constructor of the child resource. + ResourceHierarchy outRes = out.getResource().getResourceHierarchy(); + if (outRes.getChildren().size() == 1 && outRes.getChildren().iterator().next().getNumParameters() > 0) { + ResourceHierarchy descendantRes = outRes; + Set children = descendantRes.getChildren(); + do { + descendantRes = children.iterator().next(); + if (generatesComponent(descendantRes)) { + updateStatements.put(update, new AbstractMap.SimpleEntry<>(updateExp, new AbstractMap.SimpleEntry<>(outRes, descendantRes))); + updateExp = null; + break; + } + children = descendantRes.getChildren(); + } while (children != null && children.size() == 1); + } + // Replace the type of the state field. + Type fieldType = getImplStateType(outRes, langSpec); + if (updateExp instanceof Term) { + ((Term) updateExp).setType(fieldType); + for (Map.Entry varEnt: ((Term) updateExp).getVariables().entrySet()) { + if (varEnt.getValue().getName().equals(fieldOfResourceState)) { + varEnt.getValue().setType(fieldType); } } - String updateStatement = ""; - if (generatesComponent(outRes)) { - if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { - updateStatement = sideEffects[0]; - } else { - updateStatement = sideEffects[0] + langSpec.getFieldAccessor(fieldOfResourceState) + langSpec.getAssignment() + newState + langSpec.getStatementDelimiter(); // this.value = ... + } else if (updateExp instanceof Variable) { + ((Variable) updateExp).setType(fieldType); + } + // Add statements to the update method. + String[] sideEffects = new String[] {""}; + String newState = updateExp.toImplementation(sideEffects); + int numOfOutResourcesWithTheSameHierarchy = 0; + for (ResourcePath outResPath: ch.getOutputResources()) { + if (outResPath.getResourceHierarchy().equals(outRes)) { + numOfOutResourcesWithTheSameHierarchy++; + } + } + String updateStatement = ""; + if (generatesComponent(outRes)) { + if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { + updateStatement = sideEffects[0]; + if (updateStatement.endsWith("\n")) { + updateStatement = updateStatement.substring(0, updateStatement.length() - 1); } } else { - if (sideEffects[0] != null) { - updateStatement = sideEffects[0]; - String resourceName = langSpec.toVariableName(getComponentName(outRes, langSpec)); - updateStatement = updateStatement.replace(langSpec.getFieldAccessor(fieldOfResourceState), langSpec.getFieldAccessor(resourceName)); - } - if (DataConstraintModel.typeList.isAncestorOf(resourceNode.getParent().getResourceStateType())) { - Term selector = new Term(DataConstraintModel.set); - selector.addChild(new Constant(langSpec.getFieldAccessor(fieldOfResourceState))); - selector.addChild(new Variable(update.getParameters().get(update.getParameters().size() - 2).getName())); - selector.addChild(new Constant(newState)); - String[] sideEffects2 = new String[] {""}; - String newList = selector.toImplementation(sideEffects2); - updateStatement += sideEffects2[0]; - } else if (DataConstraintModel.typeMap.isAncestorOf(resourceNode.getParent().getResourceStateType())) { - Term selector = new Term(DataConstraintModel.insert); - selector.addChild(new Constant(langSpec.getFieldAccessor(fieldOfResourceState))); - selector.addChild(new Variable(update.getParameters().get(update.getParameters().size() - 2).getName())); - selector.addChild(new Constant(newState)); - String[] sideEffects2 = new String[] {""}; - String newMap = selector.toImplementation(sideEffects2); - updateStatement += sideEffects2[0]; - } else if (!(updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect())) { - String resourceName = langSpec.toVariableName(getComponentName(outRes, langSpec)); - updateStatement += langSpec.getFieldAccessor(resourceName) + langSpec.getAssignment() + newState + langSpec.getStatementDelimiter(); + updateStatement = sideEffects[0] + langSpec.getFieldAccessor(fieldOfResourceState) + langSpec.getAssignment() + newState + langSpec.getStatementDelimiter(); // this.value = ... + } + } else { + if (sideEffects[0] != null) { + updateStatement = sideEffects[0]; + String resourceName = langSpec.toVariableName(getComponentName(outRes, langSpec)); + updateStatement = updateStatement.replace(langSpec.getFieldAccessor(fieldOfResourceState), langSpec.getFieldAccessor(resourceName)); + if (updateStatement.endsWith("\n")) { + updateStatement = updateStatement.substring(0, updateStatement.length() - 1); } } - if (numOfOutResourcesWithTheSameHierarchy == 1) { - update.addFirstStatement(updateStatement); - } else { - Term conditions = null; - int i = 1; - Map>> resourcePaths = ch.fillOutsideResourcePaths(out, JerseyCodeGenerator.pushAccessor); - for (Expression pathParam: out.getResource().getPathParams()) { - if (pathParam instanceof Variable) { - String selfParamName = ((Variable) pathParam).getName(); - Expression arg = null; - for (Selector selector: ch.getAllSelectors()) { - if (selector.getExpression() instanceof Variable) { - Variable selVar = (Variable) selector.getExpression(); - if (selVar.getName().equals(selfParamName)) { - arg = selVar; - break; - } + if (DataConstraintModel.typeList.isAncestorOf(resourceNode.getParent().getResourceStateType())) { + Term selector = new Term(DataConstraintModel.set); + selector.addChild(new Constant(langSpec.getFieldAccessor(fieldOfResourceState))); + selector.addChild(new Variable(update.getParameters().get(update.getParameters().size() - 2).getName())); + selector.addChild(new Constant(newState)); + String[] sideEffects2 = new String[] {""}; + String newList = selector.toImplementation(sideEffects2); + updateStatement += sideEffects2[0]; + } else if (DataConstraintModel.typeMap.isAncestorOf(resourceNode.getParent().getResourceStateType())) { + Term selector = new Term(DataConstraintModel.insert); + selector.addChild(new Constant(langSpec.getFieldAccessor(fieldOfResourceState))); + selector.addChild(new Variable(update.getParameters().get(update.getParameters().size() - 2).getName())); + selector.addChild(new Constant(newState)); + String[] sideEffects2 = new String[] {""}; + String newMap = selector.toImplementation(sideEffects2); + updateStatement += sideEffects2[0]; + } else if (!(updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect())) { + String resourceName = langSpec.toVariableName(getComponentName(outRes, langSpec)); + updateStatement += langSpec.getFieldAccessor(resourceName) + langSpec.getAssignment() + newState + langSpec.getStatementDelimiter(); + } + } + // add an update statement of the state of dst side resource. + if (numOfOutResourcesWithTheSameHierarchy == 1) { + update.addFirstStatement(updateStatement); + } else { + Term conditions = null; + int i = 1; + Map>> resourcePaths = ch.fillOutsideResourcePaths(out, getPushAccessor(platformSpec)); + for (Expression pathParam: out.getResource().getPathParams()) { + if (pathParam instanceof Variable) { + String selfParamName = ((Variable) pathParam).getName(); + Expression arg = null; + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + if (selVar.getName().equals(selfParamName)) { + arg = selVar; + break; } } - if (arg == null) { - ResourcePath filledPath = resourcePaths.get(out).getKey(); - arg = filledPath.getPathParams().get(i - 1); - } - Term condition = new Term(DataConstraintModel.eq, new Expression[] { - new Parameter("self" + (i > 1 ? i : ""), DataConstraintModel.typeString), - arg}); - if (conditions == null) { - conditions = condition; - } else { - conditions = new Term(DataConstraintModel.and, new Expression[] { - conditions, - condition}); - } } - i++; + if (arg == null) { + ResourcePath filledPath = resourcePaths.get(out).getKey(); + arg = filledPath.getPathParams().get(i - 1); + } + Term condition = new Term(DataConstraintModel.eq, new Expression[] { + new Parameter("self" + (i > 1 ? i : ""), DataConstraintModel.typeString), + arg}); + if (conditions == null) { + conditions = condition; + } else { + conditions = new Term(DataConstraintModel.and, new Expression[] { + conditions, + condition}); + } } - update.addFirstStatement(langSpec.getIfStatement(conditions, updateStatement)); + i++; } - break; + update.addFirstStatement(langSpec.getIfStatement(conditions, updateStatement)); } } } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork @@ -1018,14 +1676,15 @@ } // 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 (inDegree > 1 + || (ch.getInputChannelMembers().size() == 1 && ch.getInputChannelMembers().iterator().next().getStateTransition().isRightPartial())) { // If incoming edges are multiple, or the current state of an input member is needed. if (langSpec.declareField()) { // Declare the cache field. + String cacheFieldName = langSpec.toVariableName(getComponentName(srcResPath.getResourceHierarchy(), langSpec)); FieldDeclaration cacheField = langSpec.newFieldDeclaration( srcResPath.getResourceStateType(), - srcResPath.getLeafResourceName(), + cacheFieldName, langSpec.getFieldInitializer(srcResPath.getResourceStateType(), srcResPath.getResourceHierarchy().getInitialValue())); if (component != null) { component.addField(cacheField); @@ -1041,54 +1700,58 @@ } } - Set outsideInputMembers = new HashSet<>(); - for (ChannelMember cm: ch.getInputChannelMembers()) { - if (cm.isOutside()) { - outsideInputMembers.add(cm); - } - } - if (outsideInputMembers.size() > 0) { - Map>> resourcePaths = null; - for (ChannelMember out: ch.getOutputChannelMembers()) { - if (resourceNode.getInSideResources().contains(out.getResource())) { - try { - resourcePaths = ch.fillOutsideResourcePaths(out, getPullAccessor()); - } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork - | InvalidMessage | UnificationFailed | ValueUndefined e) { - e.printStackTrace(); - } - break; + // Update and initialize a field to refer to an outside input resource for PULL transfer. + if (platformSpec.isMonolithic()) { + // For a monolithic application. + Set outsideInputMembers = new HashSet<>(); + for (ChannelMember cm: ch.getInputChannelMembers()) { + if (cm.isOutside()) { + outsideInputMembers.add(cm); } } - if (resourcePaths != null && resourcePaths.size() > 0) { - for (ChannelMember outsideMember: outsideInputMembers) { - for (ChannelMember dependingMember: resourcePaths.get(outsideMember).getValue()) { - if (dependingMember.getResource().equals(srcResPath)) { - // An outside input resource path depends on srcRes. - ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey(); - String outsideResName = langSpec.toVariableName(getComponentName(outsidePath.getResourceHierarchy(), langSpec)); - Expression outsideExp = getPullAccessor().getDirectStateAccessorFor(outsidePath, null); - if (generatesComponent(outsidePath.getResourceHierarchy())) { - outsideExp = ((Term) outsideExp).getChild(0); - } - Expression nextExp = dependingMember.getStateTransition().getNextStateExpression(); - if (nextExp != null && outsideExp instanceof Term) { - if (nextExp instanceof Variable) { - outsideExp = ((Term) outsideExp).substitute((Variable) nextExp, new Field(langSpec.toVariableName(getComponentName(dependingMember.getResource().getResourceHierarchy(), langSpec)))); - } else { - // ToDo. + if (outsideInputMembers.size() > 0) { + Map>> resourcePaths = null; + for (ChannelMember out1: ch.getOutputChannelMembers()) { + if (resourceNode.getInSideResources().contains(out1.getResource())) { + try { + resourcePaths = ch.fillOutsideResourcePaths(out1, getPullAccessor(platformSpec)); + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork + | InvalidMessage | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + break; + } + } + if (resourcePaths != null && resourcePaths.size() > 0) { + for (ChannelMember outsideMember: outsideInputMembers) { + for (ChannelMember dependingMember: resourcePaths.get(outsideMember).getValue()) { + if (dependingMember.getResource().equals(srcResPath)) { + // An outside input resource path depends on srcRes. + ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey(); + String outsideResName = langSpec.toVariableName(getComponentName(outsidePath.getResourceHierarchy(), langSpec)); + Expression outsideExp = getPullAccessor(platformSpec).getDirectStateAccessorFor(outsidePath, null); + if (generatesComponent(outsidePath.getResourceHierarchy())) { + outsideExp = ((Term) outsideExp).getChild(0); } - } - 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); + Expression nextExp = dependingMember.getStateTransition().getNextStateExpression(); + if (nextExp != null && outsideExp instanceof Term) { + if (nextExp instanceof Variable) { + outsideExp = ((Term) outsideExp).substitute((Variable) nextExp, new Field(langSpec.toVariableName(getComponentName(dependingMember.getResource().getResourceHierarchy(), langSpec)))); + } else { + // ToDo. + } + } + String[] sideEffects = new String[] {""}; + String outsideAccessor = outsideExp.toImplementation(sideEffects); + String updateReference = langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter(); + update.addStatement(updateReference); // Update the reference field. + // Update constructor. + if (component != null) { + MethodDeclaration constructor = getConstructor(component); + constructor.addStatement(updateReference); // Initialize the reference field. + } else if (parentComponent != null){ + constructorStatements.add(updateReference); + } } } } @@ -1097,15 +1760,17 @@ } // Add an invocation to another update method (for a chain of update method invocations). + boolean hasUpdateMethodinvoked = false; for (Edge resToCh2: resourceNode.getOutEdges()) { DataFlowEdge dOut = (DataFlowEdge) resToCh2; - 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,144 +1780,392 @@ 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; + ChannelMember out1 = null; for (ChannelMember cm: ch2.getOutputChannelMembers()) { if (dstNode.getInSideResources().contains(cm.getResource())) { - out = cm; + out1 = cm; if (cm.isOutside()) { outsideOutputResource2 = true; break; } } } - if ((((PushPullAttribute) dOut.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource2) || outsideOutputResource2) { + // 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 - 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()); + boolean addForStatement = false; + String forVarName = null; + if (descendantDstChannels.contains(chNode2)) { + // For hierarchical channels (broadcasting push transfer). + if (ch2.getSelectors() != null && ch2.getSelectors().size() > 0) { + Expression selExp = ch2.getSelectors().get(0).getExpression(); + Type selType = null; + if (selExp instanceof Variable) { + selType = ((Variable) selExp).getType(); + forVarName = ((Variable) selExp).getName(); + ChannelMember insideChMem = null; + for (ChannelMember cm :ch2.getInputChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + if (insideChMem == null) { + for (ChannelMember cm :ch2.getReferenceChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + } + if (insideChMem == null) { + for (ChannelMember cm :ch2.getOutputChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + } + ResourcePath insideResPath = insideChMem.getResource(); + while (insideResPath.getParent() != null && (insideResPath.getLastParam() == null || !insideResPath.getLastParam().equals(selExp))) { + insideResPath = insideResPath.getParent(); + } + insideResPath = insideResPath.getParent(); + if (insideResPath != null) { + String parent = null; + if (platformSpec.isMonolithic() + || insideResPath.getCommonPrefix(resourceNode.getOutSideResource(directDstCh)) != null + || !platformSpec.isDifferentTreesAsDifferentServices()) { + if (!platformSpec.isMonolithic() && generatesComponent(insideResPath.getResourceHierarchy())) { + Expression getter = getPullAccessor(platformSpec).getDirectStateAccessorFor(insideResPath, resourceNode.getOutSideResource(directDstCh)); + Term valueGetter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); + valueGetter.addChild(getter); + parent = valueGetter.toImplementation(new String[] {}); + } else { + parent = getPullAccessor(platformSpec).getDirectStateAccessorFor(insideResPath, resourceNode.getOutSideResource(directDstCh)).toImplementation(new String[] {}); + } + } else { + // for REST API + parent = langSpec.toVariableName(getComponentName(insideResPath.getResourceHierarchy(), langSpec)); + } + if (selType.equals(DataConstraintModel.typeInt)) { + // make a for loop (for a list) for broadcasting. + update.addStatement(langSpec.getForStatementForList(forVarName, parent)); + addForStatement = true; + } else if (selType.equals(DataConstraintModel.typeString)) { + // make a for loop (for a map) for broadcasting. + update.addStatement(langSpec.getForStatementForMap(forVarName, DataConstraintModel.typeString.getInterfaceTypeName(), parent)); + addForStatement = true; + } + if (!platformSpec.isMonolithic() + && insideResPath.getCommonPrefix(resourceNode.getOutSideResource(directDstCh)) == null + && platformSpec.isDifferentTreesAsDifferentServices()) { + // for REST API + Type parentResType = insideResPath.getResourceStateType(); + String parentResName = langSpec.toVariableName(getComponentName(insideResPath.getResourceHierarchy(), langSpec)); + String parentResPath = insideResPath.toResourcePath().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + generatePullDataTransfer(update, parentResName, parentResPath, parentResType, true, platformSpec, langSpec); + } + } + } else if (selExp instanceof Term) { + // not supported. + } } } - // 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(); - } + // Get the value of reference member to call the update method. + List>> refParams = new ArrayList<>(); Map> referredResources = new HashMap<>(); Set referredSet = referredResources.get(update); for (ChannelMember rc: ch2.getReferenceChannelMembers()) { - // 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)); + Type refResourceType = ref.getResourceStateType(); if (!referredSet.contains(ref)) { referredSet.add(ref); - Expression refGetter = getPullAccessor().getCurrentStateAccessorFor(rc, in); String[] sideEffects = new String[] {""}; - String refExp = refGetter.toImplementation(sideEffects); - String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); - update.addStatement(sideEffects[0] + langSpec.getVariableDeclaration(refTypeName, refVarName) + langSpec.getAssignment() + refExp + langSpec.getStatementDelimiter()); + if (!platformSpec.isMonolithic() && rc.isOutside()) { + List pathParams = new ArrayList<>(); + for (Expression pathExp: ref.getPathParams()) { + pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); + } + generatePullDataTransfer(update, refVarName, ref.getResourceHierarchy().toResourcePath(pathParams), refResourceType, false, platformSpec, langSpec); + } else { + ResourcePath srcRes = in.getResource(); + if (!generatesComponent(srcRes.getResourceHierarchy())) { + srcRes = srcRes.getParent(); + } + Expression refGetter = getPullAccessor(platformSpec).getDirectStateAccessorFor(ref, srcRes); + String refExp = refGetter.toImplementation(sideEffects); + String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); + update.addStatement(sideEffects[0] + langSpec.getVariableDeclaration(refTypeName, refVarName) + langSpec.getAssignment() + refExp + langSpec.getStatementDelimiter()); + } } - params.add(refVarName); + refParams.add(new AbstractMap.SimpleEntry<>(ref.getResourceStateType(), + new AbstractMap.SimpleEntry<>(refVarName, + refVarName))); } } + List>> pathParams = new ArrayList<>(); + if (platformSpec.isMonolithic()) { + // Update fields to refer to outside resources. + ResourcePath filledOutsideResourcePath = null; + try { + Map>> resourcePaths = ch2.fillOutsideResourcePaths(out1, getPullAccessor(platformSpec)); + if (resourcePaths != null && resourcePaths.size() > 0) { + for (ChannelMember outsideMember: resourcePaths.keySet()) { + ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey(); + if (out1.equals(outsideMember)) { + filledOutsideResourcePath = outsidePath; + } + if (!generatesComponent(outsidePath.getResourceHierarchy())) { + outsidePath = outsidePath.getParent(); + } + String outsideResName = langSpec.toVariableName(getComponentName(outsidePath.getResourceHierarchy(), langSpec)); + Expression outsideExp = getPullAccessor(platformSpec).getDirectStateAccessorFor(outsidePath, null); + if (generatesComponent(outsidePath.getResourceHierarchy())) { + outsideExp = ((Term) outsideExp).getChild(0); + } + if (outsideExp instanceof Field) { + outsideExp = new Variable(((Field) outsideExp).getSymbol().getName(), ((Field) outsideExp).getType()); + } else if (outsideExp instanceof Term) { + for (Entry fieldEnt: ((Term) outsideExp).getSubTerms(Field.class).entrySet()) { + Position pos = fieldEnt.getKey(); + Field field = fieldEnt.getValue(); + Variable var = new Variable(field.getSymbol().getName(), field.getType()); + ((Term) outsideExp).replaceSubTerm(pos, var); + } + } + String[] sideEffects = new String[] {""}; + String outsideAccessor = outsideExp.toImplementation(sideEffects); + update.addStatement(langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter()); // change the reference field. + } + } + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork + | InvalidMessage | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + // Values of path parameters to call the update method. + if (filledOutsideResourcePath == null) { + filledOutsideResourcePath = out1.getResource(); + } + for (Expression pathParam: filledOutsideResourcePath.getPathParams()) { + if (pathParam instanceof Variable) { + Variable pathVar = (Variable) pathParam; + pathParams.add(new AbstractMap.SimpleEntry<>(pathVar.getType(), + new AbstractMap.SimpleEntry<>(pathVar.getName(), + pathVar.getName()))); + } else if (pathParam instanceof Constant) { + Constant pathVar = (Constant) pathParam; + pathParams.add(new AbstractMap.SimpleEntry<>(pathVar.getType(), + new AbstractMap.SimpleEntry<>(pathVar.getSymbol().getName(), + pathVar.getSymbol().getName()))); + } + } + } else { + // Values of path parameters to call the update method. + for (Expression pathParam: out1.getResource().getPathParams()) { + if (pathParam instanceof Variable) { + Variable pathVar = (Variable) pathParam; + pathParams.add(new AbstractMap.SimpleEntry<>(pathVar.getType(), + new AbstractMap.SimpleEntry<>(pathVar.getName(), + pathVar.getName()))); + } + } + } + // Values of channel parameters to call the update method. + List>> params = new ArrayList<>(); + for (Selector selector: ch2.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + params.add(new AbstractMap.SimpleEntry<>(selVar.getType(), + new AbstractMap.SimpleEntry<>(selVar.getName(), + selVar.getName()))); + } + } + // Value of the source side (input side) resource to call the update method. + ResourceHierarchy srcRes2 = resourceNode.getResourceHierarchy(); + if (generatesComponent(srcRes2)) { + params.add(new AbstractMap.SimpleEntry<>(srcRes2.getResourceStateType(), + new AbstractMap.SimpleEntry<>(langSpec.toVariableName(srcRes2.getResourceName()), + langSpec.getFieldAccessor(fieldOfResourceState)))); + } else { + params.add(new AbstractMap.SimpleEntry<>(srcRes2.getResourceStateType(), + new AbstractMap.SimpleEntry<>(langSpec.toVariableName(srcRes2.getResourceName()), + langSpec.getFieldAccessor(langSpec.toVariableName(srcRes2.getResourceName()))))); + srcRes2 = srcRes2.getParent(); + } + params.addAll(refParams); + // Call the update method. String updateMethodName = null; ResourceHierarchy dstRes = dstNode.getResourceHierarchy(); if (!generatesComponent(dstRes)) { - updateMethodName = updateMethodPrefix + getComponentName(dstRes, langSpec) + from + resComponentName; + updateMethodName = updateMethodPrefix + getComponentName(dstRes, langSpec) + from + resComponentName; dstRes = dstRes.getParent(); } else { updateMethodName = updateMethodPrefix + from + resComponentName; } String dstCompName = langSpec.toVariableName(getComponentName(dstRes, langSpec)); - if (!outsideOutputResource2) { - // The destination resource is not outside. - if (srcRes2 != dstRes) { - update.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, params) - + langSpec.getStatementDelimiter()); // this.dst.updateDstFromSrc(value, refParams); + if (outsideOutputResource2 + || (!platformSpec.isMonolithic() && in.getResource().getCommonPrefix(out1.getResource()) == null && platformSpec.isDifferentTreesAsDifferentServices())) { + // Inter-servces + if (!platformSpec.isMonolithic()) { + // REST API + RestApiSpecific restApiSpec = (RestApiSpecific) platformSpec; + String httpMethod = null; + if (out1.getStateTransition().isRightUnary()) { + httpMethod = "put"; + } else { + httpMethod = "post"; + } + String[] sideEffects = new String[] {""}; + List pathParamsUrl = new ArrayList<>(); + for (Expression pathExp: out1.getResource().getPathParams()) { + pathParamsUrl.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); + } + String resName = langSpec.toVariableName(resComponentName); + if (inDegree <= 1) { + resName = null; + } + Map>> filledPaths = null; + try { + filledPaths = ch2.fillOutsideResourcePaths(out1, getPushAccessor(platformSpec)); + } catch (ParameterizedIdentifierIsFutureWork + | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage + | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + String dstPath = null; + if (filledPaths != null && filledPaths.get(out1) != null) { + ResourcePath filledDstPath = filledPaths.get(out1).getKey(); + dstPath = filledDstPath.toResourcePath().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + } else { + dstPath = dstRes.toResourcePath(pathParamsUrl); + } + // Call the update method. + if (!hasUpdateMethodinvoked) { + // The first call to an update method in this method + update.addStatement(restApiSpec.getHttpMethodParamsConstructionStatement(srcRes2.getResourceName(), params, true)); + update.addStatement(langSpec.getVariableDeclaration(DataConstraintModel.typeString.getInterfaceTypeName(), "result") + + langSpec.getAssignment() + restApiSpec.getHttpMethodCallStatement(restApiSpec.getBaseURL(), dstPath, resName, httpMethod)); + hasUpdateMethodinvoked = true; + } else { + // After the second time of call to update methods in this method + update.addStatement(restApiSpec.getHttpMethodParamsConstructionStatement(srcRes2.getResourceName(), params, false)); + update.addStatement("result" + langSpec.getAssignment() + restApiSpec.getHttpMethodCallStatement(restApiSpec.getBaseURL(), dstPath, resName, httpMethod)); + } } else { - update.addStatement(langSpec.getMethodInvocation(updateMethodName, params) - + langSpec.getStatementDelimiter()); // this.updateDstFromSrc(value, refParams); + // Use the reference field to refer to outside destination resource. + List args = new ArrayList<>(); + for (Map.Entry> paramEnt: pathParams) { + args.add(paramEnt.getValue().getValue()); + } + for (Map.Entry> paramEnt: params) { + args.add(paramEnt.getValue().getValue()); + } + update.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, args) + + langSpec.getStatementDelimiter()); // this.dst.updateDstFromSrc(value, refParams); } } else { - // Use the reference field to refer to outside destination resource. - update.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, params) - + langSpec.getStatementDelimiter()); // this.dst.updateDstFromSrc(value, refParams); + // Intra-service + // The destination resource is not outside. + List args = new ArrayList<>(); + for (Map.Entry> paramEnt: pathParams) { + args.add(paramEnt.getValue().getValue()); + } + for (Map.Entry> paramEnt: params) { + args.add(paramEnt.getValue().getValue()); + } + if (srcRes2 != dstRes) { + update.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, args) + + langSpec.getStatementDelimiter()); // this.dst.updateDstFromSrc(value, refParams); + } else { + update.addStatement(langSpec.getMethodInvocation(updateMethodName, args) + + langSpec.getStatementDelimiter()); // this.updateDstFromSrc(value, refParams); + } + } + if (addForStatement) { + // Close the for loop + update.addStatement(langSpec.getEndForStatement(forVarName)); } } } if (outsideInputMembers2.size() > 0) { if (!generatesComponent(resourceNode.getResourceHierarchy())) { // srcRes2 does not have a component. - ResourcePath srcRes2 = resourceNode.getOutSideResource(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 out2: ch2.getOutputChannelMembers()) { + if (!generatesComponent(out2.getResource().getResourceHierarchy())) { + // Also dstRes2 does not have a component. + ResourcePath dstRes2 = out2.getResource(); + if (srcRes2.getParent().equals(dstRes2.getParent())) { + Map>> resourcePaths = null; + try { + resourcePaths = ch2.fillOutsideResourcePaths(out2, getPullAccessor(platformSpec)); + if (resourcePaths != null && resourcePaths.size() > 0) { + for (ChannelMember outsideMember: outsideInputMembers2) { + for (ChannelMember dependedMember: resourcePaths.get(outsideMember).getValue()) { + if (dependedMember.getResource().equals(srcRes2)) { + // An outside input resource path depends on srcRes. + ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey(); + if (!generatesComponent(outsidePath.getResourceHierarchy())) { + outsidePath = outsidePath.getParent(); } - } - String[] 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(platformSpec).getDirectStateAccessorFor(outsidePath, null); + if (generatesComponent(outsidePath.getResourceHierarchy())) { + outsideExp = ((Term) outsideExp).getChild(0); + } + String[] sideEffects = new String[] {""}; + String outsideAccessor = outsideExp.toImplementation(sideEffects); + String updateReference = langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter(); + update.addStatement(updateReference); // Update the reference field. + // Update constructor. + if (component != null) { + MethodDeclaration constructor = getConstructor(component); + constructor.addStatement(updateReference); // Initialize the reference field. + } else if (parentComponent != null) { + constructorStatements.add(updateReference); + } } } } } + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork + | InvalidMessage | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); } - } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork - | InvalidMessage | UnificationFailed | ValueUndefined e) { - e.printStackTrace(); } } } @@ -1267,22 +2180,40 @@ } private Map.Entry, Map>>> declareInputMethodsInThisAndMainComponents(ResourceNode resourceNode, TypeDeclaration component, - TypeDeclaration parentComponent, TypeDeclaration mainComponent, DataTransferModel model, ILanguageSpecific langSpec) { + TypeDeclaration parentComponent, TypeDeclaration mainComponent, TypeDeclaration rootComponent, DataTransferModel model, Map priorMemberForInputChannel, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { // Declare input methods. String resName = resourceNode.getResourceName(); String resComponentName = langSpec.toComponentName(resName); List constructorStatements = new ArrayList<>(); Map>> inputStatements = new HashMap<>(); for (Channel ch: model.getInputChannels()) { + for (ChannelMember cm : ((DataTransferChannel) ch).getOutputChannelMembers()) { + if (!cm.isOutside()) { + if (priorMemberForInputChannel.get(ch) == null) { + priorMemberForInputChannel.put(ch, cm); // The receiver of the input event when multiple output resources are defined for the channel. + } + } + } for (ChannelMember out: ((DataTransferChannel) ch).getOutputChannelMembers()) { if (resourceNode.getInSideResources().contains(out.getResource())) { Expression message = out.getStateTransition().getMessageExpression(); MethodDeclaration input = null; - MethodDeclaration inputAccessor = null; + MethodDeclaration mainInputAccessor = null; + MethodDeclaration rootInputAccessor = null; if (message instanceof Term) { // Declare an input method in this component. ArrayList resInputParams = new ArrayList<>(); ArrayList mainInputParams = new ArrayList<>(); + ArrayList rootInputParams = new ArrayList<>(); + String resourcePath = null; + if (!platformSpec.isMonolithic()) { + resourcePath = getInputMethodResourcePathAndPathParams(out.getResource(), rootInputParams, platformSpec, langSpec); // Path parameters for the input REST API. + if (resourcePath.indexOf('/') > 0) { + resourcePath = resourcePath.substring(resourcePath.indexOf('/')); + } else { + resourcePath = ""; + } + } // The path parameters are not to be passed to the input method of each resource (resInputParams) // because they are always equal to either channel selectors or message parameters. @@ -1341,58 +2272,83 @@ if (!bExists) { mainInputParams.add(langSpec.newVariableDeclaration(var.getType(), var.getName())); } - } - } - String inputMethodName = ((Term) message).getSymbol().getImplName(); - if (((DataTransferChannel) ch).getOutputChannelMembers().size() > 1) { - inputMethodName += _for + getComponentName(out.getResource().getResourceHierarchy(), langSpec); - } - if (component != null) { - // A component is created for this resource. - for (MethodDeclaration method: component.getMethods()) { - if (method.getName().equals(inputMethodName)) { - input = method; - break; + if (!platformSpec.isMonolithic() && !resourcePath.contains("{" + var.getName()+ "}")) { + VariableDeclaration param = langSpec.newVariableDeclaration(var.getType(), var.getName()); + ((RestApiSpecific) platformSpec).addFormParamAnnotation(param, var.getName()); + rootInputParams.add(param); } } - if (input == null) { - input = langSpec.newMethodDeclaration(inputMethodName, false, null, resInputParams); - component.addMethod(input); + } + if (platformSpec.isMonolithic() + || (resourceNode.getResourceHierarchy().getParent() != null && resourceNode.getResourceHierarchy().getParent().getParent() != null)) { + String inputMethodName = ((Term) message).getSymbol().getImplName(); + if (((DataTransferChannel) ch).getOutputChannelMembers().size() > 1) { + inputMethodName += _for + getComponentName(out.getResource().getResourceHierarchy(), langSpec); } - } else if (parentComponent != null) { - // No component is created for this resource. - for (MethodDeclaration method: parentComponent.getMethods()) { - if (method.getName().equals(inputMethodName)) { - input = method; - break; + if (component != null) { + // A component is created for this resource. + for (MethodDeclaration method: component.getMethods()) { + if (method.getName().equals(inputMethodName)) { + input = method; + break; + } } - } - if (input == null) { - input = langSpec.newMethodDeclaration(inputMethodName, false, null, resInputParams); - parentComponent.addMethod(input); + if (input == null) { + input = langSpec.newMethodDeclaration(inputMethodName, false, null, resInputParams); + component.addMethod(input); + } + } else if (parentComponent != null) { + // No component is created for this resource. + for (MethodDeclaration method: parentComponent.getMethods()) { + if (method.getName().equals(inputMethodName)) { + input = method; + break; + } + } + if (input == null) { + input = langSpec.newMethodDeclaration(inputMethodName, false, null, resInputParams); + parentComponent.addMethod(input); + } } } - // Declare the accessor in the main component to call the input method. - String messageSymbol = ((Term) message).getSymbol().getImplName(); - inputAccessor = getMethod(mainComponent, messageSymbol); - if (inputAccessor == null) { - inputAccessor = langSpec.newMethodDeclaration(messageSymbol, false, null, mainInputParams); - mainComponent.addMethod(inputAccessor); - } else { - // Add type to a parameter without type. - if (inputAccessor.getParameters() != null) { - for (VariableDeclaration param: inputAccessor.getParameters()) { - if (param.getType() == null) { - for (VariableDeclaration p: mainInputParams) { - if (param.getName().equals(p.getName()) && p.getType() != null) { - param.setType(p.getType()); + // Declare the accessor in the main component to call the input method. (for monolithic application) + if (platformSpec.hasMain()) { + String messageSymbol = ((Term) message).getSymbol().getImplName(); + mainInputAccessor = getMethod(mainComponent, messageSymbol); + if (mainInputAccessor == null) { + mainInputAccessor = langSpec.newMethodDeclaration(messageSymbol, false, null, mainInputParams); + mainComponent.addMethod(mainInputAccessor); + } else { + // Add type to a parameter without type. + if (mainInputAccessor.getParameters() != null) { + for (VariableDeclaration param: mainInputAccessor.getParameters()) { + if (param.getType() == null) { + for (VariableDeclaration p: mainInputParams) { + if (param.getName().equals(p.getName()) && p.getType() != null) { + param.setType(p.getType()); + } } } } } } } + + // For the root resource. (for REST API) + if (!platformSpec.isMonolithic()) { + if (priorMemberForInputChannel.get(ch) == null || out == priorMemberForInputChannel.get(ch)) { + // If out is the receiver of the input event. + priorMemberForInputChannel.put(ch, out); + String messageSymbol = ((Term) message).getSymbol().getImplName(); + rootInputAccessor = declareInputAccessorInTheRootResource(messageSymbol, rootInputParams, out, resourcePath, + rootComponent, platformSpec, langSpec); + if (input == null) { + input = rootInputAccessor; + rootInputAccessor = null; + } + } + } } else if (message instanceof Variable) { // Declare an input method in this component. ArrayList resInputParams = new ArrayList<>(); @@ -1423,72 +2379,103 @@ v++; } } - String inputMethodName = ((Variable) message).getName(); - if (((DataTransferChannel) ch).getOutputChannelMembers().size() > 1) { - inputMethodName += _for + getComponentName(out.getResource().getResourceHierarchy(), langSpec); - } - if (component != null) { - // A component is created for this resource. - for (MethodDeclaration method: component.getMethods()) { - if (method.getName().equals(inputMethodName)) { - input = method; - break; - } + if (platformSpec.isMonolithic() + || (resourceNode.getResourceHierarchy().getParent() != null && resourceNode.getResourceHierarchy().getParent().getParent() != null)) { + String inputMethodName = ((Variable) message).getName(); + if (((DataTransferChannel) ch).getOutputChannelMembers().size() > 1) { + inputMethodName += _for + getComponentName(out.getResource().getResourceHierarchy(), langSpec); } - if (input == null) { - if (resInputParams.size() == 0) { - input = langSpec.newMethodDeclaration(inputMethodName, null); - } else { - input = langSpec.newMethodDeclaration(inputMethodName, false, null, resInputParams); + if (component != null) { + // A component is created for this resource. + for (MethodDeclaration method: component.getMethods()) { + if (method.getName().equals(inputMethodName)) { + input = method; + break; + } } - component.addMethod(input); - } - } else if (parentComponent != null) { - // No component is created for this resource. - for (MethodDeclaration method: parentComponent.getMethods()) { - if (method.getName().equals(inputMethodName)) { - input = method; - break; + if (input == null) { + if (resInputParams.size() == 0) { + input = langSpec.newMethodDeclaration(inputMethodName, null); + } else { + input = langSpec.newMethodDeclaration(inputMethodName, false, null, resInputParams); + } + component.addMethod(input); } - } - if (input == null) { - if (resInputParams.size() == 0) { - input = langSpec.newMethodDeclaration(inputMethodName, null); - } else { - input = langSpec.newMethodDeclaration(inputMethodName, false, null, resInputParams); + } else if (parentComponent != null) { + // No component is created for this resource. + for (MethodDeclaration method: parentComponent.getMethods()) { + if (method.getName().equals(inputMethodName)) { + input = method; + break; + } } - parentComponent.addMethod(input); + if (input == null) { + if (resInputParams.size() == 0) { + input = langSpec.newMethodDeclaration(inputMethodName, null); + } else { + input = langSpec.newMethodDeclaration(inputMethodName, false, null, resInputParams); + } + parentComponent.addMethod(input); + } } } - // Declare the accessor in the main component to call the input method. - String messageSymbol = ((Variable) message).getName(); - inputAccessor = getMethod(mainComponent, messageSymbol); - if (inputAccessor == null) { - if (mainInputParams.size() == 0) { - inputAccessor = langSpec.newMethodDeclaration(messageSymbol, null); - } else { - inputAccessor = langSpec.newMethodDeclaration(messageSymbol, false, null, mainInputParams); + // Declare the accessor in the main component to call the input method. (for monolithic application) + if (platformSpec.hasMain()) { + String messageSymbol = ((Variable) message).getName(); + mainInputAccessor = getMethod(mainComponent, messageSymbol, mainInputParams); + if (mainInputAccessor == null) { + if (mainInputParams.size() == 0) { + mainInputAccessor = langSpec.newMethodDeclaration(messageSymbol, null); + } else { + mainInputAccessor = langSpec.newMethodDeclaration(messageSymbol, false, null, mainInputParams); + } + mainComponent.addMethod(mainInputAccessor); } - mainComponent.addMethod(inputAccessor); + } + + // For the root resource. (for REST API) + if (!platformSpec.isMonolithic()) { + if (priorMemberForInputChannel.get(ch) == null || out == priorMemberForInputChannel.get(ch)) { + // If out is the receiver of the input event. + priorMemberForInputChannel.put(ch, out); + ArrayList rootInputParams = new ArrayList<>(); + String resourcePath = getGetterResourcePathAndPathParams(out.getResource(), rootInputParams, platformSpec, langSpec); + if (resourcePath.indexOf('/') > 0) { + resourcePath = resourcePath.substring(resourcePath.indexOf('/')); + } else { + resourcePath = ""; + } + String messageSymbol = ((Variable) message).getName(); + rootInputAccessor = declareInputAccessorInTheRootResource(messageSymbol, rootInputParams, out, resourcePath, + rootComponent, platformSpec, langSpec); + if (input == null) { + input = rootInputAccessor; + rootInputAccessor = null; + } + } } } // Add an invocation to the accessor method. - if (inputAccessor != null) { - for (ChannelMember rc: ((DataTransferChannel) ch).getReferenceChannelMembers()) { - // For each reference channel member, get the current state of the reference side resource by pull data transfer. - ResourcePath ref = rc.getResource(); - if (!out.getResource().equals(ref)) { - String refVarName = ref.getLeafResourceName(); - Expression refGetter = getPullAccessor().getDirectStateAccessorFor(ref, null); - String[] sideEffects = new String[] {""}; - String refExp = refGetter.toImplementation(sideEffects); - String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); - inputAccessor.addFirstStatement(sideEffects[0] + refTypeName + " " + refVarName + " = " + refExp + ";"); + if (mainInputAccessor != null) { + if (platformSpec.hasMain()) { + // For an application with a main component, the reference resource is accessed from the main component. + for (ChannelMember rc: ((DataTransferChannel) ch).getReferenceChannelMembers()) { + // For each reference channel member, get the current state of the reference resource by pull data transfer. + ResourcePath ref = rc.getResource(); + if (!out.getResource().equals(ref)) { + String refVarName = ref.getLeafResourceName(); + Expression refGetter = getPullAccessor(platformSpec).getDirectStateAccessorFor(ref, null); + String[] sideEffects = new String[] {""}; + String refExp = refGetter.toImplementation(sideEffects); + String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); + mainInputAccessor.addFirstStatement(sideEffects[0] + langSpec.getVariableDeclaration(refTypeName, refVarName) + + langSpec.getAssignment() + refExp + langSpec.getStatementDelimiter()); + } } } - Expression resExp = getPullAccessor().getDirectStateAccessorFor(out.getResource(), null); + Expression resExp = getPullAccessor(platformSpec).getDirectStateAccessorFor(out.getResource(), null); List args = new ArrayList<>(); if (resExp instanceof Term) { // to access the parent @@ -1527,13 +2514,43 @@ } } } - inputAccessor.addStatement(langSpec.getMethodInvocation(resourceAccess, input.getName(), args) + langSpec.getStatementDelimiter()); + mainInputAccessor.addStatement(langSpec.getMethodInvocation(resourceAccess, input.getName(), args) + langSpec.getStatementDelimiter()); } if (input != null) { // Add a statement to update the state field to the input method. try { - Expression updateExp = ((DataTransferChannel) ch).deriveUpdateExpressionOf(out, getRefAccessor()).getKey(); + if (!platformSpec.hasMain()) { + // For an application with no main component, the reference resource is accessed from each resource. + for (ChannelMember rc: ((DataTransferChannel) ch).getReferenceChannelMembers()) { + // For each reference channel member, get the current state of the reference side resource by pull data transfer. + ResourcePath ref = rc.getResource(); + if (!out.getResource().equals(ref)) { + String refResourceName = ref.getLeafResourceName(); + Type refResourceType = ref.getResourceStateType(); + String[] sideEffects = new String[] {""}; + if (!platformSpec.isMonolithic() && rc.isOutside()) { + List pathParams = new ArrayList<>(); + for (Expression pathExp: ref.getPathParams()) { + pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); + } + generatePullDataTransfer(input, refResourceName, ref.getResourceHierarchy().toResourcePath(pathParams), refResourceType, true, platformSpec, langSpec); + } else { + ResourcePath dstRes = out.getResource(); + if (!generatesComponent(dstRes.getResourceHierarchy())) { + dstRes = dstRes.getParent(); + } + Expression refGetter = getPullAccessor(platformSpec).getDirectStateAccessorFor(ref, dstRes); + String refExp = refGetter.toImplementation(sideEffects); + String refTypeName = refResourceType.getInterfaceTypeName(); + input.addFirstStatement(sideEffects[0] + langSpec.getVariableDeclaration(refTypeName, refResourceName) + + langSpec.getAssignment() + refExp + langSpec.getStatementDelimiter()); + } + } + } + } + + Expression updateExp = ((DataTransferChannel) ch).deriveUpdateExpressionOf(out, getRefAccessor(platformSpec)).getKey(); // Replace Json constructor with a constructor of a descendant resource. ResourceHierarchy outRes = out.getResource().getResourceHierarchy(); if (outRes.getChildren().size() == 1 && outRes.getChildren().iterator().next().getNumParameters() > 0) { @@ -1558,6 +2575,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 +2588,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); @@ -1594,20 +2617,58 @@ } } } + if (rootInputAccessor != null) { + // In the root resource + // The expression of the receiver (resource) of the input method. + ResourcePath outResPath = new ResourcePath(out.getResource()); + for (int i = 0; i < outResPath.getPathParams().size(); i++) { + Parameter pathParam = new Parameter(rootInputAccessor.getParameters().get(i).getName()); + outResPath.replacePathParam(i, pathParam, null); + } + Expression resExp = getPullAccessor(platformSpec).getDirectStateAccessorFor(outResPath, outResPath.getRoot()); + List args = new ArrayList<>(); + if (resExp instanceof Term) { + // to access the parent + if (((Term) resExp).getChildren().size() > 1 && ((Term) resExp).getChild(1) instanceof Variable) { + args.add(((Variable)((Term) resExp).getChild(1)).getName()); + } + resExp = ((Term) resExp).getChild(0); + } + String resourceAccess = resExp.toImplementation(new String[] {""}); + // Values of channel parameters. + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + args.add(selVar.getName()); + } + } + // Values of message parameters. + if (message instanceof Term) { + for (Variable mesVar: message.getVariables().values()) { + args.add(mesVar.getName()); + } + } + rootInputAccessor.addStatement(langSpec.getMethodInvocation(resourceAccess, input.getName(), args) + langSpec.getStatementDelimiter()); + if (input != null && input.getThrows() != null && ((RestApiSpecific) platformSpec).hasJsonException(input)) { + ((RestApiSpecific) platformSpec).addJsonException(rootInputAccessor); + } + } } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage | UnificationFailed | ValueUndefined e) { e.printStackTrace(); } // Add an invocation to an update method (for a chain of update method invocations). + boolean hasUpdateMethodinvoked = false; for (Edge resToCh: resourceNode.getOutEdges()) { DataFlowEdge dOut = (DataFlowEdge) resToCh; - 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 +2679,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,53 +2707,231 @@ } } } - if ((((PushPullAttribute) dOut.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource2) || outsideOutputResource2) { + // Also take into account the channel hierarchy to determine push/pull transfer. + if (descendantDstChannels.contains(chNode2)) { + outsideOutputResource2 = true; // Regarded as (broadcasting) push transfer. + } + if (ancestorDstChannels.contains(chNode2)) { + outsideInputResource2 = true; // Regarded as (collecting) pull transfer. + } + if ((((PushPullAttribute) dOut.getAttribute()).getSelectedOption() == PushPullValue.PUSH && !outsideInputResource2) || outsideOutputResource2) { // PUSH transfer + + // Calculate in-degree (PUSH transfer) of the destination resource. + Set inEdges = new HashSet<>(); + inEdges.addAll(directDstChNode.getInEdges()); + for (ChannelNode ancestorSrc: ancestorDstChannels) { + inEdges.addAll(ancestorSrc.getInEdges()); + } + for (ChannelNode descendantSrc: descendantDstChannels) { + inEdges.addAll(descendantSrc.getInEdges()); + } + int inDegree = 0; + for (Edge resToCh2: inEdges) { + DataFlowEdge df =(DataFlowEdge) resToCh2; + if (((PushPullAttribute) df.getAttribute()).getSelectedOption() == PushPullValue.PUSH) { + inDegree++; + } + } + boolean addForStatement = false; + String forVarName = null; + if (descendantDstChannels.contains(chNode2)) { + // For hierarchical channels (broadcasting push transfer). + if (ch2.getSelectors() != null && ch2.getSelectors().size() > 0) { + Expression selExp = ch2.getSelectors().get(0).getExpression(); + Type selType = null; + if (selExp instanceof Variable) { + selType = ((Variable) selExp).getType(); + forVarName = ((Variable) selExp).getName(); + ChannelMember insideChMem = null; + for (ChannelMember cm :ch2.getInputChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + if (insideChMem == null) { + for (ChannelMember cm :ch2.getReferenceChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + } + if (insideChMem == null) { + for (ChannelMember cm :ch2.getOutputChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + } + ResourcePath insideResPath = insideChMem.getResource(); + while (insideResPath.getParent() != null && (insideResPath.getLastParam() == null || !insideResPath.getLastParam().equals(selExp))) { + insideResPath = insideResPath.getParent(); + } + insideResPath = insideResPath.getParent(); + if (insideResPath != null) { + String parent = null; + if (platformSpec.isMonolithic() + || insideResPath.getCommonPrefix(resourceNode.getOutSideResource(directDstCh)) != null + || !platformSpec.isDifferentTreesAsDifferentServices()) { + if (!platformSpec.isMonolithic() && generatesComponent(insideResPath.getResourceHierarchy())) { + Expression getter = getPullAccessor(platformSpec).getDirectStateAccessorFor(insideResPath, resourceNode.getOutSideResource(directDstCh)); + Term valueGetter = new Term(new Symbol(getterOfResourceState, 1, Symbol.Type.METHOD)); + valueGetter.addChild(getter); + parent = valueGetter.toImplementation(new String[] {}); + } else { + parent = getPullAccessor(platformSpec).getDirectStateAccessorFor(insideResPath, resourceNode.getOutSideResource(directDstCh)).toImplementation(new String[] {}); + } + } else { + // for REST API + parent = langSpec.toVariableName(getComponentName(insideResPath.getResourceHierarchy(), langSpec)); + } + if (selType.equals(DataConstraintModel.typeInt)) { + // make a for loop (for a list) for broadcasting. + input.addStatement(langSpec.getForStatementForList(forVarName, parent)); + addForStatement = true; + } else if (selType.equals(DataConstraintModel.typeString)) { + // make a for loop (for a map) for broadcasting. + input.addStatement(langSpec.getForStatementForMap(forVarName, DataConstraintModel.typeString.getInterfaceTypeName(), parent)); + addForStatement = true; + } + if (!platformSpec.isMonolithic() + && insideResPath.getCommonPrefix(resourceNode.getOutSideResource(directDstCh)) == null + && platformSpec.isDifferentTreesAsDifferentServices()) { + // for REST API + Type parentResType = insideResPath.getResourceStateType(); + String parentResName = langSpec.toVariableName(getComponentName(insideResPath.getResourceHierarchy(), langSpec)); + String parentResPath = insideResPath.toResourcePath().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + generatePullDataTransfer(input, parentResName, parentResPath, parentResType, true, platformSpec, langSpec); + } + } + } else if (selExp instanceof Term) { + // not supported. + } + } + } + // Get the value of reference member to call the update method. + List>> refParams = new ArrayList<>(); Map> referredResources = new HashMap<>(); - List params = new ArrayList<>(); - // Values of path parameters. - for (Expression pathParam: out2.getResource().getPathParams()) { - if (pathParam instanceof Variable) { - Variable pathVar = (Variable) pathParam; - params.add(pathVar.getName()); - } - } - // Values of channel parameters. - for (Selector selector: ch2.getAllSelectors()) { - if (selector.getExpression() instanceof Variable) { - Variable selVar = (Variable) selector.getExpression(); - params.add(selVar.getName()); - } - } - // Value of the source side (input side) resource. - ResourceHierarchy srcRes = resourceNode.getResourceHierarchy(); - if (generatesComponent(srcRes)) { - params.add(langSpec.getFieldAccessor(fieldOfResourceState)); - } else { - params.add(langSpec.getFieldAccessor(langSpec.toVariableName(srcRes.getResourceName()))); - srcRes = srcRes.getParent(); - } Set referredSet = referredResources.get(input); for (ChannelMember rc: ch2.getReferenceChannelMembers()) { - // to get the value of reference member. ResourcePath ref = rc.getResource(); if (referredSet == null) { referredSet = new HashSet<>(); referredResources.put(input, referredSet); } if (!resourceNode.getOutSideResources().contains(ref)) { - String refVarName = ref.getLeafResourceName(); + 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(platformSpec).getDirectStateAccessorFor(ref, srcRes); String[] sideEffects = new String[] {""}; String refExp = refGetter.toImplementation(sideEffects); String refTypeName = ref.getResourceStateType().getInterfaceTypeName(); input.addStatement(sideEffects[0] + langSpec.getVariableDeclaration(refTypeName, refVarName) + langSpec.getAssignment() + refExp + langSpec.getStatementDelimiter()); } - params.add(refVarName); + refParams.add(new AbstractMap.SimpleEntry<>(ref.getResourceStateType(), + new AbstractMap.SimpleEntry<>(refVarName, + refVarName))); } } + List>> pathParams = new ArrayList<>(); + if (platformSpec.isMonolithic()) { + // Update fields to refer to outside resources. + ResourcePath filledOutsideResourcePath = null; + try { + Map>> resourcePaths = ch2.fillOutsideResourcePaths(out2, getPullAccessor(platformSpec)); + if (resourcePaths != null && resourcePaths.size() > 0) { + for (ChannelMember outsideMember: resourcePaths.keySet()) { + ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey(); + if (out2.equals(outsideMember)) { + filledOutsideResourcePath = outsidePath; + } + if (!generatesComponent(outsidePath.getResourceHierarchy())) { + outsidePath = outsidePath.getParent(); + } + String outsideResName = langSpec.toVariableName(getComponentName(outsidePath.getResourceHierarchy(), langSpec)); + Expression outsideExp = getPullAccessor(platformSpec).getDirectStateAccessorFor(outsidePath, null); + if (generatesComponent(outsidePath.getResourceHierarchy())) { + outsideExp = ((Term) outsideExp).getChild(0); + } + if (outsideExp instanceof Field) { + outsideExp = new Variable(((Field) outsideExp).getSymbol().getName(), ((Field) outsideExp).getType()); + } else if (outsideExp instanceof Term) { + for (Entry fieldEnt: ((Term) outsideExp).getSubTerms(Field.class).entrySet()) { + Position pos = fieldEnt.getKey(); + Field field = fieldEnt.getValue(); + Variable var = new Variable(field.getSymbol().getName(), field.getType()); + ((Term) outsideExp).replaceSubTerm(pos, var); + } + } + String[] sideEffects = new String[] {""}; + String outsideAccessor = outsideExp.toImplementation(sideEffects); + input.addStatement(langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter()); // change the reference field. + } + } + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork + | InvalidMessage | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + // Values of path parameters to call the update method. + if (filledOutsideResourcePath == null) { + filledOutsideResourcePath = out2.getResource(); + } + for (Expression pathParam: filledOutsideResourcePath.getPathParams()) { + if (pathParam instanceof Variable) { + Variable pathVar = (Variable) pathParam; + pathParams.add(new AbstractMap.SimpleEntry<>(pathVar.getType(), + new AbstractMap.SimpleEntry<>(pathVar.getName(), + pathVar.getName()))); + } else if (pathParam instanceof Constant) { + Constant pathVar = (Constant) pathParam; + pathParams.add(new AbstractMap.SimpleEntry<>(pathVar.getType(), + new AbstractMap.SimpleEntry<>(pathVar.getSymbol().getName(), + pathVar.getSymbol().getName()))); + } + } + } else { + // Values of path parameters to call the update method. + for (Expression pathParam: out2.getResource().getPathParams()) { + if (pathParam instanceof Variable) { + Variable pathVar = (Variable) pathParam; + pathParams.add(new AbstractMap.SimpleEntry<>(pathVar.getType(), + new AbstractMap.SimpleEntry<>(pathVar.getName(), + pathVar.getName()))); + } + } + } + // Values of channel parameters to call the update method. + List>> params = new ArrayList<>(); + for (Selector selector: ch2.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + params.add(new AbstractMap.SimpleEntry<>(selVar.getType(), + new AbstractMap.SimpleEntry<>(selVar.getName(), + selVar.getName()))); + } + } + // Value of the source side (input side) resource to call the update method. + ResourceHierarchy srcRes = resourceNode.getResourceHierarchy(); + if (generatesComponent(srcRes)) { + params.add(new AbstractMap.SimpleEntry<>(srcRes.getResourceStateType(), + new AbstractMap.SimpleEntry<>(langSpec.toVariableName(srcRes.getResourceName()), + langSpec.getFieldAccessor(fieldOfResourceState)))); + } else { + params.add(new AbstractMap.SimpleEntry<>(srcRes.getResourceStateType(), + new AbstractMap.SimpleEntry<>(langSpec.toVariableName(srcRes.getResourceName()), + langSpec.getFieldAccessor(langSpec.toVariableName(srcRes.getResourceName()))))); + srcRes = srcRes.getParent(); + } + params.addAll(refParams); + // Call the update method. String updateMethodName = null; ResourceHierarchy dstRes = dstNode.getResourceHierarchy(); if (!generatesComponent(dstRes)) { @@ -1688,72 +2941,141 @@ updateMethodName = updateMethodPrefix + from + resComponentName; } String dstCompName = langSpec.toVariableName(getComponentName(dstRes, langSpec)); - if (!outsideOutputResource2) { - // The destination resource is not outside. - if (srcRes != dstRes) { - input.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, params) - + langSpec.getStatementDelimiter()); // this.dst.updateDstFromSrc(value, refParams); + if (outsideOutputResource2 + || (!platformSpec.isMonolithic() && in.getResource().getCommonPrefix(out2.getResource()) == null && platformSpec.isDifferentTreesAsDifferentServices())) { + // Inter-servces + if (!platformSpec.isMonolithic()) { + // REST API + RestApiSpecific restApiSpec = (RestApiSpecific) platformSpec; + String httpMethod = null; + if (out2.getStateTransition().isRightUnary()) { + httpMethod = "put"; + } else { + httpMethod = "post"; + } + String[] sideEffects = new String[] {""}; + List pathParamsUrl = new ArrayList<>(); + for (Expression pathExp: out2.getResource().getPathParams()) { + pathParamsUrl.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); + } + String resName2 = langSpec.toVariableName(resComponentName); + if (inDegree <= 1) { + resName2 = null; + } + Map>> filledPaths = null; + try { + filledPaths = ch2.fillOutsideResourcePaths(out2, getPushAccessor(platformSpec)); + } catch (ParameterizedIdentifierIsFutureWork + | ResolvingMultipleDefinitionIsFutureWork | InvalidMessage + | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); + } + String dstPath = null; + if (filledPaths != null && filledPaths.get(out2) != null) { + ResourcePath filledDstPath = filledPaths.get(out2).getKey(); + dstPath = filledDstPath.toResourcePath().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + } else { + dstPath = dstRes.toResourcePath(pathParamsUrl); + } + // Call the update method. + if (!hasUpdateMethodinvoked) { + // The first call to an update method in this method + input.addStatement(restApiSpec.getHttpMethodParamsConstructionStatement(srcRes.getResourceName(), params, true)); + input.addStatement(langSpec.getVariableDeclaration(DataConstraintModel.typeString.getInterfaceTypeName(), "result") + + langSpec.getAssignment() + restApiSpec.getHttpMethodCallStatement(restApiSpec.getBaseURL(), dstPath, resName2, httpMethod)); + hasUpdateMethodinvoked = true; + if (component != null && !((RestApiSpecific) platformSpec).hasHttpClientFieldDeclaration(component)) { + // Declare a client field to connect to the destination resource of push transfer. + ((RestApiSpecific) platformSpec).addHttpClientFieldDeclaration(component); + } + } else { + // After the second time of call to update methods in this method + input.addStatement(restApiSpec.getHttpMethodParamsConstructionStatement(srcRes.getResourceName(), params, false)); + input.addStatement("result" + langSpec.getAssignment() + restApiSpec.getHttpMethodCallStatement(restApiSpec.getBaseURL(), dstPath, resName2, httpMethod)); + } } else { - input.addStatement(langSpec.getMethodInvocation(updateMethodName, params) - + langSpec.getStatementDelimiter()); // this.updateDstFromSrc(value, refParams); + // Use the reference field to refer to outside destination resource. + List args = new ArrayList<>(); + for (Map.Entry> paramEnt: pathParams) { + args.add(paramEnt.getValue().getValue()); + } + for (Map.Entry> paramEnt: params) { + args.add(paramEnt.getValue().getValue()); + } + input.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, args) + + langSpec.getStatementDelimiter()); // this.dst.updateDstFromSrc(value, refParams); } } else { - // Use the reference field to refer to outside destination resource. - input.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, params) - + langSpec.getStatementDelimiter()); // this.dst.updateDstFromSrc(value, refParams); + // Intra-service + // The destination resource is not outside. + List args = new ArrayList<>(); + for (Map.Entry> paramEnt: pathParams) { + args.add(paramEnt.getValue().getValue()); + } + for (Map.Entry> paramEnt: params) { + args.add(paramEnt.getValue().getValue()); + } + if (srcRes != dstRes) { + input.addStatement(langSpec.getMethodInvocation(langSpec.getFieldAccessor(dstCompName), updateMethodName, args) + + langSpec.getStatementDelimiter()); // this.dst.updateDstFromSrc(value, refParams); + } else { + input.addStatement(langSpec.getMethodInvocation(updateMethodName, args) + + langSpec.getStatementDelimiter()); // this.updateDstFromSrc(value, refParams); + } + } + if (addForStatement) { + // Close the for loop. + input.addStatement(langSpec.getEndForStatement(forVarName)); } } } - if (outsideInputMembers2.size() > 0) { - if (!generatesComponent(resourceNode.getResourceHierarchy())) { - ResourcePath srcRes2 = resourceNode.getOutSideResource(ch2); - for (ChannelMember out2: ch2.getOutputChannelMembers()) { - if (!generatesComponent(out2.getResource().getResourceHierarchy())) { - ResourcePath dstRes2 = out2.getResource(); - if (srcRes2.getParent().equals(dstRes2.getParent())) { - Map>> resourcePaths = null; - try { - resourcePaths = ch2.fillOutsideResourcePaths(out2, getPullAccessor()); - if (resourcePaths != null && resourcePaths.size() > 0) { - for (ChannelMember outsideMember: outsideInputMembers2) { - for (ChannelMember dependingMember: resourcePaths.get(outsideMember).getValue()) { - if (dependingMember.getResource().equals(srcRes2)) { - // An outside input resource path depends on srcRes. - ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey(); - if (!generatesComponent(outsidePath.getResourceHierarchy())) { - outsidePath = outsidePath.getParent(); - } - String outsideResName = langSpec.toVariableName(getComponentName(outsidePath.getResourceHierarchy(), langSpec)); - Expression outsideExp = getPullAccessor().getDirectStateAccessorFor(outsidePath, null); - if (generatesComponent(outsidePath.getResourceHierarchy())) { - outsideExp = ((Term) outsideExp).getChild(0); - } - Expression nextExp = dependingMember.getStateTransition().getNextStateExpression(); - if (nextExp != null && outsideExp instanceof Term) { - if (nextExp instanceof Variable) { - outsideExp = ((Term) outsideExp).substitute((Variable) nextExp, new Field(langSpec.toVariableName(getComponentName(dependingMember.getResource().getResourceHierarchy(), langSpec)))); - } else { - // ToDo. + + // Update and initialize a field to refer to an outside input resource for PULL transfer. + if (platformSpec.isMonolithic()) { + // For a monolithic application. + if (outsideInputMembers2.size() > 0) { + if (!generatesComponent(resourceNode.getResourceHierarchy())) { + ResourcePath srcRes2 = resourceNode.getOutSideResource(directDstCh); + for (ChannelMember out2: directDstCh.getOutputChannelMembers()) { + if (!generatesComponent(out2.getResource().getResourceHierarchy())) { + ResourcePath dstRes2 = out2.getResource(); + if (srcRes2.getParent().equals(dstRes2.getParent())) { + Map>> resourcePaths = null; + try { + resourcePaths = directDstCh.fillOutsideResourcePaths(out2, getPullAccessor(platformSpec)); + if (resourcePaths != null && resourcePaths.size() > 0) { + for (ChannelMember outsideMember: outsideInputMembers2) { + for (ChannelMember dependedMember: resourcePaths.get(outsideMember).getValue()) { + if (dependedMember.getResource().equals(srcRes2)) { + // An outside input resource path depends on srcRes. + ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey(); + if (!generatesComponent(outsidePath.getResourceHierarchy())) { + outsidePath = outsidePath.getParent(); } - } - String[] sideEffects = new String[] {""}; - String outsideAccessor = outsideExp.toImplementation(sideEffects); - input.addStatement(langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter()); // change the reference field. - // Update constructor. - String initializingStatement = langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter(); - if (component != null) { - MethodDeclaration constructor = getConstructor(component); - constructor.addStatement(initializingStatement); // initialize the reference field. - } else { - constructorStatements.add(initializingStatement); // initialize the reference field. + String outsideResName = langSpec.toVariableName(getComponentName(outsidePath.getResourceHierarchy(), langSpec)); + Expression outsideExp = getPullAccessor(platformSpec).getDirectStateAccessorFor(outsidePath, null); + if (generatesComponent(outsidePath.getResourceHierarchy())) { + outsideExp = ((Term) outsideExp).getChild(0); + } + String[] sideEffects = new String[] {""}; + String outsideAccessor = outsideExp.toImplementation(sideEffects); + input.addStatement(langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter()); // change the reference field. + // Update constructor. + String initializingStatement = langSpec.getFieldAccessor(outsideResName) + langSpec.getAssignment() + outsideAccessor + langSpec.getStatementDelimiter(); + if (component != null) { + MethodDeclaration constructor = getConstructor(component); + constructor.addStatement(initializingStatement); // initialize the reference field. + } else { + constructorStatements.add(initializingStatement); // initialize the reference field. + } } } } } + } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork + | InvalidMessage | UnificationFailed | ValueUndefined e) { + e.printStackTrace(); } - } catch (ParameterizedIdentifierIsFutureWork | ResolvingMultipleDefinitionIsFutureWork - | InvalidMessage | UnificationFailed | ValueUndefined e) { - e.printStackTrace(); } } } @@ -1767,4 +3089,139 @@ } return new AbstractMap.SimpleEntry<>(constructorStatements, inputStatements); } + + protected void declareGetterAccessorInTheRootResource(ResourceNode resourceNode, TypeDeclaration rootComponent, + IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { + if (resourceNode.getResourceHierarchy().getParent() != null) { + // For a non-root resource + MethodDeclaration getterAccessor = null; + List mainGetterParams = new ArrayList<>(); + String resourcePath = getGetterResourcePathAndPathParams(resourceNode.getPrimaryResourcePath(), mainGetterParams, platformSpec, langSpec); + if (resourcePath.indexOf('/') > 0) { + resourcePath = resourcePath.substring(resourcePath.indexOf('/')); + } else { + resourcePath = ""; + } + if (mainGetterParams.size() > 0) { + getterAccessor = langSpec.newMethodDeclaration(getterPrefix + getComponentName(resourceNode.getResourceHierarchy(), langSpec) + methoNameOfResourceState, + false, + getImplStateType(resourceNode.getResourceHierarchy(), langSpec), + mainGetterParams); + } else { + getterAccessor = langSpec.newMethodDeclaration(getterPrefix + getComponentName(resourceNode.getResourceHierarchy(), langSpec) + methoNameOfResourceState, + getImplStateType(resourceNode.getResourceHierarchy(), langSpec)); + } + getterAccessor.setBody(new Block()); + ResourcePath resPath = new ResourcePath(resourceNode.getPrimaryResourcePath()); + for (int i = 0; i < mainGetterParams.size(); i++) { + Parameter pathParam = new Parameter(mainGetterParams.get(i).getName()); + resPath.replacePathParam(i, pathParam, null); + } + Expression getState = getPullAccessor(platformSpec).getDirectStateAccessorFor(resPath, resPath.getRoot()); + getterAccessor.getBody().addStatement(langSpec.getReturnStatement(getState.toImplementation(new String[] {null})) + langSpec.getStatementDelimiter()); + if (!platformSpec.isMonolithic()) { + ((RestApiSpecific) platformSpec).addGetAnnotations(getterAccessor); + if (resourcePath.length() > 0) { + ((RestApiSpecific) platformSpec).addPathAnnotation(getterAccessor, resourcePath); + } + } + rootComponent.addMethod(getterAccessor); + } + } + + protected void declareUpdateAccessorInTheRootResource(ResourceNode resourceNode, String updateMethodName, + DataTransferChannel ch, ChannelMember cm, ResourcePath srcResPath, ResourcePath dstResPath, TypeDeclaration rootComponent, + int inDegree, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { + ArrayList parameters; + VariableDeclaration param; + parameters = new ArrayList<>(); + String resourcePath = getUpdateResourcePathAndPathParams(dstResPath, parameters, true, platformSpec, langSpec); // Path parameters to identify the self resource. + ResourcePath resPath = new ResourcePath(dstResPath); + for (int i = 0; i < parameters.size(); i++) { + Parameter pathParam = new Parameter(parameters.get(i).getName()); + resPath.replacePathParam(i, pathParam, null); + } + for (Selector selector: ch.getAllSelectors()) { + if (selector.getExpression() instanceof Variable) { + Variable selVar = (Variable) selector.getExpression(); + VariableDeclaration chParam = langSpec.newVariableDeclaration(selVar.getType(), selVar.getName()); + if (!platformSpec.isMonolithic()) { + ((RestApiSpecific) platformSpec).addFormParamAnnotation(chParam, selVar.getName()); + } + parameters.add(chParam); // A channel parameter to specify the context of the collaboration. + } + } + Type srcType = srcResPath.getResourceStateType(); + String srcResName = langSpec.toVariableName(getComponentName(srcResPath.getResourceHierarchy(), langSpec)); + param = langSpec.newVariableDeclaration(srcType, srcResName); + if (!platformSpec.isMonolithic()) ((RestApiSpecific) platformSpec).addFormParamAnnotation(param, srcResName); + parameters.add(param); // The state of the source resource to carry the data-flow. + for (ResourcePath refRes: ch.getReferenceResources()) { + if (!refRes.equals(resourceNode.getInSideResource(ch))) { + String refName = langSpec.toVariableName(getComponentName(refRes.getResourceHierarchy(), langSpec)); + param = langSpec.newVariableDeclaration(refRes.getResourceStateType(), refName); + if (!platformSpec.isMonolithic()) { + ((RestApiSpecific) platformSpec).addFormParamAnnotation(param, refName); + } + parameters.add(param); + } + } + MethodDeclaration updateAccessor = langSpec.newMethodDeclaration(updateMethodName, false, null, parameters); + if (!platformSpec.isMonolithic()) { + if (isPut(cm)) { + ((RestApiSpecific) platformSpec).addPutAnnotations(updateAccessor); + } else { + if (!isDelete(cm)) { + ((RestApiSpecific) platformSpec).addPostAnnotations(updateAccessor); + } else { + ((RestApiSpecific) platformSpec).addDeleteAnnotations(updateAccessor); + } + } + } + if (inDegree > 1) { + // For each source resource, a child resource is defined in the destination resource so that its state can be updated separately. + resourcePath += "/" + langSpec.toVariableName(srcResName); + } + if (!platformSpec.isMonolithic()) ((RestApiSpecific) platformSpec).addPathAnnotation(updateAccessor, resourcePath); + + // To make the accessor call the update method. + Expression resExp = getPullAccessor(platformSpec).getDirectStateAccessorFor(resPath, resPath.getRoot()); + List args = new ArrayList<>(); + if (resExp instanceof Term) { + // to access the parent + if (((Term) resExp).getChildren().size() > 1 && ((Term) resExp).getChild(1) instanceof Variable) { + args.add(((Variable)((Term) resExp).getChild(1)).getName()); + } + resExp = ((Term) resExp).getChild(0); + } + String resourceAccess = resExp.toImplementation(new String[] {""}); + for (VariableDeclaration var: updateAccessor.getParameters()) { + args.add(var.getName()); + } + updateAccessor.addStatement(langSpec.getMethodInvocation(resourceAccess, updateMethodName, args) + langSpec.getStatementDelimiter()); + rootComponent.addMethod(updateAccessor); + } + + protected MethodDeclaration declareInputAccessorInTheRootResource(String inputMethodName, + ArrayList rootInputParams, ChannelMember cm, String resourcePath, + TypeDeclaration rootComponent, IPlatformSpecific platformSpec, ILanguageSpecific langSpec) { + MethodDeclaration rootInputAccessor; + rootInputAccessor = langSpec.newMethodDeclaration(inputMethodName, false, null, rootInputParams); + if (!platformSpec.isMonolithic()) { + if (isPut(cm)) { + ((RestApiSpecific) platformSpec).addPutAnnotations(rootInputAccessor); + } else { + if (!isDelete(cm)) { + ((RestApiSpecific) platformSpec).addPostAnnotations(rootInputAccessor); + } else { + ((RestApiSpecific) platformSpec).addDeleteAnnotations(rootInputAccessor); + } + } + if (resourcePath.length() > 0) { + ((RestApiSpecific) platformSpec).addPathAnnotation(rootInputAccessor, resourcePath); + } + } + rootComponent.addMethod(rootInputAccessor); + return rootInputAccessor; + } } diff --git a/AlgebraicDataflowArchitectureModel/src/generators/DataTransferMethodAnalyzer.java b/AlgebraicDataflowArchitectureModel/src/generators/DataTransferMethodAnalyzer.java index 518ec7f..d15f6ad 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/DataTransferMethodAnalyzer.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/DataTransferMethodAnalyzer.java @@ -37,7 +37,7 @@ boolean flag = true; for (Edge chToRes: resource.getInEdges()) { for (Edge resToCh: chToRes.getSource().getInEdges()) { - if (((PushPullAttribute) resToCh.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { + if (((PushPullAttribute) resToCh.getAttribute()).getSelectedOption() != PushPullValue.PUSH) { // Traverse pull edges only. trackNode((ResourceNode) resToCh.getSource()); flag = false; diff --git a/AlgebraicDataflowArchitectureModel/src/generators/ILanguageSpecific.java b/AlgebraicDataflowArchitectureModel/src/generators/ILanguageSpecific.java index 6c86540..5e5ea4a 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/ILanguageSpecific.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/ILanguageSpecific.java @@ -27,6 +27,7 @@ String getVariableDeclaration(String typeName, String varName); String getFieldInitializer(Type type, Expression initialValue); boolean declareField(); + String getSelfExp(); String getFieldAccessor(String fieldName); String getMethodInvocation(String methodName); String getMethodInvocation(String receiverName, List parameters); @@ -35,14 +36,25 @@ String getConstructorInvocation(String componentName, List parameters); String getReturnStatement(String returnValue); String getIfStatement(Term condition, String block); + String getForStatementForList(String varName, String list); + String getForStatementForCollection(String varName, String varType, String collection); + String getForStatementForMap(String varName, String varType, String map); + String getEndForStatement(); + String getEndForStatement(String varName); String toComponentName(String name); String toVariableName(String name); String getMainComponentName(); - String getTupleGet(String tupleExp, int idx, int length); - String getDecomposedTuple(String tupleExp, VariableDeclaration tupleVar, List vars); String getAssignment(); String getStatementDelimiter(); String getStringDelimiter(); + String getOpeningScoreDelimiter(); + String getClosingScoreDelimiter(); + String getValueToStringExp(String typeName, String valueExp); + String getStringToValueExp(String typeName, String strExp); + String getPairExp(String first, String second); + String getFirstEntryFromMapExp(String map); + String getTupleGet(String tupleExp, int idx, int length); + String getDecomposedTuple(String tupleExp, VariableDeclaration tupleVar, List vars); boolean isValueType(Type type); boolean isVoidType(Type type); } diff --git a/AlgebraicDataflowArchitectureModel/src/generators/IPlatformSpecific.java b/AlgebraicDataflowArchitectureModel/src/generators/IPlatformSpecific.java new file mode 100644 index 0000000..c609b7a --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/generators/IPlatformSpecific.java @@ -0,0 +1,9 @@ +package generators; + +import java.util.List; + +public interface IPlatformSpecific { + boolean hasMain(); + boolean isMonolithic(); + boolean isDifferentTreesAsDifferentServices(); +} diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JavaCodeGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/JavaCodeGenerator.java index a15057e..b362030 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/JavaCodeGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/JavaCodeGenerator.java @@ -123,6 +123,8 @@ if (res.getParent() == null) return true; if (res.getChildren() == null || res.getChildren().size() == 0) return false; if (res.getNumParameters() > 0 && res.getChildren().size() == 1 && res.getChildren().iterator().next().getNumParameters() > 0) return false; + if (res.getChildren().size() == 1 && res.getChildren().iterator().next().getNumParameters() > 0 + && (res.getChildren().iterator().next().getChildren() == null || res.getChildren().iterator().next().getChildren().size() == 0)) return false; return true; // return res.getParent() == null || !(res.getChildren() == null || res.getChildren().size() == 0); } @@ -173,7 +175,7 @@ String fieldInitializer = "new " + resourceName + "("; for (Edge resToCh: resourceNode.getOutEdges()) { DataFlowEdge re = (DataFlowEdge) resToCh; - if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { + if (((PushPullAttribute) re.getAttribute()).getSelectedOption() == PushPullValue.PUSH) { for (Edge chToRes: re.getDestination().getOutEdges()) { ResourceHierarchy dstRes = ((ResourceNode) chToRes.getDestination()).getResourceHierarchy(); String resName = getComponentName(dstRes); @@ -188,7 +190,7 @@ DataFlowEdge re = (DataFlowEdge) resToCh; ResourceHierarchy srcRes = ((ResourceNode) re.getSource()).getResourceHierarchy(); String resName = getComponentName(srcRes); - if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { + if (((PushPullAttribute) re.getAttribute()).getSelectedOption() != PushPullValue.PUSH) { depends.add(srcRes); fieldInitializer += toVariableName(resName) + ","; f = true; @@ -277,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<>(); @@ -323,7 +325,7 @@ } } - // Declare the state field in the parent component. + // Declare the state field and reference fields in the parent component. if (component == null) { // Declare reference fields for push/pull data transfer. boolean noPullTransfer = true; @@ -348,7 +350,7 @@ break; } } - if ((((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { + if ((((PushPullAttribute) re.getAttribute()).getSelectedOption() != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { noPullTransfer = false; } } @@ -363,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; @@ -415,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()) { @@ -434,7 +450,14 @@ break; } } - if ((((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) { + // Also take into account the channel hierarchy to determine push/pull transfer. + if (descendantDstChannels.contains(chNode)) { + outsideOutputResource = true; // Regarded as (broadcasting) push transfer. + } + if (ancestorDstChannels.contains(chNode)) { + outsideInputResource = true; // Regarded as (collecting) pull transfer. + } + if ((((PushPullAttribute) re.getAttribute()).getSelectedOption() == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) { // Declare a field to refer to the destination resource of push transfer. if (!generatesComponent(dstRes)) { dstRes = dstRes.getParent(); @@ -482,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; } @@ -508,14 +547,21 @@ } } } - 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()).getOptions().get(0) != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { + if ((((PushPullAttribute) re.getAttribute()).getSelectedOption() != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { // Declare a field to refer to the source resource of pull transfer. depends.add(srcRes2.getResourceHierarchy()); FieldDeclaration srcRefField = new FieldDeclaration(new Type(srcResName2, srcResName2), toVariableName(srcResName2)); @@ -564,7 +610,11 @@ if (exp instanceof Variable) { Variable pathVar = (Variable) exp; String varName = "self" + (v > 1 ? v : ""); -// String varName = pathVar.getName(); + VariableDeclaration pathParam = new VariableDeclaration(pathVar.getType(), varName); + params.add(pathParam); // A path parameter to identify the self resource. + } else if (exp instanceof Term) { + Term pathVar = (Term) exp; + String varName = "self" + (v > 1 ? v : ""); VariableDeclaration pathParam = new VariableDeclaration(pathVar.getType(), varName); params.add(pathParam); // A path parameter to identify the self resource. } @@ -577,10 +627,10 @@ 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(), toVariableName(getComponentName(srcResPath.getResourceHierarchy())))); // The state of the source resource to carry the data-flow. for (ResourcePath ref: ch.getReferenceResources()) { if (!ref.equals(resourceNode.getInSideResource(ch))) { - params.add(new VariableDeclaration(ref.getResourceStateType(), ref.getLeafResourceName())); + params.add(new VariableDeclaration(ref.getResourceStateType(), toVariableName(getComponentName(ref.getResourceHierarchy())))); } } MethodDeclaration update = null; @@ -617,7 +667,16 @@ VariableDeclaration refVar = new VariableDeclaration(new Type(resName, resName), toVariableName(resName)); if (component != null) { // A component is created for this resource. - component.addField(refField); + boolean existsField = false; + for (FieldDeclaration field: component.getFields()) { + if (field.getName().equals(refVar.getName())) { + existsField = true; + break; + } + } + if (!existsField) { + component.addField(refField); + } constructorParams.add(new AbstractMap.SimpleEntry<>(resourceNode.getResourceHierarchy(), refVar)); } else { // No component is created for this resource. @@ -924,16 +983,9 @@ private static Map> getDependedRootComponentGraph(DataTransferModel model) { Map> dependedComponentGraph = new HashMap<>(); for (Channel ch: model.getChannels()) { - DataTransferChannel dtCh = (DataTransferChannel) ch; Set inRes = new HashSet<>(); Set outRes = new HashSet<>(); - for (ChannelMember cm: dtCh.getChannelMembers()) { - if (cm.isOutside()) { - outRes.add(cm.getResource().getResourceHierarchy()); - } else { - inRes.add(cm.getResource().getResourceHierarchy()); - } - } + getDependedRootComponentGraphSub(ch, inRes, outRes, true); if (outRes.size() > 0 && inRes.size() > 0) { for (ResourceHierarchy out: outRes) { for (ResourceHierarchy in: inRes) { @@ -942,7 +994,9 @@ dependings = new HashSet<>(); dependedComponentGraph.put(out.getRoot(), dependings); } - dependings.add(in.getRoot()); + if (!out.getRoot().equals(in.getRoot())) { + dependings.add(in.getRoot()); + } } } } @@ -950,7 +1004,24 @@ return dependedComponentGraph; } - static private ArrayList determineResourceOrder(DataFlowGraph graph, Map> dependedRootComponentGraph) { + private static void getDependedRootComponentGraphSub(Channel ch, Set inRes, Set outRes, boolean isRoot) { + DataTransferChannel dtCh = (DataTransferChannel) ch; + for (ChannelMember cm: dtCh.getChannelMembers()) { + if (!isRoot && !cm.isOutside()) { + outRes.add(cm.getResource().getResourceHierarchy()); // dependency to a descendant channel resource. + } + if (cm.isOutside()) { + outRes.add(cm.getResource().getResourceHierarchy()); // dependency to an outside resource. + } else { + inRes.add(cm.getResource().getResourceHierarchy()); // dependency from an inside resource. + } + } + for (Channel childCh: ch.getChildren()) { + getDependedRootComponentGraphSub(childCh, inRes, outRes, false); + } + } + + private static ArrayList determineResourceOrder(DataFlowGraph graph, Map> dependedRootComponentGraph) { ArrayList resources = new ArrayList<>(); Set visited = new HashSet<>(); for (Node n : graph.getResourceNodes()) { @@ -967,7 +1038,7 @@ for (Edge chToRes: curResNode.getInEdges()) { for (Edge resToCh: chToRes.getSource().getInEdges()) { DataFlowEdge re = (DataFlowEdge) resToCh; - if (((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { + if (((PushPullAttribute) re.getAttribute()).getSelectedOption() == PushPullValue.PUSH) { topologicalSort((ResourceNode) re.getSource(), graph, dependedRootComponentGraph, visited, orderedList); } } @@ -975,7 +1046,7 @@ // For each outgoing PULL transfer. for (Edge resToCh: curResNode.getOutEdges()) { DataFlowEdge de = (DataFlowEdge) resToCh; - if (((PushPullAttribute) de.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { + if (((PushPullAttribute) de.getAttribute()).getSelectedOption() != PushPullValue.PUSH) { for (Edge chToRes : resToCh.getDestination().getOutEdges()) { topologicalSort((ResourceNode) chToRes.getDestination(), graph, dependedRootComponentGraph, visited, orderedList); } @@ -1020,6 +1091,11 @@ } v++; } + ResourcePath resPath = new ResourcePath(resourceNode.getPrimaryResourcePath()); + for (int i = 0; i < mainGetterParams.size(); i++) { + Parameter pathParam = new Parameter(mainGetterParams.get(i).getName()); + resPath.replacePathParam(i, pathParam, null); + } if (mainGetterParams.size() > 0) { getterAccessor = new MethodDeclaration("get" + getComponentName(resourceNode.getResourceHierarchy()), false, @@ -1030,7 +1106,7 @@ getImplStateType(resourceNode.getResourceHierarchy())); } getterAccessor.setBody(new Block()); - Expression getState = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(resourceNode.getPrimaryResourcePath(), null); + Expression getState = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(resPath, null); getterAccessor.getBody().addStatement("return " + getState.toImplementation(new String[] {null}) + ";"); mainComponent.addMethod(getterAccessor); } @@ -1168,7 +1244,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); } @@ -1176,7 +1252,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); } @@ -1189,7 +1265,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); } @@ -1229,6 +1305,7 @@ if (!target.isOutside()) { return getDirectStateAccessorFor(targetRes, fromRes); } + // Get the next state by invoking a getter method. Term getter = null; String targetComponentName = getComponentName(targetRes.getResourceHierarchy()); if (generatesComponent(targetRes.getResourceHierarchy())) { @@ -1255,11 +1332,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 { @@ -1267,85 +1358,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; } }; @@ -1360,7 +1441,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); } @@ -1368,7 +1449,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); } @@ -1381,7 +1462,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 283e16e..f220abd 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,27 @@ 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(); + 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,7 +100,14 @@ } // Check if the output resource is outside of the channel scope. boolean outsideOutputResource = out.isOutside(); - if ((pushPull.getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) { + // 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; if (dstComponent == null) { @@ -97,9 +119,19 @@ } if (((StoreAttribute) dst.getAttribute()).isStored()) { // update stored state of dst side resource (when every incoming edge is in push style) + Term unifiedMassage = null; + if (directDstCh != ch) { + unifiedMassage = directDstCh.fillOutsideResourcePaths(out, JavaCodeGenerator.pushAccessor, null).getValue(); + } Expression updateExp = null; if (ch.getReferenceChannelMembers().size() == 0) { - updateExp = ch.deriveUpdateExpressionOf(out, JavaCodeGenerator.pushAccessor).getKey(); + Term message = ch.fillOutsideResourcePaths(out, JavaCodeGenerator.pushAccessor, null).getValue(); + if (unifiedMassage == null) { + unifiedMassage = message; + } else { + unifiedMassage = (Term) unifiedMassage.unify(message); + } + updateExp = ch.deriveUpdateExpressionOf(out, unifiedMassage, JavaCodeGenerator.pushAccessor); } else { // if there exists one or more reference channel member. HashMap inputResourceToStateAccessor = new HashMap<>(); @@ -109,7 +141,13 @@ for (ChannelMember c: ch.getReferenceChannelMembers()) { inputResourceToStateAccessor.put(c, JavaCodeGenerator.refAccessor); } - updateExp = ch.deriveUpdateExpressionOf(out, JavaCodeGenerator.pushAccessor, inputResourceToStateAccessor).getKey(); + Term message = ch.fillOutsideResourcePaths(out, JavaCodeGenerator.pushAccessor, inputResourceToStateAccessor).getValue(); + if (unifiedMassage == null) { + unifiedMassage = message; + } else { + unifiedMassage = (Term) unifiedMassage.unify(message); + } + updateExp = ch.deriveUpdateExpressionOf(out, unifiedMassage, JavaCodeGenerator.pushAccessor); } // Replace Json constructor with a constructor of a descendant resource. ResourceHierarchy outRes = out.getResource().getResourceHierarchy(); @@ -152,6 +190,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 +200,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); @@ -180,6 +224,7 @@ updateStatement += "this." + JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(outRes)) + " = " + newState + ";"; } } + // add an update statement of the state of dst side resource. if (numOfOutResourcesWithTheSameHierarchy == 1) { update.addFirstStatement(updateStatement); } else { @@ -220,8 +265,24 @@ update.addFirstStatement(ifStatement + "\t" + updateStatement.replace("\n", "\n\t") + "\n}"); } } - if (resToCh.getDestination().getIndegree() > 1 - || (resToCh.getDestination().getIndegree() == 1 && ch.getInputChannelMembers().iterator().next().getStateTransition().isRightPartial())) { + // Calculate in-degree (PUSH transfer) of the destination resource. + int inDegree = 0; + Set inEdges = new HashSet<>(); + inEdges.addAll(chNode.getInEdges()); + for (ChannelNode ancestor: chNode.getAncestors()) { + inEdges.addAll(ancestor.getInEdges()); + } + for (ChannelNode descendant: chNode.getDescendants()) { + inEdges.addAll(descendant.getInEdges()); + } + for (Edge resToCh2: inEdges) { + DataFlowEdge df =(DataFlowEdge) resToCh2; + if (((PushPullAttribute) df.getAttribute()).getSelectedOption() == PushPullValue.PUSH) { + inDegree++; + } + } + if (inDegree > 1 + || (inDegree == 1 && directDstCh.getInputChannelMembers().iterator().next().getStateTransition().isRightPartial())) { // update a cache of src side resource (when incoming edges are multiple) String cacheStatement = "this." + JavaCodeGenerator.toVariableName(srcResourceName) + " = " + JavaCodeGenerator.toVariableName(srcResourceName) + ";"; if (update.getBody() == null || !update.getBody().getStatements().contains(cacheStatement)) { @@ -279,7 +340,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,17 +352,82 @@ 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. + ResourcePath filledOutsideResourcePath = null; + Map>> resourcePaths = ch.fillOutsideResourcePaths(out, JavaCodeGenerator.pullAccessor); + if (resourcePaths != null && resourcePaths.size() > 0) { + for (ChannelMember outsideMember: resourcePaths.keySet()) { + ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey(); + if (out.equals(outsideMember)) { + filledOutsideResourcePath = outsidePath; + } + if (!JavaCodeGenerator.generatesComponent(outsidePath.getResourceHierarchy())) { + outsidePath = outsidePath.getParent(); + } + String outsideResName = JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(outsidePath.getResourceHierarchy())); + Expression outsideExp = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(outsidePath, null); + if (JavaCodeGenerator.generatesComponent(outsidePath.getResourceHierarchy())) { + outsideExp = ((Term) outsideExp).getChild(0); + } + if (outsideExp instanceof Field) { + outsideExp = new Variable(((Field) outsideExp).getSymbol().getName(), ((Field) outsideExp).getType()); + } else if (outsideExp instanceof Term) { + for (Entry fieldEnt: ((Term) outsideExp).getSubTerms(Field.class).entrySet()) { + Position pos = fieldEnt.getKey(); + Field field = fieldEnt.getValue(); + Variable var = new Variable(field.getSymbol().getName(), field.getType()); + ((Term) outsideExp).replaceSubTerm(pos, var); + } + } + String[] sideEffects = new String[] {""}; + String outsideAccessor = outsideExp.toImplementation(sideEffects); + srcUpdate.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // change the reference field. + } + } + // Values of path parameters to call the update method. + if (filledOutsideResourcePath == null) { + filledOutsideResourcePath = dstRes; + } String pathParams = ""; - for (Expression pathParam: dstRes.getPathParams()) { + for (Expression pathParam: filledOutsideResourcePath.getPathParams()) { if (pathParam instanceof Variable) { Variable pathVar = (Variable) pathParam; pathParams += pathVar.getName() + ", "; + } else if (pathParam instanceof Constant) { + Constant pathVar = (Constant) pathParam; + pathParams += pathVar.getSymbol().getName() + ", "; } } - // Values of channel parameters. + // Values of channel parameters to call the update method. String chParams = ""; for (Selector selector: ch.getAllSelectors()) { if (selector.getExpression() instanceof Variable) { @@ -309,39 +435,18 @@ 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; - } + // Value of the source side (input side) resource. + String srcFieldName = "value"; + if (!JavaCodeGenerator.generatesComponent(src.getResourceHierarchy())) { + srcFieldName = JavaCodeGenerator.toVariableName(srcResourceName); } + // Call the update method. String updateMethodName = null; if (JavaCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { updateMethodName = "updateFrom" + srcResourceName; } else { updateMethodName = "update" + dstResourceName + "From" + srcResourceName; } - // Value of the source side (input side) resource. - String srcFieldName = "value"; - if (!JavaCodeGenerator.generatesComponent(src.getResourceHierarchy())) { - srcFieldName = JavaCodeGenerator.toVariableName(srcResourceName); - } if (!outsideOutputResource) { // The destination resource is not outside. if (srcComponent != dstComponent) { @@ -351,20 +456,139 @@ } } else { // Use the reference field to refer to outside destination resource. - srcUpdate.addStatement("this." + JavaCodeGenerator.toVariableName(dstComponent.getTypeName()) + "." + updateMethodName + "(" + pathParams + chParams + srcFieldName + refParams + ");"); + srcUpdate.addStatement("this." + JavaCodeGenerator.toVariableName(dstComponent.getTypeName()) + "." + updateMethodName + "(" + pathParams + chParams + srcFieldName + refParams + ");"); + } + if (descendantDstChannels.contains(chNode)) { + // For hierarchical channels (broadcasting push transfer). + if (ch.getSelectors() != null && ch.getSelectors().size() > 0) { + Expression selExp = ch.getSelectors().get(0).getExpression(); + Type selType = null; + String forVarName = null; + if (selExp instanceof Variable) { + selType = ((Variable) selExp).getType(); + forVarName = ((Variable) selExp).getName(); + ChannelMember insideChMem = null; + for (ChannelMember cm :ch.getInputChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + if (insideChMem == null) { + for (ChannelMember cm :ch.getReferenceChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + } + if (insideChMem == null) { + for (ChannelMember cm :ch.getOutputChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + } + ResourcePath insideResPath = insideChMem.getResource(); + while (insideResPath.getParent() != null && (insideResPath.getLastParam() == null || !insideResPath.getLastParam().equals(selExp))) { + insideResPath = insideResPath.getParent(); + } + insideResPath = insideResPath.getParent(); + if (insideResPath != null) { + String parent = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(insideResPath, src.getOutSideResource(directDstCh)).toImplementation(new String[] {}); + if (selType.equals(DataConstraintModel.typeInt)) { + // make a for loop (for a list) for broadcasting. + srcUpdate.addFirstStatement("for (int " + forVarName + " = 0; " + forVarName +" < " + parent + ".size(); " + forVarName + "++) {"); + srcUpdate.addStatement("}"); + } else if (selType.equals(DataConstraintModel.typeString)) { + // make a for loop (for a map) for broadcasting. + srcUpdate.addFirstStatement("for (String " + forVarName + ": " + parent + ".keySet()) {"); + srcUpdate.addStatement("}"); + } + } + } else if (selExp instanceof Term) { + // not supported. + } + } } } + // For caller input methods for (MethodDeclaration srcInput: getInputMethods(srcComponent, src, model)) { ResourcePath dstRes = out.getResource(); - // 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. + ResourcePath filledOutsideResourcePath = null; + Map>> resourcePaths = ch.fillOutsideResourcePaths(out, JavaCodeGenerator.pullAccessor); + if (resourcePaths != null && resourcePaths.size() > 0) { + for (ChannelMember outsideMember: resourcePaths.keySet()) { + ResourcePath outsidePath = resourcePaths.get(outsideMember).getKey(); + if (out.equals(outsideMember)) { + filledOutsideResourcePath = outsidePath; + } + if (!JavaCodeGenerator.generatesComponent(outsidePath.getResourceHierarchy())) { + outsidePath = outsidePath.getParent(); + } + String outsideResName = JavaCodeGenerator.toVariableName(JavaCodeGenerator.getComponentName(outsidePath.getResourceHierarchy())); + Expression outsideExp = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(outsidePath, null); + if (JavaCodeGenerator.generatesComponent(outsidePath.getResourceHierarchy())) { + outsideExp = ((Term) outsideExp).getChild(0); + } + if (outsideExp instanceof Field) { + outsideExp = new Variable(((Field) outsideExp).getSymbol().getName(), ((Field) outsideExp).getType()); + } else if (outsideExp instanceof Term) { + for (Entry fieldEnt: ((Term) outsideExp).getSubTerms(Field.class).entrySet()) { + Position pos = fieldEnt.getKey(); + Field field = fieldEnt.getValue(); + Variable var = new Variable(field.getSymbol().getName(), field.getType()); + ((Term) outsideExp).replaceSubTerm(pos, var); + } + } + String[] sideEffects = new String[] {""}; + String outsideAccessor = outsideExp.toImplementation(sideEffects); + srcInput.addStatement("this." + outsideResName + " = " + outsideAccessor + ";"); // change the reference field. + } + } + // Values of path parameters to call the update method. + if (filledOutsideResourcePath == null) { + filledOutsideResourcePath = dstRes; + } String pathParams = ""; - for (Expression pathParam: dstRes.getPathParams()) { + for (Expression pathParam: filledOutsideResourcePath.getPathParams()) { if (pathParam instanceof Variable) { Variable pathVar = (Variable) pathParam; pathParams += pathVar.getName() + ", "; + } else if (pathParam instanceof Constant) { + Constant pathVar = (Constant) pathParam; + pathParams += pathVar.getSymbol().getName() + ", "; } } - // Values of channel parameters. + // Values of channel parameters to call the update method. String chParams = ""; for (Selector selector: ch.getAllSelectors()) { if (selector.getExpression() instanceof Variable) { @@ -372,34 +596,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,8 +619,62 @@ // Use the reference field to refer to outside destination resource. srcInput.addStatement("this." + JavaCodeGenerator.toVariableName(dstComponent.getTypeName()) + "." + updateMethodName + "(" + pathParams + chParams + srcFieldName + refParams + ");"); } + if (descendantDstChannels.contains(chNode)) { + // For hierarchical channels (broadcasting push transfer). + if (ch.getSelectors() != null && ch.getSelectors().size() > 0) { + Expression selExp = ch.getSelectors().get(0).getExpression(); + Type selType = null; + String varName = null; + if (selExp instanceof Variable) { + selType = ((Variable) selExp).getType(); + varName = ((Variable) selExp).getName(); + ChannelMember insideChMem = null; + for (ChannelMember cm :ch.getInputChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + if (insideChMem == null) { + for (ChannelMember cm :ch.getReferenceChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + } + if (insideChMem == null) { + for (ChannelMember cm :ch.getOutputChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + } + ResourcePath insideResPath = insideChMem.getResource(); + while (insideResPath.getParent() != null && (insideResPath.getLastParam() == null || !insideResPath.getLastParam().equals(selExp))) { + insideResPath = insideResPath.getParent(); + } + insideResPath = insideResPath.getParent(); + if (insideResPath != null) { + String parent = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(insideResPath, src.getOutSideResource(directDstCh)).toImplementation(new String[] {}); + if (selType.equals(DataConstraintModel.typeInt)) { + // make a for loop (for a list) for broadcasting. + srcInput.addFirstStatement("for (int " + varName + " = 0; " + varName +" < " + parent + ".size(); " + varName + "++) {"); + srcInput.addStatement("}"); + } else if (selType.equals(DataConstraintModel.typeString)) { + // make a for loop (for a map) for broadcasting. + srcInput.addFirstStatement("for (String " + varName + ": " + parent + ".keySet()) {"); + srcInput.addStatement("}"); + } + } + } else if (selExp instanceof Term) { + // not supported. + } + } + } } - } else if ((pushPull.getOptions().get(0) != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { + } else if ((pushPull.getSelectedOption() != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { // for pull (or push/pull) data transfer if (dstComponent == null) { String dstParentResourceName = JavaCodeGenerator.getComponentName(dst.getResourceHierarchy().getParent()); @@ -429,6 +687,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()) { @@ -442,7 +703,7 @@ break; } } - if (((PushPullAttribute) dIn.getAttribute()).getOptions().get(0) == PushPullValue.PUSH) { + if (((PushPullAttribute) dIn.getAttribute()).getSelectedOption() == PushPullValue.PUSH) { isContainedPush = true; inputResourceToStateAccessor.put(in, JavaCodeGenerator.pushAccessor); } else { @@ -454,18 +715,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(); + if (insideResPath != null) { + String parent = JavaCodeGenerator.pullAccessor.getDirectStateAccessorFor(insideResPath, dst.getInSideResource(ch)).toImplementation(new String[] {}); + if (selType.equals(DataConstraintModel.typeInt)) { + // make a for loop (for a list) for data collecting. + getter.addFirstStatement("for (int " + varName + " = 0; " + varName +" < " + parent + ".size(); " + varName + "++) {"); + } else if (selType.equals(DataConstraintModel.typeString)) { + // make a for loop (for a map) for data collecting. + getter.addFirstStatement("for (String " + varName + ": " + parent + ".keySet()) {"); + } + } + } + // initialize the variables to hold side effects within the loop. + for (Variable var: varsForSideEffects) { + getter.addFirstStatement(var.getType().getInterfaceTypeName() + " " + var.getName() + " = new " + var.getType().getImplementationTypeName() + "();"); + } + // end of the loop + getter.addStatement("}"); + if (curChannel.getChildren() != null && curChannel.getChildren().size() > 0) { + channelItrStack.push(chItr); + chItr = curChannel.getChildren().iterator(); + } + } + } while (!channelItrStack.isEmpty()); + } + // generate a return statement. + sideEffects = new String[] {""}; + String curState = ch.deriveUpdateExpressionOf(out, messageTerm, JavaCodeGenerator.pullAccessor).toImplementation(sideEffects); + getter.addStatement(sideEffects[0] + "return " + curState + ";"); } if (outsideInputResource) { // Update fields to refer to outside resources. @@ -477,56 +868,48 @@ 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; for (Edge resToCh2: chToRes2.getSource().getInEdges()) { - if (((PushPullAttribute) resToCh2.getAttribute()).getOptions().get(0) != PushPullValue.PUSH) { + if (((PushPullAttribute) resToCh2.getAttribute()).getSelectedOption() != PushPullValue.PUSH) { isPush = false; break; } @@ -538,30 +921,30 @@ // 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. } } } } } else { - if (pushPull2.getOptions().get(0) == PushPullValue.PUSH) { + if (pushPull2.getSelectedOption() == PushPullValue.PUSH) { // In an update method of the destination component. MethodDeclaration update = null; if (JavaCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { - update = getUpdateMethod(dstComponent, null, JavaCodeGenerator.getComponentName(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); @@ -615,7 +998,7 @@ String childTypeName = JavaCodeGenerator.getComponentName(child); String fieldName = JavaCodeGenerator.toVariableName(childTypeName); Term childGetter = null; - if ((child.getChildren() == null || child.getChildren().size() == 0) && child.getNumParameters() == 0) { + if (!JavaCodeGenerator.generatesComponent(child)) { // the child is not a class childGetter = new Term(new Symbol("get" + childTypeName, 1, Symbol.Type.METHOD)); childGetter.addChild(new Constant("this")); @@ -644,7 +1027,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 +1051,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 +1061,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 +1080,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 +1148,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 +1162,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); @@ -890,6 +1283,7 @@ } private static void replaceJsonTermWithConstructorInvocation(Expression exp, Type replacedJsonType, String replacingClassName, TypeDeclaration descendantComponent) { + // Replace each json term in exp with the corresponding constructor invocation. Type descendantType = new Type(replacingClassName, replacingClassName); Map subTerms = ((Term) exp).getSubTerms(Term.class); Iterator> termEntItr = subTerms.entrySet().iterator(); @@ -902,26 +1296,29 @@ 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()) { + // Extract the argument of each constructor parameter from jsonTerm. + JsonAccessor jsonMember = new JsonAccessor(DataConstraintModel.dot); + jsonMember.addChild(jsonTerm); + jsonMember.addChild(new Constant(var.getName(), DataConstraintModel.typeString)); + Expression param = jsonMember.reduce(); // Reduce {"name": "foo", age: 25}.name => "foo" + if (param != null) { + if (param instanceof Term) { + if (((Term) param).getType() == null) { + ((Term) param).setType(var.getType()); + } + } else if (param instanceof Variable) { + if (((Variable) param).getType() == null) { + ((Variable) param).setType(var.getType()); + } } - } 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..58357f2 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/JavaSpecific.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/JavaSpecific.java @@ -117,6 +117,11 @@ } @Override + public String getSelfExp() { + return self; + } + + @Override public String getFieldAccessor(String fieldName) { return self + "." + fieldName; } @@ -168,7 +173,7 @@ @Override public String getConstructorInvocation(String componentName, List parameters) { String invocation = "new " + componentName + "("; - if (parameters.size() > 0) { + if (parameters != null && parameters.size() > 0) { for (int i = 0; i < parameters.size(); i++) { if (i < parameters.size() - 1) { invocation += parameters.get(i) + ", "; @@ -192,6 +197,31 @@ } @Override + public String getForStatementForList(String varName, String list) { + return "for (int " + varName + getAssignment() + "0; " + varName + " < " + list + ".size(); " + varName + "++) {"; + } + + @Override + public String getForStatementForCollection(String varName, String varType, String collection) { + return "for (" + varType + " " + varName + ": " + collection + ") {"; + } + + @Override + public String getForStatementForMap(String varName, String varType, String map) { + return "for (" + varType + " " + varName + ": " + map + ".keySet()) {"; + } + + @Override + public String getEndForStatement() { + return "}"; + } + + @Override + public String getEndForStatement(String varName) { + return "}"; + } + + @Override public String toComponentName(String name) { return name.substring(0, 1).toUpperCase() + name.substring(1); } @@ -222,6 +252,58 @@ } @Override + public String getOpeningScoreDelimiter() { + return "{"; + } + + @Override + public String getClosingScoreDelimiter() { + return "}"; + } + + @Override + public String getValueToStringExp(String typeName, String valueExp) { + if (typeName.equals("int")) { + return "Integer.toString(" + valueExp + ")"; + } else if (typeName.equals("float")) { + return "Float.toString(" + valueExp + ")"; + } else if (typeName.equals("double")) { + return "Double.toString(" + valueExp + ")"; + } else if (typeName.equals("boolean")) { + return "Boolean.toString(" + valueExp + ")"; + } else { + return valueExp + ".toString()"; + } + } + + @Override + public String getStringToValueExp(String typeName, String strExp) { + if (typeName.equals("int")) { + return "Integer.parseInt(" + strExp + ")"; + } else if (typeName.equals("float")) { + return "Float.parseFloat(" + strExp + ")"; + } else if (typeName.equals("double")) { + return "Double.parseDouble(" + strExp + ")"; + } else if (typeName.equals("boolean")) { + return "Boolean.parseBoolean(" + strExp + ")"; + } else if (typeName.startsWith("ArrayList") || typeName.startsWith("List")) { + return "Arrays.asList(" + strExp + ".replace(\"[\",\"\").replace(\"]\",\"\").split(\",\",0))"; + } else { + return strExp; + } + } + + @Override + public String getPairExp(String first, String second) { + return getConstructorInvocation(DataConstraintModel.typeTuple.getImplementationTypeName() + "<>", List.of(first, second)); + } + + @Override + public String getFirstEntryFromMapExp(String map) { + return getMethodInvocation(map, "entrySet().iterator().next"); + } + + @Override public String getTupleGet(String tupleExp, int idx, int length) { Expression t = new Variable(tupleExp, DataConstraintModel.typeTuple); for (int i = 0; i < idx; i++) { diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JerseyCodeGenerator.java b/AlgebraicDataflowArchitectureModel/src/generators/JerseyCodeGenerator.java index 2d1c1c2..b744713 100644 --- a/AlgebraicDataflowArchitectureModel/src/generators/JerseyCodeGenerator.java +++ b/AlgebraicDataflowArchitectureModel/src/generators/JerseyCodeGenerator.java @@ -124,6 +124,8 @@ if (res.getParent() == null) return true; if (res.getChildren() == null || res.getChildren().size() == 0) return false; if (res.getNumParameters() > 0 && res.getChildren().size() == 1 && res.getChildren().iterator().next().getNumParameters() > 0) return false; + if (res.getChildren().size() == 1 && res.getChildren().iterator().next().getNumParameters() > 0 + && (res.getChildren().iterator().next().getChildren() == null || res.getChildren().iterator().next().getChildren().size() == 0)) return false; return true; // return res.getParent() == null || !(res.getChildren() == null || res.getChildren().size() == 0); } @@ -140,7 +142,7 @@ List> fields = new ArrayList<>(); Map> descendantGetters = new HashMap<>(); Map getterAccessors = new HashMap<>(); - Map inputAccessors = new HashMap<>(); + Map> inputAccessors = new HashMap<>(); Map> constructorParams = new HashMap<>(); Map priorMemberForInputChannel = new HashMap<>(); @@ -166,9 +168,9 @@ cu.addImport(new ImportDeclaration("java.util.*")); if (resourceNode.getResourceHierarchy().getParent() == null) { // For a root node. - cu.addImport(new ImportDeclaration("javax.ws.rs.*")); - cu.addImport(new ImportDeclaration("javax.ws.rs.client.*")); - cu.addImport(new ImportDeclaration("javax.ws.rs.core.*")); + cu.addImport(new ImportDeclaration("jakarta.ws.rs.*")); + cu.addImport(new ImportDeclaration("jakarta.ws.rs.client.*")); + cu.addImport(new ImportDeclaration("jakarta.ws.rs.core.*")); cu.addImport(new ImportDeclaration("org.springframework.stereotype.Component")); cu.addImport(new ImportDeclaration("com.fasterxml.jackson.databind.ObjectMapper")); cu.addImport(new ImportDeclaration("com.fasterxml.jackson.core.JsonProcessingException")); @@ -183,15 +185,16 @@ // leaf resource. Type fieldType = getImplStateType(res); component.addField(new FieldDeclaration(fieldType, "value", getInitializer(res))); - Map nameToParam = constructorParams.get(res); - if (nameToParam == null) { - nameToParam = new HashMap<>(); - constructorParams.put(resourceNode.getResourceHierarchy(), nameToParam); - } - String varName = toVariableName(resourceName); - if (nameToParam.get(varName) == null) { - nameToParam.put(varName, new VariableDeclaration(fieldType, varName)); - } + // Add a parameter to initialize the state field to the constructor. +// Map nameToParam = constructorParams.get(res); +// if (nameToParam == null) { +// nameToParam = new HashMap<>(); +// constructorParams.put(resourceNode.getResourceHierarchy(), nameToParam); +// } +// String varName = "value"; +// if (nameToParam.get(varName) == null) { +// nameToParam.put(varName, new VariableDeclaration(fieldType, varName)); +// } } else { ResourceHierarchy child = children.iterator().next(); if (children.size() == 1 && child.getNumParameters() > 0) { @@ -223,7 +226,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<>(); @@ -389,7 +392,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; @@ -456,7 +459,11 @@ getImplStateType(resourceNode.getResourceHierarchy())); } getterAccessor.setBody(new Block()); - ResourcePath resPath = resourceNode.getPrimaryResourcePath(); + ResourcePath resPath = new ResourcePath(resourceNode.getPrimaryResourcePath()); + for (int i = 0; i < mainGetterParams.size(); i++) { + Parameter pathParam = new Parameter(mainGetterParams.get(i).getName()); + resPath.replacePathParam(i, pathParam, null); + } Expression getState = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(resPath, resPath.getRoot()); getterAccessor.getBody().addStatement("return " + getState.toImplementation(new String[] {null}) + ";"); @@ -468,23 +475,56 @@ 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(); - if (!bDeclareClientField && ((((PushPullAttribute) re.getAttribute()).getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource)) { + boolean outsideOutputResource = false; + ChannelMember out = null; + for (ChannelMember cm: ch.getOutputChannelMembers()) { + if (dstNode.getInSideResources().contains(cm.getResource())) { + out = cm; + if (cm.isOutside()) { + outsideOutputResource = true; + break; + } + } + } + ResourcePath dstRes = out.getResource(); + // Also take into account the channel hierarchy to determine push/pull transfer. + if (descendantDstChannels.contains(chNode)) { + outsideOutputResource = true; // Regarded as (broadcasting) push transfer. + } + if (ancestorDstChannels.contains(chNode)) { + outsideInputResource = true; // Regarded as (collecting) pull transfer. + } + if (!bDeclareClientField && ((((PushPullAttribute) re.getAttribute()).getSelectedOption() == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource)) { // For push transfer. if (!generatesComponent(dstRes.getResourceHierarchy())) { dstRes = dstRes.getParent(); @@ -523,15 +563,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; } @@ -548,14 +605,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 ((((PushPullAttribute) re.getAttribute()).getOptions().get(0) != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { + 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. @@ -575,12 +639,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)); } } @@ -589,7 +653,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) { @@ -614,9 +678,10 @@ params.add(param); // The state of the source resource to carry the data-flow. for (ResourcePath refRes: ch.getReferenceResources()) { if (!refRes.equals(resourceNode.getInSideResource(ch))) { - param = new VariableDeclaration(refRes.getResourceStateType(), refRes.getLeafResourceName()); - if (isRestAPI) param.addAnnotation(new Annotation("FormParam", "\"" + refRes.getLeafResourceName() + "\"")); - params.add(param); + String refName = toVariableName(getComponentName(refRes.getResourceHierarchy())); + param = new VariableDeclaration(refRes.getResourceStateType(), refName); + if (isRestAPI) param.addAnnotation(new Annotation("FormParam", "\"" + refName + "\"")); + params.add(param); } } MethodDeclaration update = null; @@ -628,14 +693,45 @@ String resourceName = getComponentName(resourceNode.getResourceHierarchy()); update = new MethodDeclaration("update" + resourceName + "From" + srcResName, false, typeVoid, params); } - // Determine whether the update method is put or post. + // Determine whether the update method is put or post or delete. boolean isPut = false; + boolean isDelete = false; for (ChannelMember cm: ch.getOutputChannelMembers()) { if (resourceNode.getInSideResources().contains(cm.getResource())) { if (cm.getStateTransition().isRightUnary()) { isPut = true; } else { isPut = false; + Expression nextExp = cm.getStateTransition().getNextStateExpression(); + if (nextExp instanceof Term) { + Symbol rootSymbol = ((Term) nextExp).getSymbol(); + if (rootSymbol.equals(DataConstraintModel.delete) || rootSymbol.equals(DataConstraintModel.remove)) { + isDelete = true; + } else if (rootSymbol.equals(DataConstraintModel.cond)) { + Expression childExp = ((Term) nextExp).getChild(1); + if (childExp instanceof Term) { + rootSymbol = ((Term) childExp).getSymbol(); + if (rootSymbol.equals(DataConstraintModel.delete) || rootSymbol.equals(DataConstraintModel.remove)) { + isDelete = true; + } + } + childExp = ((Term) nextExp).getChild(2); + if (childExp instanceof Term) { + rootSymbol = ((Term) childExp).getSymbol(); + if (rootSymbol.equals(DataConstraintModel.delete) || rootSymbol.equals(DataConstraintModel.remove)) { + isDelete = true; + } + } + } +// HashMap subTerms = ((Term) nextExp).getSubTerms(Term.class); +// for (Term subTerm: subTerms.values()) { +// Symbol rootSymbol = subTerm.getSymbol(); +// if (rootSymbol.equals(DataConstraintModel.delete) || rootSymbol.equals(DataConstraintModel.remove)) { +// isDelete = true; +// break; +// } +// } + } } } } @@ -643,7 +739,11 @@ if (isPut) { update.addAnnotation(new Annotation("PUT")); } else { - update.addAnnotation(new Annotation("POST")); + if (!isDelete) { + update.addAnnotation(new Annotation("POST")); + } else { + update.addAnnotation(new Annotation("DELETE")); + } } } // Calculate in-degree of the destination resource. @@ -707,8 +807,9 @@ params.add(param); // The state of the source resource to carry the data-flow. for (ResourcePath refRes: ch.getReferenceResources()) { if (!refRes.equals(resourceNode.getInSideResource(ch))) { - param = new VariableDeclaration(refRes.getResourceStateType(), refRes.getLeafResourceName()); - param.addAnnotation(new Annotation("FormParam", "\"" + refRes.getLeafResourceName() + "\"")); + String refName = toVariableName(getComponentName(refRes.getResourceHierarchy())); + param = new VariableDeclaration(refRes.getResourceStateType(), refName); + param.addAnnotation(new Annotation("FormParam", "\"" + refName + "\"")); params.add(param); } } @@ -716,7 +817,11 @@ if (isPut) { updateAccessor.addAnnotation(new Annotation("PUT")); } else { - updateAccessor.addAnnotation(new Annotation("POST")); + if (!isDelete) { + updateAccessor.addAnnotation(new Annotation("POST")); + } else { + updateAccessor.addAnnotation(new Annotation("DELETE")); + } } if (inDegree > 1) { // For each source resource, a child resource is defined in the destination resource so that its state can be updated separately. @@ -752,7 +857,7 @@ // In each resource. ArrayList resInputParams = new ArrayList<>(); ArrayList rootInputParams = new ArrayList<>(); - String resourcePath = getInputMethodResourcePathAndPathParams(cm.getResource(), resInputParams, rootInputParams); // Path parameters for the input REST API. + String resourcePath = getInputMethodResourcePathAndPathParams(cm.getResource(), rootInputParams); // Path parameters for the input REST API. if (resourcePath.indexOf('/') > 0) { resourcePath = resourcePath.substring(resourcePath.indexOf('/')); } else { @@ -830,12 +935,52 @@ if (cm.getStateTransition().isRightUnary()) { inputAccessor.addAnnotation(new Annotation("PUT")); } else { - inputAccessor.addAnnotation(new Annotation("POST")); + boolean isDelete = false; + Expression nextExp = cm.getStateTransition().getNextStateExpression(); + if (nextExp instanceof Term) { + Symbol rootSymbol = ((Term) nextExp).getSymbol(); + if (rootSymbol.equals(DataConstraintModel.delete) || rootSymbol.equals(DataConstraintModel.remove)) { + isDelete = true; + } else if (rootSymbol.equals(DataConstraintModel.cond)) { + Expression childExp = ((Term) nextExp).getChild(1); + if (childExp instanceof Term) { + rootSymbol = ((Term) childExp).getSymbol(); + if (rootSymbol.equals(DataConstraintModel.delete) || rootSymbol.equals(DataConstraintModel.remove)) { + isDelete = true; + } + } + childExp = ((Term) nextExp).getChild(2); + if (childExp instanceof Term) { + rootSymbol = ((Term) childExp).getSymbol(); + if (rootSymbol.equals(DataConstraintModel.delete) || rootSymbol.equals(DataConstraintModel.remove)) { + isDelete = true; + } + } + } +// HashMap subTerms = ((Term) nextExp).getSubTerms(Term.class); +// for (Term subTerm: subTerms.values()) { +// Symbol rootSymbol = subTerm.getSymbol(); +// if (rootSymbol.equals(DataConstraintModel.delete) || rootSymbol.equals(DataConstraintModel.remove)) { +// isDelete = true; +// break; +// } +// } + } + if (!isDelete) { + inputAccessor.addAnnotation(new Annotation("POST")); + } else { + inputAccessor.addAnnotation(new Annotation("DELETE")); + } } if (resourcePath.length() > 0) { inputAccessor.addAnnotation(new Annotation("Path", "\"" + resourcePath + "\"")); } - inputAccessors.put(resourceNode.getResourceHierarchy(), inputAccessor); + Map nameToMethod = inputAccessors.get(resourceNode.getResourceHierarchy()); + if (nameToMethod == null) { + nameToMethod = new HashMap<>(); + inputAccessors.put(resourceNode.getResourceHierarchy(), nameToMethod); + } + nameToMethod.put(messageSymbol, inputAccessor); } } else if (message instanceof Variable) { // In each resource. @@ -894,12 +1039,52 @@ if (cm.getStateTransition().isRightUnary()) { inputAccessor.addAnnotation(new Annotation("PUT")); } else { - inputAccessor.addAnnotation(new Annotation("POST")); + boolean isDelete = false; + Expression nextExp = cm.getStateTransition().getNextStateExpression(); + if (nextExp instanceof Term) { + Symbol rootSymbol = ((Term) nextExp).getSymbol(); + if (rootSymbol.equals(DataConstraintModel.delete) || rootSymbol.equals(DataConstraintModel.remove)) { + isDelete = true; + } else if (rootSymbol.equals(DataConstraintModel.cond)) { + Expression childExp = ((Term) nextExp).getChild(1); + if (childExp instanceof Term) { + rootSymbol = ((Term) childExp).getSymbol(); + if (rootSymbol.equals(DataConstraintModel.delete) || rootSymbol.equals(DataConstraintModel.remove)) { + isDelete = true; + } + } + childExp = ((Term) nextExp).getChild(2); + if (childExp instanceof Term) { + rootSymbol = ((Term) childExp).getSymbol(); + if (rootSymbol.equals(DataConstraintModel.delete) || rootSymbol.equals(DataConstraintModel.remove)) { + isDelete = true; + } + } + } +// HashMap subTerms = ((Term) nextExp).getSubTerms(Term.class); +// for (Term subTerm: subTerms.values()) { +// Symbol rootSymbol = subTerm.getSymbol(); +// if (rootSymbol.equals(DataConstraintModel.delete) || rootSymbol.equals(DataConstraintModel.remove)) { +// isDelete = true; +// break; +// } +// } + } + if (!isDelete) { + inputAccessor.addAnnotation(new Annotation("POST")); + } else { + inputAccessor.addAnnotation(new Annotation("DELETE")); + } } if (resourcePath.length() > 0) { inputAccessor.addAnnotation(new Annotation("Path", "\"" + resourcePath + "\"")); } - inputAccessors.put(resourceNode.getResourceHierarchy(), inputAccessor); + Map nameToMethod = inputAccessors.get(resourceNode.getResourceHierarchy()); + if (nameToMethod == null) { + nameToMethod = new HashMap<>(); + inputAccessors.put(resourceNode.getResourceHierarchy(), nameToMethod); + } + nameToMethod.put(messageSymbol, inputAccessor); } } } @@ -943,7 +1128,7 @@ if (field.getType().equals(typeClient)) { for (CompilationUnit cu: codes) { if (cu.types().contains(component)) { - cu.addImport(new ImportDeclaration("javax.ws.rs.client.*")); + cu.addImport(new ImportDeclaration("jakarta.ws.rs.client.*")); break; } } @@ -970,7 +1155,9 @@ // Add input accessors. for (ResourceHierarchy res: inputAccessors.keySet()) { if (rootRes.isAncestorOf(res)) { - rootComponent.addMethod(inputAccessors.get(res)); + for (MethodDeclaration inputAccessor: inputAccessors.get(res).values()) { + rootComponent.addMethod(inputAccessor); + } } } } @@ -1115,7 +1302,7 @@ return resPath.getResourceHierarchy().toResourcePath(params); } - private static String getInputMethodResourcePathAndPathParams(ResourcePath resPath, ArrayList resInputParams, ArrayList rootInputParams) { + private static String getInputMethodResourcePathAndPathParams(ResourcePath resPath, ArrayList rootInputParams) { int v = 1; List params = new ArrayList<>(); if (resPath.getLastParam() != null) { @@ -1227,7 +1414,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); } @@ -1235,7 +1422,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); } @@ -1261,7 +1448,7 @@ } } // for reference channel member - return new Parameter(targetRes.getLeafResourceName(), + return new Parameter(toVariableName(getComponentName(targetRes.getResourceHierarchy())), targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } @@ -1275,7 +1462,8 @@ return getDirectStateAccessorFor(targetRes, fromRes); } } - return new Parameter(targetRes.getLeafResourceName(), + // Get the next state through a local variable which is to be initialized by a response of a GET API. + return new Parameter(toVariableName(getComponentName(targetRes.getResourceHierarchy())), targetRes.getResourceStateType() != null ? targetRes.getResourceStateType() : DataConstraintModel.typeInt); } @@ -1289,11 +1477,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 { @@ -1303,8 +1491,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()); @@ -1318,58 +1506,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++; } } } @@ -1395,7 +1568,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); } @@ -1403,7 +1576,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 621bd82..42c2067 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,7 +112,14 @@ } // Check if the output resource is outside of the channel scope. boolean outsideOutputResource = out.isOutside(); - if ((pushPull.getOptions().get(0) == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) { + // Also take into account the channel hierarchy to determine push/pull transfer. + if (descendantDstChannels.contains(chNode)) { + outsideOutputResource = true; // Regarded as (broadcasting) push transfer. + } + if (ancestorDstChannels.contains(chNode)) { + outsideInputResource = true; // Regarded as (collecting) pull transfer. + } + if ((pushPull.getSelectedOption() == PushPullValue.PUSH && !outsideInputResource) || outsideOutputResource) { // for push data transfer MethodDeclaration update = null; if (dstComponent == null) { @@ -106,30 +131,35 @@ } if (((StoreAttribute) dst.getAttribute()).isStored()) { // update stored state of dst side resource (when every incoming edge is in push style) + Term unifiedMassage = null; + if (directDstCh != ch) { + unifiedMassage = directDstCh.fillOutsideResourcePaths(out, JerseyCodeGenerator.pushAccessor, null).getValue(); + } Expression updateExp = null; if (ch.getReferenceChannelMembers().size() == 0) { - updateExp = ch.deriveUpdateExpressionOf(out, JerseyCodeGenerator.pushAccessor).getKey(); + Term message = ch.fillOutsideResourcePaths(out, JerseyCodeGenerator.pushAccessor, null).getValue(); + if (unifiedMassage == null) { + unifiedMassage = message; + } else { + unifiedMassage = (Term) unifiedMassage.unify(message); + } + updateExp = ch.deriveUpdateExpressionOf(out, unifiedMassage, JerseyCodeGenerator.pushAccessor); } else { // if there exists one or more reference channel member. HashMap inputResourceToStateAccessor = new HashMap<>(); - for (Edge chToRes2: dst.getInEdges()) { - DataTransferChannel ch2 = ((ChannelNode) chToRes2.getSource()).getChannel(); - for (Edge resToCh2: chToRes2.getSource().getInEdges()) { - DataFlowEdge dIn = (DataFlowEdge) resToCh2; - ChannelMember in2 = null; - for (ChannelMember cm: ch2.getInputChannelMembers()) { - if (((ResourceNode) dIn.getSource()).getOutSideResources().contains(cm.getResource())) { - in2 = cm; - break; - } - } - inputResourceToStateAccessor.put(in2, JerseyCodeGenerator.pushAccessor); - } + for (ChannelMember c: ch.getInputChannelMembers()) { + inputResourceToStateAccessor.put(c, JerseyCodeGenerator.pushAccessor); } for (ChannelMember c: ch.getReferenceChannelMembers()) { inputResourceToStateAccessor.put(c, JerseyCodeGenerator.refAccessor); } - updateExp = ch.deriveUpdateExpressionOf(out, JerseyCodeGenerator.pushAccessor, inputResourceToStateAccessor).getKey(); + Term message = ch.fillOutsideResourcePaths(out, JerseyCodeGenerator.pushAccessor, inputResourceToStateAccessor).getValue(); + if (unifiedMassage == null) { + unifiedMassage = message; + } else { + unifiedMassage = (Term) unifiedMassage.unify(message); + } + updateExp = ch.deriveUpdateExpressionOf(out, unifiedMassage, JerseyCodeGenerator.pushAccessor); } // Replace Json constructor with a constructor of a descendant resource. ResourceHierarchy outRes = out.getResource().getResourceHierarchy(); @@ -172,6 +202,9 @@ if (JerseyCodeGenerator.generatesComponent(outRes)) { if (updateExp instanceof Term && ((Term) updateExp).getSymbol().isImplWithSideEffect()) { updateStatement = sideEffects[0]; + if (updateStatement.endsWith("\n")) { + updateStatement = updateStatement.substring(0, updateStatement.length() - 1); + } } else { updateStatement = sideEffects[0] + "this.value = " + newState + ";"; } @@ -179,6 +212,9 @@ if (sideEffects[0] != null) { updateStatement = sideEffects[0]; updateStatement = updateStatement.replace(".value", "." + JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(outRes))); + if (updateStatement.endsWith("\n")) { + updateStatement = updateStatement.substring(0, updateStatement.length() - 1); + } } if (DataConstraintModel.typeList.isAncestorOf(outRes.getParent().getResourceStateType())) { Term selector = new Term(DataConstraintModel.set); @@ -241,19 +277,24 @@ update.addFirstStatement(ifStatement + "\t" + updateStatement.replace("\n", "\n\t") + "\n}"); } } - // Calculate in-degree of the destination resource. - Set inResources = new HashSet<>(); - for (ResourceNode rn: graph.getResourceNodes(out.getResource().getResourceHierarchy())) { - // ResourceNodes that have the same ResourceHierarchy. - for (Edge chToRes2: rn.getInEdges()) { - for (Edge resToCh2: chToRes2.getSource().getInEdges()) { - inResources.add(((ResourceNode) resToCh2.getSource()).getResourceHierarchy()); - } + // Calculate in-degree (PUSH transfer) of the destination resource. + int inDegree = 0; + Set inEdges = new HashSet<>(); + inEdges.addAll(chNode.getInEdges()); + for (ChannelNode ancestor: chNode.getAncestors()) { + inEdges.addAll(ancestor.getInEdges()); + } + for (ChannelNode descendant: chNode.getDescendants()) { + inEdges.addAll(descendant.getInEdges()); + } + for (Edge resToCh2: inEdges) { + DataFlowEdge df =(DataFlowEdge) resToCh2; + if (((PushPullAttribute) df.getAttribute()).getSelectedOption() == PushPullValue.PUSH) { + inDegree++; } } - 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)) { @@ -270,7 +311,12 @@ MethodDeclaration update2 = update; update = getMethod(rootComponent, update2.getName()); // get the accessor to the update method. // To make the accessor call the update method. - Expression resExp = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(out.getResource(), out.getResource().getRoot()); + ResourcePath outResPath = new ResourcePath(out.getResource()); + for (int i = 0; i < outResPath.getPathParams().size(); i++) { + Parameter pathParam = new Parameter(update.getParameters().get(i).getName()); + outResPath.replacePathParam(i, pathParam, null); + } + Expression resExp = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(outResPath, outResPath.getRoot()); String args = ""; String delimiter = ""; if (resExp instanceof Term) { @@ -285,7 +331,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 Term) { + args += delimiter + "v" + (v + 1); + } } else { args += delimiter + var.getName(); } @@ -380,16 +430,19 @@ getter.addStatement("return " + dstResName + ";"); } } else { + String[] sideEffects = new String[] {""}; if (DataConstraintModel.typeList.isAncestorOf(dst.getParent().getResourceStateType())) { Term selector = new Term(DataConstraintModel.get); selector.addChild(new Field("value")); selector.addChild(dst.getSelectors().get(dst.getSelectors().size() - 1).getExpression()); - getter.addStatement("return " + selector.toImplementation(new String[] {}) + ";"); + String curState = selector.toImplementation(sideEffects); + getter.addStatement(sideEffects[0] + "return " + curState + ";"); } else if (DataConstraintModel.typeMap.isAncestorOf(dst.getParent().getResourceStateType())) { Term selector = new Term(DataConstraintModel.lookup); selector.addChild(new Field("value")); selector.addChild(dst.getSelectors().get(dst.getSelectors().size() - 1).getExpression()); - getter.addStatement("return " + selector.toImplementation(new String[] {}) + ";"); + String curState = selector.toImplementation(sideEffects); + getter.addStatement(sideEffects[0] + "return " + curState + ";"); } } } @@ -407,6 +460,7 @@ srcComponent = componentMap.get(srcParentResourceName); srcName = srcResourceName; } + // For caller update methods for (MethodDeclaration srcUpdate: getUpdateMethods(srcComponent, srcName)) { if (srcUpdate != null) { List>> params = new ArrayList<>(); @@ -426,6 +480,7 @@ } params.add(new AbstractMap.SimpleEntry<>(src.getResourceStateType(), new AbstractMap.SimpleEntry<>(JerseyCodeGenerator.toVariableName(srcResourceName), srcFieldName))); + // Get the value of reference member to call the update method. Set referredSet = referredResources.get(srcUpdate); if (ch.getReferenceChannelMembers().size() > 0) { for (ChannelMember rc: ch.getReferenceChannelMembers()) { @@ -436,7 +491,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,25 +529,90 @@ if (inDegree <= 1) { srcResName = null; } + Map>> filledPaths = ch.fillOutsideResourcePaths(out, JerseyCodeGenerator.pushAccessor); + String dstPath = null; + if (filledPaths != null && filledPaths.get(out) != null) { + ResourcePath filledDstPath = filledPaths.get(out).getKey(); + dstPath = filledDstPath.toResourcePath().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + } else { + dstPath = dstRes.getResourceHierarchy().toResourcePath(pathParams); + } + // Call the update method. if (!chainedCalls.contains(srcUpdate)) { // The first call to an update method in this method srcUpdate.addStatement(getHttpMethodParamsStatement(srcComponent.getTypeName(), params, true)); - srcUpdate.addStatement("String result = " + getHttpMethodCallStatement(baseURL, - 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 { - // Inner-service + // Intra-service String updateMethodName = null; if (JerseyCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { updateMethodName = "updateFrom" + srcResourceName; @@ -514,6 +634,7 @@ callParams += delimiter + paramEnt.getValue().getValue(); delimiter = ", "; } + // Call the update method. if (srcComponent != dstComponent) { srcUpdate.addStatement("this." + JerseyCodeGenerator.toVariableName(dstResourceName) + "." + updateMethodName + "(" + callParams + ");"); } else { @@ -525,6 +646,7 @@ } } } + // For caller input methods for (MethodDeclaration srcInput: getInputMethods(srcComponent, src, model)) { List>> params = new ArrayList<>(); ResourcePath dstRes = out.getResource(); @@ -543,6 +665,7 @@ } params.add(new AbstractMap.SimpleEntry<>(src.getResourceStateType(), new AbstractMap.SimpleEntry<>(JerseyCodeGenerator.toVariableName(srcResourceName), srcFieldName))); + // Get the value of reference member to call the update method. Set referredSet = referredResources.get(srcInput); for (ChannelMember rc: ch.getReferenceChannelMembers()) { // For each reference channel member, get the current state of the reference side resource by pull data transfer. @@ -552,7 +675,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 +694,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,25 +712,90 @@ if (inDegree <= 1) { srcResName = null; } + Map>> filledPaths = ch.fillOutsideResourcePaths(out, JerseyCodeGenerator.pushAccessor); + String dstPath = null; + if (filledPaths != null && filledPaths.get(out) != null) { + ResourcePath filledDstPath = filledPaths.get(out).getKey(); + dstPath = filledDstPath.toResourcePath().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + } else { + dstPath = dstRes.getResourceHierarchy().toResourcePath(pathParams); + } + // Call the update method. if (!chainedCalls.contains(srcInput)) { // First call to an update method in this method srcInput.addStatement(getHttpMethodParamsStatement(srcComponent.getTypeName(), params, true)); - srcInput.addStatement("String result = " + getHttpMethodCallStatement(baseURL, - 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 forVarName = null; + if (selExp instanceof Variable) { + selType = ((Variable) selExp).getType(); + forVarName = ((Variable) selExp).getName(); + ChannelMember insideChMem = null; + for (ChannelMember cm :ch.getInputChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + if (insideChMem == null) { + for (ChannelMember cm :ch.getReferenceChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + } + if (insideChMem == null) { + for (ChannelMember cm :ch.getOutputChannelMembers()) { + if (!cm.isOutside()) { + insideChMem = cm; + break; + } + } + } + ResourcePath insideResPath = insideChMem.getResource(); + while (insideResPath.getParent() != null && (insideResPath.getLastParam() == null || !insideResPath.getLastParam().equals(selExp))) { + insideResPath = insideResPath.getParent(); + } + insideResPath = insideResPath.getParent(); + String parent = null; + if (JerseyCodeGenerator.generatesComponent(insideResPath.getResourceHierarchy())) { + Expression getter = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(insideResPath, src.getPrimaryResourcePath()); + Term valueGetter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); + valueGetter.addChild(getter); + parent = valueGetter.toImplementation(new String[] {}); + } else { + parent = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(insideResPath, src.getPrimaryResourcePath()).toImplementation(new String[] {}); + } + if (insideResPath != null) { + if (selType.equals(DataConstraintModel.typeInt)) { + // make a for loop (for a list) for broadcasting. + srcInput.addFirstStatement("for (int " + forVarName + " = 0; " + forVarName +" < " + parent + ".size(); " + forVarName + "++) {"); + srcInput.addStatement("}"); + } else if (selType.equals(DataConstraintModel.typeString)) { + // make a for loop (for a map) for broadcasting. + srcInput.addFirstStatement("for (String " + forVarName + ": " + parent + ".keySet()) {"); + srcInput.addStatement("}"); + } + } + } else if (selExp instanceof Term) { + // not supported. + } + } } srcInput.addThrow("JsonProcessingException"); } else { - // Inner-service + // Intra-service String updateMethodName = null; if (JerseyCodeGenerator.generatesComponent(dst.getResourceHierarchy())) { updateMethodName = "updateFrom" + srcResourceName; @@ -629,6 +817,7 @@ callParams += delimiter + paramEnt.getValue().getValue(); delimiter = ", "; } + // Call the update method. if (srcComponent != dstComponent) { srcInput.addStatement("this." + JerseyCodeGenerator.toVariableName(dstResourceName) + "." + updateMethodName + "(" + callParams + ");"); } else { @@ -639,7 +828,7 @@ } } } - } else if ((pushPull.getOptions().get(0) != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { + } else if ((pushPull.getSelectedOption() != PushPullValue.PUSH && !outsideOutputResource) || outsideInputResource) { // for pull (or push/pull) data transfer if (dstComponent == null) { String dstParentResourceName = JerseyCodeGenerator.getComponentName(dst.getResourceHierarchy().getParent()); @@ -652,20 +841,19 @@ 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. + + // Data transfer on the same channel hierarchy. 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 +863,186 @@ 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 + ";"); } } + + // Construct the base message. + Map.Entry>>, Term> resourcePathsAndMessage = ch.fillOutsideResourcePaths(out, JerseyCodeGenerator.pullAccessor, null); + Map>> resourcePaths = resourcePathsAndMessage.getKey(); + Term messageTerm = resourcePathsAndMessage.getValue(); + + // Data transfer from path depending resource. for (Entry>> pathEnt: resourcePaths.entrySet()) { ChannelMember cm = pathEnt.getKey(); ResourcePath src2 = pathEnt.getValue().getKey(); - // get outside src resource state by pull data transfer. + // get outside src2 resource state by pull data transfer. if (cm.isOutside() || src2.getCommonPrefix(dst.getInSideResource(ch)) == null) { - Type srcResourceType = src2.getResourceStateType(); + // Data transfer from an outside input resource is regarded as PULL transfer. List pathParams = new ArrayList<>(); for (Expression pathExp: src2.getPathParams()) { + sideEffects = new String[] {""}; pathParams.add("\" + " + pathExp.toImplementation(sideEffects) + " + \""); } - 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.toResourcePath().replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + generatePullDataTransfer(getter, srcResName2, srcPath2, srcResourceType); } } + + // Data transfer from the descendant channel hierarchies. + Stack> channelItrStack = new Stack<>(); + DataTransferChannel curChannel = ch; + if (curChannel.getChildren() != null && curChannel.getChildren().size() > 0) { + // retrieve descendant channels recursively. + // (For each descendant channel, data transfer from every input resource is regarded as PULL transfer.) + Iterator chItr = curChannel.getChildren().iterator(); + do { + if (!chItr.hasNext()) { + chItr = channelItrStack.pop(); + } else { + curChannel = (DataTransferChannel) chItr.next(); + // generate pull data transfers. + Set chMems = new HashSet<>(curChannel.getInputChannelMembers()); + chMems.addAll(curChannel.getReferenceChannelMembers()); + for (ChannelMember cm2: chMems) { + if (resourcePaths == null || !resourcePaths.keySet().contains(cm2)) { + // not a depending channel member. + ResourcePath src2 = cm2.getResource(); + Type srcResType2 = src2.getResourceStateType(); + String srcResName2 = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(src2.getResourceHierarchy())); + String srcPath2 = src2.toResourcePath().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + generatePullDataTransfer(getter, srcResName2, srcPath2, srcResType2); + } else { + // a depending channel member. + ResourcePath src2 = resourcePaths.get(cm2).getKey(); + // get outside src2 resource state by pull data transfer. + if (cm2.isOutside() || src2.getCommonPrefix(dst.getInSideResource(curChannel)) == null) { + // generate a pull data transfer from a depending in/ref resource. + Type srcResType2 = src2.getResourceStateType(); + String srcResName2 = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(src2.getResourceHierarchy())); + String srcPath2 = src2.toResourcePath().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + generatePullDataTransfer(getter, srcResName2, srcPath2, srcResType2); + } + } + } + // collect the message constraints by a descendant channel. + List varsForSideEffects = new ArrayList<>(); + int v = 0; + resourcePathsAndMessage = curChannel.fillOutsideResourcePaths(out, JerseyCodeGenerator.pullAccessor, null); + if (resourcePathsAndMessage != null) { + resourcePaths = resourcePathsAndMessage.getKey(); + Term messageTermSub = resourcePathsAndMessage.getValue(); + for (Map.Entry subTermEnt: messageTermSub.getSubTerms(Term.class).entrySet()) { + Term subTerm = subTermEnt.getValue(); + if (!(subTerm instanceof Constant) && subTerm.getSymbol().isImplWithSideEffect()) { + Variable var = new Variable("v" + v, subTerm.getType()); + varsForSideEffects.add(var); + v++; + // Add a side effect statement within the loop + Position pos = new Position(); + pos.addHeadOrder(0); + subTerm.replaceSubTerm(pos, var); + sideEffects = new String[] {""}; + String curState = messageTermSub.toImplementation(sideEffects); + getter.addStatement(sideEffects[0].replaceAll("\n", "")); + // Cancel the side effects in the return value. + pos = subTermEnt.getKey(); + messageTermSub.replaceSubTerm(pos, var); + } + } + if (messageTerm == null) { + messageTerm = messageTermSub; + } else { + messageTerm = (Term) messageTerm.unify(messageTermSub); + } + if (messageTerm == null) { + throw new UnificationFailed(); + } + } + // enclosed by a for loop + Expression selExp = curChannel.getSelectors().get(0).getExpression(); + Type selType = null; + String varName = null; + if (selExp instanceof Variable) { + selType = ((Variable) selExp).getType(); + varName = ((Variable) selExp).getName(); + ChannelMember insideChMem = null; + for (ChannelMember cm2 :curChannel.getInputChannelMembers()) { + if (!cm2.isOutside()) { + insideChMem = cm2; + break; + } + } + if (insideChMem == null) { + for (ChannelMember cm2 :curChannel.getReferenceChannelMembers()) { + if (!cm2.isOutside()) { + insideChMem = cm2; + break; + } + } + } + ResourcePath insideResPath = insideChMem.getResource(); + while (insideResPath.getParent() != null && (insideResPath.getLastParam() == null || !insideResPath.getLastParam().equals(selExp))) { + insideResPath = insideResPath.getParent(); + } + insideResPath = insideResPath.getParent(); + String parent = null; + if (insideResPath != null) { + if (insideResPath.getCommonPrefix(dst.getInSideResource(ch)) != null) { + if (JerseyCodeGenerator.generatesComponent(insideResPath.getResourceHierarchy())) { + Expression parentGetter = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(insideResPath, dst.getInSideResource(ch)); + Term valueGetter = new Term(new Symbol("getValue", 1, Symbol.Type.METHOD)); + valueGetter.addChild(parentGetter); + parent = valueGetter.toImplementation(new String[] {}); + } else { + parent = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(insideResPath, dst.getInSideResource(ch)).toImplementation(new String[] {}); + } + } else { + parent = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(insideResPath.getResourceHierarchy())); + } + if (selType.equals(DataConstraintModel.typeInt)) { + // make a for loop (for a list) for data collecting. + getter.addFirstStatement("for (int " + varName + " = 0; " + varName +" < " + parent + ".size(); " + varName + "++) {"); + } else if (selType.equals(DataConstraintModel.typeString)) { + // make a for loop (for a map) for data collecting. + getter.addFirstStatement("for (String " + varName + ": " + parent + ".keySet()) {"); + } + if (insideResPath.getCommonPrefix(dst.getInSideResource(ch)) == null) { + Type parentResType = insideResPath.getResourceStateType(); + String parentResName = JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(insideResPath.getResourceHierarchy())); + String parentResPath = insideResPath.toString().replaceAll(":.*\\}","\\}").replaceAll("\\{", "\"+").replaceAll("\\}", "+\""); + generatePullDataTransfer(getter, parentResName, parentResPath, parentResType); + } + } + } + // initialize the variables to hold side effects within the loop + for (Variable var: varsForSideEffects) { + getter.addFirstStatement(var.getType().getInterfaceTypeName() + " " + var.getName() + " = new " + var.getType().getImplementationTypeName() + "();"); + } + // end of the loop + getter.addStatement("}"); + if (curChannel.getChildren() != null && curChannel.getChildren().size() > 0) { + channelItrStack.push(chItr); + chItr = curChannel.getChildren().iterator(); + } + } + } while (!channelItrStack.isEmpty()); + } + // generate a return statement. + Expression curExp = ch.deriveUpdateExpressionOf(out, messageTerm, JerseyCodeGenerator.pullAccessor); + sideEffects = new String[] {""}; + String curState = curExp.toImplementation(sideEffects); + if (ch.getChildren() == null || ch.getChildren().size() == 0) { + getter.addStatement(sideEffects[0] + "return " + curState + ";"); + } else { + getter.addStatement("return " + curState + ";"); + } } // get src resource state by pull data transfer. if (src.getNumberOfParameters() == 0 && src.getOutSideResource(ch).getCommonPrefix(dst.getInSideResource(ch)) == null) { @@ -739,7 +1089,7 @@ String childTypeName = JerseyCodeGenerator.getComponentName(child); String fieldName = JerseyCodeGenerator.toVariableName(childTypeName); Term childGetter = null; - if ((child.getChildren() == null || child.getChildren().size() == 0) && child.getNumParameters() == 0) { + if (!JerseyCodeGenerator.generatesComponent(child)) { // the child is not a class childGetter = new Term(new Symbol("get" + childTypeName, 1, Symbol.Type.METHOD)); childGetter.addChild(new Constant("this")); @@ -768,7 +1118,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 +1142,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 +1152,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 +1171,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); } @@ -918,6 +1272,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 + ";"; } @@ -929,6 +1286,9 @@ if (sideEffects[0] != null) { updateStatement = sideEffects[0]; updateStatement = updateStatement.replace(".value", "." + JerseyCodeGenerator.toVariableName(JerseyCodeGenerator.getComponentName(resource))); + if (updateStatement.endsWith("\n")) { + updateStatement = updateStatement.substring(0, updateStatement.length() - 1); + } } if (DataConstraintModel.typeList.isAncestorOf(resource.getParent().getResourceStateType())) { Term selector = new Term(DataConstraintModel.set); @@ -965,8 +1325,13 @@ } MethodDeclaration inputAccessor = getMethod(componentMap.get(JerseyCodeGenerator.getComponentName(resource.getRoot())), inputAccessorName); if (inputAccessor != null) { + ResourcePath outResPath = new ResourcePath(out.getResource()); + for (int i = 0; i < outResPath.getPathParams().size(); i++) { + Parameter pathParam = new Parameter(inputAccessor.getParameters().get(i).getName()); + outResPath.replacePathParam(i, pathParam, null); + } // The expression of the receiver (resource) of the input method. - Expression resExp = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(out.getResource(), out.getResource().getRoot()); + Expression resExp = JerseyCodeGenerator.pullAccessor.getDirectStateAccessorFor(outResPath, outResPath.getRoot()); String args = ""; String delimiter = ""; if (resExp instanceof Term) { @@ -1011,6 +1376,7 @@ } private static void replaceJsonTermWithConstructorInvocation(Expression exp, Type replacedJsonType, String replacingClassName, TypeDeclaration descendantComponent) { + // Replace each json term in exp with the corresponding constructor invocation. Type descendantType = new Type(replacingClassName, replacingClassName); Map subTerms = ((Term) exp).getSubTerms(Term.class); Iterator> termEntItr = subTerms.entrySet().iterator(); @@ -1022,27 +1388,30 @@ 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()) { + // Extract the argument of each constructor parameter from jsonTerm. + JsonAccessor jsonMember = new JsonAccessor(DataConstraintModel.dot); + jsonMember.addChild(jsonTerm); + jsonMember.addChild(new Constant(var.getName(), DataConstraintModel.typeString)); + Expression param = jsonMember.reduce(); // Reduce {"name": "foo", age: 25}.name => "foo" + if (param != null) { + if (param instanceof Term) { + if (((Term) param).getType() == null) { + ((Term) param).setType(var.getType()); + } + } else if (param instanceof Variable) { + if (((Variable) param).getType() == null) { + ((Variable) param).setType(var.getType()); + } } - } 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)); @@ -1149,7 +1518,7 @@ || elmType == DataConstraintModel.typeLong || elmType == DataConstraintModel.typeFloat || elmType == DataConstraintModel.typeDouble) { - String elmVal = CodeUtil.getToValueExp(elmType.getImplementationTypeName(), elementBase + ".getKey()"); + String elmVal = new JavaSpecific().getStringToValueExp(elmType.getImplementationTypeName(), elementBase + ".getKey()"); decoded = decoded.replace("$x", "new AbstractMap.SimpleEntry<>(" + elmVal + ", $x)"); } else if (elmType == DataConstraintModel.typeString) { decoded = decoded.replace("$x", "new AbstractMap.SimpleEntry<>(" + elementBase + ".getKey(), $x)"); @@ -1182,7 +1551,7 @@ || keyType == DataConstraintModel.typeDouble) { decoded += "for (String k: " + mapVal + ".keySet()) {\n"; decoded += "\t" + mapVar + ".put("; - keyVal = CodeUtil.getToValueExp(keyType.getImplementationTypeName(), "k"); + keyVal = new JavaSpecific().getStringToValueExp(keyType.getImplementationTypeName(), "k"); decoded += keyVal + ", " + mapVal + ".get(" + keyVal + ")" + ");\n"; decoded += "}"; } else if (keyType == DataConstraintModel.typeString) { @@ -1220,7 +1589,7 @@ // typeTuple: {"1.0":2.0}, typePair: {"left": 1.0, "right":2.0} statements += "form.param(\"" + paramName + "\", new ObjectMapper().writeValueAsString(" + value + "));\n"; } else { - statements += "form.param(\"" + paramName + "\", " + CodeUtil.getToStringExp(paramType.getImplementationTypeName(), value) + ");\n"; + statements += "form.param(\"" + paramName + "\", " + new JavaSpecific().getValueToStringExp(paramType.getImplementationTypeName(), value) + ");\n"; } } if (isFirstCall) { diff --git a/AlgebraicDataflowArchitectureModel/src/generators/JerseySpecific.java b/AlgebraicDataflowArchitectureModel/src/generators/JerseySpecific.java new file mode 100644 index 0000000..a9f4c3c --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/generators/JerseySpecific.java @@ -0,0 +1,169 @@ +package generators; + +import java.util.List; +import java.util.Map; + +import algorithms.TypeInference; +import code.ast.Annotation; +import code.ast.CompilationUnit; +import code.ast.FieldDeclaration; +import code.ast.ImportDeclaration; +import code.ast.MethodDeclaration; +import code.ast.TypeDeclaration; +import code.ast.VariableDeclaration; +import models.algebra.Type; +import models.dataConstraintModel.DataConstraintModel; + +public class JerseySpecific extends RestApiSpecific { + public static final Type typeClient = new Type("Client", "Client"); + + public JerseySpecific() { + langSpec = new JavaSpecific(); + } + + @Override + public void addComponentAnnotations(TypeDeclaration component, String resourcePath) { + component.addAnnotation(new Annotation("Component")); + component.addAnnotation(new Annotation("Path", "\"/" + resourcePath + "\"")); + } + + @Override + public void addGetAnnotations(MethodDeclaration getMethod) { + getMethod.addAnnotation(new Annotation("Produces", "MediaType.APPLICATION_JSON")); + getMethod.addAnnotation(new Annotation("GET")); + } + + @Override + public void addPutAnnotations(MethodDeclaration putMethod) { + putMethod.addAnnotation(new Annotation("PUT")); + } + + @Override + public void addPostAnnotations(MethodDeclaration postMethod) { + postMethod.addAnnotation(new Annotation("POST")); + } + + @Override + public void addDeleteAnnotations(MethodDeclaration deleteMethod) { + deleteMethod.addAnnotation(new Annotation("DELETE")); + } + + @Override + public void addPathAnnotation(MethodDeclaration method, String resourcePath) { + method.addAnnotation(new Annotation("Path", "\"" + resourcePath + "\"")); + } + + @Override + public void addPathParamAnnotation(VariableDeclaration var, String paramName) { + var.addAnnotation(new Annotation("PathParam", "\"" + paramName + "\"")); + } + + @Override + public void addFormParamAnnotation(VariableDeclaration var, String paramName) { + var.addAnnotation(new Annotation("FormParam", "\"" + paramName + "\"")); + } + + @Override + public void addPlatformSpecificImports(CompilationUnit cu) { + cu.addImport(new ImportDeclaration("jakarta.ws.rs.*")); + cu.addImport(new ImportDeclaration("jakarta.ws.rs.client.*")); + cu.addImport(new ImportDeclaration("jakarta.ws.rs.core.*")); +// cu.addImport(new ImportDeclaration("javax.ws.rs.*")); +// cu.addImport(new ImportDeclaration("javax.ws.rs.client.*")); +// cu.addImport(new ImportDeclaration("javax.ws.rs.core.*")); + cu.addImport(new ImportDeclaration("org.springframework.stereotype.Component")); + cu.addImport(new ImportDeclaration("com.fasterxml.jackson.databind.ObjectMapper")); + cu.addImport(new ImportDeclaration("com.fasterxml.jackson.core.JsonProcessingException")); + } + + @Override + public boolean hasHttpClientFieldDeclaration(TypeDeclaration component) { + for (FieldDeclaration field: component.getFields()) { + if (field.getType().equals(typeClient)) { + return true; + } + } + return false; + } + + @Override + public void addHttpClientFieldDeclaration(TypeDeclaration component) { + FieldDeclaration clientField = new FieldDeclaration(typeClient, "client", "ClientBuilder.newClient()"); + component.addField(clientField); + } + + @Override + public String getConversionFromJsonString(String fromStrVarName, String toVarName, String toVarType) { + return toVarType + toVarName + langSpec.getAssignment() + langSpec.getConstructorInvocation("ObjectMapper", null) + ".readValue(" + fromStrVarName + ", HashMap.class)"; + } + + @Override + public String getHttpMethodParamsConstructionStatement(String callerResourceName, List>> params, boolean isFirstCall) { + String statements = ""; + if (isFirstCall) { + statements += "Form "; + } + statements += "form" + langSpec.getAssignment() + langSpec.getConstructorInvocation("Form", null) + langSpec.getStatementDelimiter() + "\n"; + for (Map.Entry> param: params) { + Type paramType = param.getKey(); + String paramName = param.getValue().getKey(); + String value = param.getValue().getValue(); + if (DataConstraintModel.typeList.isAncestorOf(paramType)) { + Type compType = TypeInference.getListComponentType(paramType); + String wrapperType = DataConstraintModel.getWrapperType(compType); + if (wrapperType == null) { + statements += langSpec.getForStatementForCollection("i", compType.getInterfaceTypeName(), value) + "\n"; + } else { + statements += langSpec.getForStatementForCollection("i", wrapperType, value) + "\n"; + } + if (DataConstraintModel.typeTuple.isAncestorOf(compType) || DataConstraintModel.typePair.isAncestorOf(paramType) || DataConstraintModel.typeList.isAncestorOf(compType) || DataConstraintModel.typeMap.isAncestorOf(paramType)) { + statements += "\tform.param(\"" + paramName + "\", " + langSpec.getConstructorInvocation("ObjectMapper", null) + ".writeValueAsString(i))" + langSpec.getStatementDelimiter() + "\n"; // typeTuple: {"1.0":2.0}, typePair: {"left": 1.0, "right":2.0} + } else { + statements += "\tform.param(\"" + paramName + "\", i.toString())" + langSpec.getStatementDelimiter() + "\n"; + } + statements += langSpec.getEndForStatement("i") + "\n"; +// return "Entity entity = Entity.entity(" + paramName + ".toString(), MediaType.APPLICATION_JSON);"; + } else if (DataConstraintModel.typeTuple.isAncestorOf(paramType) || DataConstraintModel.typePair.isAncestorOf(paramType) || DataConstraintModel.typeMap.isAncestorOf(paramType)) { + // typeTuple: {"1.0":2.0}, typePair: {"left": 1.0, "right":2.0} + statements += "form.param(\"" + paramName + "\", " + langSpec.getConstructorInvocation("ObjectMapper", null) + ".writeValueAsString(" + value + "))" + langSpec.getStatementDelimiter() + "\n"; + } else { + statements += "form.param(\"" + paramName + "\", " + new JavaSpecific().getValueToStringExp(paramType.getImplementationTypeName(), value) + ")" + langSpec.getStatementDelimiter() + "\n"; + } + } + if (isFirstCall) { + statements += "Entity "; + } + statements += "entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED)" + langSpec.getStatementDelimiter(); + return statements; + } + + @Override + public String getHttpMethodCallStatement(String baseURL, String resourceName, String senderResName, String httpMethod) { + if (senderResName == null) { + return "client.target(\"" + baseURL + "\").path(\"/" + resourceName + "\").request()." + httpMethod + "(entity, String.class)" + langSpec.getStatementDelimiter(); + } else { + // For each source resource, a child resource is defined in the destination resource so that its state can be updated separately. + return "client.target(\"" + baseURL + "\").path(\"/" + resourceName + "/" + senderResName + "\").request()." + httpMethod + "(entity, String.class)" + langSpec.getStatementDelimiter(); + } + } + + @Override + public String getHttpMethodCallWithResponseStatement(String baseURL, String resourceName, String httpMethod, String responseTypeName) { + String responseShortTypeName = responseTypeName; + if (responseTypeName.contains("<")) { + responseShortTypeName = responseTypeName.substring(0, responseTypeName.indexOf("<")); + } + return "client.target(\"" + baseURL + "\").path(\"/" + resourceName + "\").request()." + httpMethod + "(" + responseShortTypeName + ".class)" + langSpec.getStatementDelimiter(); + } + + @Override + public void addJsonException(MethodDeclaration method) { + method.addThrow("JsonProcessingException"); + } + + @Override + public boolean hasJsonException(MethodDeclaration method) { + if (method.getThrows() == null || method.getThrows().getExceptions() == null) return false; + return method.getThrows().getExceptions().contains("JsonProcessingException"); + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/generators/RestApiSpecific.java b/AlgebraicDataflowArchitectureModel/src/generators/RestApiSpecific.java new file mode 100644 index 0000000..3ddf549 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/generators/RestApiSpecific.java @@ -0,0 +1,72 @@ +package generators; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map.Entry; + +import code.ast.Annotation; +import code.ast.CompilationUnit; +import code.ast.FieldDeclaration; +import code.ast.ImportDeclaration; +import code.ast.MethodDeclaration; +import code.ast.TypeDeclaration; +import code.ast.VariableDeclaration; +import models.algebra.Type; + +abstract public class RestApiSpecific implements IPlatformSpecific { + protected ILanguageSpecific langSpec = null; + + @Override + public boolean hasMain() { + return false; + } + + @Override + public boolean isMonolithic() { + return false; + } + + @Override + public boolean isDifferentTreesAsDifferentServices() { + return true; + } + + abstract public void addComponentAnnotations(TypeDeclaration component, String resourcePath); + + abstract public void addGetAnnotations(MethodDeclaration getMethod); + + abstract public void addPutAnnotations(MethodDeclaration putMethod); + + abstract public void addPostAnnotations(MethodDeclaration postMethod); + + abstract public void addDeleteAnnotations(MethodDeclaration deleteMethod); + + abstract public void addPathAnnotation(MethodDeclaration method, String resourcePath); + + abstract public void addPathParamAnnotation(VariableDeclaration var, String paramName); + + abstract public void addFormParamAnnotation(VariableDeclaration var, String paramName); + + abstract public void addPlatformSpecificImports(CompilationUnit cu); + + abstract public boolean hasHttpClientFieldDeclaration(TypeDeclaration component); + + abstract public void addHttpClientFieldDeclaration(TypeDeclaration component); + + abstract public String getConversionFromJsonString(String fromStrVarName, String toVarName, String toVarType); + + abstract public String getHttpMethodParamsConstructionStatement(String callerResourceName, List>> params, boolean isFirstCall); + + abstract public String getHttpMethodCallStatement(String baseURL, String resourceName, String senderResName, String httpMethod); + + abstract public String getHttpMethodCallWithResponseStatement(String baseURL, String resourceName, String httpMethod, String responseTypeName); + + abstract public void addJsonException(MethodDeclaration method); + + abstract public boolean hasJsonException(MethodDeclaration method); + + public String getBaseURL() { + return "http://localhost:8080"; + } + +} diff --git a/AlgebraicDataflowArchitectureModel/src/generators/StandaloneSpecific.java b/AlgebraicDataflowArchitectureModel/src/generators/StandaloneSpecific.java new file mode 100644 index 0000000..ce4acd5 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/generators/StandaloneSpecific.java @@ -0,0 +1,28 @@ +package generators; + +import java.util.List; + +import code.ast.Annotation; +import code.ast.CompilationUnit; +import code.ast.ImportDeclaration; +import code.ast.MethodDeclaration; +import code.ast.TypeDeclaration; +import code.ast.VariableDeclaration; + +public class StandaloneSpecific implements IPlatformSpecific { + + @Override + public boolean hasMain() { + return true; + } + + @Override + public boolean isMonolithic() { + return true; + } + + @Override + public boolean isDifferentTreesAsDifferentServices() { + return false; // meaningless + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/models/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/algebra/Term.java b/AlgebraicDataflowArchitectureModel/src/models/algebra/Term.java index ac41269..07993fa 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/algebra/Term.java +++ b/AlgebraicDataflowArchitectureModel/src/models/algebra/Term.java @@ -84,11 +84,13 @@ thisClass = thisClass.getSuperclass(); } for (int i = 0; i < children.size(); i++) { - HashMap terms = children.get(i).getSubTerms(clazz); - for (Entry term: terms.entrySet()) { - Position pos = term.getKey(); - pos.addHeadOrder(i); - subTerms.put(pos, term.getValue()); + if (children.get(i) != null) { + HashMap terms = children.get(i).getSubTerms(clazz); + for (Entry term: terms.entrySet()) { + Position pos = term.getKey(); + pos.addHeadOrder(i); + subTerms.put(pos, term.getValue()); + } } } return subTerms; @@ -128,6 +130,8 @@ @Override public Expression unify(Expression another) { if (another instanceof Variable) return (Expression) this.clone(); + if (this instanceof Constant) return (Expression) this.clone(); + if (another instanceof Constant) return (Expression) another.clone(); if (another instanceof Term) { Term anotherTerm = (Term) another; if (!symbol.equals(anotherTerm.symbol)) return null; @@ -257,7 +261,11 @@ public Object clone() { Term newTerm = new Term(symbol); for (Expression e: children) { - newTerm.addChild((Expression) e.clone()); + if (e != null) { + newTerm.addChild((Expression) e.clone()); + } else { + newTerm.addChild(null); + } } newTerm.type = type; return newTerm; @@ -280,7 +288,11 @@ String exp = symbol.toString() + "("; String delimiter = ""; for (Expression e: children) { - exp += (delimiter + e.toString()); + if (e != null) { + exp += (delimiter + e.toString()); + } else { + exp += (delimiter + "null"); + } delimiter = ","; } return exp + ")"; diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Channel.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Channel.java index af144c8..2bec47e 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Channel.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/Channel.java @@ -60,6 +60,7 @@ public void addChild(Channel child) { children.add(child); child.parent = this; + child.refleshOutside(); } public List getSelectors() { @@ -84,6 +85,15 @@ public void addSelector(Selector selector) { selectors.add(selector); } + + public List getAllSelectorVariables() { + List allSelectorVariables = new ArrayList<>(); + if (parent != null) allSelectorVariables.addAll(parent.getAllSelectorVariables()); + for (Selector sel: selectors) { + allSelectorVariables.add(sel.getExpression()); + } + return allSelectorVariables; + } public Set getChannelMembers() { return channelMembers; @@ -115,6 +125,28 @@ } } + public void refleshOutside() { + for (ChannelMember channelMember: channelMembers) { + if (getAllSelectors().size() > 0) { + Set params = new HashSet<>(); + for (Selector s: getAllSelectors()) { + params.add(s.getExpression()); + } + if (!params.containsAll(channelMember.getResource().getPathParams())) { + channelMember.setOutside(true); + } else { + channelMember.setOutside(false); + } + } else { + if (channelMember.getResource().getPathParams().size() == 0) { + channelMember.setOutside(false); + } else { + channelMember.setOutside(true); + } + } + } + } + public void removeChannelMember(ResourcePath res) { for (ChannelMember cm: channelMembers) { if (cm.getResource() == res) { diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/DataConstraintModel.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/DataConstraintModel.java index e44c50c..4a498b1 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/DataConstraintModel.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/DataConstraintModel.java @@ -1,6 +1,7 @@ package models.dataConstraintModel; import java.util.AbstractMap; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -76,6 +77,8 @@ return new Constant(Long.toString(Long.parseLong(sArg0) + Long.parseLong(sArg1)), typeLong); } else if (arg0.getType().equals(typeInt) || arg1.getType().equals(typeInt)) { return new Constant(Integer.toString(Integer.parseInt(sArg0) + Integer.parseInt(sArg1)), typeInt); + } else if (arg0.getType().equals(typeString) || arg1.getType().equals(typeString)) { + return new Constant((String) arg0.getValue() + (String) arg1.getValue(), typeString); } } if (sArg0.contains(Parser.DOT) || sArg1.contains(Parser.DOT)) { @@ -595,6 +598,21 @@ keySet.addAll(((JsonTerm) term).keySet()); return new Constant(Integer.toString(keySet.size()), typeInt); } + } else if (typeString.isAncestorOf(type)) { + int len = 0; + ArrayDeque strTerms = new ArrayDeque<>(); + strTerms.add(term); + while (strTerms.size() > 0) { + term = strTerms.poll(); + type = term.getType(); + if (term.getSymbol().equals(DataConstraintModel.add)) { + strTerms.add((Term) term.getChild(0)); + strTerms.add((Term) term.getChild(1)); + } else if (term instanceof Constant && typeString.isAncestorOf(type)) { + len += ((String) ((Constant) term).getValue()).length(); + } + } + return new Constant(Integer.toString(len), typeInt); } } return null; @@ -962,7 +980,7 @@ lookup.setSignature(new Type[] {null, typeMap, null}); addMember.setSignature(new Type[] {typeJson, typeJson, typeString, null}); dot.setSignature(new Type[] {null, typeJson, typeString}); - dotParam.setSignature(new Type[] {null, null, null}); + dotParam.setSignature(new Type[] {null, typeJson, null}); pi.setSignature(new Type[] {typeDouble}); E.setSignature(new Type[] {typeDouble}); sqrt.setSignature(new Type[] {typeDouble, typeDouble}); @@ -1114,6 +1132,10 @@ ResourcePath resourcePath = getResourcePath(path); if (resourcePath == null) return; resourcePaths.remove(resourcePath); + removeResourcePath(resourcePath); + } + + public void removeResourcePath(ResourcePath resourcePath) { for (Channel ch: channels.values()) { ch.removeChannelMember(resourcePath); } diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonAccessor.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonAccessor.java index e7cadb5..43e428c 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonAccessor.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonAccessor.java @@ -21,45 +21,89 @@ super(symbol); } - public Type getType() { - if (symbol.equals(DataConstraintModel.dotParam)) { - Type valueType = null; - if (getChild(1) instanceof Term) { - valueType = ((Term) getChild(1)).getType(); - } else if (getChild(1) instanceof Variable) { - valueType = ((Variable) getChild(1)).getType(); - } - if (valueType != null) return valueType; - } - return super.getType(); - } - @Override public Expression reduce() { Expression reducedTerm = super.reduce(); if (reducedTerm instanceof Term) { - if (symbol.equals(DataConstraintModel.dot) && getChildren().size() >= 2) { + 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); } - if (!json.getSymbol().equals(DataConstraintModel.addMember)) return null; - if (json.getChild(1).equals(key)) { - return json.getChild(2); + if (json.getSymbol().equals(DataConstraintModel.addMember) || json.getSymbol().equals(DataConstraintModel.set)) { + if (json.getChild(1).equals(key)) { + Expression value = json.getChild(2); + if (value == null) { + return new Constant(DataConstraintModel.null_); + } + return value; + } + if (json.getChild(0) == null + || (json.getChild(0) instanceof Term && + (((Term) json.getChild(0)).getSymbol().equals(DataConstraintModel.null_)) + || ((Term) json.getChild(0)).getSymbol().equals(DataConstraintModel.nil))) return null; + return getValue((Term) json.getChild(0), key); } - if (json.getChild(0) == null || json.getChild(0).equals(DataConstraintModel.nil)) return null; - return getValue((Term) json.getChild(0), key); + if (json.getSymbol().equals(DataConstraintModel.insert)) { + if (json.getChild(1).equals(key)) { + Expression value = json.getChild(2); + if (value == null) { + return new Constant(DataConstraintModel.null_); + } + return value; + } + if (json.getChild(0) == null + || (json.getChild(0) instanceof Term && + (((Term) json.getChild(0)).getSymbol().equals(DataConstraintModel.null_)) + || ((Term) json.getChild(0)).getSymbol().equals(DataConstraintModel.nil))) return null; + return getValue((Term) json.getChild(0), key); + } + return new Constant(DataConstraintModel.null_); } @Override @@ -69,15 +113,15 @@ int i = targetPos.removeHeadOrder(); Symbol[] inverseSymbols = symbol.getInverses(); if (i == 0) { - if (symbol.equals(DataConstraintModel.dot) && getChildren().size() >= 2) { + if (symbol == DataConstraintModel.dot && getChildren().size() >= 2) { // this term is `json.key`. Expression expJson = getChild(0); Expression expKey = getChild(1); - JsonType jsonType = null; + Type jsonType = null; if (expJson instanceof Variable) { - jsonType = (JsonType) ((Variable) expJson).getType(); + jsonType = ((Variable) expJson).getType(); } else if (expJson instanceof Term) { - jsonType = (JsonType) ((Term) expJson).getType(); + jsonType = ((Term) expJson).getType(); } String keyName = null; if (expKey instanceof Constant) { @@ -89,8 +133,11 @@ Set keySet = new HashSet<>(); if (jsonType == null || jsonType == DataConstraintModel.typeJson) { keySet.add(keyName); - } else { - keySet.addAll(jsonType.getKeys()); + } else if (jsonType instanceof JsonType) { + keySet.addAll(((JsonType) jsonType).getKeys()); + if (keySet.size() == 0) { + keySet.add(keyName); + } } for (String key: keySet) { Term addMemberTerm = new Term(DataConstraintModel.addMember); // addMember(jsonTerm, key, v) @@ -106,7 +153,7 @@ LambdaAbstraction lambdaAbstraction = new LambdaAbstraction(var, jsonTerm); // v -> addMember(jsonTerm, key, v) inverseSymbols = new Symbol[] { lambdaAbstraction }; } - } else if (symbol.equals(DataConstraintModel.dotParam) && getChildren().size() >= 2) { + } else if (symbol == DataConstraintModel.dotParam && getChildren().size() >= 2) { // this term is `json.{param}`. Expression expListOrMap = getChild(0); Expression expKey = getChild(1); @@ -118,9 +165,9 @@ } Type keyType = null; if (expKey instanceof Variable) { - keyType = (JsonType) ((Variable) expKey).getType(); + keyType = ((Variable) expKey).getType(); } else if (expKey instanceof Term) { - keyType = (JsonType) ((Term) expKey).getType(); + keyType = ((Term) expKey).getType(); } if (jsonType != null && keyType != null) { if (DataConstraintModel.typeList.isAncestorOf(jsonType) || keyType.equals(DataConstraintModel.typeInt)) { diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonTerm.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonTerm.java index 5762940..3998afa 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonTerm.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/JsonTerm.java @@ -1,6 +1,7 @@ package models.dataConstraintModel; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -8,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 { @@ -40,6 +42,35 @@ if (keyToIndex.get(key.getValue()) == null) return null; return getChild(keyToIndex.get(key.getValue())); } + + public Expression get(Variable key) { + if (keyToIndex.get(key.getName()) == null) return null; + return getChild(keyToIndex.get(key.getName())); + } + + @Override + public Expression unify(Expression another) { + if (another instanceof JsonTerm) { + JsonTerm anotherTerm = (JsonTerm) another; + JsonTerm unifiedTerm = new JsonTerm(); + Set keySet = new HashSet<>(); + keySet.addAll(this.keySet()); + keySet.addAll(anotherTerm.keySet()); + for (String key: keySet) { + if (this.keySet().contains(key)) { + if (anotherTerm.keySet().contains(key)) { + unifiedTerm.addMember(key, this.get(key).unify(anotherTerm.get(key))); + } else { + unifiedTerm.addMember(key, this.get(key)); + } + } else { + unifiedTerm.addMember(key, anotherTerm.get(key)); + } + } + return unifiedTerm; + } + return this; + } @Override public Object clone() { diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ListTerm.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ListTerm.java index 91dd47d..afc4212 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ListTerm.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ListTerm.java @@ -1,7 +1,5 @@ package models.dataConstraintModel; -import java.util.HashMap; - import models.algebra.Expression; import models.algebra.Symbol; import models.algebra.Term; @@ -26,6 +24,20 @@ } @Override + public Expression unify(Expression another) { + if (another instanceof ListTerm) { + ListTerm anotherTerm = (ListTerm) another; + if (children.size() != anotherTerm.children.size()) return null; + ListTerm unifiedTerm = new ListTerm(); + for (int i = 0; i < children.size(); i++) { + unifiedTerm.addChild(children.get(i).unify(anotherTerm.children.get(i))); + } + return unifiedTerm; + } + return this; + } + + @Override public Object clone() { ListTerm newTerm = new ListTerm(); for (Expression e: children) { diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/MapTerm.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/MapTerm.java index 0badbd1..18f2c9b 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/MapTerm.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/MapTerm.java @@ -1,6 +1,7 @@ package models.dataConstraintModel; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -41,6 +42,30 @@ } @Override + public Expression unify(Expression another) { + if (another instanceof MapTerm) { + MapTerm anotherTerm = (MapTerm) another; + MapTerm unifiedTerm = new MapTerm(); + Set keySet = new HashSet<>(); + keySet.addAll(this.keySet()); + keySet.addAll(anotherTerm.keySet()); + for (String key: keySet) { + if (this.keySet().contains(key)) { + if (anotherTerm.keySet().contains(key)) { + unifiedTerm.insert(key, this.get(key).unify(anotherTerm.get(key))); + } else { + unifiedTerm.insert(key, this.get(key)); + } + } else { + unifiedTerm.insert(key, anotherTerm.get(key)); + } + } + return unifiedTerm; + } + return this; + } + + @Override public Object clone() { MapTerm newTerm = new MapTerm(); for (Expression e: children) { diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourcePath.java b/AlgebraicDataflowArchitectureModel/src/models/dataConstraintModel/ResourcePath.java index 293d148..bfd163b 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; @@ -175,7 +186,25 @@ return curPath; } + public boolean isAncestorOf(ResourcePath another) { + Set ancestors = new HashSet<>(); + while (another != null) { + ancestors.add(another); + another = another.getParent(); + } + return ancestors.contains(this); + } + public String toString() { return resourceHierarchy.toString(pathParams); } + + public String toResourcePath() { + List params = new ArrayList<>(); + String[] sideEffects = new String[] {""}; + for (Map.Entry param: pathParams) { + params.add("{" + param.getKey().toString() + "}"); + } + return resourceHierarchy.toResourcePath(params); + } } diff --git a/AlgebraicDataflowArchitectureModel/src/models/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 46bf393..6ab539a 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferChannel.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/DataTransferChannel.java @@ -22,6 +22,7 @@ import models.dataConstraintModel.ChannelMember; import models.dataConstraintModel.ResourcePath; import models.dataConstraintModel.Selector; +import parser.Parser; public class DataTransferChannel extends Channel { protected Set inputChannelMembers = null; @@ -53,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; } @@ -215,6 +225,18 @@ return deriveUpdateExpressionOf(targetMember, stateAccessor, null); } + /** + * Derive the state update calculation of the target channel member with a given resource push/pull state accessor. + * @param targetMember a channel member whose state is to be updated + * @param stateAccessor a push/pull resource state accessor + * @param inputResourceToStateAccessor push/pull resource state accessors (if null, stateAccessor is used.) + * @return the derived update calculation and the unified message + * @throws ParameterizedIdentifierIsFutureWork + * @throws ResolvingMultipleDefinitionIsFutureWork + * @throws InvalidMessage + * @throws UnificationFailed + * @throws ValueUndefined + */ public Map.Entry deriveUpdateExpressionOf(ChannelMember targetMember, IResourceStateAccessor stateAccessor, Map inputResourceToStateAccessor) throws ParameterizedIdentifierIsFutureWork, ResolvingMultipleDefinitionIsFutureWork, InvalidMessage, UnificationFailed, ValueUndefined { if (!getOutputChannelMembers().contains(targetMember)) return null; @@ -268,12 +290,24 @@ */ 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) + /** + * Fill outside resource paths with a given resource push/pull state accessor. + * @param targetMember a channel member whose state is to be updated + * @param stateAccessor a push/pull resource state accessor + * @param inputResourceToStateAccessor push/pull resource state accessors (if null, stateAccessor is used.) + * @return map from a depending channel member to the pair of the filled resource path and the set of the depended channel members + * @throws ParameterizedIdentifierIsFutureWork + * @throws ResolvingMultipleDefinitionIsFutureWork + * @throws InvalidMessage + * @throws UnificationFailed + * @throws ValueUndefined + */ + public Map.Entry>>, Term> fillOutsideResourcePaths(ChannelMember targetMember, IResourceStateAccessor stateAccessor, Map inputResourceToStateAccessor) throws ParameterizedIdentifierIsFutureWork, ResolvingMultipleDefinitionIsFutureWork, InvalidMessage, UnificationFailed, ValueUndefined { - if (!getOutputChannelMembers().contains(targetMember)) return null; Map>> resourcePaths = new HashMap<>(); // Calculate unified message constraints from input and reference state transitions @@ -340,9 +374,14 @@ } } } - return resourcePaths; + return new AbstractMap.SimpleEntry<>(resourcePaths, unifiedMessage); } + /** + * Get the dependency from the values of the 'path parameters' of a channel member to the state values of other channel members. + * + * @return a map from a depending channel member to depended channel members + */ public Map> getMemberDependency() { Map> dependency = new HashMap<>(); @@ -358,7 +397,7 @@ for (ChannelMember otherCm: substitutedPositionsInMessage.keySet()) { for (Position otherPos: substitutedPositionsInMessage.get(otherCm)) { for (Position thisPos: dependingVarPosInMessage) { - if (thisPos.isAncestorOf(otherPos)) { + if (thisPos.isAncestorOf(otherPos) && otherCm != cm) { dependingChannelMembers.add(otherCm); break; } @@ -376,7 +415,7 @@ for (ChannelMember otherCm: substitutedPositionsInMessage.keySet()) { for (Position otherPos: substitutedPositionsInMessage.get(otherCm)) { for (Position thisPos: dependingVarPosInMessage) { - if (thisPos.isAncestorOf(otherPos)) { + if (thisPos.isAncestorOf(otherPos) && otherCm != cm) { dependingChannelMembers.add(otherCm); break; } @@ -394,7 +433,7 @@ for (ChannelMember otherCm: substitutedPositionsInMessage.keySet()) { for (Position otherPos: substitutedPositionsInMessage.get(otherCm)) { for (Position thisPos: dependingVarPosInMessage) { - if (thisPos.isAncestorOf(otherPos)) { + if (thisPos.isAncestorOf(otherPos) && otherCm != cm) { dependingChannelMembers.add(otherCm); break; } @@ -527,7 +566,7 @@ return channelMembersMessageDependsOn; } - public ResourcePath fillOutsideResourcePath(ResourcePath resource, Term unifiedMessage, Expression messageTerm, Set dependingVarPosInMessage) + public ResourcePath fillOutsideResourcePath(ResourcePath resource, Expression unifiedMessage, Expression messageTerm, Set dependingVarPosInMessage) throws ResolvingMultipleDefinitionIsFutureWork { ResourcePath filledResourcePath = new ResourcePath(resource); @@ -549,8 +588,10 @@ Expression pathValue = dstParams.get(i).getValue(); if (pathParam instanceof Variable) { if (pathValue == null) { - dstParams.set(i, new AbstractMap.SimpleEntry<>(bindings.get((Variable) pathParam).getValue(), null)); // Replace a path parameter with a value in the unified message. - dependingVarPosInMessage.add(bindings.get((Variable) pathParam).getKey()); // The position of the replaced variable in the message. + if (bindings.get((Variable) pathParam) != null) { + filledResourcePath.replacePathParam(i, bindings.get((Variable) pathParam).getValue(), null); // Replace a path parameter with a value in the unified message. + dependingVarPosInMessage.add(bindings.get((Variable) pathParam).getKey()); // The position of the replaced variable in the message. + } } else { // If the path parameter has a constraint. if (pathValue instanceof Term) { @@ -564,7 +605,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) { @@ -578,7 +619,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; @@ -615,9 +656,13 @@ public String toString() { String channelSource = ""; if (isNative()) { - channelSource += "native "; + channelSource += Parser.NATIVE + " "; } - channelSource += "channel " + getChannelName(); + if (parent == null) { + channelSource += Parser.CHANNEL + " " + getChannelName(); + } else { + channelSource += Parser.SUB_CHANNEL + " " + getChannelName(); + } if (getSelectors().size() > 0) { channelSource += "("; String delimitor = ""; @@ -629,13 +674,16 @@ } channelSource += " {\n"; for (ChannelMember inputMember: inputChannelMembers) { - channelSource += "\t in " + inputMember + "\n"; + channelSource += "\t " + Parser.IN + " " + inputMember + "\n"; } for (ChannelMember refMember: referenceChannelMembers) { - channelSource += "\t ref " + refMember + "\n"; + channelSource += "\t " + Parser.REF + " " + refMember + "\n"; } for (ChannelMember outputMember: outputChannelMembers) { - channelSource += "\t out " + outputMember + "\n"; + channelSource += "\t " + Parser.OUT + " " + outputMember + "\n"; + } + for (Channel childCh: getChildren()) { + channelSource += childCh.toString(); } channelSource += "}\n"; return channelSource; 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/models/dataFlowModel/PushPullAttribute.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/PushPullAttribute.java index 598894a..ad0da87 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/PushPullAttribute.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/PushPullAttribute.java @@ -20,6 +20,10 @@ public List getOptions() { return options; } + + public PushPullValue getSelectedOption() { + return options.get(0); + } public void setOptions(List options) { this.options = options; @@ -37,6 +41,13 @@ this.options.retainAll(options); } + public boolean selectOption(PushPullValue option) { + if (!options.contains(option)) return false; + options.remove(option); + options.add(0, option); + return true; + } + public String[] getOptionStrings() { String[] optionString = new String[options.size()]; for (int i = 0; i < options.size(); i++) { diff --git a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ResourceNode.java b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ResourceNode.java index d382534..6c56bcb 100644 --- a/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ResourceNode.java +++ b/AlgebraicDataflowArchitectureModel/src/models/dataFlowModel/ResourceNode.java @@ -21,13 +21,26 @@ protected ResourceNode parent = null; protected Set children = null; protected ResourceHierarchy resourceHierarchy = null; + protected ResourcePath primaryResourcePath = null; // for temporal use at the modeling stage protected Map inSide = null; protected Map outSide = null; protected List selectors = null; + + public ResourceNode(ResourceNode parent, ResourcePath primaryResourcePath) { + this.parent = parent; + this.children = new HashSet<>(); + this.inSide = new HashMap<>(); + this.outSide = new HashMap<>(); + this.resourceHierarchy = primaryResourcePath.getResourceHierarchy(); + this.primaryResourcePath = primaryResourcePath; + this.selectors = new ArrayList<>(); + if (resourceHierarchy.getNumParameters() > 0) { + List pathParams = primaryResourcePath.getPathParams(); + selectors.add(new Selector(pathParams.get(pathParams.size() - 1))); + } + } - public ResourceNode(ResourceNode parent, - ResourcePath outSideResource, - DataTransferChannel outSideChannel) { + public ResourceNode(ResourceNode parent, ResourcePath outSideResource, DataTransferChannel outSideChannel) { this.parent = parent; this.children = new HashSet<>(); this.inSide = new HashMap<>(); @@ -45,9 +58,7 @@ } } - public ResourceNode(ResourceNode parent, - ResourcePath resource, - DataTransferChannel channel, boolean isInside) { + public ResourceNode(ResourceNode parent, ResourcePath resource, DataTransferChannel channel, boolean isInside) { this.parent = parent; this.children = new HashSet<>(); this.inSide = new HashMap<>(); @@ -69,9 +80,7 @@ } } - public ResourceNode(ResourceNode parent, - Map outSide, - Map inSide) { + public ResourceNode(ResourceNode parent, Map outSide, Map inSide) { this.parent = parent; this.children = new HashSet<>(); this.inSide = inSide; @@ -124,6 +133,7 @@ } public ResourcePath getPrimaryResourcePath() { + if (primaryResourcePath != null) return primaryResourcePath; if (outSide.size() > 0) return outSide.values().iterator().next(); return inSide.values().iterator().next(); } @@ -136,6 +146,10 @@ return inSide.get(channel); } + public Set getInSideChannels() { + return inSide.keySet(); + } + public Collection getOutSideResources() { return outSide.values(); } @@ -144,15 +158,17 @@ return outSide.get(channel); } - public Set getOutSideChannel() { + public Set getOutSideChannels() { return outSide.keySet(); } public void addInSideResource(DataTransferChannel channel, ResourcePath inResource) { + primaryResourcePath = null; inSide.put(channel, inResource); } public void addOutSideResource(DataTransferChannel channel, ResourcePath outResource) { + primaryResourcePath = null; outSide.put(channel, outResource); } diff --git a/AlgebraicDataflowArchitectureModel/src/parser/Parser.java b/AlgebraicDataflowArchitectureModel/src/parser/Parser.java index 60f5a55..951bcb0 100644 --- a/AlgebraicDataflowArchitectureModel/src/parser/Parser.java +++ b/AlgebraicDataflowArchitectureModel/src/parser/Parser.java @@ -15,6 +15,7 @@ import models.dataConstraintModel.ChannelMember; import models.dataConstraintModel.JsonAccessor; import models.dataConstraintModel.JsonTerm; +import models.dataConstraintModel.ListTerm; import models.dataConstraintModel.ResourceHierarchy; import models.dataConstraintModel.ResourcePath; import models.dataConstraintModel.StateTransition; @@ -46,7 +47,7 @@ public static final String IN = "in"; public static final String OUT = "out"; public static final String REF = "ref"; - public static final String SUB_CHANNEL = "sub"; + public static final String SUB_CHANNEL = "for"; public static final String NATIVE = "native"; public static final String LEFT_CURLY_BRACKET = "{"; public static final String RIGHT_CURLY_BRACKET = "}"; @@ -119,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); @@ -433,7 +434,7 @@ } else if (operator.equals(DOT)) { // json accessor while (operator.equals(DOT)) { - Expression exp = expressions.remove(0); + Expression exp = expressions.remove(expressions.size() - 1); stream.next(); // DOT if (stream.checkNext() == null) throw new WrongJsonExpression(stream.getLine()); String literalOrLeftCurlyBracket = stream.next(); @@ -454,7 +455,7 @@ paramType = ((Term) paramTerm).getType(); } Term term = null; - if (paramType != null && DataTransferModel.typeInt.isAncestorOf(paramType)) { + if (literalOrLeftCurlyBracket.equals(LEFT_CURLY_BRACKET)) { term = new JsonAccessor(DataTransferModel.dotParam); } else { term = new JsonAccessor(DataTransferModel.dot); @@ -464,6 +465,17 @@ expressions.add(term); operator = stream.checkNext(); if (operator == null) break; + if (operator.equals(COLON)) { + // when a type is specified. + stream.next(); + String typeName = stream.next(); + Type type = model.getType(typeName); + if (type == null) { + type = new Type(typeName, typeName); + } + term.setType(type); + break; + } } if (operator == null) { break; @@ -623,21 +635,18 @@ } private Expression parseListTerm(TokenStream stream2, DataTransferModel model) throws ExpectedRightBracket, WrongJsonExpression, ExpectedColon, ExpectedDoubleQuotation { - Term listTerm = new Constant(DataTransferModel.nil); + ListTerm listTerm = new ListTerm(); listTerm.setType(DataTransferModel.typeList); while (stream.checkNext() != null && !stream.checkNext().equals(RIGHT_SQUARE_BRACKET)) { Expression element = parseTerm(stream, model); - Term nextTerm = new Term(DataTransferModel.cons); - nextTerm.addChild(element); - nextTerm.addChild(listTerm); - listTerm = nextTerm; + listTerm.addChild(element); if (stream.checkNext() == null || !stream.checkNext().equals(COMMA)) break; String comma = stream.next(); } return listTerm; } - private Variable parseVariable(TokenStream stream, DataTransferModel model, String symbolName) { + public Variable parseVariable(TokenStream stream, DataTransferModel model, String symbolName) { Variable var; if (stream.checkNext() != null && stream.checkNext().equals(COLON)) { // when a type is specified. diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/ChannelState.java b/AlgebraicDataflowArchitectureModel/src/simulator/ChannelState.java index ce4424e..4178d73 100644 --- a/AlgebraicDataflowArchitectureModel/src/simulator/ChannelState.java +++ b/AlgebraicDataflowArchitectureModel/src/simulator/ChannelState.java @@ -10,6 +10,12 @@ import models.dataConstraintModel.Selector; import models.dataFlowModel.DataTransferChannel; +/** + * State of a channel, which represents the map from the values of the depended channel selectors to those of the depending channel selectors + * + * @author Nitta + * + */ public class ChannelState implements Cloneable { private DataTransferChannel channel; private ReferenceStructure referenceStructure; @@ -24,7 +30,7 @@ public Map getDependingParamAndValues(List channelValues) { if (referenceStructure == null) { - return null; + return new HashMap<>(); } return referenceStructure.getDependingParamAndValues(channelValues); } @@ -45,6 +51,13 @@ return referenceStructure.getDependedChannelSelectorValues(dependingVarToVal); } + /** + * Add a value of a depending channel selector + * + * @param channelValues the values of depended channel selectors + * @param dependingVariable a depending channel selector + * @param itsValue its new value + */ public void addDependingParamAndValue(List channelValues, Expression dependingVariable, Expression itsValue) { if (referenceStructure == null) { referenceStructure = new ReferenceStructure(); @@ -54,7 +67,7 @@ public Object clone() { ChannelState newChannelState = new ChannelState(channel); - newChannelState.referenceStructure = (ReferenceStructure) referenceStructure.clone(); + if (referenceStructure != null) newChannelState.referenceStructure = (ReferenceStructure) referenceStructure.clone(); return newChannelState; } @@ -72,7 +85,7 @@ if (subStructure.getDependentParamAndValues() == null) { subStructure = subStructure.getReferenceStructure(chVal); } else { - return null; + return new HashMap<>(); } } return subStructure.getDependentParamAndValues(); diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/Event.java b/AlgebraicDataflowArchitectureModel/src/simulator/Event.java index f263fe9..9cc7e6a 100644 --- a/AlgebraicDataflowArchitectureModel/src/simulator/Event.java +++ b/AlgebraicDataflowArchitectureModel/src/simulator/Event.java @@ -14,6 +14,8 @@ import models.algebra.Position; import models.algebra.Term; import models.algebra.UnificationFailed; +import models.algebra.Variable; +import models.dataConstraintModel.Channel; import models.dataConstraintModel.ChannelMember; import models.dataConstraintModel.ResourcePath; import models.dataConstraintModel.Selector; @@ -21,6 +23,12 @@ import models.dataFlowModel.DataTransferChannel.IResourceStateAccessor; import models.dataFlowModel.ResolvingMultipleDefinitionIsFutureWork; +/** + * Event occurred at a channel + * + * @author Nitta + * + */ public class Event { private DataTransferChannel channel; private Expression message; @@ -31,10 +39,18 @@ private List> channelSelectorAndValues = new ArrayList<>(); private Map dependingParameters = new HashMap<>(); private Set outputResources = new HashSet<>(); - private Set succEvents = new HashSet<>(); - private Map> channelSelectorToInputResourcePathParam = new HashMap<>(); - private Map> channelSelectorToOutputResourcePathParam = new HashMap<>(); + private Set childEvents = new HashSet<>(); + private Map>>> channelSelectorToInputOrReferenceResourcePathParam = new HashMap<>(); + private Map>>> channelSelectorToOutputResourcePathParam = new HashMap<>(); + /** + * Constructor for an input event channel + * + * @param channel an input event channel + * @param message an message for the channel + * @param outputResPath an output side resource path of the channel + * @param outputResource an output side resource of the channel + */ public Event(DataTransferChannel channel, Expression message, ResourcePath outputResPath, Resource outputResource) { this.channel = channel; this.message = message; @@ -42,16 +58,16 @@ this.outputResources.add(outputResource); connectChannelSelectorAndPathParameters(); - // Extract channel parameters from the output resource. + // Extract the values of the channel selectors from the output resource. List channelSelectors = channel.getAllSelectors(); for (Selector sel: channelSelectors) { - int paramIdx = channelSelectorToOutputResourcePathParam.get(sel).get(outputResPath); + Map.Entry> paramIdxAndPos = channelSelectorToOutputResourcePathParam.get(sel).get(outputResPath); Resource ancestor = outputResource; int idx = outputResPath.getPathParams().size(); while (ancestor != null) { if (ancestor.getResourceHierarchy().getNumParameters() > 0) { idx--; - if (idx == paramIdx) break; + if (idx == paramIdxAndPos.getKey()) break; } ancestor = ancestor.getParent(); } @@ -59,6 +75,13 @@ } } + /** + * Constructor for a non-event channel + * + * @param channel a non-event channel + * @param inputResPath an input side resource path of the channel + * @param inputResource an input side resource of the channel + */ public Event(DataTransferChannel channel, ResourcePath inputResPath, Resource inputResource) { this.channel = channel; this.isInput = false; @@ -66,18 +89,18 @@ this.inputResource = inputResource; connectChannelSelectorAndPathParameters(); - // Extract channel parameters from the input resource. + // Extract the values of the channel selectors from the input resource. List channelSelectors = channel.getAllSelectors(); for (Selector sel: channelSelectors) { if (inputResPath != null) { - Integer paramIdx = channelSelectorToInputResourcePathParam.get(sel).get(inputResPath); - if (paramIdx != null) { + Map.Entry> paramIdxAndPos = channelSelectorToInputOrReferenceResourcePathParam.get(sel).get(inputResPath); + if (paramIdxAndPos != null) { Resource ancestor = inputResource; int idx = inputResPath.getPathParams().size(); while (ancestor != null) { if (ancestor.getResourceHierarchy().getNumParameters() > 0) { idx--; - if (idx == paramIdx) break; + if (idx == paramIdxAndPos.getKey()) break; } ancestor = ancestor.getParent(); } @@ -87,6 +110,15 @@ } } + /** + * Constructor for a non-event channel + * + * @param channel a non-event channel + * @param inputResPath an input side resource path of the channel + * @param inputResource an input side resource of the channel + * @param channelSelectorValues the values of depended channel selectors + * @param dependingVarToVal the values of depending channel selectors + */ public Event(DataTransferChannel channel, ResourcePath inputResPath, Resource inputResource, List channelSelectorValues, Map dependingVarToVal) { this.channel = channel; this.isInput = false; @@ -94,7 +126,7 @@ this.inputResource = inputResource; connectChannelSelectorAndPathParameters(); - // Extract channel parameters from the input resource. + // Extract the values of the channel selectors from channelSelectorValues. List channelSelectors = channel.getAllSelectors(); for (int i = 0; i < channelSelectors.size(); i++) { Selector sel = channelSelectors.get(i); @@ -106,26 +138,54 @@ private void connectChannelSelectorAndPathParameters() { List channelSelectors = channel.getAllSelectors(); for (Selector sel: channelSelectors) { - for (ResourcePath resPath: channel.getInputResources()) { - int paramIdx = resPath.getPathParams().indexOf(sel.getExpression()); - if (paramIdx >= 0) { - Map pathToIdx = channelSelectorToInputResourcePathParam.get(sel); - if (pathToIdx == null) { - pathToIdx = new HashMap<>(); - channelSelectorToInputResourcePathParam.put(sel, pathToIdx); + Set inputAndReferenceResPaths = new HashSet<>(channel.getInputResources()); + inputAndReferenceResPaths.addAll(channel.getReferenceResources()); + for (ResourcePath resPath: inputAndReferenceResPaths) { + for (int paramIdx = 0; paramIdx < resPath.getPathParams().size(); paramIdx++) { + Expression pathParam = resPath.getPathParams().get(paramIdx); + if (pathParam.contains(sel.getExpression())) { + for (Map.Entry posAndVar: pathParam.getVariables().entrySet()) { + Position p = posAndVar.getKey(); + Variable v = posAndVar.getValue(); + if (v.equals(sel.getExpression())) { + Map>> pathToIdxAndPos = channelSelectorToInputOrReferenceResourcePathParam.get(sel); + if (pathToIdxAndPos == null) { + pathToIdxAndPos = new HashMap<>(); + channelSelectorToInputOrReferenceResourcePathParam.put(sel, pathToIdxAndPos); + } + Map.Entry> idxAndPos = pathToIdxAndPos.get(resPath); + if (idxAndPos == null) { + idxAndPos = new AbstractMap.SimpleEntry<>(paramIdx, new HashSet<>()); + pathToIdxAndPos.put(resPath, idxAndPos); + } + idxAndPos.getValue().add(p); + } + } } - pathToIdx.put(resPath, paramIdx); } } for (ResourcePath resPath: channel.getOutputResources()) { - int paramIdx = resPath.getPathParams().indexOf(sel.getExpression()); - if (paramIdx >= 0) { - Map pathToIdx = channelSelectorToOutputResourcePathParam.get(sel); - if (pathToIdx == null) { - pathToIdx = new HashMap<>(); - channelSelectorToOutputResourcePathParam.put(sel, pathToIdx); + for (int paramIdx = 0; paramIdx < resPath.getPathParams().size(); paramIdx++) { + Expression pathParam = resPath.getPathParams().get(paramIdx); + if (pathParam.contains(sel.getExpression())) { + for (Map.Entry posAndVar: pathParam.getVariables().entrySet()) { + Position p = posAndVar.getKey(); + Variable v = posAndVar.getValue(); + if (v.equals(sel.getExpression())) { + Map>> pathToIdxAndPos = channelSelectorToOutputResourcePathParam.get(sel); + if (pathToIdxAndPos == null) { + pathToIdxAndPos = new HashMap<>(); + channelSelectorToOutputResourcePathParam.put(sel, pathToIdxAndPos); + } + Map.Entry> idxAndPos = pathToIdxAndPos.get(resPath); + if (idxAndPos == null) { + idxAndPos = new AbstractMap.SimpleEntry<>(paramIdx, new HashSet<>()); + pathToIdxAndPos.put(resPath, idxAndPos); + } + idxAndPos.getValue().add(p); + } + } } - pathToIdx.put(resPath, paramIdx); } } } @@ -170,8 +230,39 @@ return dependingParameters; } + public void addChild(Event child) { + childEvents.add(child); + } - public Term updateDependingParameters(IResourceStateValueProvider resourceStateValueProvider) { + public Set getChildren() { + return childEvents; + } + + /** + * Construct channel message, collect descendant events of this event and update the values of the depending channel selectors + * + * @param resourceStateValueProvider a resourceStateValueProvider to provide current and next resource states + * @return calculated message constraint by this event + */ + public Expression constructMessageAndDescendantEvents(IResourceStateValueProvider resourceStateValueProvider, boolean doesUpdateDependingParameters) { + + try { + Map> substitutedPositionsInMessageFromChannels = new HashMap<>(); + return constructMessageAndDesdendantEvents(resourceStateValueProvider, substitutedPositionsInMessageFromChannels, doesUpdateDependingParameters); + } catch (ResolvingMultipleDefinitionIsFutureWork | InvalidMessage | UnificationFailed e) { + e.printStackTrace(); + } + return null; + } + + private Expression constructMessageAndDesdendantEvents(IResourceStateValueProvider resourceStateValueProvider, + Map> substitutedPositionsInMessageFromChannels, boolean doesUpdateDependingParameters) + throws InvalidMessage, ResolvingMultipleDefinitionIsFutureWork, UnificationFailed { + Expression unifiedMessage = null; + Expression messageConstraint = null; + if (message != null) { + unifiedMessage = (Term) message.clone(); + } IResourceStateAccessor resouceStateAccessor = new IResourceStateAccessor() { @Override public Expression getCurrentStateAccessorFor(ChannelMember target, ChannelMember from) { @@ -191,42 +282,36 @@ return resourceStateValueProvider.getCurrentStateValueOf(resId); } }; - - try { - Map> substitutedPositionsInMessageFromChannels = new HashMap<>(); - Term unifiedMessage = null; - - // Calculate message constraints from leaf channel members on the channel member dependency graph. - Map> dependency = channel.getMemberDependency(); - if (dependency.size() == 0) { - // No channel member dependency. - Expression messageConstraint = null; - for (ChannelMember channelMember: channel.getInputChannelMembers()) { - // Calculate message constraint from an input state transition - messageConstraint = channel.calcMessageConstraintForInputMember(channelMember, null, resouceStateAccessor, null, substitutedPositionsInMessageFromChannels); + + // 1. Calculate message constraints from leaf channel members on the 'channel member dependency graph'. + Map> dependency = channel.getMemberDependency(); + if (dependency.size() == 0) { + // No channel member dependency. + for (ChannelMember channelMember: channel.getInputChannelMembers()) { + // Calculate message constraint from an input state transition + messageConstraint = channel.calcMessageConstraintForInputMember(channelMember, null, resouceStateAccessor, null, substitutedPositionsInMessageFromChannels); + if (unifiedMessage == null) { + unifiedMessage = messageConstraint; + } else { + unifiedMessage = unifiedMessage.unify(messageConstraint); if (unifiedMessage == null) { - unifiedMessage = (Term) messageConstraint; - } else { - unifiedMessage = (Term) unifiedMessage.unify(messageConstraint); - if (unifiedMessage == null) { - throw new UnificationFailed(); - } + throw new UnificationFailed(); } } - for (ChannelMember channelMember: channel.getReferenceChannelMembers()) { - // Calculate message constraint from a reference state transition - messageConstraint = channel.calcMessageConstraintForReferenceMember(channelMember, null, resouceStateAccessor, null, substitutedPositionsInMessageFromChannels); - if (unifiedMessage == null) { - unifiedMessage = (Term) messageConstraint; - } else { - unifiedMessage = (Term) unifiedMessage.unify(messageConstraint); - if (unifiedMessage == null) { - throw new UnificationFailed(); - } - } - } - return unifiedMessage; } + for (ChannelMember channelMember: channel.getReferenceChannelMembers()) { + // Calculate message constraint from a reference state transition + messageConstraint = channel.calcMessageConstraintForReferenceMember(channelMember, null, resouceStateAccessor, null, substitutedPositionsInMessageFromChannels); + if (unifiedMessage == null) { + unifiedMessage = messageConstraint; + } else { + unifiedMessage = unifiedMessage.unify(messageConstraint); + if (unifiedMessage == null) { + throw new UnificationFailed(); + } + } + } + } else { Set toResolve = new HashSet<>(); Set resolved = new HashSet<>(); for (Set depended: dependency.values()) { @@ -235,9 +320,8 @@ for (ChannelMember depending: dependency.keySet()) { toResolve.remove(depending); } - Expression messageConstraint = null; if ((messageConstraint = getMessage()) instanceof Term) { - unifiedMessage = (Term) messageConstraint; + unifiedMessage = messageConstraint; } for (ChannelMember leafMember: toResolve) { if (channel.getInputChannelMembers().contains(leafMember)) { @@ -248,9 +332,9 @@ messageConstraint = channel.calcMessageConstraintForReferenceMember(leafMember, null, resouceStateAccessor, null, substitutedPositionsInMessageFromChannels); } if (unifiedMessage == null) { - unifiedMessage = (Term) messageConstraint; + unifiedMessage = messageConstraint; } else { - unifiedMessage = (Term) unifiedMessage.unify(messageConstraint); + unifiedMessage = unifiedMessage.unify(messageConstraint); if (unifiedMessage == null) { throw new UnificationFailed(); } @@ -259,7 +343,9 @@ resolved.addAll(toResolve); toResolve.clear(); + // 2. Calculate message constraints from remaining members on the channel member dependency graph. for (;;) { + // Identify the channel members to resolve next. for (Map.Entry> dependEnt: dependency.entrySet()) { ChannelMember dependingMem = dependEnt.getKey(); Set dependedMems = dependEnt.getValue(); @@ -270,27 +356,22 @@ if (toResolve.size() == 0) break; for (ChannelMember dependingMem: toResolve) { // Fill the path parameters of the resource path of a depending channel member. - Set dependingVarPosInMessage = new HashSet<>(); - ResourcePath filledResPath = channel.fillOutsideResourcePath(dependingMem.getResource(), unifiedMessage, dependingMem.getStateTransition().getMessageExpression(), dependingVarPosInMessage); - ResourcePath unfilledResPath = dependingMem.getResource(); - for (int i = 0; i < unfilledResPath.getPathParamsAndConstraints().size(); i++) { - Expression var = unfilledResPath.getPathParamsAndConstraints().get(i).getKey(); - Expression val = filledResPath.getPathParamsAndConstraints().get(i).getKey(); - Expression constraint = unfilledResPath.getPathParamsAndConstraints().get(i).getValue(); - if (constraint != null && !constraint.equals(val)) { - // The value of the path parameter does not satisfy the constraint defined for the parameter. - return null; // Not to fire this event. - } - boolean isSelector = false; - for (Selector sel: channel.getAllSelectors()) { - if (sel.getExpression().equals(var)) { - isSelector = true; - break; + if (doesUpdateDependingParameters) { + Set dependingVarPosInMessage = new HashSet<>(); + ResourcePath filledResPath = channel.fillOutsideResourcePath(dependingMem.getResource(), unifiedMessage, dependingMem.getStateTransition().getMessageExpression(), dependingVarPosInMessage); + ResourcePath unfilledResPath = dependingMem.getResource(); + for (int i = 0; i < unfilledResPath.getPathParamsAndConstraints().size(); i++) { + Expression var = unfilledResPath.getPathParamsAndConstraints().get(i).getKey(); + Expression val = filledResPath.getPathParamsAndConstraints().get(i).getKey(); + Expression constraint = unfilledResPath.getPathParamsAndConstraints().get(i).getValue(); + if (constraint != null && !constraint.equals(val)) { + // The value of the path parameter does not satisfy the constraint defined for the parameter. + return null; // Not to fire this event. } - } - if (!isSelector) { - // Update a depending channel parameter - updateDependingParameter(var, val); + if (!channel.getAllSelectorVariables().contains(var)) { + // Update a depending channel parameter + updateDependingParameter(var, val); + } } } @@ -303,9 +384,9 @@ messageConstraint = channel.calcMessageConstraintForReferenceMember(dependingMem, null, resouceStateAccessor, null, substitutedPositionsInMessageFromChannels); } if (unifiedMessage == null) { - unifiedMessage = (Term) messageConstraint; + unifiedMessage = messageConstraint; } else { - unifiedMessage = (Term) unifiedMessage.unify(messageConstraint); + unifiedMessage = unifiedMessage.unify(messageConstraint); if (unifiedMessage == null) { throw new UnificationFailed(); } @@ -314,11 +395,98 @@ resolved.addAll(toResolve); toResolve.clear(); } - return unifiedMessage; - } catch (ResolvingMultipleDefinitionIsFutureWork | InvalidMessage | UnificationFailed e) { - e.printStackTrace(); + // For the channel members that are depended by and depend on no other member. + for (ChannelMember remainingMem: channel.getChannelMembers()) { + if (!resolved.contains(remainingMem)) { + resolved.add(remainingMem); + // Calculate message constraint + messageConstraint = null; + if (channel.getInputChannelMembers().contains(remainingMem)) { + // Calculate message constraint from an input state transition + messageConstraint = channel.calcMessageConstraintForInputMember(remainingMem, null, resouceStateAccessor, null, substitutedPositionsInMessageFromChannels); + } else if (channel.getReferenceChannelMembers().contains(remainingMem)) { + // Calculate message constraint from a reference state transition + messageConstraint = channel.calcMessageConstraintForReferenceMember(remainingMem, null, resouceStateAccessor, null, substitutedPositionsInMessageFromChannels); + } + if (messageConstraint != null) { + if (unifiedMessage == null) { + unifiedMessage = messageConstraint; + } else { + unifiedMessage = unifiedMessage.unify(messageConstraint); + if (unifiedMessage == null) { + throw new UnificationFailed(); + } + } + } + } + } } - return null; + if (inputResource == null) return unifiedMessage; + + // 3. Propagate parent event message to collect child events and calculate message constraints from child channels. + Resource baseResource = inputResource; + while (baseResource.getResourceHierarchy().getNumParameters() == 0) { + if (baseResource.getParent() == null) break; + baseResource = baseResource.getParent(); + } + Expression parentEventMessage = (Expression) unifiedMessage.clone(); + for (Channel childChannel: channel.getChildren()) { + // Search the deepest input or reference side resource path in each child channel that matches the channel parameters. + ChannelMember inOrRef = null; + Set channelMembers = new HashSet<>(((DataTransferChannel) childChannel).getInputChannelMembers()); + channelMembers.addAll(((DataTransferChannel) childChannel).getReferenceChannelMembers()); + for (ChannelMember cm: channelMembers) { + if (!cm.isOutside()) { + ResourcePath resPath = cm.getResource(); + if (resPath.getPathParams().containsAll(childChannel.getAllSelectorVariables())) { + inOrRef = cm; + break; + } + } + } + if (inOrRef != null) { + // Collect events for all resources under this event's input resource that matches the deepest input side resource path. + for (Resource res: baseResource.getDescendants(inOrRef.getResource().getResourceHierarchy())) { + Event childEvent = new Event((DataTransferChannel) childChannel, inOrRef.getResource(), res); + childEvent.setMessage(parentEventMessage); + messageConstraint = childEvent.constructMessageAndDesdendantEvents(resourceStateValueProvider, substitutedPositionsInMessageFromChannels, true); + if (messageConstraint != null) { + childEvent.setMessage(messageConstraint); + if (unifiedMessage == null) { + unifiedMessage = messageConstraint; + } else { + unifiedMessage = unifiedMessage.unify(messageConstraint); + if (unifiedMessage == null) { + throw new UnificationFailed(); + } + } + childEvents.add(childEvent); + } + } + } else { + // Search the deepest output side resource path in each child channel that matches the channel parameters. + ChannelMember out = null; + for (ChannelMember cm: ((DataTransferChannel) childChannel).getOutputChannelMembers()) { + if (!cm.isOutside()) { + ResourcePath resPath = cm.getResource(); + if (resPath.getPathParams().containsAll(childChannel.getAllSelectorVariables())) { + out = cm; + break; + } + } + } + if (out != null) { + // Collect events for all resources under this event's input resource that matches the deepest output side resource path. + for (Resource res: baseResource.getDescendants(out.getResource().getResourceHierarchy())) { + Event childEvent = new Event((DataTransferChannel) childChannel, parentEventMessage, out.getResource(), res); + childEvent.constructMessageAndDesdendantEvents(resourceStateValueProvider, substitutedPositionsInMessageFromChannels, true); + childEvents.add(childEvent); + } + } + } + } + + return unifiedMessage; } public void updateDependingParameter(Expression variable, Expression value) { @@ -337,33 +505,37 @@ return outputResources; } - public Set getSuccessors() { - return succEvents; - } - - public void addSuccessor(Event succEvt) { - succEvents.add(succEvt); - } - public ResourceIdentifier getResourceIdentifier(ResourcePath resPath) { ResourceIdentifier resId = ResourceIdentifier.createFrom(resPath); for (Map.Entry chParamEnt: channelSelectorAndValues) { Selector sel = chParamEnt.getKey(); - Map inputPathParamEnt = channelSelectorToInputResourcePathParam.get(sel); - if (inputPathParamEnt != null) { - Integer paramIdx = inputPathParamEnt.get(resPath); - if (paramIdx != null) { - resId.setPathParam(paramIdx, chParamEnt.getValue()); + Map>> inputPathToIdxAndPos = channelSelectorToInputOrReferenceResourcePathParam.get(sel); + if (inputPathToIdxAndPos != null) { + Map.Entry> pathParamEnt = inputPathToIdxAndPos.get(resPath); + if (pathParamEnt != null) { + Integer paramIdx = pathParamEnt.getKey(); + if (paramIdx != null) { + Expression pathParamExp = resId.getPathParams().get(paramIdx); + if (pathParamExp instanceof Variable) { + resId.setPathParam(paramIdx, chParamEnt.getValue()); + } + } } } } for (Map.Entry chParamEnt: channelSelectorAndValues) { Selector sel = chParamEnt.getKey(); - Map outputPathParamEnt = channelSelectorToOutputResourcePathParam.get(sel); - if (outputPathParamEnt != null) { - Integer paramIdx = outputPathParamEnt.get(resPath); - if (paramIdx != null) { - resId.setPathParam(paramIdx, chParamEnt.getValue()); + Map>> outputPathToIdxAndPos = channelSelectorToOutputResourcePathParam.get(sel); + if (outputPathToIdxAndPos != null) { + Map.Entry> pathParamEnt = outputPathToIdxAndPos.get(resPath); + if (pathParamEnt != null) { + Integer paramIdx = pathParamEnt.getKey(); + if (paramIdx != null) { + Expression pathParamExp = resId.getPathParams().get(paramIdx); + if (pathParamExp instanceof Variable) { + resId.setPathParam(paramIdx, chParamEnt.getValue()); + } + } } } } @@ -373,6 +545,40 @@ resId.setPathParam(paramIdx, (Constant) dependingParameters.get(var)); } } + for (Map.Entry chParamEnt: channelSelectorAndValues) { + Selector sel = chParamEnt.getKey(); + Map>> inputPathToIdxAndPos = channelSelectorToInputOrReferenceResourcePathParam.get(sel); + if (inputPathToIdxAndPos != null) { + Map.Entry> pathParamEnt = inputPathToIdxAndPos.get(resPath); + if (pathParamEnt != null) { + Integer paramIdx = pathParamEnt.getKey(); + if (paramIdx != null) { + Expression pathParamExp = resId.getPathParams().get(paramIdx); + if (pathParamExp instanceof Term && sel.getExpression() instanceof Variable) { + pathParamExp = ((Term) pathParamExp).substitute((Variable) sel.getExpression(), chParamEnt.getValue()); + resId.setPathParam(paramIdx, ((Term) pathParamExp).reduce()); + } + } + } + } + } + for (Map.Entry chParamEnt: channelSelectorAndValues) { + Selector sel = chParamEnt.getKey(); + Map>> outputPathToIdxAndPos = channelSelectorToOutputResourcePathParam.get(sel); + if (outputPathToIdxAndPos != null) { + Map.Entry> pathParamEnt = outputPathToIdxAndPos.get(resPath); + if (pathParamEnt != null) { + Integer paramIdx = pathParamEnt.getKey(); + if (paramIdx != null) { + Expression pathParamExp = resId.getPathParams().get(paramIdx); + if (pathParamExp instanceof Term && sel.getExpression() instanceof Variable) { + pathParamExp = ((Term) pathParamExp).substitute((Variable) sel.getExpression(), chParamEnt.getValue()); + resId.setPathParam(paramIdx, ((Term) pathParamExp).reduce()); + } + } + } + } + } return resId; } @@ -380,10 +586,16 @@ ResourceIdentifier resId = ResourceIdentifier.createFrom(inputResPath); for (Map.Entry chParamEnt: channelSelectorAndValues) { Selector sel = chParamEnt.getKey(); - if (channelSelectorToInputResourcePathParam.get(sel) != null) { - Integer paramIdx = channelSelectorToInputResourcePathParam.get(sel).get(inputResPath); - if (paramIdx != null) { - resId.setPathParam(paramIdx, chParamEnt.getValue()); + if (channelSelectorToInputOrReferenceResourcePathParam.get(sel) != null) { + Map.Entry> pathParamEnt = channelSelectorToInputOrReferenceResourcePathParam.get(sel).get(inputResPath); + if (pathParamEnt != null) { + Integer paramIdx = pathParamEnt.getKey(); + if (paramIdx != null) { + Expression pathParamExp = resId.getPathParams().get(paramIdx); + if (pathParamExp instanceof Variable) { + resId.setPathParam(paramIdx, chParamEnt.getValue()); + } + } } } } @@ -393,6 +605,22 @@ resId.setPathParam(paramIdx, (Constant) dependingParameters.get(var)); } } + for (Map.Entry chParamEnt: channelSelectorAndValues) { + Selector sel = chParamEnt.getKey(); + if (channelSelectorToInputOrReferenceResourcePathParam.get(sel) != null) { + Map.Entry> pathParamEnt = channelSelectorToInputOrReferenceResourcePathParam.get(sel).get(inputResPath); + if (pathParamEnt != null) { + Integer paramIdx = pathParamEnt.getKey(); + if (paramIdx != null) { + Expression pathParamExp = resId.getPathParams().get(paramIdx); + if (pathParamExp instanceof Term && sel.getExpression() instanceof Variable) { + pathParamExp = ((Term) pathParamExp).substitute((Variable) sel.getExpression(), chParamEnt.getValue()); + resId.setPathParam(paramIdx, ((Term) pathParamExp).reduce()); + } + } + } + } + } return resId; } @@ -401,16 +629,38 @@ for (Map.Entry chParamEnt: channelSelectorAndValues) { Selector sel = chParamEnt.getKey(); if (channelSelectorToOutputResourcePathParam.get(sel) != null) { - Integer paramIdx = channelSelectorToOutputResourcePathParam.get(sel).get(outputResPath); - if (paramIdx != null) { - resId.setPathParam(paramIdx, chParamEnt.getValue()); + Map.Entry> pathParamEnt = channelSelectorToOutputResourcePathParam.get(sel).get(outputResPath); + if (pathParamEnt != null) { + Integer paramIdx = pathParamEnt.getKey(); + if (paramIdx != null) { + Expression pathParamExp = resId.getPathParams().get(paramIdx); + if (pathParamExp instanceof Variable) { + resId.setPathParam(paramIdx, chParamEnt.getValue()); + } + } } } } for (Expression var: dependingParameters.keySet()) { int paramIdx = resId.getPathParams().indexOf(var); if (paramIdx >= 0) { - resId.setPathParam(paramIdx, (Constant) dependingParameters.get(var)); + resId.setPathParam(paramIdx, dependingParameters.get(var)); + } + } + for (Map.Entry chParamEnt: channelSelectorAndValues) { + Selector sel = chParamEnt.getKey(); + if (channelSelectorToOutputResourcePathParam.get(sel) != null) { + Map.Entry> pathParamEnt = channelSelectorToOutputResourcePathParam.get(sel).get(outputResPath); + if (pathParamEnt != null) { + Integer paramIdx = pathParamEnt.getKey(); + if (paramIdx != null) { + Expression pathParamExp = resId.getPathParams().get(paramIdx); + if (pathParamExp instanceof Term && sel.getExpression() instanceof Variable) { + pathParamExp = ((Term) pathParamExp).substitute((Variable) sel.getExpression(), chParamEnt.getValue()); + resId.setPathParam(paramIdx, ((Term) pathParamExp).reduce()); + } + } + } } } return resId; diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/Resource.java b/AlgebraicDataflowArchitectureModel/src/simulator/Resource.java index ac2c94b..8cd943f 100644 --- a/AlgebraicDataflowArchitectureModel/src/simulator/Resource.java +++ b/AlgebraicDataflowArchitectureModel/src/simulator/Resource.java @@ -1,8 +1,10 @@ package simulator; import java.util.Collection; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; +import java.util.Set; import models.algebra.Constant; import models.algebra.Expression; @@ -11,6 +13,11 @@ import models.dataConstraintModel.ResourceHierarchy; import simulator.states.*; +/** + * A runtime instance of a resource + * @author Nitta + * + */ public class Resource { private Resource parent = null; private Map children = null; @@ -172,6 +179,20 @@ return children; } + public Set getDescendants(ResourceHierarchy res) { + if (this.getResourceHierarchy().equals(res)) { + Set descendants = new HashSet<>(); + descendants.add(this); + return descendants; + } + if (!res.toString().startsWith(this.getResourceHierarchy().toString())) return new HashSet<>(); + Set descendants = new HashSet<>(); + for (Resource child: getChildren()) { + descendants.addAll(child.getDescendants(res)); + } + return descendants; + } + public Resource getDescendant(ResourceIdentifier resId) { if (this.getResourceIdentifier().equals(resId)) return this; if (!resId.startsWith(this.getResourceIdentifier())) return null; diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/ResourceIdentifier.java b/AlgebraicDataflowArchitectureModel/src/simulator/ResourceIdentifier.java index 8987ee0..13b2c92 100644 --- a/AlgebraicDataflowArchitectureModel/src/simulator/ResourceIdentifier.java +++ b/AlgebraicDataflowArchitectureModel/src/simulator/ResourceIdentifier.java @@ -25,7 +25,7 @@ super(parentResId, exp, resourceHierarchy); } - public void setPathParam(int paramIdx, Constant param) { + public void setPathParam(int paramIdx, Expression param) { if (paramIdx < pathParams.size()) { pathParams.set(paramIdx, new AbstractMap.SimpleEntry<>(param, null)); if (parent != null) { @@ -130,7 +130,7 @@ return new ResourceIdentifier(parent, resPath.getLeafResourceName(), res); } } else { - return new ResourceIdentifier(parent, resPath.getLastParam(), res); + return new ResourceIdentifier(parent, (Expression) resPath.getLastParam().clone(), res); } } diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/Simulator.java b/AlgebraicDataflowArchitectureModel/src/simulator/Simulator.java index a2df91d..2a93a6e 100644 --- a/AlgebraicDataflowArchitectureModel/src/simulator/Simulator.java +++ b/AlgebraicDataflowArchitectureModel/src/simulator/Simulator.java @@ -1,6 +1,7 @@ package simulator; import java.util.AbstractMap; +import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -30,6 +31,11 @@ import simulator.interfaces.INativeInitializer; import simulator.interfaces.INativeReceiver; +/** + * Simulator to run a model + * @author Nitta + * + */ public class Simulator { private DataTransferModel model; private SystemState curState; @@ -95,7 +101,7 @@ public SystemState transition(Event inputEvent) throws ParameterizedIdentifierIsFutureWork, ResolvingMultipleDefinitionIsFutureWork, InvalidMessage, UnificationFailed, ValueUndefined { SystemState nextSystemState = new SystemState(curState); - inputEvent.updateDependingParameters(new ResourceStateValueProvider(curState, nextSystemState)); + inputEvent.constructMessageAndDescendantEvents(new ResourceStateValueProvider(curState, nextSystemState), true); nextSystemState.addEvent(inputEvent); fireEvent(inputEvent, curState, nextSystemState); @@ -192,8 +198,14 @@ return res.getState().getValue(); } }; - - DataTransferChannel channel = event.getChannel(); + + // First, fire the all child events. + for (Event childEvent: event.getChildren()) { + fireEvent(childEvent, curSystemState, nextSystemState); + } + + // Fire this event. + DataTransferChannel channel = event.getChannel(); if (channel.getOutputResources().size() > 0) { // For each output resource, calculate the next state. for (ChannelMember out: channel.getOutputChannelMembers()) { @@ -201,7 +213,7 @@ Expression nextResState = null; outTarget[0] = out; outResVar[0] = new Variable(channel.getChannelName() + "$" + out.getResource().toString() + "$this"); // A special variable to represent the current state of each output resource. - if (!event.isInput() || event.getMessage() instanceof Variable) { + if (event.getMessage() instanceof Variable) { nextResState = channel.deriveUpdateExpressionOf(out, resouceStateAccessor).getKey(); } else { nextResState = channel.deriveUpdateExpressionOf(out, (Term) event.getMessage(), resouceStateAccessor); @@ -252,13 +264,16 @@ } } - private Set getNextEvents(ResourceIdentifier inResId, SystemState curSystemState, SystemState nextSystemState) - throws ParameterizedIdentifierIsFutureWork, ResolvingMultipleDefinitionIsFutureWork, InvalidMessage, UnificationFailed, ValueUndefined { - Set nextEvents = new HashSet<>(); + private Set getNextEvents(ResourceIdentifier inResId, SystemState curSystemState, SystemState nextSystemState) { IResourceStateValueProvider resourceStateValueProvider = new ResourceStateValueProvider(curSystemState, nextSystemState); - for (Map.Entry chEntry: nextSystemState.getChannelStates().entrySet()) { - DataTransferChannel channel = chEntry.getKey(); - ChannelState nextChState = chEntry.getValue(); + Set nextEvents = new HashSet<>(); + for (Channel ch: model.getChannels()) { + DataTransferChannel channel = (DataTransferChannel) ch; + ChannelState nextChannelState = nextSystemState.getChannelState(channel); + if (nextChannelState == null) { + nextChannelState = new ChannelState(channel); + nextSystemState.updateChannelState(channel, nextChannelState); + } Map> dependency = channel.getMemberDependency(); Map> invDependency = new HashMap<>(); for (ChannelMember dependingMem: dependency.keySet()) { @@ -277,18 +292,16 @@ boolean isInputResourceDepended = false; for (ChannelMember dependedMem: invDependency.keySet()) { if (inResPath == dependedMem.getResource()) { - // If some depending resources are to be updated by the update of an depended input resource. + // 1) If some depending resources are to be updated by the update of an 'depended' input resource. if (doesSatifsyPathConstraints(inResPath, inResId)) { Event nextEvent = new Event(channel, inResPath, nextSystemState.getResource(inResId)); - Expression message = nextEvent.updateDependingParameters(resourceStateValueProvider); + Expression message = nextEvent.constructMessageAndDescendantEvents(resourceStateValueProvider, true); if (message != null) { - if (nextChState == null) { - nextChState = new ChannelState(channel); - nextSystemState.updateChannelState(channel, nextChState); - } + nextEvent.setMessage(message); + nextSystemState.updateChannelState(channel, nextChannelState); List channelSelValues = nextEvent.getChannelSelectorValues(); for (Map.Entry paramEnt: nextEvent.getDependingParameters().entrySet()) { - nextChState.addDependingParamAndValue(channelSelValues, paramEnt.getKey(), paramEnt.getValue()); + nextChannelState.addDependingParamAndValue(channelSelValues, paramEnt.getKey(), paramEnt.getValue()); } nextEvents.add(nextEvent); } @@ -305,63 +318,58 @@ if (!isInputResourceDepended && !isInputResourceDepending) { if (doesSatifsyPathConstraints(inResPath, inResId)) { Event nextEvent = new Event(channel, inResPath, nextSystemState.getResource(inResId)); - Expression message = nextEvent.updateDependingParameters(resourceStateValueProvider); + Expression message = nextEvent.constructMessageAndDescendantEvents(resourceStateValueProvider, true); if (message != null) { nextEvent.setMessage(message); nextEvents.add(nextEvent); } } } - if (nextChState != null) { - for (ChannelMember dependingMem: dependency.keySet()) { - if (inResPath == dependingMem.getResource()) { - // If a depending resource is directly updated. - ResourcePath filledResPath = inResId; - ResourcePath unfilledResPath = inResPath; - // Extract the values of path parameters from the updated resource identifier. - Map selectorVarToVal = new HashMap<>(); - Map dependingVarToVal = new HashMap<>(); - boolean doesSatisfyConstraint = true; - for (int i = 0; i < unfilledResPath.getPathParamsAndConstraints().size(); i++) { - Expression var = unfilledResPath.getPathParamsAndConstraints().get(i).getKey(); - Expression val = filledResPath.getPathParamsAndConstraints().get(i).getKey(); - Expression constraint = unfilledResPath.getPathParamsAndConstraints().get(i).getValue(); - if (constraint != null && !constraint.equals(val)) { - // The value of the path parameter does not satisfy the constraint defined for the parameter. - doesSatisfyConstraint = false; - break; - } - boolean isSelector = false; - for (Selector sel: channel.getAllSelectors()) { - if (sel.getExpression().equals(var)) { - isSelector = true; - break; - } - } - if (isSelector) { - selectorVarToVal.put(var, val); - } else { - dependingVarToVal.put(var, val); - } + for (ChannelMember dependingMem: dependency.keySet()) { + if (inResPath == dependingMem.getResource()) { + // 2) If a 'depending' resource is directly updated. + ResourcePath filledResPath = inResId; + ResourcePath unfilledResPath = inResPath; + // Extract the values of path parameters from the updated resource identifier. + Map selectorVarToVal = new HashMap<>(); + Map dependingVarToVal = new HashMap<>(); + boolean doesSatisfyConstraint = true; + for (int i = 0; i < unfilledResPath.getPathParamsAndConstraints().size(); i++) { + Expression var = unfilledResPath.getPathParamsAndConstraints().get(i).getKey(); + Expression val = filledResPath.getPathParamsAndConstraints().get(i).getKey(); + Expression constraint = unfilledResPath.getPathParamsAndConstraints().get(i).getValue(); + if (constraint != null && !constraint.equals(val)) { + // The value of the path parameter does not satisfy the constraint defined for the parameter. + doesSatisfyConstraint = false; + break; } - if (doesSatisfyConstraint) { - List> dependedChannelSelectorValues = nextChState.getDependedChannelSelectorValues(dependingVarToVal); - if (dependedChannelSelectorValues != null) { - for (List channelSelectorValues: dependedChannelSelectorValues) { - // Guess every tuple of channel selector values that may affects the updated resource. - boolean doesMatch = true; - for (Expression var: selectorVarToVal.keySet()) { - for (int i = 0; i < channel.getAllSelectors().size(); i++) { - if (channel.getAllSelectors().get(i).getExpression().equals(var)) { - if (!channelSelectorValues.get(i).equals(selectorVarToVal.get(var))) { - // If the value of a selector in the updated resource path does not matches a guessed channel selector value. - doesMatch = false; - } + if (channel.getAllSelectorVariables().contains(var)) { + selectorVarToVal.put(var, val); + } else { + dependingVarToVal.put(var, val); + } + } + if (doesSatisfyConstraint) { + List> dependedChannelSelectorValues = nextChannelState.getDependedChannelSelectorValues(dependingVarToVal); + if (dependedChannelSelectorValues != null) { + for (List channelSelectorValues: dependedChannelSelectorValues) { + // Guess every tuple of channel selector values that may affects the updated resource. + boolean doesMatch = true; + for (Expression var: selectorVarToVal.keySet()) { + for (int i = 0; i < channel.getAllSelectors().size(); i++) { + if (channel.getAllSelectors().get(i).getExpression().equals(var)) { + if (!channelSelectorValues.get(i).equals(selectorVarToVal.get(var))) { + // If the value of a selector in the updated resource path does not matches a guessed channel selector value. + doesMatch = false; } } } - if (doesMatch) { - Event nextEvent = new Event(channel, inResPath, nextSystemState.getResource(inResId), channelSelectorValues, dependingVarToVal); + } + if (doesMatch) { + Event nextEvent = new Event(channel, inResPath, nextSystemState.getResource(inResId), channelSelectorValues, dependingVarToVal); + Expression message = nextEvent.constructMessageAndDescendantEvents(resourceStateValueProvider, false); + if (message != null) { + nextEvent.setMessage(message); nextEvents.add(nextEvent); } } @@ -372,10 +380,182 @@ } } } + // 3) If a resource in a descendant channel is directly updated. + ResourcePath inResPath = null; + for (ResourcePath path: channel.getInputResources()) { + if (path.getPathParams().containsAll(channel.getAllSelectorVariables())) { + inResPath = path; + } + } + if (inResPath != null) { + for (Map.Entry, Event> childEventEnt: collectDescendantEvents(channel, channel, inResId, nextSystemState, resourceStateValueProvider)) { + Event childEvent = childEventEnt.getValue(); + nextEvents.add(childEvent); + } + } } return nextEvents; } + private List, Event>> collectDescendantEvents(DataTransferChannel rootChannel, DataTransferChannel channel, ResourceIdentifier inResId, + SystemState nextSystemState, IResourceStateValueProvider resourceStateValueProvider) { + List, Event>> childEvents = new ArrayList<>(); + ChannelState nextChannelState = nextSystemState.getChannelState(rootChannel); + for (Channel childCh: channel.getChildren()) { + DataTransferChannel childChannel = (DataTransferChannel) childCh; + Map> dependency = childChannel.getMemberDependency(); + Map> invDependency = new HashMap<>(); + for (ChannelMember dependingMem: dependency.keySet()) { + for (ChannelMember dependedMem: dependency.get(dependingMem)) { + Set dependings = invDependency.get(dependedMem); + if (dependings == null) { + dependings = new HashSet<>(); + invDependency.put(dependedMem, dependings); + } + dependings.add(dependingMem); + } + } + for (ResourcePath childInResPath: childChannel.getInputResources()) { + if (inResId.isInstanceOf(childInResPath)) { + boolean isInputResourceDepended = false; + for (ChannelMember dependedMem: invDependency.keySet()) { + if (childInResPath == dependedMem.getResource()) { + // 1) If some depending resources are to be updated by the update of an 'depended' input resource. + if (doesSatifsyPathConstraints(childInResPath, inResId)) { + Event childEvent = new Event(childChannel, childInResPath, nextSystemState.getResource(inResId)); + Expression message = childEvent.constructMessageAndDescendantEvents(resourceStateValueProvider, true); + if (message != null) { + childEvent.setMessage(message); + List childChannelSelValues = childEvent.getChannelSelectorValues(); + for (Map.Entry paramEnt: childEvent.getDependingParameters().entrySet()) { + nextChannelState.addDependingParamAndValue(childChannelSelValues, paramEnt.getKey(), paramEnt.getValue()); + } + if (childChannelSelValues.size() >= childChannel.getSelectors().size()) { + List channelSelectorValues = new ArrayList<>(childChannelSelValues); + for (int i = 0; i < childChannel.getSelectors().size(); i++) { + channelSelectorValues.remove(childChannelSelValues.size() - 1 - i); + } + childEvents.add(new AbstractMap.SimpleEntry<>(channelSelectorValues, childEvent)); + } + } + } + isInputResourceDepended = true; + } + } + boolean isInputResourceDepending = false; + for (ChannelMember dependingMem: dependency.keySet()) { + if (childInResPath == dependingMem.getResource()) { + isInputResourceDepending = true; + } + } + if (!isInputResourceDepended && !isInputResourceDepending) { + if (doesSatifsyPathConstraints(childInResPath, inResId)) { + Event childEvent = new Event(childChannel, childInResPath, nextSystemState.getResource(inResId)); + Expression message = childEvent.constructMessageAndDescendantEvents(resourceStateValueProvider, true); + if (message != null) { + childEvent.setMessage(message); + List childChannelSelValues = childEvent.getChannelSelectorValues(); + for (Map.Entry paramEnt: childEvent.getDependingParameters().entrySet()) { + nextChannelState.addDependingParamAndValue(childChannelSelValues, paramEnt.getKey(), paramEnt.getValue()); + } + if (childChannelSelValues.size() >= childChannel.getSelectors().size()) { + List channelSelectorValues = new ArrayList<>(childChannelSelValues); + for (int i = 0; i < childChannel.getSelectors().size(); i++) { + channelSelectorValues.remove(childChannelSelValues.size() - 1 - i); + } + childEvents.add(new AbstractMap.SimpleEntry<>(channelSelectorValues, childEvent)); + } + } + } + } + for (ChannelMember dependingMem: dependency.keySet()) { + if (childInResPath == dependingMem.getResource()) { + // 2) If a 'depending' resource is directly updated. + ResourcePath filledResPath = inResId; + ResourcePath unfilledResPath = childInResPath; + // Extract the values of path parameters from the updated resource identifier. + Map selectorVarToVal = new HashMap<>(); + Map dependingVarToVal = new HashMap<>(); + boolean doesSatisfyConstraint = true; + for (int i = 0; i < unfilledResPath.getPathParamsAndConstraints().size(); i++) { + Expression var = unfilledResPath.getPathParamsAndConstraints().get(i).getKey(); + Expression val = filledResPath.getPathParamsAndConstraints().get(i).getKey(); + Expression constraint = unfilledResPath.getPathParamsAndConstraints().get(i).getValue(); + if (constraint != null && !constraint.equals(val)) { + // The value of the path parameter does not satisfy the constraint defined for the parameter. + doesSatisfyConstraint = false; + break; + } + if (channel.getAllSelectorVariables().contains(var)) { + selectorVarToVal.put(var, val); + } else { + dependingVarToVal.put(var, val); + } + } + if (doesSatisfyConstraint) { + List> dependedChannelSelectorValues = nextChannelState.getDependedChannelSelectorValues(dependingVarToVal); + if (dependedChannelSelectorValues != null) { + for (List childChannelSelectorValues: dependedChannelSelectorValues) { + // Guess every tuple of channel selector values that may affects the updated resource. + boolean doesMatch = true; + for (Expression var: selectorVarToVal.keySet()) { + for (int i = 0; i < channel.getAllSelectors().size(); i++) { + if (channel.getAllSelectors().get(i).getExpression().equals(var)) { + if (!childChannelSelectorValues.get(i).equals(selectorVarToVal.get(var))) { + // If the value of a selector in the updated resource path does not matches a guessed channel selector value. + doesMatch = false; + } + } + } + } + if (doesMatch) { + Event childEvent = new Event(childChannel, childInResPath, nextSystemState.getResource(inResId), childChannelSelectorValues, dependingVarToVal); + Expression message = childEvent.constructMessageAndDescendantEvents(resourceStateValueProvider, false); + if (message != null) { + childEvent.setMessage(message); + if (childChannelSelectorValues.size() >= childChannel.getSelectors().size()) { + List channelSelectorValues = new ArrayList<>(childChannelSelectorValues); + for (int i = 0; i < childChannel.getSelectors().size(); i++) { + channelSelectorValues.remove(childChannelSelectorValues.size() - 1 - i); + } + childEvents.add(new AbstractMap.SimpleEntry<>(channelSelectorValues, childEvent)); + } + } + } + } + } + } + } + } + } + } + // 3) If a resource in a descendant channel is directly updated. + childEvents.addAll(collectDescendantEvents(rootChannel, childChannel, inResId, nextSystemState, resourceStateValueProvider)); + } + List, Event>> events = new ArrayList<>(); + ResourcePath inResPath = null; + for (ResourcePath path: channel.getInputResources()) { + if (path.getPathParams().containsAll(channel.getAllSelectorVariables())) { + inResPath = path; + } + } + for (Map.Entry, Event> childEventEnt: childEvents) { + List channelSelectorValues = childEventEnt.getKey(); + Event childEvent = childEventEnt.getValue(); + Map dependingVarToVal = nextChannelState.getDependingParamAndValues(channelSelectorValues); + Event event = new Event((DataTransferChannel) channel, inResPath, nextSystemState.getResource(inResId), channelSelectorValues, dependingVarToVal); + event.addChild(childEvent); + if (channelSelectorValues.size() >= channel.getSelectors().size()) { + List parentChannelSelectorValues = new ArrayList<>(channelSelectorValues); + for (int i = 0; i < channel.getSelectors().size(); i++) { + parentChannelSelectorValues.remove(channelSelectorValues.size() - 1 - i); + } + events.add(new AbstractMap.SimpleEntry<>(parentChannelSelectorValues, event)); + } + } + return events; + } + private boolean doesSatifsyPathConstraints(ResourcePath resPath, ResourceIdentifier resId) { for (int i = 0; i < resPath.getPathParamsAndConstraints().size(); i++) { Expression val = resId.getPathParamsAndConstraints().get(i).getKey(); diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/SystemState.java b/AlgebraicDataflowArchitectureModel/src/simulator/SystemState.java index e078ccb..3fb236f 100644 --- a/AlgebraicDataflowArchitectureModel/src/simulator/SystemState.java +++ b/AlgebraicDataflowArchitectureModel/src/simulator/SystemState.java @@ -26,6 +26,11 @@ import simulator.states.PrimitiveResourceState; import simulator.states.ResourceState; +/** + * The whole state of a running model + * @author Nitta + * + */ public class SystemState { private Set rootResources = new HashSet<>(); private Map channelStates = new HashMap<>(); @@ -278,6 +283,7 @@ } } ((ListResourceState) state).removeChildState(Integer.parseInt(childIdxExp.toString())); + createdResources.add(resourceIdentifier); return createdResources; } } @@ -317,27 +323,31 @@ MapTerm mapValue = (MapTerm) resNextStateVal; Resource res = getResource(resourceIdentifier); ResourceState state = res.getState(); - List createdResources = new ArrayList<>(); - for (String key: mapValue.keySet()) { - Expression childExp = new Constant(key, DataConstraintModel.typeString); - ResourceHierarchy childResourceHierarchy = null; - if (resourceIdentifier.getResourceHierarchy().getChildren() != null && resourceIdentifier.getResourceHierarchy().getChildren().size() > 0) { - childResourceHierarchy = resourceIdentifier.getResourceHierarchy().getChildren().iterator().next(); - } else { - Type childResType = null; - if (mapValue.get(key) instanceof Variable) { - childResType = ((Variable) mapValue.get(key)).getType(); - } else if (mapValue.get(key) instanceof Term) { - childResType = ((Term) mapValue.get(key)).getType(); + if (state instanceof MapResourceState) { + ((MapResourceState) state).clearChildStates(); + List createdResources = new ArrayList<>(); + for (String key: mapValue.keySet()) { + Expression childExp = new Constant(key, DataConstraintModel.typeString); + ResourceHierarchy childResourceHierarchy = null; + if (resourceIdentifier.getResourceHierarchy().getChildren() != null && resourceIdentifier.getResourceHierarchy().getChildren().size() > 0) { + childResourceHierarchy = resourceIdentifier.getResourceHierarchy().getChildren().iterator().next(); + } else { + Type childResType = null; + if (mapValue.get(key) instanceof Variable) { + childResType = ((Variable) mapValue.get(key)).getType(); + } else if (mapValue.get(key) instanceof Term) { + childResType = ((Term) mapValue.get(key)).getType(); + } + childResourceHierarchy = new ResourceHierarchy(resourceIdentifier.getResourceHierarchy(), 1, childResType); } - childResourceHierarchy = new ResourceHierarchy(resourceIdentifier.getResourceHierarchy(), 1, childResType); + ResourceIdentifier childResourceIdentifier = new ResourceIdentifier(resourceIdentifier, childExp, childResourceHierarchy); + Map.Entry> childInfo = createResourceState(childResourceIdentifier, curResVar, resCurStateVal, mapValue.get(key)); + ((MapResourceState) state).addChildState(key, childInfo.getKey()); + createdResources.addAll(childInfo.getValue()); } - ResourceIdentifier childResourceIdentifier = new ResourceIdentifier(resourceIdentifier, childExp, childResourceHierarchy); - Map.Entry> childInfo = createResourceState(childResourceIdentifier, curResVar, resCurStateVal, mapValue.get(key)); - ((MapResourceState) state).addChildState(key, childInfo.getKey()); - createdResources.addAll(childInfo.getValue()); + return createdResources; } - return createdResources; + return null; } else if (resNextStateVal instanceof Term) { Term mapValue = (Term) resNextStateVal; if (mapValue.getSymbol().equals(DataConstraintModel.insert)) { @@ -437,6 +447,7 @@ } String childId = (String) ((Constant) childKeyExp).getValue(); ((MapResourceState) state).removeChildState(childId); + createdResources.add(resourceIdentifier); return createdResources; } } diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/states/PrimitiveResourceState.java b/AlgebraicDataflowArchitectureModel/src/simulator/states/PrimitiveResourceState.java index adfb7f3..19db331 100644 --- a/AlgebraicDataflowArchitectureModel/src/simulator/states/PrimitiveResourceState.java +++ b/AlgebraicDataflowArchitectureModel/src/simulator/states/PrimitiveResourceState.java @@ -26,6 +26,7 @@ } public Object clone() { - return this; + if (value == null) return new PrimitiveResourceState(null); + return new PrimitiveResourceState((Constant) value.clone()); } } diff --git a/AlgebraicDataflowArchitectureModel/src/simulator/states/State.java b/AlgebraicDataflowArchitectureModel/src/simulator/states/State.java index 2dfa5c0..00883d9 100644 --- a/AlgebraicDataflowArchitectureModel/src/simulator/states/State.java +++ b/AlgebraicDataflowArchitectureModel/src/simulator/states/State.java @@ -2,6 +2,11 @@ import models.algebra.Expression; +/** + * A state of a resource + * @author Nitta + * + */ abstract public class State implements Cloneable { abstract public Expression getValue(); abstract public Object clone(); diff --git a/AlgebraicDataflowArchitectureModel/src/tests/CodeGeneratorTest.java b/AlgebraicDataflowArchitectureModel/src/tests/CodeGeneratorTest.java deleted file mode 100644 index 8ee6b46..0000000 --- a/AlgebraicDataflowArchitectureModel/src/tests/CodeGeneratorTest.java +++ /dev/null @@ -1,56 +0,0 @@ -package tests; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.util.ArrayList; - -import algorithms.*; -import code.ast.CompilationUnit; -import code.ast.TypeDeclaration; -import generators.DataTransferMethodAnalyzer; -import generators.JavaCodeGenerator; -import generators.JavaMethodBodyGenerator; -import models.dataFlowModel.*; -import parser.*; -import parser.exceptions.ExpectedAssignment; -import parser.exceptions.ExpectedChannel; -import parser.exceptions.ExpectedChannelName; -import parser.exceptions.ExpectedColon; -import parser.exceptions.ExpectedDoubleQuotation; -import parser.exceptions.ExpectedEquals; -import parser.exceptions.ExpectedInOrOutOrRefOrSubKeyword; -import parser.exceptions.ExpectedLeftCurlyBracket; -import parser.exceptions.ExpectedRHSExpression; -import parser.exceptions.ExpectedRightBracket; -import parser.exceptions.ExpectedRightCurlyBracket; -import parser.exceptions.ExpectedStateTransition; -import parser.exceptions.WrongJsonExpression; -import parser.exceptions.WrongLHSExpression; -import parser.exceptions.WrongPathExpression; -import parser.exceptions.WrongRHSExpression; - -public class CodeGeneratorTest { - public static void main(String[] args) { - File file = new File("models/POS.model"); - try { - Parser parser = new Parser(new BufferedReader(new FileReader(file))); - DataTransferModel model; - try { - model = parser.doParse(); - DataFlowGraph graph = DataTransferModelAnalyzer.createDataFlowGraphWithStateStoringAttribute(model); - DataTransferModelAnalyzer.annotateWithSelectableDataTransferAttiribute(graph); - DataTransferMethodAnalyzer.decideToStoreResourceStates(graph); - ArrayList codetree = JavaMethodBodyGenerator.doGenerate(graph, model, JavaCodeGenerator.doGenerate(graph, model)); - System.out.println(codetree); - } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword - | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression - | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { - e.printStackTrace(); - } - } catch (FileNotFoundException e) { - e.printStackTrace(); - } - } -} diff --git a/AlgebraicDataflowArchitectureModel/src/tests/DataStorageDecisionTest.java b/AlgebraicDataflowArchitectureModel/src/tests/DataStorageDecisionTest.java index 0db9f4b..9ba67ab 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/DataStorageDecisionTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/DataStorageDecisionTest.java @@ -1,9 +1,14 @@ package tests; +import static org.junit.Assert.*; + import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; +import java.util.HashMap; + +import org.junit.Test; import algorithms.*; import generators.DataTransferMethodAnalyzer; @@ -28,8 +33,15 @@ import parser.exceptions.WrongRHSExpression; public class DataStorageDecisionTest { - public static void main(String[] args) { + + @Test + public void test() { File file = new File("models/POS2.model"); + HashMap exprectedDecision = new HashMap<>(); + exprectedDecision.put("history", true); + exprectedDecision.put("total", true); + exprectedDecision.put("points", false); + exprectedDecision.put("payment", true); try { Parser parser = new Parser(new BufferedReader(new FileReader(file))); DataTransferModel model = null; @@ -40,7 +52,11 @@ DataTransferModelAnalyzer.annotateWithSelectableDataTransferAttiribute(graph); DataTransferMethodAnalyzer.decideToStoreResourceStates(graph); for(Node resNode: graph.getResourceNodes()) { - System.out.println(((ResourceNode) resNode).getResourceName() + ":" + ((StoreAttribute) ((ResourceNode) resNode).getAttribute()).isStored()); + String resName = ((ResourceNode) resNode).getResourceName(); + boolean decision = ((StoreAttribute) ((ResourceNode) resNode).getAttribute()).isStored(); + System.out.println(resName + ":" + decision); + assertNotNull(exprectedDecision.get(resName)); + assertEquals(decision, exprectedDecision.get(resName)); } } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression diff --git a/AlgebraicDataflowArchitectureModel/src/tests/DataStorageNecessityTest.java b/AlgebraicDataflowArchitectureModel/src/tests/DataStorageNecessityTest.java index d95fb08..5d2c4d8 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/DataStorageNecessityTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/DataStorageNecessityTest.java @@ -1,9 +1,15 @@ package tests; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; +import java.util.HashMap; + +import org.junit.Test; import algorithms.DataTransferModelAnalyzer; import models.Node; @@ -27,8 +33,15 @@ import parser.exceptions.WrongRHSExpression; public class DataStorageNecessityTest { - public static void main(String[] args) { + + @Test + public void test() { File file = new File("models/POS2.model"); + HashMap exprectedNecessity = new HashMap<>(); + exprectedNecessity.put("history", true); + exprectedNecessity.put("total", true); + exprectedNecessity.put("points", false); + exprectedNecessity.put("payment", false); try { Parser parser = new Parser(new BufferedReader(new FileReader(file))); DataTransferModel model; @@ -36,10 +49,16 @@ model = parser.doParse(); System.out.println(model); DataFlowGraph graph = DataTransferModelAnalyzer.createDataFlowGraphWithStateStoringAttribute(model); - for (Node n:graph.getNodes()) { - ResourceNode resource = (ResourceNode)n; - if((StoreAttribute)resource.getAttribute() != null) { - System.out.println(resource.toString() + ":" + ((StoreAttribute)resource.getAttribute()).isNeeded()); + for (Node n: graph.getNodes()) { + if (n instanceof ResourceNode) { + ResourceNode resource = (ResourceNode) n; + if((StoreAttribute) resource.getAttribute() != null) { + String resName = resource.getResourceName(); + boolean necessity = ((StoreAttribute) resource.getAttribute()).isNeeded(); + System.out.println(resName + ":" + necessity); + assertNotNull(exprectedNecessity.get(resName)); + assertEquals(necessity, exprectedNecessity.get(resName)); + } } } } catch (ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword diff --git a/AlgebraicDataflowArchitectureModel/src/tests/JAXRSCodeGeneratorTest.java b/AlgebraicDataflowArchitectureModel/src/tests/JAXRSCodeGeneratorTest.java new file mode 100644 index 0000000..9df8290 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/tests/JAXRSCodeGeneratorTest.java @@ -0,0 +1,1569 @@ +package tests; + +import static org.junit.Assert.*; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.junit.Test; + +import algorithms.*; +import code.ast.Annotation; +import code.ast.CompilationUnit; +import code.ast.FieldDeclaration; +import code.ast.MethodDeclaration; +import code.ast.TypeDeclaration; +import code.ast.VariableDeclaration; +import generators.CodeGeneratorFromDataFlowGraph; +import generators.DataTransferMethodAnalyzer; +import generators.JavaCodeGenerator; +import generators.JavaMethodBodyGenerator; +import generators.JavaSpecific; +import generators.JerseyCodeGenerator; +import generators.JerseyMethodBodyGenerator; +import generators.JerseySpecific; +import models.Edge; +import models.dataFlowModel.*; +import parser.*; +import parser.exceptions.ExpectedAssignment; +import parser.exceptions.ExpectedChannel; +import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; +import parser.exceptions.ExpectedDoubleQuotation; +import parser.exceptions.ExpectedEquals; +import parser.exceptions.ExpectedInOrOutOrRefOrSubKeyword; +import parser.exceptions.ExpectedLeftCurlyBracket; +import parser.exceptions.ExpectedRHSExpression; +import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.ExpectedRightCurlyBracket; +import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; +import parser.exceptions.WrongLHSExpression; +import parser.exceptions.WrongPathExpression; +import parser.exceptions.WrongRHSExpression; + +public class JAXRSCodeGeneratorTest { + + @Test + public void test() { + testAccounts(); + testClock(); + testCustomerManagement(); + testGroupChat(); + testInventoryManagement(); + testOnlineBattleGame(); + testOnlineBattleGame2(); + testPOS(); + testSimpleTwitter(); + testVotingSystem(); + testWeatherObservationSystem(); + } + + private void testAccounts() { + try { + ArrayList generatedCode = generateCode("models/Accounts.model", null); + Map, // class annotations + Entry, // field name to type + Map, // class annotations + Entry, // arg types + Integer>>>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Accounts", Map.entry(Set.of("@Path(\"/accounts\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "List")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("List", + Map.entry(List.of(), + 1)))), + Map.entry("getAccount", Map.entry(Set.of(), + Map.entry("Account", + Map.entry(List.of("int"), + 1)))), + Map.entry("getNameValue", Map.entry(Set.of("@Path(\"/{uid}/name\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("String", + Map.entry(List.of("int"), + 1)))), + Map.entry("getAccountValue", Map.entry(Set.of("@Path(\"/{uid}\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of("int"), + 1)))), + Map.entry("changeName", Map.entry(Set.of("@Path(\"/{uid}/name\")","@PUT"), + Map.entry("void", + Map.entry(List.of("int","String"), + 1)))), + Map.entry("signup", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("String"), + 1)))))))); + exprectedStructure.put("Account", Map.entry(Set.of(), + Map.entry(Map.ofEntries(Map.entry("name", "String")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of(), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getName", Map.entry(Set.of(), + Map.entry("String", + Map.entry(List.of(), + 1)))), + Map.entry("changeName", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("int","String"), + 1)))), + Map.entry("Account", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String"), + 1)))))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private void testClock() { + try { + // check PULL-first + ArrayList generatedCode = generateCode("models/Clock.model", PushPullValue.PULL); + Map, // class annotations + Entry, // field name to type + Map, // class annotations + Entry, // arg types + Integer>>>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Hour", Map.entry(Set.of("@Path(\"/hour\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "int")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("int", + Map.entry(List.of(), + 1)))), + Map.entry("updateFromMin", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("int"), + 1)))))))); + exprectedStructure.put("Min", Map.entry(Set.of("@Path(\"/min\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "int"), + Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("int", + Map.entry(List.of(), + 1)))), + Map.entry("tick", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of(), + 3)))))))); + exprectedStructure.put("Min_ang", Map.entry(Set.of("@Path(\"/min_ang\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("double", + Map.entry(List.of(), + 2)))))))); + exprectedStructure.put("Hour_ang", Map.entry(Set.of("@Path(\"/hour_ang\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("double", + Map.entry(List.of(), + 2)))))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + + // check PUSH-first + generatedCode = generateCode("models/Clock.model", PushPullValue.PUSH); + exprectedStructure.clear(); + exprectedStructure.put("Hour_ang", Map.entry(Set.of("@Path(\"/hour_ang\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "double")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("double", + Map.entry(List.of(), + 1)))), + Map.entry("updateFromHour", Map.entry(Set.of("@PUT"), + Map.entry("void", + Map.entry(List.of("int"), + 1)))))))); + exprectedStructure.put("Hour", Map.entry(Set.of("@Path(\"/hour\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "int"), + Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("int", + Map.entry(List.of(), + 1)))), + Map.entry("updateFromMin", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("int"), + 3)))))))); + exprectedStructure.put("Min", Map.entry(Set.of("@Path(\"/min\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "int"), + Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("int", + Map.entry(List.of(), + 1)))), + Map.entry("tick", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of(), + 5)))))))); + exprectedStructure.put("Min_ang", Map.entry(Set.of("@Path(\"/min_ang\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "double")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("double", + Map.entry(List.of(), + 1)))), + Map.entry("updateFromMin", Map.entry(Set.of("@PUT"), + Map.entry("void", + Map.entry(List.of("int"), + 1)))))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private void testCustomerManagement() { + try { + ArrayList generatedCode = generateCode("models/CustomerManagement.model", null); + Map, // class annotations + Entry, // field name to type + Map, // class annotations + Entry, // arg types + Integer>>>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Companies", Map.entry(Set.of("@Path(\"/companies\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "Map")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getCompany", Map.entry(Set.of(), + Map.entry("Company", + Map.entry(List.of("String"), + 1)))), + Map.entry("getAddressValue", Map.entry(Set.of("@Path(\"/{cid}/address\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("String", + Map.entry(List.of("String"), + 1)))), + Map.entry("getCompanyValue", Map.entry(Set.of("@Path(\"/{cid}\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of("String"), + 1)))), + Map.entry("setAddress", Map.entry(Set.of("@Path(\"/{cid}/address\")","@PUT"), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))), + Map.entry("addCampany", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))))))); + exprectedStructure.put("Customers", Map.entry(Set.of("@Path(\"/customers\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "Map")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getCustomer", Map.entry(Set.of(), + Map.entry("Customer", + Map.entry(List.of("String"), + 1)))), + Map.entry("getAddressValue", Map.entry(Set.of("@Path(\"/{uid}/address\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("String", + Map.entry(List.of("String"), + 1)))), + Map.entry("getCustomerValue", Map.entry(Set.of("@Path(\"/{uid}\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of("String"), + 1)))), + Map.entry("getOrganizationValue", Map.entry(Set.of("@Path(\"/{uid}/organization\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("String", + Map.entry(List.of("String"), + 1)))), + Map.entry("addCustomer", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))), + Map.entry("setOrganization", Map.entry(Set.of("@Path(\"/{uid}/organization\")","@PUT"), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))))))); + exprectedStructure.put("Customer", Map.entry(Set.of(), + Map.entry(Map.ofEntries(Map.entry("organization", "String"), + Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of(), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getOrganization", Map.entry(Set.of(), + Map.entry("String", + Map.entry(List.of(), + 1)))), + Map.entry("getAddress", Map.entry(Set.of(), + Map.entry("String", + Map.entry(List.of(), + 2)))), + Map.entry("setOrganization", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))), + Map.entry("Customer", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String"), + 1)))))))); + exprectedStructure.put("Company", Map.entry(Set.of(), + Map.entry(Map.ofEntries(Map.entry("address", "String")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of(), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getAddress", Map.entry(Set.of(), + Map.entry("String", + Map.entry(List.of(), + 1)))), + Map.entry("setAddress", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))), + Map.entry("Company", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String"), + 1)))))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private void testGroupChat() { + try { + ArrayList generatedCode = generateCode("models/GroupChat.model", null); + Map, // class annotations + Entry, // field name to type + Map, // class annotations + Entry, // arg types + Integer>>>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Groups", Map.entry(Set.of("@Path(\"/groups\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "Map")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getGroup", Map.entry(Set.of(), + Map.entry("Group", + Map.entry(List.of("String"), + 1)))), + Map.entry("getMembersValue", Map.entry(Set.of("@Path(\"/{gid}/members\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("List", + Map.entry(List.of("String"), + 1)))), + Map.entry("getMessagesValue", Map.entry(Set.of("@Path(\"/{gid}/messages\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("List", + Map.entry(List.of("String"), + 1)))), + Map.entry("getGroupValue", Map.entry(Set.of("@Path(\"/{gid}\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of("String"), + 1)))), + Map.entry("addGroupMember", Map.entry(Set.of("@Path(\"/{gid}/members\")","@POST"), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))), + Map.entry("postMessage", Map.entry(Set.of("@Path(\"/{gid}/messages\")","@POST"), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))), + Map.entry("createGroup", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("String"), + 1)))))))); + exprectedStructure.put("Account", Map.entry(Set.of(), + Map.entry(Map.ofEntries(Map.entry("notifications", "Map")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of(), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getNotifications", Map.entry(Set.of(), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("updateNotificationsFromMessages", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String","String","int","List","String"), + 1)))), + Map.entry("hasRead", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))), + Map.entry("Account", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("Map"), + 1)))))))); + exprectedStructure.put("Group", Map.entry(Set.of(), + Map.entry(Map.ofEntries(Map.entry("messages", "List"), + Map.entry("client", "Client"), + Map.entry("members", "List")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of(), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getMessages", Map.entry(Set.of(), + Map.entry("List", + Map.entry(List.of(), + 1)))), + Map.entry("getMembers", Map.entry(Set.of(), + Map.entry("List", + Map.entry(List.of(), + 1)))), + Map.entry("postMessage", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String","String"), + 6)))), + Map.entry("addGroupMember", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))), + Map.entry("Group", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("List","List"), + 2)))))))); + exprectedStructure.put("Accounts", Map.entry(Set.of("@Path(\"/accounts\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "Map")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getAccount", Map.entry(Set.of(), + Map.entry("Account", + Map.entry(List.of("String"), + 1)))), + Map.entry("updateNotificationsFromMessages", Map.entry(Set.of("@Path(\"accounts/{v1}/notifications\")","@POST"), + Map.entry("void", + Map.entry(List.of("String","String","int","List","String"), + 1)))), + Map.entry("getNotificationsValue", Map.entry(Set.of("@Path(\"/{v1}/notifications\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of("String"), + 1)))), + Map.entry("getAccountValue", Map.entry(Set.of("@Path(\"/{v1}\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of("String"), + 1)))), + Map.entry("hasRead", Map.entry(Set.of("@Path(\"/{aid}/notifications\")","@DELETE"), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))), + Map.entry("signUp", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("String"), + 1)))))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private void testInventoryManagement() { + try { + ArrayList generatedCode = generateCode("models/InventoryManagement.model", null); + Map, // class annotations + Entry, // field name to type + Map, // class annotations + Entry, // arg types + Integer>>>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("InventoryElement", Map.entry(Set.of(), + Map.entry(Map.ofEntries(Map.entry("count", "int")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of(), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getCount", Map.entry(Set.of(), + Map.entry("int", + Map.entry(List.of(), + 1)))), + Map.entry("receiveOrShip", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String","int"), + 1)))), + Map.entry("InventoryElement", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("int"), + 1)))))))); + exprectedStructure.put("Inventory", Map.entry(Set.of("@Path(\"/inventory\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "Map")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getInventoryElement", Map.entry(Set.of(), + Map.entry("InventoryElement", + Map.entry(List.of("String"), + 1)))), + Map.entry("getInventoryElementValue", Map.entry(Set.of("@Path(\"/{itemId}\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of("String"), + 1)))), + Map.entry("getCountValue", Map.entry(Set.of("@Path(\"/{itemId}/count\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("int", + Map.entry(List.of("String"), + 1)))), + Map.entry("registerItem", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("String","int","String"), + 1)))), + Map.entry("receiveOrShip", Map.entry(Set.of("@Path(\"/{itemId}/count\")","@POST"), + Map.entry("void", + Map.entry(List.of("String","int"), + 1)))))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private void testOnlineBattleGame() { + try { + // check PULL-first + ArrayList generatedCode = generateCode("models/OnlineBattleGame.model", null); + Map, // class annotations + Entry, // field name to type + Map, // class annotations + Entry, // arg types + Integer>>>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Rooms", Map.entry(Set.of("@Path(\"/rooms\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "Map")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getRoom", Map.entry(Set.of(), + Map.entry("Room", + Map.entry(List.of("String"), + 1)))), + Map.entry("getBlue_idValue", Map.entry(Set.of("@Path(\"/{rid}/blue_id\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("String", + Map.entry(List.of("String"), + 1)))), + Map.entry("getRed_nameValue", Map.entry(Set.of("@Path(\"/{rid}/red_name\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("String", + Map.entry(List.of("String"), + 1)))), + Map.entry("getBlue_nameValue", Map.entry(Set.of("@Path(\"/{rid}/blue_name\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("String", + Map.entry(List.of("String"), + 1)))), + Map.entry("getRed_idValue", Map.entry(Set.of("@Path(\"/{rid}/red_id\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("String", + Map.entry(List.of("String"), + 1)))), + Map.entry("getRoomValue", Map.entry(Set.of("@Path(\"/{rid}\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of("String"), + 1)))), + Map.entry("changeBlueId", Map.entry(Set.of("@Path(\"/{rid}/blue_id\")","@PUT"), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))), + Map.entry("createRoom", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("String","String","String"), + 1)))), + Map.entry("changeRedId", Map.entry(Set.of("@Path(\"/{rid}/red_id\")","@PUT"), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))))))); + exprectedStructure.put("Room", Map.entry(Set.of(), + Map.entry(Map.ofEntries(Map.entry("blue_id", "String"), + Map.entry("red_id", "String"), + Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of(), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getBlue_id", Map.entry(Set.of(), + Map.entry("String", + Map.entry(List.of(), + 1)))), + Map.entry("getRed_id", Map.entry(Set.of(), + Map.entry("String", + Map.entry(List.of(), + 1)))), + Map.entry("getRed_name", Map.entry(Set.of(), + Map.entry("String", + Map.entry(List.of(), + 2)))), + Map.entry("getBlue_name", Map.entry(Set.of(), + Map.entry("String", + Map.entry(List.of(), + 2)))), + Map.entry("changeRedId", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))), + Map.entry("changeBlueId", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))), + Map.entry("Room", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String","String"), + 2)))))))); + exprectedStructure.put("Account", Map.entry(Set.of(), + Map.entry(Map.ofEntries(Map.entry("name", "String")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of(), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getName", Map.entry(Set.of(), + Map.entry("String", + Map.entry(List.of(), + 1)))), + Map.entry("changeName", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))), + Map.entry("Account", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String"), + 1)))))))); + exprectedStructure.put("Accounts", Map.entry(Set.of("@Path(\"/accounts\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "Map")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getAccount", Map.entry(Set.of(), + Map.entry("Account", + Map.entry(List.of("String"), + 1)))), + Map.entry("getAccountValue", Map.entry(Set.of("@Path(\"/{aid}\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of("String"), + 1)))), + Map.entry("getNameValue", Map.entry(Set.of("@Path(\"/{aid}/name\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("String", + Map.entry(List.of("String"), + 1)))), + Map.entry("signUp", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))), + Map.entry("changeName", Map.entry(Set.of("@Path(\"/{aid}/name\")","@PUT"), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + + private void testOnlineBattleGame2() { + try { + // check PULL-first + ArrayList generatedCode = generateCode("models/OnlineBattleGame2.model", null); + Map, // class annotations + Entry, // field name to type + Map, // class annotations + Entry, // arg types + Integer>>>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Rooms", Map.entry(Set.of("@Path(\"/rooms\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "Map")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getRoom", Map.entry(Set.of(), + Map.entry("Room", + Map.entry(List.of("String"), + 1)))), + Map.entry("getIdValue", Map.entry(Set.of("@Path(\"/{rid}/members/{mno}/id\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("String", + Map.entry(List.of("String","int"), + 1)))), + Map.entry("getBattleValue", Map.entry(Set.of("@Path(\"/{rid}/battle\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("boolean", + Map.entry(List.of("String"), + 1)))), + Map.entry("getRoomValue", Map.entry(Set.of("@Path(\"/{rid}\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of("String"), + 1)))), + Map.entry("getMemberValue", Map.entry(Set.of("@Path(\"/{rid}/members/{mno}\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of("String","int"), + 1)))), + Map.entry("getMembersValue", Map.entry(Set.of("@Path(\"/{rid}/members\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("List", + Map.entry(List.of("String"), + 1)))), + Map.entry("getNameValue", Map.entry(Set.of("@Path(\"/{rid}/members/{mno}/name\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("String", + Map.entry(List.of("String","int"), + 1)))), + Map.entry("battle", Map.entry(Set.of("@Path(\"/{rid}/battle\")","@PUT"), + Map.entry("void", + Map.entry(List.of("String","boolean"), + 1)))), + Map.entry("addRoomMember", Map.entry(Set.of("@Path(\"/{rid}/members\")","@POST"), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))), + Map.entry("createRoom", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("String"), + 1)))))))); + exprectedStructure.put("Member", Map.entry(Set.of(), + Map.entry(Map.ofEntries(Map.entry("id", "String"), + Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of(), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getId", Map.entry(Set.of(), + Map.entry("String", + Map.entry(List.of(), + 1)))), + Map.entry("getName", Map.entry(Set.of(), + Map.entry("String", + Map.entry(List.of(), + 2)))), + Map.entry("Member", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String"), + 1)))))))); + exprectedStructure.put("Account", Map.entry(Set.of(), + Map.entry(Map.ofEntries(Map.entry("name", "String"), + Map.entry("point", "int")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of(), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getName", Map.entry(Set.of(), + Map.entry("String", + Map.entry(List.of(), + 1)))), + Map.entry("getPoint", Map.entry(Set.of(), + Map.entry("int", + Map.entry(List.of(), + 1)))), + Map.entry("updatePointFromBattle", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String","String","int","boolean","String"), + 1)))), + Map.entry("changeName", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))), + Map.entry("Account", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String","int"), + 2)))))))); + exprectedStructure.put("Room", Map.entry(Set.of(), + Map.entry(Map.ofEntries(Map.entry("members", "Members"), + Map.entry("battle", "boolean"), + Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of(), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getMembers", Map.entry(Set.of(), + Map.entry("Members", + Map.entry(List.of(), + 1)))), + Map.entry("getBattle", Map.entry(Set.of(), + Map.entry("boolean", + Map.entry(List.of(), + 1)))), + Map.entry("battle", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String","boolean"), + 6)))), + Map.entry("Room", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("boolean"), + 1)))))))); + exprectedStructure.put("Accounts", Map.entry(Set.of("@Path(\"/accounts\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "Map")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getAccount", Map.entry(Set.of(), + Map.entry("Account", + Map.entry(List.of("String"), + 1)))), + Map.entry("updatePointFromBattle", Map.entry(Set.of("@Path(\"accounts/{mid}/point\")","@POST"), + Map.entry("void", + Map.entry(List.of("String","String","int","boolean","String"), + 1)))), + Map.entry("getAccountValue", Map.entry(Set.of("@Path(\"/{mid}\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of("String"), + 1)))), + Map.entry("getNameValue", Map.entry(Set.of("@Path(\"/{mid}/name\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("String", + Map.entry(List.of("String"), + 1)))), + Map.entry("getPointValue", Map.entry(Set.of("@Path(\"/{mid}/point\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("int", + Map.entry(List.of("String"), + 1)))), + Map.entry("signUp", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))), + Map.entry("changeName", Map.entry(Set.of("@Path(\"/{aid}/name\")","@PUT"), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))))))); + exprectedStructure.put("Members", Map.entry(Set.of(), + Map.entry(Map.ofEntries(Map.entry("value", "List")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of(), + Map.entry("List", + Map.entry(List.of(), + 1)))), + Map.entry("getMember", Map.entry(Set.of(), + Map.entry("Member", + Map.entry(List.of("int"), + 1)))), + Map.entry("addRoomMember", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private void testPOS() { + try { + // check PULL-first + ArrayList generatedCode = generateCode("models/POS.model", PushPullValue.PULL); + Map, // class annotations + Entry, // field name to type + Map, // class annotations + Entry, // arg types + Integer>>>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Points", Map.entry(Set.of("@Path(\"/points\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("int", + Map.entry(List.of(), + 2)))))))); + exprectedStructure.put("Total", Map.entry(Set.of("@Path(\"/total\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("int", + Map.entry(List.of(), + 2)))))))); + exprectedStructure.put("Payment", Map.entry(Set.of("@Path(\"/payment\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "int"), + Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("int", + Map.entry(List.of(), + 1)))), + Map.entry("purchase", Map.entry(Set.of("@PUT"), + Map.entry("void", + Map.entry(List.of("int"), + 3)))))))); + exprectedStructure.put("History", Map.entry(Set.of("@Path(\"/history\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "List")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("List", + Map.entry(List.of(), + 1)))), + Map.entry("updateFromPayment", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("int"), + 1)))))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + + // check PUSH-first + generatedCode = generateCode("models/POS.model", PushPullValue.PUSH); + exprectedStructure.clear(); + exprectedStructure.put("Payment", Map.entry(Set.of("@Path(\"/payment\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "int"), + Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("int", + Map.entry(List.of(), + 1)))), + Map.entry("purchase", Map.entry(Set.of("@PUT"), + Map.entry("void", + Map.entry(List.of("int"), + 5)))))))); + exprectedStructure.put("Total", Map.entry(Set.of("@Path(\"/total\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "int")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("int", + Map.entry(List.of(), + 1)))), + Map.entry("updateFromHistory", Map.entry(Set.of("@PUT"), + Map.entry("void", + Map.entry(List.of("List"), + 1)))))))); + exprectedStructure.put("History", Map.entry(Set.of("@Path(\"/history\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "List"), + Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("List", + Map.entry(List.of(), + 1)))), + Map.entry("updateFromPayment", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("int"), + 3)))))))); + exprectedStructure.put("Points", Map.entry(Set.of("@Path(\"/points\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "int")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("int", + Map.entry(List.of(), + 1)))), + Map.entry("updateFromPayment", Map.entry(Set.of("@PUT"), + Map.entry("void", + Map.entry(List.of("int"), + 1)))))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private void testSimpleTwitter() { + try { + ArrayList generatedCode = generateCode("models/SimpleTwitter.model", null); + Map, // class annotations + Entry, // field name to type + Map, // class annotations + Entry, // arg types + Integer>>>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Accounts", Map.entry(Set.of("@Path(\"/accounts\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "Map")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getAccount", Map.entry(Set.of(), + Map.entry("Account", + Map.entry(List.of("String"), + 1)))), + Map.entry("getAccountValue", Map.entry(Set.of("@Path(\"/{accountId}\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of("String"), + 1)))), + Map.entry("getTweetsValue", Map.entry(Set.of("@Path(\"/{accountId}/tweets\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("List", + Map.entry(List.of("String"), + 1)))), + Map.entry("tweet", Map.entry(Set.of("@Path(\"/{accountId}/tweets\")","@POST"), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))), + Map.entry("signUp", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))))))); + exprectedStructure.put("Account", Map.entry(Set.of(), + Map.entry(Map.ofEntries(Map.entry("tweets", "List")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of(), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getTweets", Map.entry(Set.of(), + Map.entry("List", + Map.entry(List.of(), + 1)))), + Map.entry("tweet", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))), + Map.entry("Account", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("List"), + 1)))))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private void testVotingSystem() { + try { + ArrayList generatedCode = generateCode("models/VotingSystem.model", null); + Map, // class annotations + Entry, // field name to type + Map, // class annotations + Entry, // arg types + Integer>>>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Counts", Map.entry(Set.of("@Path(\"/counts\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "Map"), + Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of(), + 8)))))))); + exprectedStructure.put("Account", Map.entry(Set.of(), + Map.entry(Map.ofEntries(Map.entry("vote", "String")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of(), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getVote", Map.entry(Set.of(), + Map.entry("String", + Map.entry(List.of(), + 1)))), + Map.entry("cast", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))), + Map.entry("Account", Map.entry(Set.of(), + Map.entry("void", + Map.entry(List.of("String"), + 1)))))))); + exprectedStructure.put("Accounts", Map.entry(Set.of("@Path(\"/accounts\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "Map")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of(), + 1)))), + Map.entry("getAccount", Map.entry(Set.of(), + Map.entry("Account", + Map.entry(List.of("String"), + 1)))), + Map.entry("getVoteValue", Map.entry(Set.of("@Path(\"/{aid}/vote\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("String", + Map.entry(List.of("String"), + 1)))), + Map.entry("getAccountValue", Map.entry(Set.of("@Path(\"/{aid}\")","@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("Map", + Map.entry(List.of("String"), + 1)))), + Map.entry("cast", Map.entry(Set.of("@Path(\"/{aid}/vote\")","@PUT"), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))), + Map.entry("signUp", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("String","String"), + 1)))))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private void testWeatherObservationSystem() { + try { + // check PULL-first + ArrayList generatedCode = generateCode("models/WeatherObservationSystem.model", PushPullValue.PULL); + Map, // class annotations + Entry, // field name to type + Map, // class annotations + Entry, // arg types + Integer>>>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Temp_c", Map.entry(Set.of("@Path(\"/temp_c\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("double", + Map.entry(List.of(), + 2)))))))); + exprectedStructure.put("Highest", Map.entry(Set.of("@Path(\"/highest\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "double")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("double", + Map.entry(List.of(), + 1)))), + Map.entry("updateFromTemp_f", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("double"), + 1)))), + Map.entry("reset", Map.entry(Set.of("@PUT"), + Map.entry("void", + Map.entry(List.of("double"), + 1)))))))); + exprectedStructure.put("Temp_f", Map.entry(Set.of("@Path(\"/temp_f\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "double"), + Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("double", + Map.entry(List.of(), + 1)))), + Map.entry("observe", Map.entry(Set.of("@PUT"), + Map.entry("void", + Map.entry(List.of("double"), + 3)))))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + + // check PUSH-first + generatedCode = generateCode("models/WeatherObservationSystem.model", PushPullValue.PUSH); + exprectedStructure.clear(); + exprectedStructure.put("Highest", Map.entry(Set.of("@Path(\"/highest\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "double")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("double", + Map.entry(List.of(), + 1)))), + Map.entry("updateFromTemp_f", Map.entry(Set.of("@POST"), + Map.entry("void", + Map.entry(List.of("double"), + 1)))), + Map.entry("reset", Map.entry(Set.of("@PUT"), + Map.entry("void", + Map.entry(List.of("double"), + 1)))))))); + exprectedStructure.put("Temp_f", Map.entry(Set.of("@Path(\"/temp_f\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "double"), + Map.entry("client", "Client")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("double", + Map.entry(List.of(), + 1)))), + Map.entry("observe", Map.entry(Set.of("@PUT"), + Map.entry("void", + Map.entry(List.of("double"), + 5)))))))); + exprectedStructure.put("Temp_c", Map.entry(Set.of("@Path(\"/temp_c\")","@Component"), + Map.entry(Map.ofEntries(Map.entry("value", "double")), + Map.ofEntries(Map.entry("getValue", Map.entry(Set.of("@Produces(MediaType.APPLICATION_JSON)","@GET"), + Map.entry("double", + Map.entry(List.of(), + 1)))), + Map.entry("updateFromTemp_f", Map.entry(Set.of("@PUT"), + Map.entry("void", + Map.entry(List.of("double"), + 1)))))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private ArrayList generateCode(String fileName, PushPullValue pushPullValue) throws FileNotFoundException, + ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedRightCurlyBracket, ExpectedInOrOutOrRefOrSubKeyword, + ExpectedStateTransition, ExpectedEquals, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, + WrongPathExpression, WrongJsonExpression, ExpectedColon, ExpectedDoubleQuotation { + File file = new File(fileName); + Parser parser = new Parser(new BufferedReader(new FileReader(file))); + DataTransferModel model; + model = parser.doParse(); + DataFlowGraph graph = DataTransferModelAnalyzer.createDataFlowGraphWithStateStoringAttribute(model); + DataTransferModelAnalyzer.annotateWithSelectableDataTransferAttiribute(graph); + if (pushPullValue != null) { + // Select a specified push/pull transfer method if possible. + for (Edge e : graph.getEdges()) { + if (!((DataFlowEdge) e).isChannelToResource() && ((DataFlowEdge) e).getAttribute() instanceof PushPullAttribute) { + PushPullAttribute ppat = (PushPullAttribute) ((DataFlowEdge) e).getAttribute(); + ppat.selectOption(pushPullValue); + } + } + } + TypeInference.infer(model); + DataTransferMethodAnalyzer.decideToStoreResourceStates(graph); + ArrayList codetree = JerseyMethodBodyGenerator.doGenerate(graph, model, JerseyCodeGenerator.doGenerate(graph, model)); +// ArrayList codetree = new CodeGeneratorFromDataFlowGraph().generateCode(model, graph, new JerseySpecific(), new JavaSpecific()); + return codetree; + } + + private void checkStructure(ArrayList generatedCode, + Map, Entry, Map, Entry, Integer>>>>>>> exprectedStructure) { + for (var classEnt: exprectedStructure.entrySet()) { + String expectedClassName = classEnt.getKey(); + Entry, Entry, Map, Entry, Integer>>>>>> expectedClassInfo = classEnt.getValue(); + TypeDeclaration generatedClass = null; + for (CompilationUnit cu: generatedCode) { + for (TypeDeclaration type: cu.types()) { + if (type.getTypeName().equals(expectedClassName)) { + generatedClass = type; + break; + } + } + } + assertNotNull(generatedClass); + + Set expectedClassAnnotations = expectedClassInfo.getKey(); + for (String expectedAnnotation: expectedClassAnnotations) { + boolean existsAnnotation = false; + for (Annotation generatedAnnotation: generatedClass.getAnnotations()) { + if (expectedAnnotation.equals(generatedAnnotation.toString())) { + existsAnnotation = true; + } + } + assertTrue(existsAnnotation); + } + Entry, Map, Entry, Integer>>>>> expectedClassStructure = expectedClassInfo.getValue(); + Map exprectedFields = expectedClassStructure.getKey(); + Map, Entry, Integer>>>> exprectedMethods = expectedClassStructure.getValue(); + + for (String expectedFieldName: exprectedFields.keySet()) { + FieldDeclaration generatedField = null; + for (FieldDeclaration field: generatedClass.getFields()) { + if (field.getName().equals(expectedFieldName)) { + generatedField = field; + break; + } + } + assertNotNull(generatedField); + + String expectedFieldType = exprectedFields.get(expectedFieldName); + if (expectedFieldType.equals("void")) { + assertNull(generatedField.getType()); + } else { + assertEquals(expectedFieldType, generatedField.getType().getInterfaceTypeName()); + } + } + + for (String expectedMethodName: exprectedMethods.keySet()) { + MethodDeclaration generatedMethod= null; + for (MethodDeclaration method: generatedClass.getMethods()) { + if (method.getName().equals(expectedMethodName)) { + generatedMethod = method; + break; + } + } + assertNotNull(generatedMethod); + + Entry, Entry, Integer>>> expectedMethodInfo = exprectedMethods.get(expectedMethodName); + Set expectedMethodAnnotations = expectedMethodInfo.getKey(); + for (String expectedAnnotation: expectedMethodAnnotations) { + boolean existsAnnotation = false; + for (Annotation generatedAnnotation: generatedMethod.getAnnotations()) { + if (expectedAnnotation.replaceAll("\\{.*\\}", "\\{\\}").equals(generatedAnnotation.toString().replaceAll("\\{.*\\}", "\\{\\}"))) { + existsAnnotation = true; + } + } + assertTrue(existsAnnotation); + } + Entry, Integer>> expectedMethodSignature = expectedMethodInfo.getValue(); + String expectedReturnType = expectedMethodSignature.getKey(); + if (expectedReturnType.equals("void")) { + if (generatedMethod.getReturnType() != null) { + assertEquals(expectedReturnType, generatedMethod.getReturnType().getInterfaceTypeName()); + } else { + assertNull(generatedMethod.getReturnType()); + } + } else { + assertEquals(expectedReturnType, generatedMethod.getReturnType().getInterfaceTypeName()); + } + Entry, Integer> expectedMethodInfo2 = expectedMethodSignature.getValue(); + List expectedArgTypes = expectedMethodInfo2.getKey(); + for (String expectedArgType: expectedArgTypes) { + boolean existsArg = false; + for (VariableDeclaration var: generatedMethod.getParameters()) { + if (expectedArgType.equals(var.getType().getInterfaceTypeName())) { + existsArg = true; + } + } + assertTrue(existsArg); + } + int expectedLinesOfCode = expectedMethodInfo2.getValue(); + assertEquals(expectedLinesOfCode, generatedMethod.getBody().getStatements().size()); + } + } + } + + private void generateCheckCode(ArrayList generatedCode) { +// exprectedStructure.put("Main", Map.entry(Set.of(), +// Map.entry(Map.ofEntries(Map.entry("history", "History"), +// Map.entry("total", "Total"), +// Map.entry("payment", "Payment"), +// Map.entry("points", "Points")), +// Map.ofEntries(Map.entry("Main", Map.entry(Set.of(), +// Map.entry("void", +// Map.entry(List.of(), +// 4)))), +// Map.entry("getHistory", Map.entry(Set.of(), +// Map.entry("List", +// Map.entry(List.of(), +// 1)))), +// Map.entry("getTotal", Map.entry(Set.of(), +// Map.entry("int", +// Map.entry(List.of(), +// 1)))), +// Map.entry("getPayment", Map.entry(Set.of(), +// Map.entry("int", +// Map.entry(List.of(), +// 1)))), +// Map.entry("purchase", Map.entry(Set.of(), +// Map.entry("void", +// Map.entry(List.of("int"), +// 1)))), +// Map.entry("getPoints", Map.entry(Set.of(), +// Map.entry("int", +// Map.entry(List.of(), +// 1)))))))); + for (CompilationUnit cu: generatedCode) { + for (TypeDeclaration type: cu.types()) { + Collection annotations = type.getAnnotations(); + List fields = type.getFields(); + List methods = type.getMethods(); + // class annotations + if (annotations.size() == 0) { + System.out.println("\t\t\texprectedStructure.put(\"" + type.getTypeName() + "\", Map.entry(Set.of(),"); + } else { + System.out.print("\t\t\texprectedStructure.put(\"" + type.getTypeName() + "\", Map.entry(Set.of("); + String delim = ""; + for (Annotation annotation: annotations) { + System.out.print(delim + "\"" + annotation.toString().replace("\"", "\\\"") + "\""); + delim = ","; + } + System.out.println("),"); + } + // fields + System.out.print("\t\t\t\t\t\t\t\t\t\t\t\t"); + for (int j = 0; j < (type.getTypeName().length() + 1) / 4; j++) { + System.out.print("\t"); + } + for (int j = 0; j < (type.getTypeName().length() + 1) % 4; j++) { + System.out.print(" "); + } + if (fields.size() == 0) { + System.out.println("Map.entry(Map.ofEntries(),"); + } else if (fields.size() == 1) { + FieldDeclaration field = fields.get(0); + System.out.println("Map.entry(Map.ofEntries(Map.entry(\"" + field.getName() + "\", \"" + field.getType().getInterfaceTypeName() + "\")),"); + } else { + int i = 0; + for (FieldDeclaration field: fields) { + if (i == 0) { + System.out.println("Map.entry(Map.ofEntries(Map.entry(\"" + field.getName() + "\", \"" + field.getType().getInterfaceTypeName() + "\"),"); + } else { + System.out.print("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"); + for (int j = 0; j < (type.getTypeName().length() + 1) / 4; j++) { + System.out.print("\t"); + } + for (int j = 0; j < (type.getTypeName().length() + 1) % 4; j++) { + System.out.print(" "); + } + if (i < fields.size() - 1) { + System.out.println("Map.entry(\"" + field.getName() + "\", \"" + field.getType().getInterfaceTypeName() + "\"),"); + } else { + System.out.println("Map.entry(\"" + field.getName() + "\", \"" + field.getType().getInterfaceTypeName() + "\")),"); + } + } + i++; + } + } + // methods + if (methods.size() == 0) { + System.out.print("\t\t\t\t\t\t\t\t\t\t\t\t\t\t"); + for (int j = 0; j < (type.getTypeName().length() + 3) / 4; j++) { + System.out.print("\t"); + } + for (int j = 0; j < (type.getTypeName().length() + 3) % 4; j++) { + System.out.print(" "); + } + System.out.println("Map.ofEntries())));"); + } else { + int i = 0; + for (MethodDeclaration method: methods) { + // method name and method annotations + if (i == 0) { + System.out.print("\t\t\t\t\t\t\t\t\t\t\t\t\t\t"); + for (int j = 0; j < (type.getTypeName().length() + 3) / 4; j++) { + System.out.print("\t"); + } + for (int j = 0; j < (type.getTypeName().length() + 3) % 4; j++) { + System.out.print(" "); + } + Collection methodAnnotations = method.getAnnotations(); + if (methodAnnotations.size() == 0) { + System.out.println("Map.ofEntries(Map.entry(\"" + method.getName() + "\", Map.entry(Set.of(),"); + } else { + System.out.print("Map.ofEntries(Map.entry(\"" + method.getName() + "\", Map.entry(Set.of("); + String delim = ""; + for (Annotation annotation: methodAnnotations) { + System.out.print(delim + "\"" + annotation.toString().replace("\"", "\\\"") + "\""); + delim = ","; + } + System.out.println("),"); + } + } else { + System.out.print("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"); + for (int j = 0; j < (type.getTypeName().length() + 1) / 4; j++) { + System.out.print("\t"); + } + for (int j = 0; j < (type.getTypeName().length() + 1) % 4; j++) { + System.out.print(" "); + } + Collection methodAnnotations = method.getAnnotations(); + if (methodAnnotations.size() == 0) { + System.out.println("Map.entry(\"" + method.getName() + "\", Map.entry(Set.of(),"); + } else { + System.out.print("Map.entry(\"" + method.getName() + "\", Map.entry(Set.of("); + String delim = ""; + for (Annotation annotation: methodAnnotations) { + System.out.print(delim + "\"" + annotation.toString().replace("\"", "\\\"") + "\""); + delim = ","; + } + System.out.println("),"); + } + } + // return type + System.out.print("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"); + for (int j = 0; j < (type.getTypeName().length() + method.getName().length() + 3) / 4; j++) { + System.out.print("\t"); + } + for (int j = 0; j < (type.getTypeName().length() + method.getName().length() + 3) % 4; j++) { + System.out.print(" "); + } + if (method.getReturnType() == null) { + System.out.println("Map.entry(\"void\", "); + } else { + System.out.println("Map.entry(\"" + method.getReturnType().getInterfaceTypeName() + "\", "); + } + // method parameters + System.out.print("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"); + for (int j = 0; j < (type.getTypeName().length() + method.getName().length() + 3) / 4; j++) { + System.out.print("\t"); + } + for (int j = 0; j < (type.getTypeName().length() + method.getName().length() + 3) % 4; j++) { + System.out.print(" "); + } + if (method.getParameters() == null || method.getParameters().size() == 0) { + System.out.println("Map.entry(List.of(),"); + } else { + System.out.print("Map.entry(List.of("); + String delim = ""; + for (VariableDeclaration arg: method.getParameters()) { + System.out.print(delim + "\"" + arg.getType().getInterfaceTypeName() + "\""); + delim = ","; + } + System.out.println("),"); + } + // method lines of code + System.out.print("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"); + for (int j = 0; j < (type.getTypeName().length() + method.getName().length() + 3) / 4; j++) { + System.out.print("\t"); + } + for (int j = 0; j < (type.getTypeName().length() + method.getName().length() + 3) % 4; j++) { + System.out.print(" "); + } + if (i < methods.size() - 1) { + System.out.println("" + method.getBody().getStatements().size() + ")))),"); + } else { + System.out.println("" + method.getBody().getStatements().size() + "))))))));"); + } + i++; + } + } + } + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/tests/JavaCodeGeneratorTest.java b/AlgebraicDataflowArchitectureModel/src/tests/JavaCodeGeneratorTest.java new file mode 100644 index 0000000..b9a5dc0 --- /dev/null +++ b/AlgebraicDataflowArchitectureModel/src/tests/JavaCodeGeneratorTest.java @@ -0,0 +1,1654 @@ +package tests; + +import static org.junit.Assert.*; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.junit.Test; + +import algorithms.*; +import code.ast.CompilationUnit; +import code.ast.FieldDeclaration; +import code.ast.MethodDeclaration; +import code.ast.TypeDeclaration; +import code.ast.VariableDeclaration; +import generators.CodeGeneratorFromDataFlowGraph; +import generators.DataTransferMethodAnalyzer; +import generators.JavaCodeGenerator; +import generators.JavaMethodBodyGenerator; +import generators.JavaSpecific; +import generators.StandaloneSpecific; +import models.Edge; +import models.dataFlowModel.*; +import parser.*; +import parser.exceptions.ExpectedAssignment; +import parser.exceptions.ExpectedChannel; +import parser.exceptions.ExpectedChannelName; +import parser.exceptions.ExpectedColon; +import parser.exceptions.ExpectedDoubleQuotation; +import parser.exceptions.ExpectedEquals; +import parser.exceptions.ExpectedInOrOutOrRefOrSubKeyword; +import parser.exceptions.ExpectedLeftCurlyBracket; +import parser.exceptions.ExpectedRHSExpression; +import parser.exceptions.ExpectedRightBracket; +import parser.exceptions.ExpectedRightCurlyBracket; +import parser.exceptions.ExpectedStateTransition; +import parser.exceptions.WrongJsonExpression; +import parser.exceptions.WrongLHSExpression; +import parser.exceptions.WrongPathExpression; +import parser.exceptions.WrongRHSExpression; + +public class JavaCodeGeneratorTest { + + @Test + public void test() { + testAccounts(); + testClock(); + testCustomerManagement(); // Two methods with the same signature are generated. + testGroupChat(); + testInventoryManagement(); +// testOnlineBattleGame(); // A feature has not been implemented for Java prototype generation. + testOnlineBattleGame2(); // Two methods with the same signature are generated. PUSH-first implementation still does not work. + testPOS(); + testSimpleTwitter(); + testVotingSystem(); + testWeatherObservationSystem(); + } + + private void testAccounts() { + try { + ArrayList generatedCode = generateCode("models/Accounts.model", null); + Map, // field name to type + Map, // arg types + Integer>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Main", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("Main", Map.entry("void", + Map.entry(List.of(), + 1))), + Map.entry("getAccount", Map.entry("Map", + Map.entry(List.of("int"), + 1))), + Map.entry("getName", Map.entry("String", + Map.entry(List.of("int"), + 1))), + Map.entry("changeName", Map.entry("void", + Map.entry(List.of("int","String"), + 1))), + Map.entry("getAccounts", Map.entry("List", + Map.entry(List.of(), + 1))), + Map.entry("signup", Map.entry("void", + Map.entry(List.of("String"), + 1)))))); + exprectedStructure.put("Account", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getName", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("changeName", Map.entry("void", + Map.entry(List.of("int","String"), + 1))), + Map.entry("Account", Map.entry("void", + Map.entry(List.of("String"), + 1)))))); + exprectedStructure.put("Accounts", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("List", + Map.entry(List.of(), + 1))), + Map.entry("getAccount", Map.entry("Account", + Map.entry(List.of("int"), + 1))), + Map.entry("signup", Map.entry("void", + Map.entry(List.of("String"), + 1)))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private void testClock() { + try { + // check PULL-first + ArrayList generatedCode = generateCode("models/Clock.model", PushPullValue.PULL); + Map, // field name to type + Map, // arg types + Integer>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Main", Map.entry(Map.ofEntries(Map.entry("hour", "Hour"), + Map.entry("hour_ang", "Hour_ang"), + Map.entry("min", "Min"), + Map.entry("min_ang", "Min_ang")), + Map.ofEntries(Map.entry("Main", Map.entry("void", + Map.entry(List.of(), + 4))), + Map.entry("getHour", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("getHour_ang", Map.entry("double", + Map.entry(List.of(), + 1))), + Map.entry("getMin", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("tick", Map.entry("void", + Map.entry(List.of(), + 1))), + Map.entry("getMin_ang", Map.entry("double", + Map.entry(List.of(), + 1)))))); + exprectedStructure.put("Hour", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("updateFromMin", Map.entry("void", + Map.entry(List.of("int"), + 1)))))); + exprectedStructure.put("Hour_ang", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("double", + Map.entry(List.of(), + 1))), + Map.entry("Hour_ang", Map.entry("void", + Map.entry(List.of("Hour"), + 1)))))); + exprectedStructure.put("Min", Map.entry(Map.ofEntries(Map.entry("value", "int"), + Map.entry("hour", "Hour")), + Map.ofEntries(Map.entry("getValue", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("tick", Map.entry("void", + Map.entry(List.of(), + 2))), + Map.entry("Min", Map.entry("void", + Map.entry(List.of("Hour"), + 1)))))); + exprectedStructure.put("Min_ang", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("double", + Map.entry(List.of(), + 1))), + Map.entry("Min_ang", Map.entry("void", + Map.entry(List.of("Min"), + 1)))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + + // check PUSH-first + generatedCode = generateCode("models/Clock.model", PushPullValue.PUSH); + exprectedStructure.clear(); + exprectedStructure.put("Main", Map.entry(Map.ofEntries(Map.entry("hour_ang", "Hour_ang"), + Map.entry("min_ang", "Min_ang"), + Map.entry("hour", "Hour"), + Map.entry("min", "Min")), + Map.ofEntries(Map.entry("Main", Map.entry("void", + Map.entry(List.of(), + 4))), + Map.entry("getHour_ang", Map.entry("double", + Map.entry(List.of(), + 1))), + Map.entry("getMin_ang", Map.entry("double", + Map.entry(List.of(), + 1))), + Map.entry("getHour", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("getMin", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("tick", Map.entry("void", + Map.entry(List.of(), + 1)))))); + exprectedStructure.put("Hour_ang", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("double", + Map.entry(List.of(), + 1))), + Map.entry("updateFromHour", Map.entry("void", + Map.entry(List.of("int"), + 1)))))); + exprectedStructure.put("Min_ang", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("double", + Map.entry(List.of(), + 1))), + Map.entry("updateFromMin", Map.entry("void", + Map.entry(List.of("int"), + 1)))))); + exprectedStructure.put("Hour", Map.entry(Map.ofEntries(Map.entry("value", "int"), + Map.entry("hour_ang", "Hour_ang")), + Map.ofEntries(Map.entry("getValue", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("updateFromMin", Map.entry("void", + Map.entry(List.of("int"), + 2))), + Map.entry("Hour", Map.entry("void", + Map.entry(List.of("Hour_ang"), + 1)))))); + exprectedStructure.put("Min", Map.entry(Map.ofEntries(Map.entry("value", "int"), + Map.entry("min_ang", "Min_ang"), + Map.entry("hour", "Hour")), + Map.ofEntries(Map.entry("getValue", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("tick", Map.entry("void", + Map.entry(List.of(), + 3))), + Map.entry("Min", Map.entry("void", + Map.entry(List.of("Min_ang","Hour"), + 2)))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private void testCustomerManagement() { + try { + ArrayList generatedCode = generateCode("models/CustomerManagement.model", null); + Map, // field name to type + Map, // arg types + Integer>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Main", Map.entry(Map.ofEntries(Map.entry("companies", "Companies"), + Map.entry("customers", "Customers")), + Map.ofEntries(Map.entry("Main", Map.entry("void", + Map.entry(List.of(), + 2))), + Map.entry("getAddress", Map.entry("String", + Map.entry(List.of("String"), + 1))), + Map.entry("setAddress", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("getCustomer", Map.entry("Map", + Map.entry(List.of("String"), + 1))), + Map.entry("getCompanies", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("addCampany", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("getOrganization", Map.entry("String", + Map.entry(List.of("String"), + 1))), + Map.entry("setOrganization", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), +// Map.entry("getAddress", Map.entry("String", +// Map.entry(List.of("String"), +// 1))), + Map.entry("getCompany", Map.entry("Map", + Map.entry(List.of("String"), + 1))), + Map.entry("getCustomers", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("addCustomer", Map.entry("void", + Map.entry(List.of("String","String"), + 1)))))); + exprectedStructure.put("Customer", Map.entry(Map.ofEntries(Map.entry("organization", "String"), + Map.entry("company", "Company"), + Map.entry("companies", "Companies")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getOrganization", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("getAddress", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("setOrganization", Map.entry("void", + Map.entry(List.of("String","String"), + 2))), + Map.entry("Customer", Map.entry("void", + Map.entry(List.of("String","Companies"), + 3)))))); + exprectedStructure.put("Companies", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getCompany", Map.entry("Company", + Map.entry(List.of("String"), + 1))), + Map.entry("addCampany", Map.entry("void", + Map.entry(List.of("String","String"), + 1)))))); + exprectedStructure.put("Company", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getAddress", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("setAddress", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("Company", Map.entry("void", + Map.entry(List.of("String"), + 1)))))); + exprectedStructure.put("Customers", Map.entry(Map.ofEntries(Map.entry("value", "Map"), + Map.entry("companies", "Companies")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getCustomer", Map.entry("Customer", + Map.entry(List.of("String"), + 1))), + Map.entry("addCustomer", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("Customers", Map.entry("void", + Map.entry(List.of("Companies"), + 1)))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private void testGroupChat() { + try { + ArrayList generatedCode = generateCode("models/GroupChat.model", null); + Map, // field name to type + Map, // arg types + Integer>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Main", Map.entry(Map.ofEntries(Map.entry("accounts", "Accounts"), + Map.entry("groups", "Groups")), + Map.ofEntries(Map.entry("Main", Map.entry("void", + Map.entry(List.of(), + 2))), + Map.entry("getAccounts", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("signUp", Map.entry("void", + Map.entry(List.of("String"), + 1))), + Map.entry("getGroup", Map.entry("Map", + Map.entry(List.of("String"), + 1))), + Map.entry("getMessages", Map.entry("List", + Map.entry(List.of("String"), + 1))), + Map.entry("postMessage", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("getAccount", Map.entry("Map", + Map.entry(List.of("String"), + 1))), + Map.entry("getMember", Map.entry("String", + Map.entry(List.of("String","int"), + 1))), + Map.entry("getNotifications", Map.entry("Map", + Map.entry(List.of("String"), + 1))), + Map.entry("hasRead", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("getMembers", Map.entry("List", + Map.entry(List.of("String"), + 1))), + Map.entry("addGroupMember", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("getGroups", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("createGroup", Map.entry("void", + Map.entry(List.of("String"), + 1)))))); + exprectedStructure.put("Accounts", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getAccount", Map.entry("Account", + Map.entry(List.of("String"), + 1))), + Map.entry("signUp", Map.entry("void", + Map.entry(List.of("String"), + 1)))))); + exprectedStructure.put("Group", Map.entry(Map.ofEntries(Map.entry("messages", "List"), + Map.entry("account", "Account"), + Map.entry("accounts", "Accounts"), + Map.entry("members", "List")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getMessages", Map.entry("List", + Map.entry(List.of(), + 1))), + Map.entry("getMember", Map.entry("String", + Map.entry(List.of("int"), + 1))), + Map.entry("getMembers", Map.entry("List", + Map.entry(List.of(), + 1))), + Map.entry("postMessage", Map.entry("void", + Map.entry(List.of("String","String"), + 6))), + Map.entry("addGroupMember", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("Group", Map.entry("void", + Map.entry(List.of("List","Accounts","List"), + 3)))))); + exprectedStructure.put("Account", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getNotifications", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("updateNotificationsFromMessages", Map.entry("void", + Map.entry(List.of("String","String","int","List","String"), + 1))), + Map.entry("hasRead", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("Account", Map.entry("void", + Map.entry(List.of("Map"), + 1)))))); + exprectedStructure.put("Groups", Map.entry(Map.ofEntries(Map.entry("value", "Map"), + Map.entry("accounts", "Accounts")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getGroup", Map.entry("Group", + Map.entry(List.of("String"), + 1))), + Map.entry("createGroup", Map.entry("void", + Map.entry(List.of("String"), + 1))), + Map.entry("Groups", Map.entry("void", + Map.entry(List.of("Accounts"), + 1)))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private void testInventoryManagement() { + try { + ArrayList generatedCode = generateCode("models/InventoryManagement.model", null); + Map, // field name to type + Map, // arg types + Integer>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Main", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("Main", Map.entry("void", + Map.entry(List.of(), + 1))), + Map.entry("getInventoryElement", Map.entry("Map", + Map.entry(List.of("String"), + 1))), + Map.entry("getInventory", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("registerItem", Map.entry("void", + Map.entry(List.of("String","int","String"), + 1))), + Map.entry("getCount", Map.entry("int", + Map.entry(List.of("String"), + 1))), + Map.entry("receiveOrShip", Map.entry("void", + Map.entry(List.of("String","int"), + 1)))))); + exprectedStructure.put("InventoryElement", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getCount", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("receiveOrShip", Map.entry("void", + Map.entry(List.of("String","int"), + 1))), + Map.entry("InventoryElement", Map.entry("void", + Map.entry(List.of("int"), + 1)))))); + exprectedStructure.put("Inventory", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getInventoryElement", Map.entry("InventoryElement", + Map.entry(List.of("String"), + 1))), + Map.entry("registerItem", Map.entry("void", + Map.entry(List.of("String","int","String"), + 1)))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private void testOnlineBattleGame() { + try { + // check PULL-first + ArrayList generatedCode = generateCode("models/OnlineBattleGame.model", PushPullValue.PULL); + Map, // field name to type + Map, // arg types + Integer>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Main", Map.entry(Map.ofEntries(Map.entry("accounts", "Accounts"), + Map.entry("rooms", "Rooms")), + Map.ofEntries(Map.entry("Main", Map.entry("void", + Map.entry(List.of(), + 2))), + Map.entry("getRoom", Map.entry("Map", + Map.entry(List.of("String"), + 1))), + Map.entry("getName", Map.entry("String", + Map.entry(List.of("String"), + 1))), + Map.entry("changeName", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("getAccount", Map.entry("Map", + Map.entry(List.of("String"), + 1))), + Map.entry("getBlue_id", Map.entry("String", + Map.entry(List.of("String"), + 1))), + Map.entry("changeBlueId", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("getRed_id", Map.entry("String", + Map.entry(List.of("String"), + 1))), + Map.entry("changeRedId", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("getAccounts", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("signUp", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("getRooms", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("createRoom", Map.entry("void", + Map.entry(List.of("String","String","String"), + 1))), + Map.entry("getRed_name", Map.entry("String", + Map.entry(List.of("String"), + 1))), + Map.entry("getBlue_name", Map.entry("String", + Map.entry(List.of("String"), + 1)))))); + exprectedStructure.put("Room", Map.entry(Map.ofEntries(Map.entry("blue_id", "String"), + Map.entry("red_id", "String"), + Map.entry("account", "Account"), + Map.entry("accounts", "Accounts")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getBlue_id", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("getRed_id", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("getRed_name", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("getBlue_name", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("changeRedId", Map.entry("void", + Map.entry(List.of("String","String"), + 2))), + Map.entry("changeBlueId", Map.entry("void", + Map.entry(List.of("String","String"), + 2))), + Map.entry("Room", Map.entry("void", + Map.entry(List.of("String","String","Accounts"), + 5)))))); + exprectedStructure.put("Account", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getName", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("changeName", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("Account", Map.entry("void", + Map.entry(List.of("String"), + 1)))))); + exprectedStructure.put("Accounts", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getAccount", Map.entry("Account", + Map.entry(List.of("String"), + 1))), + Map.entry("signUp", Map.entry("void", + Map.entry(List.of("String","String"), + 1)))))); + exprectedStructure.put("Rooms", Map.entry(Map.ofEntries(Map.entry("value", "Map"), + Map.entry("accounts", "Accounts")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getRoom", Map.entry("Room", + Map.entry(List.of("String"), + 1))), + Map.entry("createRoom", Map.entry("void", + Map.entry(List.of("String","String","String"), + 1))), + Map.entry("Rooms", Map.entry("void", + Map.entry(List.of("Accounts"), + 1)))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + + // check PUSH-first + generatedCode = generateCode("models/OnlineBattleGame.model", PushPullValue.PUSH); + exprectedStructure.clear(); + exprectedStructure.put("Main", Map.entry(Map.ofEntries(Map.entry("accounts", "Accounts"), + Map.entry("rooms", "Rooms")), + Map.ofEntries(Map.entry("Main", Map.entry("void", + Map.entry(List.of(), + 2))), + Map.entry("getAccount", Map.entry("Map", + Map.entry(List.of("String"), + 1))), + Map.entry("getBlue_name", Map.entry("String", + Map.entry(List.of("String"), + 1))), + Map.entry("getBlue_id", Map.entry("String", + Map.entry(List.of("String"), + 1))), + Map.entry("changeBlueId", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("getRed_name", Map.entry("String", + Map.entry(List.of("String"), + 1))), + Map.entry("getRed_id", Map.entry("String", + Map.entry(List.of("String"), + 1))), + Map.entry("changeRedId", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("getName", Map.entry("String", + Map.entry(List.of("String"), + 1))), + Map.entry("changeName", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("getAccounts", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("signUp", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("getRooms", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("createRoom", Map.entry("void", + Map.entry(List.of("String","String","String"), + 1))), + Map.entry("getRoom", Map.entry("Map", + Map.entry(List.of("String"), + 1)))))); + exprectedStructure.put("Account", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getName", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("changeName", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("Account", Map.entry("void", + Map.entry(List.of("String"), + 1)))))); + exprectedStructure.put("Accounts", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getAccount", Map.entry("Account", + Map.entry(List.of("String"), + 1))), + Map.entry("signUp", Map.entry("void", + Map.entry(List.of("String","String"), + 1)))))); + exprectedStructure.put("Rooms", Map.entry(Map.ofEntries(Map.entry("value", "Map"), + Map.entry("accounts", "Accounts")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getRoom", Map.entry("Room", + Map.entry(List.of("String"), + 1))), + Map.entry("createRoom", Map.entry("void", + Map.entry(List.of("String","String","String"), + 1))), + Map.entry("Rooms", Map.entry("void", + Map.entry(List.of("Accounts"), + 1)))))); + exprectedStructure.put("Room", Map.entry(Map.ofEntries(Map.entry("account", "Account"), + Map.entry("accounts", "Accounts"), + Map.entry("blue_id", "String"), + Map.entry("red_id", "String")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getBlue_name", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("getBlue_id", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("getRed_name", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("getRed_id", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("updateBlue_nameFromBlue_id", Map.entry("void", + Map.entry(List.of("String","String","String"), + 2))), + Map.entry("updateRed_nameFromRed_id", Map.entry("void", + Map.entry(List.of("String","String","String"), + 2))), + Map.entry("changeRedId", Map.entry("void", + Map.entry(List.of("String","String"), + 3))), + Map.entry("changeBlueId", Map.entry("void", + Map.entry(List.of("String","String"), + 3))), + Map.entry("Room", Map.entry("void", + Map.entry(List.of("Accounts","String","String"), + 5)))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + + private void testOnlineBattleGame2() { + try { + ArrayList generatedCode = generateCode("models/OnlineBattleGame2.model", null); + Map, // field name to type + Map, // arg types + Integer>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Main", Map.entry(Map.ofEntries(Map.entry("accounts", "Accounts"), + Map.entry("rooms", "Rooms")), + Map.ofEntries(Map.entry("Main", Map.entry("void", + Map.entry(List.of(), + 2))), + Map.entry("getAccount", Map.entry("Map", + Map.entry(List.of("String"), + 1))), + Map.entry("getMember", Map.entry("Map", + Map.entry(List.of("String","int"), + 1))), + Map.entry("getMembers", Map.entry("List", + Map.entry(List.of("String"), + 1))), + Map.entry("addRoomMember", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("getAccounts", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("signUp", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("getRoom", Map.entry("Map", + Map.entry(List.of("String"), + 1))), + Map.entry("getId", Map.entry("String", + Map.entry(List.of("String","int"), + 1))), + Map.entry("getName", Map.entry("String", + Map.entry(List.of("String"), + 1))), + Map.entry("changeName", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("getRooms", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("createRoom", Map.entry("void", + Map.entry(List.of("String"), + 1))), +// Map.entry("getName", Map.entry("String", +// Map.entry(List.of("String","int"), +// 1))), + Map.entry("getBattle", Map.entry("boolean", + Map.entry(List.of("String"), + 1))), + Map.entry("battle", Map.entry("void", + Map.entry(List.of("String","boolean"), + 1))), + Map.entry("getPoint", Map.entry("int", + Map.entry(List.of("String"), + 1)))))); + exprectedStructure.put("Account", Map.entry(Map.ofEntries(Map.entry("name", "String"), + Map.entry("point", "int")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getName", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("getPoint", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("updatePointFromBattle", Map.entry("void", + Map.entry(List.of("String","String","int","boolean","String"), + 1))), + Map.entry("changeName", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("Account", Map.entry("void", + Map.entry(List.of("String","int"), + 2)))))); + exprectedStructure.put("Member", Map.entry(Map.ofEntries(Map.entry("id", "String"), + Map.entry("account", "Account"), + Map.entry("accounts", "Accounts")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getId", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("getName", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("Member", Map.entry("void", + Map.entry(List.of("String","Accounts"), + 2)))))); + exprectedStructure.put("Members", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("List", + Map.entry(List.of(), + 1))), + Map.entry("getMember", Map.entry("Member", + Map.entry(List.of("int"), + 1))), + Map.entry("addRoomMember", Map.entry("void", + Map.entry(List.of("String","String"), + 1)))))); + exprectedStructure.put("Accounts", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getAccount", Map.entry("Account", + Map.entry(List.of("String"), + 1))), + Map.entry("signUp", Map.entry("void", + Map.entry(List.of("String","String"), + 1)))))); + exprectedStructure.put("Room", Map.entry(Map.ofEntries(Map.entry("members", "Members"), + Map.entry("battle", "boolean"), + Map.entry("account", "Account"), + Map.entry("accounts", "Accounts")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getMembers", Map.entry("Members", + Map.entry(List.of(), + 1))), + Map.entry("getBattle", Map.entry("boolean", + Map.entry(List.of(), + 1))), + Map.entry("battle", Map.entry("void", + Map.entry(List.of("String","boolean"), + 6))), + Map.entry("Room", Map.entry("void", + Map.entry(List.of("boolean","Accounts"), + 2)))))); + exprectedStructure.put("Rooms", Map.entry(Map.ofEntries(Map.entry("value", "Map"), + Map.entry("accounts", "Accounts")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getRoom", Map.entry("Room", + Map.entry(List.of("String"), + 1))), + Map.entry("createRoom", Map.entry("void", + Map.entry(List.of("String"), + 1))), + Map.entry("Rooms", Map.entry("void", + Map.entry(List.of("Accounts"), + 1)))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private void testPOS() { + try { + // check PULL-first + ArrayList generatedCode = generateCode("models/POS.model", PushPullValue.PULL); + Map, // field name to type + Map, // arg types + Integer>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Main", Map.entry(Map.ofEntries(Map.entry("history", "History"), + Map.entry("total", "Total"), + Map.entry("payment", "Payment"), + Map.entry("points", "Points")), + Map.ofEntries(Map.entry("Main", Map.entry("void", + Map.entry(List.of(), + 4))), + Map.entry("getHistory", Map.entry("List", + Map.entry(List.of(), + 1))), + Map.entry("getTotal", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("getPayment", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("purchase", Map.entry("void", + Map.entry(List.of("int"), + 1))), + Map.entry("getPoints", Map.entry("int", + Map.entry(List.of(), + 1)))))); + exprectedStructure.put("History", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("List", + Map.entry(List.of(), + 1))), + Map.entry("updateFromPayment", Map.entry("void", + Map.entry(List.of("int"), + 1)))))); + exprectedStructure.put("Total", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("Total", Map.entry("void", + Map.entry(List.of("History"), + 1)))))); + exprectedStructure.put("Payment", Map.entry(Map.ofEntries(Map.entry("value", "int"), + Map.entry("history", "History")), + Map.ofEntries(Map.entry("getValue", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("purchase", Map.entry("void", + Map.entry(List.of("int"), + 2))), + Map.entry("Payment", Map.entry("void", + Map.entry(List.of("History"), + 1)))))); + exprectedStructure.put("Points", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("Points", Map.entry("void", + Map.entry(List.of("Payment"), + 1)))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + + // check PUSH-first + generatedCode = generateCode("models/POS.model", PushPullValue.PUSH); + exprectedStructure.clear(); + exprectedStructure.put("Main", Map.entry(Map.ofEntries(Map.entry("points", "Points"), + Map.entry("total", "Total"), + Map.entry("history", "History"), + Map.entry("payment", "Payment")), + Map.ofEntries(Map.entry("Main", Map.entry("void", + Map.entry(List.of(), + 4))), + Map.entry("getPoints", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("getTotal", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("getHistory", Map.entry("List", + Map.entry(List.of(), + 1))), + Map.entry("getPayment", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("purchase", Map.entry("void", + Map.entry(List.of("int"), + 1)))))); + exprectedStructure.put("Points", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("updateFromPayment", Map.entry("void", + Map.entry(List.of("int"), + 1)))))); + exprectedStructure.put("Total", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("updateFromHistory", Map.entry("void", + Map.entry(List.of("List"), + 1)))))); + exprectedStructure.put("History", Map.entry(Map.ofEntries(Map.entry("value", "List"), + Map.entry("total", "Total")), + Map.ofEntries(Map.entry("getValue", Map.entry("List", + Map.entry(List.of(), + 1))), + Map.entry("updateFromPayment", Map.entry("void", + Map.entry(List.of("int"), + 2))), + Map.entry("History", Map.entry("void", + Map.entry(List.of("Total"), + 1)))))); + exprectedStructure.put("Payment", Map.entry(Map.ofEntries(Map.entry("value", "int"), + Map.entry("points", "Points"), + Map.entry("history", "History")), + Map.ofEntries(Map.entry("getValue", Map.entry("int", + Map.entry(List.of(), + 1))), + Map.entry("purchase", Map.entry("void", + Map.entry(List.of("int"), + 3))), + Map.entry("Payment", Map.entry("void", + Map.entry(List.of("Points","History"), + 2)))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private void testSimpleTwitter() { + try { + ArrayList generatedCode = generateCode("models/SimpleTwitter.model", null); + Map, // field name to type + Map, // arg types + Integer>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Main", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("Main", Map.entry("void", + Map.entry(List.of(), + 1))), + Map.entry("getAccount", Map.entry("Map", + Map.entry(List.of("String"), + 1))), + Map.entry("getAccounts", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("signUp", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("getTweets", Map.entry("List", + Map.entry(List.of("String"), + 1))), + Map.entry("tweet", Map.entry("void", + Map.entry(List.of("String","String"), + 1)))))); + exprectedStructure.put("Account", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getTweets", Map.entry("List", + Map.entry(List.of(), + 1))), + Map.entry("tweet", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("Account", Map.entry("void", + Map.entry(List.of("List"), + 1)))))); + exprectedStructure.put("Accounts", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getAccount", Map.entry("Account", + Map.entry(List.of("String"), + 1))), + Map.entry("signUp", Map.entry("void", + Map.entry(List.of("String","String"), + 1)))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private void testVotingSystem() { + try { + ArrayList generatedCode = generateCode("models/VotingSystem.model", null); + Map, // field name to type + Map, // arg types + Integer>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Main", Map.entry(Map.ofEntries(Map.entry("accounts", "Accounts"), + Map.entry("counts", "Counts")), + Map.ofEntries(Map.entry("Main", Map.entry("void", + Map.entry(List.of(), + 2))), + Map.entry("getAccounts", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("signUp", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("getVote", Map.entry("String", + Map.entry(List.of("String"), + 1))), + Map.entry("cast", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("getAccount", Map.entry("Map", + Map.entry(List.of("String"), + 1))), + Map.entry("getCounts", Map.entry("Map", + Map.entry(List.of(), + 1)))))); + exprectedStructure.put("Accounts", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getAccount", Map.entry("Account", + Map.entry(List.of("String"), + 1))), + Map.entry("signUp", Map.entry("void", + Map.entry(List.of("String","String"), + 1)))))); + exprectedStructure.put("Account", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 1))), + Map.entry("getVote", Map.entry("String", + Map.entry(List.of(), + 1))), + Map.entry("cast", Map.entry("void", + Map.entry(List.of("String","String"), + 1))), + Map.entry("Account", Map.entry("void", + Map.entry(List.of("String"), + 1)))))); + exprectedStructure.put("Counts", Map.entry(Map.ofEntries(Map.entry("value", "Map"), + Map.entry("account", "Account"), + Map.entry("accounts", "Accounts")), + Map.ofEntries(Map.entry("getValue", Map.entry("Map", + Map.entry(List.of(), + 6))), + Map.entry("Counts", Map.entry("void", + Map.entry(List.of("Accounts"), + 1)))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private void testWeatherObservationSystem() { + try { + // check PULL-first + ArrayList generatedCode = generateCode("models/WeatherObservationSystem.model", PushPullValue.PULL); + Map, // field name to type + Map, // arg types + Integer>>>>> // lines of code + exprectedStructure = new HashMap<>(); + exprectedStructure.put("Main", Map.entry(Map.ofEntries(Map.entry("highest", "Highest"), + Map.entry("temp_f", "Temp_f"), + Map.entry("temp_c", "Temp_c")), + Map.ofEntries(Map.entry("Main", Map.entry("void", + Map.entry(List.of(), + 3))), + Map.entry("getHighest", Map.entry("double", + Map.entry(List.of(), + 1))), + Map.entry("reset", Map.entry("void", + Map.entry(List.of("double"), + 1))), + Map.entry("getTemp_f", Map.entry("double", + Map.entry(List.of(), + 1))), + Map.entry("observe", Map.entry("void", + Map.entry(List.of("double"), + 1))), + Map.entry("getTemp_c", Map.entry("double", + Map.entry(List.of(), + 1)))))); + exprectedStructure.put("Highest", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("double", + Map.entry(List.of(), + 1))), + Map.entry("updateFromTemp_f", Map.entry("void", + Map.entry(List.of("double"), + 1))), + Map.entry("reset", Map.entry("void", + Map.entry(List.of("double"), + 1)))))); + exprectedStructure.put("Temp_f", Map.entry(Map.ofEntries(Map.entry("value", "double"), + Map.entry("highest", "Highest")), + Map.ofEntries(Map.entry("getValue", Map.entry("double", + Map.entry(List.of(), + 1))), + Map.entry("observe", Map.entry("void", + Map.entry(List.of("double"), + 2))), + Map.entry("Temp_f", Map.entry("void", + Map.entry(List.of("Highest"), + 1)))))); + exprectedStructure.put("Temp_c", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("double", + Map.entry(List.of(), + 1))), + Map.entry("Temp_c", Map.entry("void", + Map.entry(List.of("Temp_f"), + 1)))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + + // check PUSH-first + generatedCode = generateCode("models/WeatherObservationSystem.model", PushPullValue.PUSH); + exprectedStructure.clear(); + exprectedStructure.put("Main", Map.entry(Map.ofEntries(Map.entry("temp_c", "Temp_c"), + Map.entry("highest", "Highest"), + Map.entry("temp_f", "Temp_f")), + Map.ofEntries(Map.entry("Main", Map.entry("void", + Map.entry(List.of(), + 3))), + Map.entry("getTemp_c", Map.entry("double", + Map.entry(List.of(), + 1))), + Map.entry("getHighest", Map.entry("double", + Map.entry(List.of(), + 1))), + Map.entry("reset", Map.entry("void", + Map.entry(List.of("double"), + 1))), + Map.entry("getTemp_f", Map.entry("double", + Map.entry(List.of(), + 1))), + Map.entry("observe", Map.entry("void", + Map.entry(List.of("double"), + 1)))))); + exprectedStructure.put("Temp_c", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("double", + Map.entry(List.of(), + 1))), + Map.entry("updateFromTemp_f", Map.entry("void", + Map.entry(List.of("double"), + 1)))))); + exprectedStructure.put("Highest", Map.entry(Map.ofEntries(), + Map.ofEntries(Map.entry("getValue", Map.entry("double", + Map.entry(List.of(), + 1))), + Map.entry("updateFromTemp_f", Map.entry("void", + Map.entry(List.of("double"), + 1))), + Map.entry("reset", Map.entry("void", + Map.entry(List.of("double"), + 1)))))); + exprectedStructure.put("Temp_f", Map.entry(Map.ofEntries(Map.entry("value", "double"), + Map.entry("highest", "Highest"), + Map.entry("temp_c", "Temp_c")), + Map.ofEntries(Map.entry("getValue", Map.entry("double", + Map.entry(List.of(), + 1))), + Map.entry("observe", Map.entry("void", + Map.entry(List.of("double"), + 3))), + Map.entry("Temp_f", Map.entry("void", + Map.entry(List.of("Highest","Temp_c"), + 2)))))); + + checkStructure(generatedCode, exprectedStructure); +// generateCheckCode(generatedCode); + } catch (FileNotFoundException + | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword + | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression + | WrongRHSExpression | ExpectedRightBracket | ExpectedAssignment | ExpectedRightCurlyBracket + | WrongPathExpression | WrongJsonExpression | ExpectedColon | ExpectedDoubleQuotation e) { + e.printStackTrace(); + } + } + + private ArrayList generateCode(String fileName, PushPullValue pushPullValue) throws FileNotFoundException, + ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedRightCurlyBracket, ExpectedInOrOutOrRefOrSubKeyword, + ExpectedStateTransition, ExpectedEquals, ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, + WrongPathExpression, WrongJsonExpression, ExpectedColon, ExpectedDoubleQuotation { + File file = new File(fileName); + Parser parser = new Parser(new BufferedReader(new FileReader(file))); + DataTransferModel model; + model = parser.doParse(); + DataFlowGraph graph = DataTransferModelAnalyzer.createDataFlowGraphWithStateStoringAttribute(model); + DataTransferModelAnalyzer.annotateWithSelectableDataTransferAttiribute(graph); + if (pushPullValue != null) { + // Select a specified push/pull transfer method if possible. + for (Edge e : graph.getEdges()) { + if (!((DataFlowEdge) e).isChannelToResource() && ((DataFlowEdge) e).getAttribute() instanceof PushPullAttribute) { + PushPullAttribute ppat = (PushPullAttribute) ((DataFlowEdge) e).getAttribute(); + ppat.selectOption(pushPullValue); + } + } + } + TypeInference.infer(model); + DataTransferMethodAnalyzer.decideToStoreResourceStates(graph); + ArrayList codetree = JavaMethodBodyGenerator.doGenerate(graph, model, JavaCodeGenerator.doGenerate(graph, model)); +// ArrayList codetree = new CodeGeneratorFromDataFlowGraph().generateCode(model, graph, new StandaloneSpecific(), new JavaSpecific()); + return codetree; + } + + private void checkStructure(ArrayList generatedCode, + Map, Map, Integer>>>>> exprectedStructure) { + for (var classEnt: exprectedStructure.entrySet()) { + String expectedClassName = classEnt.getKey(); + Entry, Map, Integer>>>> expectedClassStructure = classEnt.getValue(); + TypeDeclaration generatedClass = null; + for (CompilationUnit cu: generatedCode) { + for (TypeDeclaration type: cu.types()) { + if (type.getTypeName().equals(expectedClassName)) { + generatedClass = type; + break; + } + } + } + assertNotNull(generatedClass); + + Map exprectedFields = expectedClassStructure.getKey(); + Map, Integer>>> exprectedMethods = expectedClassStructure.getValue(); + + for (String expectedFieldName: exprectedFields.keySet()) { + FieldDeclaration generatedField = null; + for (FieldDeclaration field: generatedClass.getFields()) { + if (field.getName().equals(expectedFieldName)) { + generatedField = field; + break; + } + } + assertNotNull(generatedField); + + String expectedFieldType = exprectedFields.get(expectedFieldName); + if (expectedFieldType.equals("void")) { + assertNull(generatedField.getType()); + } else { + assertEquals(expectedFieldType, generatedField.getType().getInterfaceTypeName()); + } + } + + for (String expectedMethodName: exprectedMethods.keySet()) { + MethodDeclaration generatedMethod= null; + for (MethodDeclaration method: generatedClass.getMethods()) { + if (method.getName().equals(expectedMethodName)) { + generatedMethod = method; + break; + } + } + assertNotNull(generatedMethod); + + Entry, Integer>> expectedMethodInfo = exprectedMethods.get(expectedMethodName); + String expectedReturnType = expectedMethodInfo.getKey(); + if (expectedReturnType.equals("void")) { + if (generatedMethod.getReturnType() != null) { + assertEquals(expectedReturnType, generatedMethod.getReturnType().getInterfaceTypeName()); + } else { + assertNull(generatedMethod.getReturnType()); + } + } else { + assertEquals(expectedReturnType, generatedMethod.getReturnType().getInterfaceTypeName()); + } + Entry, Integer> expectedMethodInfo2 = expectedMethodInfo.getValue(); + List expectedArgTypes = expectedMethodInfo2.getKey(); + for (String expectedArgType: expectedArgTypes) { + boolean existsArg = false; + for (VariableDeclaration var: generatedMethod.getParameters()) { + if (expectedArgType.equals(var.getType().getInterfaceTypeName())) { + existsArg = true; + } + } + assertTrue(existsArg); + } + int expectedLinesOfCode = expectedMethodInfo2.getValue(); + assertEquals(expectedLinesOfCode, generatedMethod.getBody().getStatements().size()); + } + } + } + + private void generateCheckCode(ArrayList generatedCode) { +// exprectedStructure.put("Main", Map.entry(Map.ofEntries(Map.entry("history", "History"), +// Map.entry("total", "Total"), +// Map.entry("payment", "Payment"), +// Map.entry("points", "Points")), +// Map.ofEntries(Map.entry("Main", Map.entry("void", +// Map.entry(List.of(), +// 4))), +// Map.entry("getHistory", Map.entry("List", +// Map.entry(List.of(), +// 1))), +// Map.entry("getTotal", Map.entry("int", +// Map.entry(List.of(), +// 1))), +// Map.entry("getPayment", Map.entry("int", +// Map.entry(List.of(), +// 1))), +// Map.entry("purchase", Map.entry("void", +// Map.entry(List.of("int"), +// 1))), +// Map.entry("getPoints", Map.entry("int", +// Map.entry(List.of(), +// 1)))))); + for (CompilationUnit cu: generatedCode) { + for (TypeDeclaration type: cu.types()) { + List fields = type.getFields(); + List methods = type.getMethods(); + // fields + if (fields.size() == 0) { + System.out.println("\t\t\texprectedStructure.put(\"" + type.getTypeName() + "\", Map.entry(Map.ofEntries(),"); + } if (fields.size() == 1) { + System.out.println("\t\t\texprectedStructure.put(\"" + type.getTypeName() + "\", Map.entry(Map.ofEntries(),"); + } else { + int i = 0; + for (FieldDeclaration field: fields) { + if (i == 0) { + System.out.println("\t\t\texprectedStructure.put(\"" + type.getTypeName() + "\", Map.entry(Map.ofEntries(Map.entry(\"" + field.getName() + "\", \"" + field.getType().getInterfaceTypeName() + "\"),"); + } else { + System.out.print("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"); + for (int j = 0; j < (type.getTypeName().length() + 3) / 4; j++) { + System.out.print("\t"); + } + for (int j = 0; j < (type.getTypeName().length() + 3) % 4; j++) { + System.out.print(" "); + } + if (i < fields.size() - 1) { + System.out.println("Map.entry(\"" + field.getName() + "\", \"" + field.getType().getInterfaceTypeName() + "\"),"); + } else { + System.out.println("Map.entry(\"" + field.getName() + "\", \"" + field.getType().getInterfaceTypeName() + "\")),"); + } + } + i++; + } + } + // methods + if (methods.size() == 0) { + System.out.print("\t\t\t\t\t\t\t\t\t\t\t\t"); + for (int j = 0; j < (type.getTypeName().length() + 1) / 4; j++) { + System.out.print("\t"); + } + for (int j = 0; j < (type.getTypeName().length() + 1) % 4; j++) { + System.out.print(" "); + } + System.out.println("Map.ofEntries())));"); + } else { + int i = 0; + for (MethodDeclaration method: methods) { + // method name and return type + if (i == 0) { + System.out.print("\t\t\t\t\t\t\t\t\t\t\t\t"); + for (int j = 0; j < (type.getTypeName().length() + 1) / 4; j++) { + System.out.print("\t"); + } + for (int j = 0; j < (type.getTypeName().length() + 1) % 4; j++) { + System.out.print(" "); + } + if (method.getReturnType() == null) { + System.out.println("Map.ofEntries(Map.entry(\"" + method.getName() + "\", Map.entry(\"void\", "); + } else { + System.out.println("Map.ofEntries(Map.entry(\"" + method.getName() + "\", Map.entry(\"" + method.getReturnType().getInterfaceTypeName() + "\", "); + } + } else { + System.out.print("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"); + for (int j = 0; j < (type.getTypeName().length() + 3) / 4; j++) { + System.out.print("\t"); + } + for (int j = 0; j < (type.getTypeName().length() + 3) % 4; j++) { + System.out.print(" "); + } + if (method.getReturnType() == null) { + System.out.println("Map.entry(\"" + method.getName() + "\", Map.entry(\"void\", "); + } else { + System.out.println("Map.entry(\"" + method.getName() + "\", Map.entry(\"" + method.getReturnType().getInterfaceTypeName() + "\", "); + } + } + // method parameters + System.out.print("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"); + for (int j = 0; j < (type.getTypeName().length() + method.getName().length() + 3) / 4; j++) { + System.out.print("\t"); + } + for (int j = 0; j < (type.getTypeName().length() + method.getName().length() + 3) % 4; j++) { + System.out.print(" "); + } + if (method.getParameters() == null || method.getParameters().size() == 0) { + System.out.println("Map.entry(List.of(),"); + } else { + System.out.print("Map.entry(List.of("); + String delim = ""; + for (VariableDeclaration arg: method.getParameters()) { + System.out.print(delim + "\"" + arg.getType().getInterfaceTypeName() + "\""); + delim = ","; + } + System.out.println("),"); + } + // method lines of code + System.out.print("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"); + for (int j = 0; j < (type.getTypeName().length() + method.getName().length() + 3) / 4; j++) { + System.out.print("\t"); + } + for (int j = 0; j < (type.getTypeName().length() + method.getName().length() + 3) % 4; j++) { + System.out.print(" "); + } + if (i < methods.size() - 1) { + System.out.println("" + method.getBody().getStatements().size() + "))),"); + } else { + System.out.println("" + method.getBody().getStatements().size() + "))))));"); + } + i++; + } + } + } + } + } +} diff --git a/AlgebraicDataflowArchitectureModel/src/tests/UpdateConflictCheckTest.java b/AlgebraicDataflowArchitectureModel/src/tests/UpdateConflictCheckTest.java index de362c9..a7d7d6a 100644 --- a/AlgebraicDataflowArchitectureModel/src/tests/UpdateConflictCheckTest.java +++ b/AlgebraicDataflowArchitectureModel/src/tests/UpdateConflictCheckTest.java @@ -1,10 +1,14 @@ package tests; +import static org.junit.Assert.*; + import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; +import org.junit.Test; + import algorithms.*; import models.dataFlowModel.DataTransferModel; import parser.Parser; @@ -26,14 +30,16 @@ import parser.exceptions.WrongRHSExpression; public class UpdateConflictCheckTest { - public static void main(String[] args) { + + @Test + public void test() { File file = new File("models/POS2.model"); try { Parser parser = new Parser(new BufferedReader(new FileReader(file))); DataTransferModel model; try { model = parser.doParse(); - System.out.println(Validation.checkUpdateConflict(model)); + assertTrue(Validation.checkUpdateConflict(model)); } catch (ExpectedRightBracket | ExpectedChannel | ExpectedChannelName | ExpectedLeftCurlyBracket | ExpectedRightCurlyBracket | ExpectedInOrOutOrRefOrSubKeyword | ExpectedStateTransition | ExpectedEquals | ExpectedRHSExpression | WrongLHSExpression | WrongRHSExpression | ExpectedAssignment