package org.scribble.ast.local; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import org.antlr.runtime.tree.CommonTree; import org.scribble.ast.AstFactoryImpl; import org.scribble.ast.Choice; import org.scribble.ast.ProtocolBlock; import org.scribble.ast.ScribNodeBase; import org.scribble.ast.name.simple.RoleNode; import org.scribble.del.ScribDel; import org.scribble.main.ScribbleException; import org.scribble.sesstype.Message; import org.scribble.sesstype.kind.Local; import org.scribble.sesstype.name.Role; import org.scribble.util.ScribUtil; import org.scribble.visit.context.ProjectedChoiceSubjectFixer; public class LChoice extends Choice<Local> implements LCompoundInteractionNode { public LChoice(CommonTree source, RoleNode subj, List<LProtocolBlock> blocks) { super(source, subj, blocks); } @Override protected ScribNodeBase copy() { return new LChoice(this.source, this.subj, getBlocks()); } @Override public LChoice clone() { RoleNode subj = this.subj.clone(); List<LProtocolBlock> blocks = ScribUtil.cloneList(getBlocks()); return AstFactoryImpl.FACTORY.LChoice(this.source, subj, blocks); } @Override public LChoice reconstruct(RoleNode subj, List<? extends ProtocolBlock<Local>> blocks) { ScribDel del = del(); LChoice lc = new LChoice(this.source, subj, castBlocks(blocks)); lc = (LChoice) lc.del(del); return lc; } @Override public List<LProtocolBlock> getBlocks() { return castBlocks(super.getBlocks()); } @Override public Role inferLocalChoiceSubject(ProjectedChoiceSubjectFixer fixer) { // Relies on: will never be inferring from a "continue X;" -- if choice is first statement in seq, continue must be guarded; if continue is not guarded, choice cannot be first statement in seq return getBlocks().get(0).getInteractionSeq().getInteractions().get(0).inferLocalChoiceSubject(fixer); } // FIXME: shouldn't be needed, but here due to Eclipse bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=436350 @Override public Local getKind() { return LCompoundInteractionNode.super.getKind(); } private static List<LProtocolBlock> castBlocks(List<? extends ProtocolBlock<Local>> blocks) { return blocks.stream().map((b) -> (LProtocolBlock) b).collect(Collectors.toList()); } @Override public LChoice merge(LInteractionNode ln) throws ScribbleException { if (!(ln instanceof LChoice) || !this.canMerge(ln)) { throw new ScribbleException("Cannot merge " + this.getClass() + " and " + ln.getClass() + ": " + this + ", " + ln); } LChoice them = ((LChoice) ln); /*if (!this.subj.toName().equals(them.subj.toName())) // NO: pointless, always DummyProjectionRoleNode at this point -- maybe unnecessary? { throw new ScribbleException("Cannot merge choices for " + this.subj + " and " + them.subj + ": " + this + ", " + ln); }*/ List<LProtocolBlock> blocks = new LinkedList<>(); getBlocks().forEach((b) -> blocks.add(b.clone())); them.getBlocks().forEach((b) -> blocks.add(b.clone())); return AstFactoryImpl.FACTORY.LChoice(this.source, this.subj, blocks); // Not reconstruct: leave context building to post-projection passes // Hacky: this.source } @Override public boolean canMerge(LInteractionNode ln) { // Merge currently does "nothing"; validation takes direct non-deterministic interpretation -- purpose of syntactic merge is to convert non-det to "equivalent" safe det in certain sitations return ln instanceof LChoice; } @Override public Set<Message> getEnabling() { return getBlocks().stream().flatMap((b) -> b.getEnabling().stream()).collect(Collectors.toSet()); } }