package org.scribble.del.global;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.scribble.ast.AstFactoryImpl;
import org.scribble.ast.ScribNode;
import org.scribble.ast.global.GChoice;
import org.scribble.ast.global.GProtocolBlock;
import org.scribble.ast.local.LChoice;
import org.scribble.ast.local.LProtocolBlock;
import org.scribble.ast.name.simple.RoleNode;
import org.scribble.del.ChoiceDel;
import org.scribble.main.RuntimeScribbleException;
import org.scribble.main.ScribbleException;
import org.scribble.sesstype.name.MessageId;
import org.scribble.sesstype.name.Role;
import org.scribble.visit.ProtocolDefInliner;
import org.scribble.visit.context.Projector;
import org.scribble.visit.context.env.ProjectionEnv;
import org.scribble.visit.env.InlineProtocolEnv;
import org.scribble.visit.wf.WFChoiceChecker;
import org.scribble.visit.wf.env.WFChoiceEnv;
public class GChoiceDel extends ChoiceDel implements GCompoundInteractionNodeDel
{
@Override
public ScribNode leaveProtocolInlining(ScribNode parent, ScribNode child, ProtocolDefInliner inl, ScribNode visited) throws ScribbleException
{
GChoice gc = (GChoice) visited;
List<GProtocolBlock> blocks =
gc.getBlocks().stream().map((b) -> (GProtocolBlock) ((InlineProtocolEnv) b.del().env()).getTranslation()).collect(Collectors.toList());
RoleNode subj = gc.subj.clone();
GChoice inlined = AstFactoryImpl.FACTORY.GChoice(gc.getSource(), subj, blocks);
inl.pushEnv(inl.popEnv().setTranslation(inlined));
return (GChoice) super.leaveProtocolInlining(parent, child, inl, gc);
}
@Override
public void enterInlinedWFChoiceCheck(ScribNode parent, ScribNode child, WFChoiceChecker checker) throws ScribbleException
{
WFChoiceEnv env = checker.peekEnv().enterContext();
env = env.clear();
env = env.enableChoiceSubject(((GChoice) child).subj.toName());
checker.pushEnv(env);
}
@Override
public GChoice leaveInlinedWFChoiceCheck(ScribNode parent, ScribNode child, WFChoiceChecker checker, ScribNode visited) throws ScribbleException
{
GChoice cho = (GChoice) visited;
Role subj = cho.subj.toName();
if (!checker.peekParentEnv().isEnabled(subj))
{
throw new ScribbleException(cho.subj.getSource(), "Subject not enabled: " + subj);
}
// Enabled senders checked in GMessageTransferDel
List<WFChoiceEnv> all =
cho.getBlocks().stream().map((b) -> (WFChoiceEnv) b.del().env()).collect(Collectors.toList());
if (checker.job.useOldWf) // ****
{
if (all.size() > 1)
{
try
{
WFChoiceEnv benv0 = all.get(0);
List<WFChoiceEnv> benvs = all.subList(1, all.size());
Set<Role> dests = benv0.getEnabled().getDestinations();
// Same roles enabled in every block
benvs.stream().map((e) -> e.getEnabled().getDestinations()).forEach((rs) ->
{
if (!dests.equals(rs))
{
throw new RuntimeScribbleException("Mismatched enabled roles: " + dests + ", " + rs);
}
});
dests.remove(subj);
for (Role dest : dests)
{
// Same enabler(s) for each enabled role
Set<Role> srcs = benv0.getEnabled().getSources(dest); // Always singleton?
benvs.stream().map((e) -> e.getEnabled().getSources(dest)).forEach((rs) ->
{
if (!srcs.equals(rs))
{
throw new RuntimeScribbleException("Mismatched enabler roles for " + dest + ": " + srcs + ", " + rs);
}
});
// Distinct enabling messages
Set<MessageId<?>> mids = benv0.getEnabled().getMessages(dest);
benvs.stream().map((e) -> e.getEnabled().getMessages(dest)).forEach((ms) ->
{
if (!Collections.disjoint(mids, ms))
{
throw new RuntimeScribbleException("Non disjoint enabling messages for " + dest + ": " + mids + ", " + ms);
}
mids.addAll(ms);
});
}
}
catch (RuntimeScribbleException rse) // Lambda hack
{
throw new ScribbleException(rse.getMessage(), rse.getCause());
}
}
}
/*// No: do via Env merge, with "trinary state semantics"
ConnectedMap c0 = all.get(0).getConnected();
//if (all.subList(1, all.size() - 1).stream().anyMatch((e) -> !c0.equals(e.getConnected())))
for (WFChoiceEnv e : all.subList(1, all.size()))
{
ConnectedMap c = e.getConnected();
if (!c0.equals(c))
{
// FIXME: check on model?
throw new ScribbleException("Inconsistent connection configurations:\n " + c0 + "\n " + c);
}
}*/
// On leaving global choice, we're doing both the merging of block envs into the choice env, and the merging of the choice env to the parent-of-choice env
// In principle, for the envLeave we should only be doing the latter (as countpart to enterEnv), but it is much more convenient for the compound-node (choice) to collect all the child block envs and merge here, rather than each individual block env trying to (partially) merge into the parent-choice as they are visited
WFChoiceEnv merged = checker.popEnv().mergeContexts(all);
checker.pushEnv(merged); // Merges the child block envs into the current choice env; super call below merges this choice env into the parent env of the choice
return (GChoice) super.leaveInlinedWFChoiceCheck(parent, child, checker, visited); // Replaces base popAndSet to do pop, merge and set
}
@Override
public GChoice leaveProjection(ScribNode parent, ScribNode child, Projector proj, ScribNode visited) throws ScribbleException
{
GChoice gc = (GChoice) visited;
List<LProtocolBlock> blocks =
gc.getBlocks().stream().map((b) -> (LProtocolBlock) ((ProjectionEnv) b.del().env()).getProjection()).collect(Collectors.toList());
//gc.getBlocks().stream().map((b) -> ((GProtocolBlockDel) b.del()).project(b, self)).collect(Collectors.toList());
LChoice projection = gc.project(proj.peekSelf(), blocks);
proj.pushEnv(proj.popEnv().setProjection(projection));
return (GChoice) GCompoundInteractionNodeDel.super.leaveProjection(parent, child, proj, gc);
}
}