package org.scribble.codegen.java.endpointapi;
import org.scribble.ast.DataTypeDecl;
import org.scribble.ast.MessageSigNameDecl;
import org.scribble.ast.Module;
import org.scribble.codegen.java.util.ClassBuilder;
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.PayloadType;
public class ReceiveSocketGenerator extends ScribSocketGenerator
{
public ReceiveSocketGenerator(StateChannelApiGenerator apigen, EState curr)
{
super(apigen, curr);
}
@Override
protected String getSuperClassType()
{
return RECEIVESOCKET_CLASS + "<" + getSessionClassName() + ", " + getSelfClassName() + ">";
}
@Override
protected void addImports()
{
super.addImports();
this.cb.addImports(getOpsPackageName() + ".*");
}
// Single receive (unary branch -- singleton next state)
// But generates a set of methods related to this input (regular receive, async with future, async with discard, arrived polling) -- TODO: call-back
// FIXME: most general async would also allow whole input-only compound statements (choice, recursion) to be bypassed
//private void addReceiveMethods(ClassBuilder cb, EndpointState curr)
@Override
protected void addMethods() throws ScribbleException
{
EAction a = curr.getActions().iterator().next();
//String nextClass = this.apigen.getSocketClassName(curr.accept(a));
EState succ = curr.getSuccessor(a);
ClassBuilder futureClass = new InputFutureGenerator(this.apigen, this.cb, a).generateType(); // Wraps all payload elements as fields (set by future completion)
// FIXME: problem if package and protocol have the same name -- still?
this.apigen.addTypeDecl(futureClass);
makeReceiveMethod(a, succ); // [nextClass] receive([opClass] op, Buff<? super T> arg, ...)
makeAsyncMethod(a, succ, futureClass.getName()); // [nextClass] async([opClass] op, Buff<futureClass> arg)
makeIsDoneMethod(a); // boolean isDone()
makeAsyncDiscardMethod(a, succ, futureClass.getName()); // [nextClass] async([opClass] op)
}
// [nextClass] receive([opClass] op, Buf<? super T> arg, ...)
//private void makeReceiveMethod(ClassBuilder cb, Module main, IOAction a, String nextClass, String opClass)
private void makeReceiveMethod(EAction a, EState succ) throws ScribbleException
{
Module main = this.apigen.getMainModule(); // FIXME: main not necessarily the right module?
MethodBuilder mb = makeReceiveHeader(a, succ);
if (a.mid.isOp())
{
//addReceiveOpParams(mb, main, a);
String ln = a.payload.isEmpty() ? "" : StateChannelApiGenerator.SCRIBMESSAGE_CLASS + " " + RECEIVE_MESSAGE_PARAM + " = ";
ln += JavaBuilder.SUPER + ".readScribMessage(" + getSessionApiRoleConstant(a.obj) + ");";
mb.addBodyLine(ln);
addPayloadBuffSetters(main, a, mb);
}
else //if (a.mid.isMessageSigName())
{
MessageSigNameDecl msd = main.getMessageSigDecl(((MessageSigName) a.mid).getSimpleName()); // FIXME: might not belong to main module
//addReceiveMessageSigNameParams(mb, a, msd);*/
mb.addBodyLine(StateChannelApiGenerator.SCRIBMESSAGE_CLASS + " " + RECEIVE_MESSAGE_PARAM + " = "
+ JavaBuilder.SUPER + ".readScribMessage(" + getSessionApiRoleConstant(a.obj) + ");");
mb.addBodyLine(RECEIVE_ARG_PREFIX + "." + BUFF_VAL_FIELD + " = (" + msd.extName + ") " + RECEIVE_MESSAGE_PARAM + ";");
}
addReturnNextSocket(mb, succ);
}
// Payload parameters added later
private MethodBuilder makeReceiveHeader(EAction a, EState succ) throws ScribbleException
{
MethodBuilder mb = this.cb.newMethod();
setReceiveHeaderWithoutReturnType(this.apigen, a, mb);
setNextSocketReturnType(this.apigen, mb, succ);
return mb;
}
// [nextClass] async([opClass] op, Buf<? super futureClass> arg)
private void makeAsyncMethod(EAction a, EState succ, String futureClass)
{
final String ROLE_PARAM = "role";
String opClass = SessionApiGenerator.getOpClassName(a.mid);
MethodBuilder mb = this.cb.newMethod("async");
// Blurb stuff similar to makeReceiveHeader
mb.addModifiers(JavaBuilder.PUBLIC);//, ClassBuilder.SYNCHRONIZED);
//setNextSocketReturnType(mb, succ);
setNextSocketReturnType(this.apigen, mb, succ);
mb.addExceptions(StateChannelApiGenerator.SCRIBBLERUNTIMEEXCEPTION_CLASS);
mb.addParameters(SessionApiGenerator.getRoleClassName(a.obj) + " " + ROLE_PARAM);
mb.addParameters(opClass + " " + StateChannelApiGenerator.RECEIVE_OP_PARAM);
mb.addParameters(BUF_CLASS + "<" + futureClass + "> " + RECEIVE_ARG_PREFIX); // Method for future-buf even if no payload, for sync action
//mb.addBodyLine(ClassBuilder.SUPER + ".use();");
//mb2.addBodyLine(ARG_PREFIX + ".val = " + " " + ClassBuilder.SUPER + ".getFuture(" + getPrefixedRoleClassName(a.peer) + ");");
mb.addBodyLine(RECEIVE_ARG_PREFIX + "." + BUFF_VAL_FIELD + " = "
+ JavaBuilder.NEW + " " + futureClass + "(" + JavaBuilder.SUPER + ".getFuture(" + getSessionApiRoleConstant(a.obj) + "));");
addReturnNextSocket(mb, succ);
}
// [nextClass] async([opClass] op) -- wrapper for makeAsyncMethod
private void makeAsyncDiscardMethod(EAction a, EState succ, String futureClass)
{
MethodBuilder mb = makeAsyncDiscardHeader(a, succ, futureClass);
mb.addBodyLine(JavaBuilder.RETURN + " async(" + SessionApiGenerator.getSessionClassName(apigen.getGProtocolName()) + "." + a.obj + ", " + StateChannelApiGenerator.RECEIVE_OP_PARAM + ", " + getGarbageBuf(futureClass) + ");");
mb.addAnnotations("@SuppressWarnings(\"unchecked\")"); // To cast the generic garbage buf
}
private MethodBuilder makeAsyncDiscardHeader(EAction a, EState succ, String futureClass)
{
MethodBuilder mb = this.cb.newMethod();
setAsyncDiscardHeaderWithoutReturnType(this.apigen, a, mb, futureClass);
setNextSocketReturnType(this.apigen, mb, succ);
return mb;
}
// boolean isDone()
private void makeIsDoneMethod(EAction a)
{
MethodBuilder mb = cb.newMethod("isDone");
mb.addModifiers(JavaBuilder.PUBLIC);
mb.setReturn("boolean");
mb.addBodyLine(JavaBuilder.RETURN + " " + JavaBuilder.SUPER + ".isDone(" + getSessionApiRoleConstant(a.obj) + ");");
}
// Doesn't include return type
//public static void makeReceiveHeader(StateChannelApiGenerator apigen, IOAction a, EndpointState succ, MethodBuilder mb)
public static void setReceiveHeaderWithoutReturnType(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.addExceptions(StateChannelApiGenerator.SCRIBBLERUNTIMEEXCEPTION_CLASS, "java.io.IOException", "ClassNotFoundException");
//, "ExecutionException", "InterruptedException");
mb.addParameters(SessionApiGenerator.getRoleClassName(a.obj) + " " + ROLE_PARAM, opClass + " " + StateChannelApiGenerator.RECEIVE_OP_PARAM);
if (a.mid.isOp())
{
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
addReceiveMessageSigNameParams(mb, msd, true);
}
}
// FIXME: main may not be the right module
protected static void addReceiveOpParams(MethodBuilder mb, Module main, EAction a, boolean superr) throws ScribbleException
{
if (!a.payload.isEmpty())
{
String buffSuper = BUF_CLASS + "<" + ((superr) ? "? " + JavaBuilder.SUPER + " " : "");
int i = 1;
for (PayloadType<?> pt : a.payload.elems)
{
if (!pt.isDataType())
{
throw new ScribbleException("[TODO] API generation not supported for non- data type payloads: " + pt);
}
DataTypeDecl dtd = main.getDataTypeDecl((DataType) pt); // TODO: if not DataType
ScribSocketGenerator.checkJavaDataTypeDecl(dtd);
mb.addParameters(buffSuper + dtd.extName + "> " + RECEIVE_ARG_PREFIX + i++);
}
}
}
protected static void addPayloadBuffSetters(Module main, EAction a, MethodBuilder mb)
{
if (!a.payload.isEmpty())
{
int i = 1;
for (PayloadType<?> pt : a.payload.elems) // Could factor out this loop (arg names) with addReceiveOpParams (as for send)
{
DataTypeDecl dtd = main.getDataTypeDecl((DataType) pt); // TODO: if not DataType
mb.addBodyLine(RECEIVE_ARG_PREFIX + i + "." + BUFF_VAL_FIELD + " = (" + dtd.extName + ") "
+ RECEIVE_MESSAGE_PARAM + "." + SCRIBMESSAGE_PAYLOAD_FIELD + "[" + (i++ - 1) +"];");
}
}
}
protected static void addReceiveMessageSigNameParams(MethodBuilder mb, MessageSigNameDecl msd, boolean superr) throws ScribbleException
{
ScribSocketGenerator.checkMessageSigNameDecl(msd);
mb.addParameters(BUF_CLASS + "<" + ((superr) ? "? " + JavaBuilder.SUPER + " " : "") + msd.extName + "> " + RECEIVE_ARG_PREFIX);
}
// Similar to setReceiveHeader
public static void setAsyncDiscardHeaderWithoutReturnType(StateChannelApiGenerator apigen, EAction a, MethodBuilder mb, String futureClass)
{
final String ROLE_PARAM = "role";
final String opClass = SessionApiGenerator.getOpClassName(a.mid);
mb.setName("async");
mb.addModifiers(JavaBuilder.PUBLIC);
mb.addParameters(SessionApiGenerator.getRoleClassName(a.obj) + " " + ROLE_PARAM, opClass + " " + StateChannelApiGenerator.RECEIVE_OP_PARAM);
mb.addExceptions(StateChannelApiGenerator.SCRIBBLERUNTIMEEXCEPTION_CLASS);
}
}