package org.scribble.visit.wf.env; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import org.scribble.sesstype.Message; import org.scribble.sesstype.MessageSig; import org.scribble.sesstype.Payload; import org.scribble.sesstype.name.MessageId; import org.scribble.sesstype.name.Op; import org.scribble.sesstype.name.Role; import org.scribble.util.ConnectedMap; import org.scribble.util.MessageIdMap; import org.scribble.visit.env.Env; public class WFChoiceEnv extends Env<WFChoiceEnv> { private static final Role DUMMY_ROLE = new Role("__ROLE"); private static final Op ROOT_OPERATOR = new Op("__ROOT"); private static final Op SUBJECT_OPERATOR = new Op("__SUBJECT"); private static final MessageSig ROOT_MESSAGESIGNATURE = new MessageSig(ROOT_OPERATOR, Payload.EMPTY_PAYLOAD); private static final MessageSig SUBJECT_MESSAGESIGNATURE = new MessageSig(SUBJECT_OPERATOR, Payload.EMPTY_PAYLOAD); // dest -> src -> msg private MessageIdMap initial; // message transfers recorded here in block envs //private MessageIdMap initialInterrupts; // interrupts recorded here in interruptible env private ConnectedMap connected; // FIXME: use roles to initialise MessageIdMap properly, cf. WFBuffers public WFChoiceEnv(Set<Role> roles, boolean implicit) { //this(new MessageIdMap(), new MessageIdMap()); this(new MessageIdMap(), new ConnectedMap(roles, implicit)); } //protected InlinedWFChoiceEnv(MessageIdMap initial, MessageIdMap initialInterrupts) protected WFChoiceEnv(MessageIdMap initial, ConnectedMap connected) { this.initial = new MessageIdMap(initial); //this.initialInterrupts = new MessageIdMap(initialInterrupts); this.connected = new ConnectedMap(connected); } @Override protected WFChoiceEnv copy() { //return new InlinedWFChoiceEnv(this.initial, this.initialInterrupts); return new WFChoiceEnv(this.initial, this.connected); } public WFChoiceEnv clear() { WFChoiceEnv copy = copy(); copy.initial.clear(); //copy.initialInterrupts.clear(); return copy; } @Override public WFChoiceEnv enterContext() { //return new InlinedWFChoiceEnv(this.initial, this.initialInterrupts); return new WFChoiceEnv(this.initial, this.connected); // i.e., copy } @Override public WFChoiceEnv mergeContext(WFChoiceEnv child) { return mergeContexts(Arrays.asList(child)); } @Override public WFChoiceEnv mergeContexts(List<WFChoiceEnv> children) { WFChoiceEnv copy = copy(); for (WFChoiceEnv child : children) { merge(this, copy.initial, child.initial); ////merge(this, copy.initialInterrupts, child.initialInterrupts); //merge(this, copy.connected, child.connected); } // FIXME: refactor ConnectedMap cm = children.get(0).getConnected(); for (WFChoiceEnv e : children.subList(1, children.size())) { cm = cm.merge(e.getConnected()); } copy.connected = cm; return copy; } // Pre: foo is parent.copy().initial // updates foo according to state of parent and child private static void merge(WFChoiceEnv parent, MessageIdMap foo, MessageIdMap child) { for (Role dest : child.getDestinations()) { for (Role src : child.getSources(dest)) { if (!parent.isEnabled(dest)) { foo.putMessages(dest, src, child.getMessages(dest, src)); } } } } /*// FIXME: factor out with MessageIdMap private static void merge(WFChoiceEnv parent, ConnectedMap foo, ConnectedMap child) { for (Role dest : child.getDestinations()) { for (Role src : child.getSources(dest)) { //if (!parent.isConnected(src, dest)) { // If not previously connected, update connected according to latest state -- No: also need to write disconnected (if parent is connected) foo.setConnected(dest, src, child.isConnected(dest, src)); } } } }*/ public WFChoiceEnv enableRoleForRootProtocolDecl(Role role) { return addMessage(WFChoiceEnv.DUMMY_ROLE, role, WFChoiceEnv.ROOT_MESSAGESIGNATURE); } public WFChoiceEnv enableChoiceSubject(Role role) { return addMessage(WFChoiceEnv.DUMMY_ROLE, role, WFChoiceEnv.SUBJECT_MESSAGESIGNATURE); } public WFChoiceEnv connect(Role src, Role dest) { WFChoiceEnv copy = copy(); copy.connected.connect(src, dest); return copy; } public WFChoiceEnv disconnect(Role src, Role dest) { WFChoiceEnv copy = copy(); copy.connected.disconnect(src, dest); return copy; } // The "main" public routine // Rename: more like enable-if-not-already public WFChoiceEnv addMessage(Role src, Role dest, Message msg) { WFChoiceEnv copy = copy(); addMessages(copy.initial, src, dest, Arrays.asList(msg.getId())); return copy; } /*public WFChoiceEnv removeMessage(Role src, Role dest, Message msg) { WFChoiceEnv copy = copy(); copy.initial.removeMessage(dest, src, msg.getId()); return copy; } public WFChoiceEnv disableRole(Role r) { WFChoiceEnv copy = copy(); for (...) { copy.initial.removeMessage(dest, src, ...); } return copy; }*/ // FIXME: List/Set argument // Means: record message as initial enabling message if dest not already enabled private static void addMessages(MessageIdMap map, Role src, Role dest, List<MessageId<?>> msgs) { if (!map.containsDestination(dest)) // FIXME: factor out isEnabled { map.putMessages(dest, src, new HashSet<>(msgs)); } } /*public InlinedWFChoiceEnv addInterrupt(Role src, Role dest, Message msg) { InlinedWFChoiceEnv copy = copy(); if (!copy.initial.containsLeftKey(dest)) { copy.initialInterrupts.putMessage(dest, src, msg.getId()); } return copy; }*/ public boolean isEnabled(Role role) { return this.initial.containsDestination(role); } public MessageIdMap getEnabled() { MessageIdMap tmp = new MessageIdMap(this.initial); //tmp.merge(this.initialInterrupts); return tmp; } public ConnectedMap getConnected() { ConnectedMap tmp = new ConnectedMap(this.connected); return tmp; } /*public WFChoiceEnv setConnected(ConnectedMap cm) { WFChoiceEnv copy = copy(); copy.connected = cm; return copy; }*/ public boolean isConnected(Role r1, Role r2) { return this.connected.isConnected(r1, r2); } @Override public String toString() { //return "initial=" + this.initial.toString() + ", initialInterrupts=" + this.initialInterrupts.toString(); return "initial=" + this.initial.toString(); } }