package org.scribble.codegen.java.endpointapi; import java.util.Iterator; import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; import org.scribble.ast.DataTypeDecl; import org.scribble.ast.MessageSigNameDecl; import org.scribble.ast.Module; 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 OutputSocketGenerator extends ScribSocketGenerator { public OutputSocketGenerator(StateChannelApiGenerator apigen, EState curr) { super(apigen, curr); } @Override protected String getSuperClassType() { return OUTPUTSOCKET_CLASS + "<" + getSessionClassName() + ", " + getSelfClassName() + ">"; } @Override protected void addImports() { this.cb.addImports("java.io.IOException"); super.addImports(); } // A method for each successor state //private void addSendMethods(ClassBuilder cb, EndpointState curr) @Override protected void addMethods() throws ScribbleException { final String ROLE_PARAM = "role"; // Mixed sends and connects boolean hasConnect = false; boolean hasWrap = false; for (EAction a : curr.getActions()) // (Scribble ensures all "a" are input or all are output) { EState succ = curr.getSuccessor(a); MethodBuilder mb = this.cb.newMethod(); if (a.isSend()) { setSendHeaderWithoutReturnType(apigen, a, mb); } else if (a.isConnect()) { hasConnect = true; setConnectHeaderWithoutReturnType(apigen, a, mb); } else if (a.isDisconnect()) { setDisconnectHeaderWithoutReturnType(apigen, a, mb); } else if (a.isWrapClient()) { hasWrap = true; setWrapClientHeaderWithoutReturnType(apigen, a, mb); } else { throw new RuntimeException("[TODO] OutputSocket API generation for: " + a); } setNextSocketReturnType(this.apigen, mb, succ); if (a.mid.isOp()) { this.cb.addImports(getOpsPackageName() + ".*"); // FIXME: repeated } if (a.isSend()) { if (a.mid.isOp()) { List<String> args = getSendPayloadArgs(a); String body = JavaBuilder.SUPER + ".writeScribMessage(" + ROLE_PARAM + ", " + getSessionApiOpConstant(a.mid); if (!a.payload.isEmpty()) { body += ", " + args.stream().collect(Collectors.joining(", ")); } body += ");\n"; mb.addBodyLine(body); } else //if (a.mid.isMessageSigName()) { final String MESSAGE_PARAM = "m"; // FIXME: factor out mb.addBodyLine(JavaBuilder.SUPER + ".writeScribMessage(" + ROLE_PARAM + ", " + MESSAGE_PARAM + ");"); } } else if (a.isConnect()) { //throw new RuntimeException("Shouldn't get in here: " + a); mb.addBodyLine(JavaBuilder.SUPER + ".connect(" + ROLE_PARAM + ", cons, host, port);\n"); } else if (a.isDisconnect()) { mb.addBodyLine(JavaBuilder.SUPER + ".disconnect(" + ROLE_PARAM + ");\n"); } else if (a.isWrapClient()) { mb.addBodyLine(JavaBuilder.SUPER + ".wrapClient(" + ROLE_PARAM + ", wrapper);\n"); } else { throw new RuntimeException("Shouldn't get in here: " + a); } addReturnNextSocket(mb, succ); } if (hasConnect) { this.cb.addImports("java.util.concurrent.Callable"); this.cb.addImports("org.scribble.net.session.BinaryChannelEndpoint"); } if (hasWrap) { this.cb.addImports("java.util.concurrent.Callable"); this.cb.addImports("org.scribble.net.session.BinaryChannelWrapper"); } } private static List<String> getSendPayloadArgs(EAction a) { final String ARG_PREFIX = "arg"; return IntStream.range(0, a.payload.elems.size()).mapToObj((i) -> ARG_PREFIX + i++).collect(Collectors.toList()); // FIXME: factor out with params } public static void setSendHeaderWithoutReturnType(StateChannelApiGenerator apigen, EAction a, MethodBuilder mb) throws ScribbleException { final String ROLE_PARAM = "role"; Module main = apigen.getMainModule(); // FIXME: main not necessarily the right module? mb.setName("send"); mb.addModifiers(JavaBuilder.PUBLIC); mb.addExceptions(StateChannelApiGenerator.SCRIBBLERUNTIMEEXCEPTION_CLASS, "IOException"); mb.addParameters(SessionApiGenerator.getRoleClassName(a.obj) + " " + ROLE_PARAM); // More params added below if (a.mid.isOp()) { addSendOpParams(apigen, mb, main, a); } else //if (a.mid.isMessageSigName()) { MessageSigNameDecl msd = main.getMessageSigDecl(((MessageSigName) a.mid).getSimpleName()); // FIXME: might not belong to main module addSendMessageSigNameParams(mb, msd); } } public static void setConnectHeaderWithoutReturnType(StateChannelApiGenerator apigen, EAction a, MethodBuilder mb) { final String ROLE_PARAM = "role"; mb.setName("connect"); mb.addModifiers(JavaBuilder.PUBLIC); mb.addExceptions(StateChannelApiGenerator.SCRIBBLERUNTIMEEXCEPTION_CLASS, "IOException"); mb.addParameters(SessionApiGenerator.getRoleClassName(a.obj) + " " + ROLE_PARAM); // More params added below mb.addParameters("Callable<? extends BinaryChannelEndpoint> cons"); mb.addParameters("String host"); mb.addParameters("int port"); } public static void setDisconnectHeaderWithoutReturnType(StateChannelApiGenerator apigen, EAction a, MethodBuilder mb) { final String ROLE_PARAM = "role"; mb.setName("disconnect"); mb.addModifiers(JavaBuilder.PUBLIC); mb.addExceptions(StateChannelApiGenerator.SCRIBBLERUNTIMEEXCEPTION_CLASS, "IOException"); mb.addParameters(SessionApiGenerator.getRoleClassName(a.obj) + " " + ROLE_PARAM); } public static void setWrapClientHeaderWithoutReturnType(StateChannelApiGenerator apigen, EAction a, MethodBuilder mb) { final String ROLE_PARAM = "role"; mb.setName("wrapClient"); mb.addModifiers(JavaBuilder.PUBLIC); mb.addExceptions(StateChannelApiGenerator.SCRIBBLERUNTIMEEXCEPTION_CLASS, "IOException"); mb.addParameters(SessionApiGenerator.getRoleClassName(a.obj) + " " + ROLE_PARAM); mb.addParameters("Callable<? extends BinaryChannelWrapper> wrapper"); } protected static void addSendOpParams(StateChannelApiGenerator apigen, MethodBuilder mb, Module main, EAction a) throws ScribbleException { List<String> args = getSendPayloadArgs(a); mb.addParameters(SessionApiGenerator.getOpClassName(a.mid) + " op"); // opClass -- op param not actually used in body if (!a.payload.isEmpty()) { Iterator<String> as = args.iterator(); 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); // FIXME: might not belong to main module // TODO: if not DataType ScribSocketGenerator.checkJavaDataTypeDecl(dtd); mb.addParameters(dtd.extName + " " + as.next()); } } } protected static void addSendMessageSigNameParams(MethodBuilder mb, MessageSigNameDecl msd) throws ScribbleException { final String MESSAGE_PARAM = "m"; ScribSocketGenerator.checkMessageSigNameDecl(msd); mb.addParameters(msd.extName + " " + MESSAGE_PARAM); } }