package org.scribble.codegen.java.endpointapi;
import java.util.stream.Collectors;
import org.scribble.ast.MessageSigNameDecl;
import org.scribble.ast.Module;
import org.scribble.codegen.java.endpointapi.ioifaces.BranchInterfaceGenerator;
import org.scribble.codegen.java.endpointapi.ioifaces.IOStateInterfaceGenerator;
import org.scribble.codegen.java.util.ClassBuilder;
import org.scribble.codegen.java.util.FieldBuilder;
import org.scribble.codegen.java.util.JavaBuilder;
import org.scribble.codegen.java.util.MethodBuilder;
import org.scribble.main.ScribbleException;
import org.scribble.model.endpoint.EState;
import org.scribble.model.endpoint.actions.EAction;
import org.scribble.sesstype.name.DataType;
import org.scribble.sesstype.name.MessageSigName;
import org.scribble.sesstype.name.Role;
public class CaseSocketGenerator extends ScribSocketGenerator
{
public CaseSocketGenerator(StateChannelApiGenerator apigen, EState curr)
{
super(apigen, curr);
}
@Override
protected String getClassName()
{
return getCaseSocketName(super.getClassName());
}
@Override
protected String getSuperClassType()
{
return CASESOCKET_CLASS + "<" + getSessionClassName() + ", " + getSelfClassName() + ">";
}
@Override
protected void addImports()
{
super.addImports();
this.cb.addImports(getOpsPackageName() + ".*");
}
@Override
protected void addInitialStateConstructor()
{
return;
}
@Override
protected MethodBuilder addConstructor()
{
String branchName = this.apigen.getSocketClassName(curr); // Name of "parent" branch class (curr state is the branch state)
String enumClassName = branchName + "." + BranchSocketGenerator.getBranchEnumClassName(this.apigen, this.curr);
MethodBuilder ctor = super.addConstructor();
ctor.addParameters(enumClassName + " " + CASE_OP_PARAM, StateChannelApiGenerator.SCRIBMESSAGE_CLASS + " " + CASE_MESSAGE_PARAM);
ctor.addBodyLine(JavaBuilder.THIS + "." + CASE_OP_FIELD + " = " + CASE_OP_PARAM + ";");
ctor.addBodyLine(JavaBuilder.THIS + "." + CASE_MESSAGE_FIELD + " = " + CASE_MESSAGE_PARAM + ";");
return ctor;
}
//private String constructCaseClass(EndpointState curr, Module main)
@Override
protected void addMethods() throws ScribbleException
{
String branchName = this.apigen.getSocketClassName(curr); // Name of "parent" branch class (curr state is the branch state)
String enumClassName = branchName + "." + BranchSocketGenerator.getBranchEnumClassName(this.apigen, this.curr);
//String className = newClassName(); // Name of branch-receive class
FieldBuilder fb1 = this.cb.newField(CASE_OP_FIELD); // The op enum, for convenient switch/if/etc by user (correctly derived by code generation from the received ScribMessage)
fb1.addModifiers(JavaBuilder.PUBLIC, JavaBuilder.FINAL);
fb1.setType(enumClassName);
FieldBuilder fb2 = this.cb.newField(CASE_MESSAGE_FIELD); // The received ScribMessage (branch-check checks the user-selected receive op against the ScribMessage op)
fb2.addModifiers(JavaBuilder.PRIVATE, JavaBuilder.FINAL);
fb2.setType(StateChannelApiGenerator.SCRIBMESSAGE_CLASS);
for (EAction a : this.curr.getActions())
{
EState succ = this.curr.getSuccessor(a);
addReceiveMethod(this.cb, a, succ);
addCaseReceiveMethod(this.cb, a, succ);
if (!a.payload.isEmpty() || a.mid.isMessageSigName())
{
addCaseReceiveDiscardMethod(this.cb, a, succ);
}
}
if (!this.apigen.skipIOInterfacesGeneration)
{
Role self = this.apigen.getSelf();
MethodBuilder mb = this.cb.newMethod("getOp");
mb.addAnnotations("@Override");
mb.addModifiers(JavaBuilder.PUBLIC);
mb.setReturn(IOStateInterfaceGenerator.getIOStateInterfaceName(self, this.curr) + "." + BranchInterfaceGenerator.getBranchInterfaceEnumName(self, this.curr));
mb.addBodyLine(JavaBuilder.RETURN + " " + JavaBuilder.THIS + "." + CASE_OP_FIELD + ";");
}
this.apigen.addTypeDecl(this.cb); // CaseSocketBuilder used by BranchSocketBuilder, not EndpointApiGenerator
}
// Same as in ReceiveSocketGenerator
private MethodBuilder makeReceiveHeader(ClassBuilder cb, EAction a, EState succ) throws ScribbleException
{
MethodBuilder mb = cb.newMethod();
ReceiveSocketGenerator.setReceiveHeaderWithoutReturnType(this.apigen, a, mb);
setNextSocketReturnType(this.apigen, mb, succ);
return mb;
}
private void addReceiveMethod(ClassBuilder cb, EAction a, EState succ) throws ScribbleException
{
Module main = this.apigen.getMainModule();
MethodBuilder mb = makeReceiveHeader(cb, a, succ);
if (a.mid.isOp())
{
mb.addBodyLine(JavaBuilder.SUPER + ".use();");
addBranchCheck(getSessionApiOpConstant(a.mid), mb, CASE_MESSAGE_FIELD);
ReceiveSocketGenerator.addPayloadBuffSetters(main, a, mb);
}
else //if (a.mid.isMessageSigName())
{
MessageSigNameDecl msd = main.getMessageSigDecl(((MessageSigName) a.mid).getSimpleName()); // FIXME: might not belong to main module
mb.addBodyLine(JavaBuilder.SUPER + ".use();");
addBranchCheck(getSessionApiOpConstant(a.mid), mb, CASE_MESSAGE_FIELD);
mb.addBodyLine(CASE_ARG_PREFIX + "." + BUFF_VAL_FIELD + " = (" + msd.extName + ") " + CASE_MESSAGE_FIELD + ";");
}
addReturnNextSocket(mb, succ);
}
private MethodBuilder makeCaseReceiveHeader(ClassBuilder cb, EAction a, EState succ) throws ScribbleException
{
MethodBuilder mb = cb.newMethod();
setCaseReceiveHeaderWithoutReturnType(this.apigen, a, mb);
setNextSocketReturnType(this.apigen, mb, succ);
return mb;
}
private void addCaseReceiveMethod(ClassBuilder cb, EAction a, EState succ) throws ScribbleException
{
MethodBuilder mb = makeCaseReceiveHeader(cb, a, succ);
String ln = JavaBuilder.RETURN + " " + "receive(" + getSessionApiRoleConstant(a.obj) + ", ";
//ln += mb.getParameters().stream().map((p) -> p.substring(p.indexOf(" ") + 1, p.length())).collect(Collectors.joining(", ")) + ");";
boolean first = true;
for (String param : mb.getParameters())
{
if (first)
{
first = false;
}
else
{
ln += ", ";
}
if (param.contains("<"))
{
param = param.substring(param.lastIndexOf('>') + 1, param.length());
}
ln += param.substring(param.indexOf(" ") + 1, param.length());
}
mb.addBodyLine(ln + ");");
}
private void addCaseReceiveDiscardMethod(ClassBuilder cb, EAction a, EState succ)
{
Module main = this.apigen.getMainModule();
MethodBuilder mb = cb.newMethod();
setCaseReceiveDiscardHeaderWithoutReturnType(this.apigen, a, mb);
setNextSocketReturnType(this.apigen, mb, succ);
mb.addAnnotations("@SuppressWarnings(\"unchecked\")");
String ln = JavaBuilder.RETURN + " ";
ln += "receive(" + CASE_OP_PARAM + ", ";
if (a.mid.isOp())
{
ln += a.payload.elems.stream()
.map((pt) -> getGarbageBuf(main.getDataTypeDecl(((DataType) pt)).extName)).collect(Collectors.joining(", ")) + ");";
}
else
{
MessageSigNameDecl msd = main.getMessageSigDecl(((MessageSigName) a.mid).getSimpleName()); // Factor out? (send/receive/branchreceive/...)
ln += getGarbageBuf(msd.extName) + ");";
}
mb.addBodyLine(ln);
}
private static void addBranchCheck(String opClassName, MethodBuilder mb, String messageField)
{
String op = JavaBuilder.THIS + "." + messageField + "." + StateChannelApiGenerator.SCRIBMESSAGE_OP_FIELD;
mb.addBodyLine("if (!" + op + ".equals(" + opClassName + ")) {");
mb.addBodyLine(1, "throw " + JavaBuilder.NEW + " " + StateChannelApiGenerator.SCRIBBLERUNTIMEEXCEPTION_CLASS + "(\"Wrong branch, received: \" + " + op + ");");
mb.addBodyLine("}");
}
// As for ReceiveSocket, but without peer param
public static void setCaseReceiveHeaderWithoutReturnType(StateChannelApiGenerator apigen, EAction a, MethodBuilder mb) throws ScribbleException
{
//final String ROLE_PARAM = "role";
Module main = apigen.getMainModule(); // FIXME: main not necessarily the right module?
String opClass = SessionApiGenerator.getOpClassName(a.mid);
mb.setName("receive");
mb.addModifiers(JavaBuilder.PUBLIC);
mb.addParameters(opClass + " " + StateChannelApiGenerator.RECEIVE_OP_PARAM); // More params may be added later (payload-arg/future Buffs)
mb.addExceptions(StateChannelApiGenerator.SCRIBBLERUNTIMEEXCEPTION_CLASS, "java.io.IOException", "ClassNotFoundException");//, "ExecutionException", "InterruptedException");
if (a.mid.isOp())
{
ReceiveSocketGenerator.addReceiveOpParams(mb, main, a, true);
}
else //if (a.mid.isMessageSigName())
{
MessageSigNameDecl msd = main.getMessageSigDecl(((MessageSigName) a.mid).getSimpleName()); // FIXME: might not belong to main module
ReceiveSocketGenerator.addReceiveMessageSigNameParams(mb, msd, true);
}
}
public static void setCaseReceiveDiscardHeaderWithoutReturnType(StateChannelApiGenerator apigen, EAction a, MethodBuilder mb)
{
// Duplicated from makeCaseReceiveHeader, without parameters
final String opClass = SessionApiGenerator.getOpClassName(a.mid);
mb.setName("receive");
mb.addModifiers(JavaBuilder.PUBLIC);
mb.addParameters(opClass + " " + StateChannelApiGenerator.RECEIVE_OP_PARAM); // More params may be added later (payload-arg/future Buffs)
mb.addExceptions(StateChannelApiGenerator.SCRIBBLERUNTIMEEXCEPTION_CLASS, "java.io.IOException", "ClassNotFoundException");//, "ExecutionException", "InterruptedException");
}
public static String getCaseSocketName(String branchSocketName)
{
return branchSocketName + "_Cases";
}
}