package org.scribble.codegen.java.endpointapi; import java.util.stream.Collectors; import org.scribble.ast.DataTypeDecl; 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.HandleInterfaceGenerator; import org.scribble.codegen.java.endpointapi.ioifaces.IOStateInterfaceGenerator; import org.scribble.codegen.java.endpointapi.ioifaces.SuccessorInterfaceGenerator; import org.scribble.codegen.java.util.ClassBuilder; import org.scribble.codegen.java.util.EnumBuilder; 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; import org.scribble.sesstype.name.Role; public class BranchSocketGenerator extends ScribSocketGenerator { public BranchSocketGenerator(StateChannelApiGenerator apigen, EState curr) { super(apigen, curr); } @Override protected String getSuperClassType() { return BRANCHSOCKET_CLASS + "<" + getSessionClassName() + ", " + getSelfClassName() + ">"; } @Override protected void addImports() { this.cb.addImports("java.io.IOException"); super.addImports(); } //private void addBranchMethod(ClassBuilder cb, EndpointState curr) @Override protected void addMethods() throws ScribbleException { final String ROLE_PARAM = "role"; final String MESSAGE_VAR = "m"; final String OPENUM_VAR = "openum"; final String OP = MESSAGE_VAR + "." + StateChannelApiGenerator.SCRIBMESSAGE_OP_FIELD; Module main = this.apigen.getMainModule(); //String next = constructCaseClass(curr, main); ClassBuilder caseclass = new CaseSocketGenerator(this.apigen, this.curr).generateType(); String next = caseclass.getName(); String enumClass = getBranchEnumClassName(this.apigen, this.curr); //cb.addImports("java.util.concurrent.ExecutionException"); //boolean first; Role peer = this.curr.getActions().iterator().next().obj; // Branch method addBranchMethod(ROLE_PARAM, MESSAGE_VAR, OPENUM_VAR, OP, peer, next, enumClass); //if (IOInterfacesGenerator.skipIOInterfacesGeneration(apigen.getInitialState())) if (this.apigen.skipIOInterfacesGeneration) { EnumBuilder eb = cb.newMemberEnum(enumClass); eb.addModifiers(JavaBuilder.PUBLIC); eb.addInterfaces(OPENUM_INTERFACE); this.curr.getActions().stream().forEach((a) -> eb.addValues(SessionApiGenerator.getOpClassName(a.mid))); addDirectBranchCallbackMethod(ROLE_PARAM, MESSAGE_VAR, OP, main, peer); // Hack: callback apigen while i/o i/f's not supported for connect/accept/etc } else { // Callback methods String handleif = addBranchCallbackMethod(ROLE_PARAM, peer); addHandleInterfaceCallbackMethod(ROLE_PARAM, MESSAGE_VAR, OP, main, peer, handleif); addHandleMethod(ROLE_PARAM, MESSAGE_VAR, OP, main, peer); } this.apigen.addTypeDecl(new HandlerInterfaceGenerator(this.apigen, this.cb, this.curr).generateType()); } private void addBranchMethod(final String ROLE_PARAM, final String MESSAGE_VAR, final String OPENUM_VAR, final String OP, Role peer, String next, String enumClass) { MethodBuilder mb = cb.newMethod("branch"); mb.setReturn(next); mb.addParameters(SessionApiGenerator.getRoleClassName(curr.getActions().iterator().next().obj) + " " + ROLE_PARAM); mb.addModifiers(JavaBuilder.PUBLIC); mb.addExceptions(StateChannelApiGenerator.SCRIBBLERUNTIMEEXCEPTION_CLASS, "IOException", "ClassNotFoundException");//, "ExecutionException", "InterruptedException"); if (!this.apigen.skipIOInterfacesGeneration) { mb.addAnnotations("@Override"); } mb.addBodyLine(StateChannelApiGenerator.SCRIBMESSAGE_CLASS + " " + MESSAGE_VAR + " = " + JavaBuilder.SUPER + ".readScribMessage(" + getSessionApiRoleConstant(peer) + ");"); mb.addBodyLine(enumClass + " " + OPENUM_VAR + ";"); boolean first = true; for (EAction a : this.curr.getActions()) { mb.addBodyLine(((first) ? "" : "else ") + "if (" + OP + ".equals(" + getSessionApiOpConstant(a.mid) + ")) {"); mb.addBodyLine(1, OPENUM_VAR + " = " + enumClass + "." + SessionApiGenerator.getOpClassName(a.mid) + ";"); mb.addBodyLine("}"); first = false; } mb.addBodyLine("else {"); mb.addBodyLine(1, "throw " + JavaBuilder.NEW + " RuntimeException(\"Won't get here: \" + " + OP + ");"); mb.addBodyLine("}"); mb.addBodyLine(JavaBuilder.RETURN + " " + JavaBuilder.NEW + " " + next + "(" + SCRIBSOCKET_SE_FIELD + ", true, " + OPENUM_VAR + ", " + MESSAGE_VAR + ");"); // FIXME: dummy boolean not needed } private String addBranchCallbackMethod(final String ROLE_PARAM, Role peer) { boolean first; String handlerif = HandlerInterfaceGenerator.getHandlerInterfaceName(this.cb.getName()); String handleif = HandleInterfaceGenerator.getHandleInterfaceName(this.apigen.getSelf(), this.curr); MethodBuilder mb2 = this.cb.newMethod("branch"); mb2.addParameters(SessionApiGenerator.getRoleClassName(peer) + " " + ROLE_PARAM); //mb2.addParameters("java.util.concurrent.Callable<" + ifname + "> branch"); mb2.addParameters(handlerif + " handler"); mb2.setReturn(JavaBuilder.VOID); mb2.addModifiers(JavaBuilder.PUBLIC); mb2.addExceptions(StateChannelApiGenerator.SCRIBBLERUNTIMEEXCEPTION_CLASS, "IOException", "ClassNotFoundException");//, "ExecutionException", "InterruptedException"); first = true; handleif += "<"; for (EAction a : this.curr.getActions().stream().sorted(IOStateInterfaceGenerator.IOACTION_COMPARATOR).collect(Collectors.toList())) { if (first) { first = false; } else { handleif += ", "; } EState succ = this.curr.getSuccessor(a); if (succ.isTerminal()) { handleif += ScribSocketGenerator.GENERATED_ENDSOCKET_NAME; } else { handleif += this.apigen.getSocketClassName(succ); } } handleif += ">"; mb2.addBodyLine("branch(role, (" + handleif + ") handler);"); return handleif; } private void addHandleInterfaceCallbackMethod(final String ROLE_PARAM, final String MESSAGE_VAR, final String OP, Module main, Role peer, String handleif) { MethodBuilder mb3 = this.cb.newMethod("branch"); mb3.addParameters(SessionApiGenerator.getRoleClassName(peer) + " " + ROLE_PARAM); //mb2.addParameters("java.util.concurrent.Callable<" + ifname + "> branch"); mb3.addParameters(handleif + " handler"); mb3.setReturn(JavaBuilder.VOID); mb3.addModifiers(JavaBuilder.PUBLIC); mb3.addAnnotations("@Override"); mb3.addExceptions(StateChannelApiGenerator.SCRIBBLERUNTIMEEXCEPTION_CLASS, "IOException", "ClassNotFoundException");//, "ExecutionException", "InterruptedException"); addCallbackCases(mb3, ROLE_PARAM, MESSAGE_VAR, OP, main, peer); /*boolean first; mb3.addBodyLine(StateChannelApiGenerator.SCRIBMESSAGE_CLASS + " " + MESSAGE_VAR + " = " + JavaBuilder.SUPER + ".readScribMessage(" + getSessionApiRoleConstant(peer) + ");"); first = true; for (EAction a : this.curr.getActions()) { EState succ = this.curr.getSuccessor(a); if (first) { first = false; } else { mb3.addBodyLine("else"); } mb3.addBodyLine("if (" + MESSAGE_VAR + "." + StateChannelApiGenerator.SCRIBMESSAGE_OP_FIELD + ".equals(" + getSessionApiOpConstant(a.mid) + ")) {"); if (succ.isTerminal()) { mb3.addBodyLine(1, SCRIBSOCKET_SE_FIELD + ".setCompleted();"); } String ln = "handler.receive("; //if (!succ.isTerminal()) { //FIXME: factor out with addReturn? ln += JavaBuilder.NEW + " " + (succ.isTerminal() ? ScribSocketGenerator.GENERATED_ENDSOCKET_NAME : this.apigen.getSocketClassName(succ)) + "(" + SCRIBSOCKET_SE_FIELD + ", true), "; } ln += getSessionApiOpConstant(a.mid); // Based on receive parameters if (a.mid.isOp()) { if (!a.payload.isEmpty()) { String buffSuper = JavaBuilder.NEW + " " + BUF_CLASS + "<>("; int i = 0; for (PayloadType<?> pt : a.payload.elems) { DataTypeDecl dtd = main.getDataTypeDecl((DataType) pt); // TODO: if not DataType ln += ", " + buffSuper + "(" + dtd.extName + ") " + RECEIVE_MESSAGE_PARAM + "." + SCRIBMESSAGE_PAYLOAD_FIELD + "[" + i++ + "])"; } } } else { MessageSigNameDecl msd = main.getMessageSigDecl(((MessageSigName) a.mid).getSimpleName()); // FIXME: might not belong to main module ln += ", " + JavaBuilder.NEW + " " + BUF_CLASS + "<>((" + msd.extName + ") " + RECEIVE_MESSAGE_PARAM + "." + SCRIBMESSAGE_PAYLOAD_FIELD + "[0])"; } ln += ");"; mb3.addBodyLine(1, ln); mb3.addBodyLine("}"); } mb3.addBodyLine("else {"); mb3.addBodyLine(1, "throw " + JavaBuilder.NEW + " RuntimeException(\"Won't get here: \" + " + OP + ");"); mb3.addBodyLine("}");*/ } // FIXME: factor out with others (addCallbackCases) private void addHandleMethod(final String ROLE_PARAM, final String MESSAGE_VAR, final String OP, Module main, Role peer) { boolean first; MethodBuilder mb4 = this.cb.newMethod("handle"); mb4.addParameters(SessionApiGenerator.getRoleClassName(peer) + " " + ROLE_PARAM); String tmp = HandleInterfaceGenerator.getHandleInterfaceName(this.apigen.getSelf(), this.curr) + "<"; tmp += this.curr.getActions().stream().sorted(IOStateInterfaceGenerator.IOACTION_COMPARATOR) .map((a) -> SuccessorInterfaceGenerator.getSuccessorInterfaceName(a)).collect(Collectors.joining(", ")) + ">"; mb4.addParameters(tmp + " handler"); mb4.setReturn(JavaBuilder.VOID); mb4.addModifiers(JavaBuilder.PUBLIC); mb4.addExceptions(StateChannelApiGenerator.SCRIBBLERUNTIMEEXCEPTION_CLASS, "IOException", "ClassNotFoundException");//, "ExecutionException", "InterruptedException"); mb4.addAnnotations("@Override"); mb4.addBodyLine(StateChannelApiGenerator.SCRIBMESSAGE_CLASS + " " + MESSAGE_VAR + " = " + JavaBuilder.SUPER + ".readScribMessage(" + getSessionApiRoleConstant(peer) + ");"); first = true; for (EAction a : this.curr.getActions()) { EState succ = this.curr.getSuccessor(a); if (first) { first = false; } else { mb4.addBodyLine("else"); } mb4.addBodyLine("if (" + MESSAGE_VAR + "." + StateChannelApiGenerator.SCRIBMESSAGE_OP_FIELD + ".equals(" + getSessionApiOpConstant(a.mid) + ")) {"); if (succ.isTerminal()) { mb4.addBodyLine(1, SCRIBSOCKET_SE_FIELD + ".setCompleted();"); } String ln = "handler.receive("; //if (!succ.isTerminal()) { //FIXME: factor out with addReturn? ln += JavaBuilder.NEW + " " + (succ.isTerminal() ? ScribSocketGenerator.GENERATED_ENDSOCKET_NAME : this.apigen.getSocketClassName(succ)) + "(" + SCRIBSOCKET_SE_FIELD + ", true), "; } ln += getSessionApiOpConstant(a.mid); // Based on receive parameters if (a.mid.isOp()) { if (!a.payload.isEmpty()) { String buffSuper = JavaBuilder.NEW + " " + BUF_CLASS + "<>("; int i = 0; for (PayloadType<?> pt : a.payload.elems) { DataTypeDecl dtd = main.getDataTypeDecl((DataType) pt); // TODO: if not DataType ln += ", " + buffSuper + "(" + dtd.extName + ") " + RECEIVE_MESSAGE_PARAM + "." + SCRIBMESSAGE_PAYLOAD_FIELD + "[" + i++ + "])"; } } } else { MessageSigNameDecl msd = main.getMessageSigDecl(((MessageSigName) a.mid).getSimpleName()); // FIXME: might not belong to main module ln += ", " + JavaBuilder.NEW + " " + BUF_CLASS + "<>((" + msd.extName + ") " + RECEIVE_MESSAGE_PARAM + "." + SCRIBMESSAGE_PAYLOAD_FIELD + "[0])"; } ln += ");"; mb4.addBodyLine(1, ln); mb4.addBodyLine("}"); } mb4.addBodyLine("else {"); mb4.addBodyLine(1, "throw " + JavaBuilder.NEW + " RuntimeException(\"Won't get here: \" + " + OP + ");"); mb4.addBodyLine("}"); } protected static String getBranchEnumClassName(StateChannelApiGenerator apigen, EState curr) { //return BranchInterfaceGenerator.getBranchInterfaceEnumName(apigen.getSelf(), curr); //return (IOInterfacesGenerator.skipIOInterfacesGeneration(apigen.getInitialState())) return apigen.skipIOInterfacesGeneration ? apigen.getSocketClassName(curr) + "_Enum" : BranchInterfaceGenerator.getBranchInterfaceEnumName(apigen.getSelf(), curr); } // branch callback, that doesn't just call handle (cf. addBranchCallbackMethod) -- hack, callback apigen while i/o i/f's not supported for connect/accept/etc private void addDirectBranchCallbackMethod(final String ROLE_PARAM, final String MESSAGE_VAR, final String OP, Module main, Role peer) { String handlerif = HandlerInterfaceGenerator.getHandlerInterfaceName(this.cb.getName()); MethodBuilder mb2 = this.cb.newMethod("branch"); mb2.addParameters(SessionApiGenerator.getRoleClassName(peer) + " " + ROLE_PARAM); mb2.addParameters(handlerif + " handler"); mb2.setReturn(JavaBuilder.VOID); mb2.addModifiers(JavaBuilder.PUBLIC); mb2.addExceptions(StateChannelApiGenerator.SCRIBBLERUNTIMEEXCEPTION_CLASS, "IOException", "ClassNotFoundException");//, "ExecutionException", "InterruptedException"); //mb2.addBodyLine("branch(role, (" + handleif + ") handler);"); addCallbackCases(mb2, ROLE_PARAM, MESSAGE_VAR, OP, main, peer); } private void addCallbackCases(MethodBuilder mb2, final String ROLE_PARAM, final String MESSAGE_VAR, final String OP, Module main, Role peer) { boolean first; mb2.addBodyLine(StateChannelApiGenerator.SCRIBMESSAGE_CLASS + " " + MESSAGE_VAR + " = " + JavaBuilder.SUPER + ".readScribMessage(" + getSessionApiRoleConstant(peer) + ");"); first = true; for (EAction a : this.curr.getActions()) { EState succ = this.curr.getSuccessor(a); if (first) { first = false; } else { mb2.addBodyLine("else"); } mb2.addBodyLine("if (" + MESSAGE_VAR + "." + StateChannelApiGenerator.SCRIBMESSAGE_OP_FIELD + ".equals(" + getSessionApiOpConstant(a.mid) + ")) {"); if (succ.isTerminal()) { mb2.addBodyLine(1, SCRIBSOCKET_SE_FIELD + ".setCompleted();"); } String ln = "handler.receive("; //if (!succ.isTerminal()) { //FIXME: factor out with addReturn? ln += JavaBuilder.NEW + " " + (succ.isTerminal() ? ScribSocketGenerator.GENERATED_ENDSOCKET_NAME : this.apigen.getSocketClassName(succ)) + "(" + SCRIBSOCKET_SE_FIELD + ", true), "; } ln += getSessionApiOpConstant(a.mid); // Based on receive parameters if (a.mid.isOp()) { if (!a.payload.isEmpty()) { String buffSuper = JavaBuilder.NEW + " " + BUF_CLASS + "<>("; int i = 0; for (PayloadType<?> pt : a.payload.elems) { DataTypeDecl dtd = main.getDataTypeDecl((DataType) pt); // TODO: if not DataType ln += ", " + buffSuper + "(" + dtd.extName + ") " + RECEIVE_MESSAGE_PARAM + "." + SCRIBMESSAGE_PAYLOAD_FIELD + "[" + i++ + "])"; } } } else { MessageSigNameDecl msd = main.getMessageSigDecl(((MessageSigName) a.mid).getSimpleName()); // FIXME: might not belong to main module ln += ", " + JavaBuilder.NEW + " " + BUF_CLASS + "<>((" + msd.extName + ") " + RECEIVE_MESSAGE_PARAM + "." + SCRIBMESSAGE_PAYLOAD_FIELD + "[0])"; } ln += ");"; mb2.addBodyLine(1, ln); mb2.addBodyLine("}"); } mb2.addBodyLine("else {"); mb2.addBodyLine(1, "throw " + JavaBuilder.NEW + " RuntimeException(\"Won't get here: \" + " + OP + ");"); mb2.addBodyLine("}"); } }