package org.scribble.codegen.java.endpointapi.ioifaces; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.IntStream; import org.scribble.codegen.java.endpointapi.ApiGenerator; import org.scribble.codegen.java.endpointapi.CaseSocketGenerator; import org.scribble.codegen.java.endpointapi.HandlerInterfaceGenerator; import org.scribble.codegen.java.endpointapi.ScribSocketGenerator; import org.scribble.codegen.java.endpointapi.SessionApiGenerator; import org.scribble.codegen.java.endpointapi.StateChannelApiGenerator; import org.scribble.codegen.java.util.AbstractMethodBuilder; import org.scribble.codegen.java.util.ClassBuilder; import org.scribble.codegen.java.util.FieldBuilder; import org.scribble.codegen.java.util.InterfaceBuilder; import org.scribble.codegen.java.util.JavaBuilder; import org.scribble.codegen.java.util.MethodBuilder; import org.scribble.codegen.java.util.TypeBuilder; import org.scribble.main.JobContext; import org.scribble.main.RuntimeScribbleException; import org.scribble.main.ScribbleException; import org.scribble.model.endpoint.EState; import org.scribble.model.endpoint.EStateKind; import org.scribble.model.endpoint.actions.EAction; import org.scribble.sesstype.name.GProtocolName; import org.scribble.sesstype.name.Role; // Cf. StateChannelApiGenerator // TODO: concrete state channel "to" casts for supertype i/f's (the info is there in the Java type hierachy though) // Maybe record subtype hierarchy explicitly // TODO: check if generated subtypes scalability issue is just Eclipse or also javac public class IOInterfacesGenerator extends ApiGenerator { private final boolean SUBTYPES; protected StateChannelApiGenerator apigen; private final Map<EAction, InterfaceBuilder> actions = new HashMap<>(); private final Map<EAction, InterfaceBuilder> succs = new HashMap<>(); private final Map<String, InterfaceBuilder> iostates = new HashMap<>(); // Key is interface simple name private final Map<EAction, InterfaceBuilder> caseActions = new HashMap<>(); private final Map<EState, Set<EAction>> preActions = new HashMap<>(); // Pre set: the actions that lead to each state private final Map<EState, Set<InterfaceBuilder>> preds = new HashMap<>(); private final Map<EState, Set<EAction>> branchPostActions = new HashMap<>(); //private final Map<EndpointState, Set<InterfaceBuilder>> branchSuccs = new HashMap<>(); private final Map<String, List<EAction>> branchSuccs = new HashMap<>(); // key: HandleInterface name // Sorted when collected public IOInterfacesGenerator(StateChannelApiGenerator apigen, boolean subtypes) throws RuntimeScribbleException, ScribbleException { super(apigen.getJob(), apigen.getGProtocolName()); this.apigen = apigen; this.SUBTYPES = subtypes; GProtocolName fullname = apigen.getGProtocolName(); Role self = getSelf(); //EndpointState init = this.job.getContext().getEndpointGraph(fullname, self).init; JobContext jc = this.job.getContext(); EState init = this.job.minEfsm ? jc.getMinimisedEGraph(fullname, self).init : jc.getEGraph(fullname, self).init; //if (IOInterfacesGenerator.skipIOInterfacesGeneration(init)) { // FIXME: factor out with skipIOInterfacesGeneration Set<EAction> as = EState.getReachableActions(init); if (as.stream().anyMatch((a) -> !a.isSend() && !a.isReceive())) // HACK FIXME (connect/disconnect) { throw new RuntimeScribbleException("[TODO] I/O Interface generation not supported for: " + as.stream().filter((a) -> !a.isSend() && !a.isReceive()).collect(Collectors.toList())); } } generateActionAndSuccessorInterfacesAndCollectPreActions(new HashSet<>(), init); generateIOStateInterfacesFirstPass(new HashSet<>(), init); collectPreds(); //EndpointState term = EndpointState.findTerminalState(new HashSet<>(), init); EState term = EState.getTerminal(init); ClassBuilder endsock = null; if (term != null) { endsock = (ClassBuilder) this.apigen.getType(ScribSocketGenerator.GENERATED_ENDSOCKET_NAME); for (InterfaceBuilder ib : this.preds.get(term)) { endsock.addInterfaces(ib.getName()); MethodBuilder mb2 = addToCastMethod(ib, "EndSocket"); if (mb2 != null) { ib.addImports(SessionApiGenerator.getStateChannelPackageName(this.gpn, self) + ".EndSocket"); } } endsock.addImports(getIOInterfacePackageName(this.gpn, self) + ".*"); } generateIOStateInterfacesSecondPass(new HashSet<>(), init); collectBranchSuccs(); generateHandleInterfaces(new HashSet<>(), init); generateHandleInterfacesSecondPass(new HashSet<>(), init); addIOStateInterfacesToStateChannels(new HashSet<>(), init); // Except EndSocket if (this.SUBTYPES) { addSupertypeInterfaces(); } // Successor I/f's for EndSocket // FIXME: refactor EndSocket into main collection of generated types (this.apigen.getType) //EndpointState term = EndpointState.findTerminalState(new HashSet<>(), init); if (term != null) { //endsock = (ClassBuilder) this.apigen.getType(ScribSocketGenerator.GENERATED_ENDSOCKET_NAME); //endsock.addImports(getIOInterfacePackageName(this.gpn, self) + ".*"); for (InterfaceBuilder ib : this.preds.get(term)) { //endsock.addInterfaces(ib.getName()); // Duplicated from generateIOStateInterfacesSecondPass for (MethodBuilder cast : ib.getDefaultMethods()) // cast is a default method (a to cast -- hacky) in pred { // Overriding every Successor I/f to methods in the I/O State I/f, even if unnecessary if (!cast.getReturn().equals("EndSocket")) { MethodBuilder mb = addEndSocketToCastMethod(endsock, cast.getReturn(), "throw new RuntimeScribbleException(\"Invalid cast of EndSocket: \" + cast);"); if (mb != null) { mb.addModifiers(JavaBuilder.PUBLIC); mb.addAnnotations("@Override"); endsock.addImports("org.scribble.main.RuntimeScribbleException"); } } } } /*MethodBuilder mb2 = addToCastMethod(ib, "EndSocket"); if (mb2 != null) { ib.addImports(SessionApiGenerator.getStateChannelPackageName(this.gpn, self) + ".EndSocket"); }*/ MethodBuilder mb3 = addEndSocketToCastMethod(endsock, "EndSocket", "return (EndSocket) this;"); if (mb3 != null) { mb3.addModifiers(JavaBuilder.PUBLIC); mb3.addAnnotations("@Override"); } } } /*public static boolean skipIOInterfacesGeneration(EndpointState init) { Set<IOAction> as = EndpointState.getAllReachableActions(init); if (as.stream().anyMatch((a) -> !a.isSend() && !a.isReceive())) // HACK FIXME (connect/disconnect) { return true; } return false; }*/ @Override public Map<String, String> generateApi() { Map<String, String> output = new HashMap<>(); String prefix = getIOInterfacePackageName(this.gpn, getSelf()).replace('.', '/') + "/"; this.actions.values().stream().forEach((ib) -> output.put(prefix + ib.getName() + ".java", ib.build())); this.succs.values().stream().forEach((ib) -> output.put(prefix + ib.getName() + ".java", ib.build())); this.iostates.values().stream().forEach((tb) -> output.put(prefix + tb.getName() + ".java", tb.build())); this.caseActions.values().stream().forEach((ib) -> output.put(prefix + ib.getName() + ".java", ib.build())); return output; } // Factor out FSM visitor? private void generateActionAndSuccessorInterfacesAndCollectPreActions(Set<EState> visited, EState s) throws ScribbleException { if (visited.contains(s) || s.isTerminal()) { return; } visited.add(s); //Set<EAction> as = s.getActions(); List<EAction> as = s.getActions(); for (EAction a : as.stream().sorted(IOStateInterfaceGenerator.IOACTION_COMPARATOR).collect(Collectors.toList())) { if (!a.isSend() && !a.isReceive()) // TODO (connect/disconnect) { throw new RuntimeException("TODO: " + a); } if (!this.actions.containsKey(a)) { this.actions.put(a, new ActionInterfaceGenerator(this.apigen, s, a).generateType()); this.succs.put(a, new SuccessorInterfaceGenerator(this.apigen, s, a).generateType()); if (s.getStateKind() == EStateKind.POLY_INPUT) { // Duplicated from ActionInterfaceGenerator InterfaceBuilder ib = new InterfaceBuilder(); ib.setName("Callback_" + ActionInterfaceGenerator.getActionString(a)); ib.setPackage(IOInterfacesGenerator.getIOInterfacePackageName(this.apigen.getGProtocolName(), this.apigen.getSelf())); ib.addImports("java.io.IOException"); ib.addImports(SessionApiGenerator.getEndpointApiRootPackageName(this.gpn) + ".*"); ib.addImports(SessionApiGenerator.getRolesPackageName(this.gpn) + ".*"); ib.addImports(SessionApiGenerator.getOpsPackageName(this.gpn) + ".*"); ib.addModifiers(JavaBuilder.PUBLIC); ib.addParameters("__Succ extends " + SuccessorInterfaceGenerator.getSuccessorInterfaceName(a)); AbstractMethodBuilder mb = ib.newAbstractMethod(); // Duplicated from HandleInterfaceGenerator HandlerInterfaceGenerator.setHandleMethodHeaderWithoutParamTypes(this.apigen, mb); mb.addParameters("__Succ schan"); HandlerInterfaceGenerator.addHandleMethodOpAndPayloadParams(this.apigen, a, mb); this.caseActions.put(a, ib); } } EState succ = s.getSuccessor(a); putPreAction(succ, a); if (s.getStateKind() == EStateKind.POLY_INPUT) { /*for (IOAction b : s.accept(a).getAcceptable().stream().sorted(IOStateInterfaceGenerator.IOACTION_COMPARATOR).collect(Collectors.toList())) { putBranchPostAction(s, b); }*/ putBranchPostAction(s, a); } generateActionAndSuccessorInterfacesAndCollectPreActions(visited, succ); /*Set<EndpointState> tmp = new HashSet<>(visited); tmp.add(s); // merge paths visited multiple times generateActionAndSuccessorInterfacesAndCollectPredecessors(tmp, succ);*/ } } // Generates partial IO State Interfaces private void generateIOStateInterfacesFirstPass(Set<EState> visited, EState s) throws ScribbleException { if (visited.contains(s) || s.isTerminal()) { return; } String key = IOStateInterfaceGenerator.getIOStateInterfaceName(getSelf(), s); if (!this.iostates.containsKey(key)) // Don't generate if one already exists (up to this pass, repeats will all be the same, i.e. name, Action Interfaces, and action-succ parameters) { // Make the partial I/O State Interface (Successor Interfaces and cast methods added later -- different states may share same State I/f) IOStateInterfaceGenerator ifgen = null; switch (s.getStateKind()) { case OUTPUT: // Send, connect, disconnect ifgen = new SelectInterfaceGenerator(this.apigen, this.actions, s); break; case UNARY_INPUT: ifgen = new ReceiveInterfaceGenerator(this.apigen, this.actions, s); break; case POLY_INPUT: InterfaceBuilder cases = new CaseInterfaceGenerator(this.apigen, this.actions, s).generateType(); this.iostates.put(cases.getName(), cases); ifgen = new BranchInterfaceGenerator(this.apigen, this.actions, s); break; case TERMINAL: default: throw new RuntimeException("(TODO) I/O interface generation: " + s.getStateKind()); } this.iostates.put(key, ifgen.generateType()); } visited.add(s); for (EAction a : s.getActions()) { generateIOStateInterfacesFirstPass(visited, s.getSuccessor(a)); } } // Adds Successor Interfaces and to-cast methods to IO State Interfaces private void generateIOStateInterfacesSecondPass(Set<EState> visited, EState s) { if (visited.contains(s) || s.isTerminal()) { return; } Set<InterfaceBuilder> succifs = this.preds.get(s); // Successor interfaces to be implemented by IO State Interface of s if (succifs != null) { InterfaceBuilder iostate = this.iostates.get(IOStateInterfaceGenerator.getIOStateInterfaceName(getSelf(), s)); if (this.SUBTYPES) // HACK moved here from addSupertypeInterfaces to ensure duplicate inherited to's get overridden { // Generate Succ I/f supertype "to" cast methods for supertype to's String name = iostate.getName(); if (name.startsWith("Select")) { List<String> ifs = this.iostates.get(name).getInterfaces(); List<String> outs = ifs.stream().filter((i) -> i.startsWith("Out")) .map((o) -> (o = o.substring(0, o.indexOf("<"))).substring(o.indexOf("_") + 1, o.length())) .sorted((s1, s2) -> s1.compareTo(s2)) .collect(Collectors.toList()); addSupertypeInterfaceToMethods(s, outs, "Select", "Out"); } } for (InterfaceBuilder pred : succifs) // pred is a Successor Interface for the state s { // May already have "visited" this State I/f for a different state -- Interfaces recorded as a Set, to support repeated adds iostate.addInterfaces(pred.getName()); // Adds Successor Interfaces to this I/O State Interface String ret = iostate.getName() + "<" + IntStream.range(0, iostate.getParameters().size()).mapToObj((i) -> "?").collect(Collectors.joining(", ")) + ">"; addToCastMethod(pred, ret); for (MethodBuilder cast : pred.getDefaultMethods()) // cast is a default method (a to cast -- hacky) in pred { // Overriding every Successor I/f "to" method in the I/O State I/f, even if unnecessary MethodBuilder mb = addToCastMethod(iostate, cast.getReturn()); if (mb != null) { mb.addAnnotations("@Override"); } } } } visited.add(s); for (EAction a : s.getActions()) { generateIOStateInterfacesSecondPass(visited, s.getSuccessor(a)); } } private void generateHandleInterfaces(Set<EState> visited, EState s) throws ScribbleException { if (visited.contains(s) || s.isTerminal()) { return; } if (s.getStateKind() == EStateKind.POLY_INPUT) { //Set<InterfaceBuilder> succifs = this.preds.get(s); String key = HandleInterfaceGenerator.getHandleInterfaceName(getSelf(), s); if (!this.iostates.containsKey(key)) { IOStateInterfaceGenerator ifgen = new HandleInterfaceGenerator(this, this.actions, s, this.caseActions); this.iostates.put(key, ifgen.generateType()); } } visited.add(s); for (EAction a : s.getActions()) { generateHandleInterfaces(visited, s.getSuccessor(a)); } } private void generateHandleInterfacesSecondPass(Set<EState> visited, EState s) { if (visited.contains(s) || s.isTerminal()) { return; } if (s.getStateKind() == EStateKind.POLY_INPUT) { //GProtocolName gpn = this.apigen.getGProtocolName(); Role self = this.apigen.getSelf(); //String foo = HandlerInterfaceGenerator.getHandlerInterfaceName(IOStateInterfaceGenerator.getIOStateInterfaceName(self, s)); String key = HandleInterfaceGenerator.getHandleInterfaceName(self, s); List<EAction> succifs = this.branchSuccs.get(key); if (succifs != null) { ////InterfaceBuilder handleif = this.iostates.get(HandleInterfaceGenerator.getHandleInterfaceName(self, s)); //InterfaceBuilder handleif = this.iostates.get(key); /*if (handleif.getParameters().isEmpty()) // Hacky? { int i = 1; for (IOAction b : succifs) // Already sorted { handleif.addParameters("__Succ" + i + " extends " + SuccessorInterfaceGenerator.getSuccessorInterfaceName(b));//this.succs.get(b).getName()); i++; } handleif.addImports(SessionApiGenerator.getOpsPackageName(gpn) + ".*"); //int j = 1; /*Iterator<IOAction> foo = s.getAcceptable().stream().sorted(IOStateInterfaceGenerator.IOACTION_COMPARATOR).iterator(); EndpointState succ = s.accept(foo.next());* / }*/ ////Map<IOAction, Integer> count = new HashMap<>(); //List<IOAction> tmp = this.branchSuccs.get(key); ////tmp.stream().forEach((a) -> count.put(a, (int) tmp.stream().filter((b) -> b.equals(a)).count())); //Map<IOAction, Integer> count = new HashMap<>(); /*for (IOAction a : this.branchPostActions.get(s).stream().sorted(IOStateInterfaceGenerator.IOACTION_COMPARATOR).collect(Collectors.toList())) { /*for (IOAction b : succifs) { if (!succ.isAcceptable(b)) { succ = s.accept(foo.next()); }* / EndpointState succ = s.accept(a); MethodBuilder mb = handleif.newAbstractMethod(); HandlerInterfaceGenerator.setHandleMethodHeaderWithoutParamTypes(this.apigen, mb); //j = HandleInterfaceGenerator.setHandleMethodSuccessorParam(this, self, succ, mb, j); HandleInterfaceGenerator.setHandleMethodSuccessorParam(this, self, succ, mb, tmp, count); /*for (IOAction b : count.keySet()) { for (int j = 0; j < count.get(b); j++) { tmp.remove(b); } }* / HandlerInterfaceGenerator.addHandleMethodOpAndPayloadParams(this.apigen, a, mb); handleif.checkDuplicateMethods(mb); // Hacky //} }*/ } } visited.add(s); for (EAction a : s.getActions()) { generateHandleInterfacesSecondPass(visited, s.getSuccessor(a)); } } // Except EndSocket private void addIOStateInterfacesToStateChannels(Set<EState> visited, EState s) { if (visited.contains(s) || s.isTerminal()) { return; } Role self = getSelf(); String scname = this.apigen.getSocketClassName(s); String ioname = IOStateInterfaceGenerator.getIOStateInterfaceName(self, s); TypeBuilder tb = this.apigen.getType(scname); // Add I/O State Interface to each ScribSocket (except CaseSocket) // Do here (not inside I/O State Interface generators) because multiple states can share the same I/O State Interface tb.addImports(getIOInterfacePackageName(this.gpn, self) + ".*"); tb.addInterfaces(ioname + getConcreteSuccessorParameters(s)); InterfaceBuilder iostate = this.iostates.get(ioname); MethodBuilder mb = addToCastMethod(iostate, scname); if (mb != null) { iostate.addImports(SessionApiGenerator.getStateChannelPackageName(this.gpn, self) + ".*"); } if (s.getStateKind() == EStateKind.POLY_INPUT) { // Add CaseInterface to each CaseSocket TypeBuilder cases = this.apigen.getType(CaseSocketGenerator.getCaseSocketName(this.apigen.getSocketClassName(s))); cases.addImports(getIOInterfacePackageName(this.gpn, self) + ".*"); cases.addInterfaces(CaseInterfaceGenerator.getCasesInterfaceName(self, s) + getConcreteSuccessorParameters(s)); // Add HandleInterface to each HandlerInterface InterfaceBuilder handler = (InterfaceBuilder) this.apigen.getType(HandlerInterfaceGenerator.getHandlerInterfaceName(this.apigen.getSocketClassName(s))); handler.addImports(getIOInterfacePackageName(this.gpn, self) + ".*"); // FIXME: factor out with HandleInterfaceGenerator and getConcreteSuccessorParameters String tmp = ""; boolean first = true; /*for (IOAction a : s.getAcceptable().stream().sorted(IOStateInterfaceGenerator.IOACTION_COMPARATOR).collect(Collectors.toList())) { EndpointState succ = s.accept(a); for (IOAction b : succ.getAcceptable().stream().sorted(IOStateInterfaceGenerator.IOACTION_COMPARATOR).collect(Collectors.toList())) { if (first) { first = false; } else { tmp += ", "; } tmp += this.getSuccName.apply(succ.accept(b)); } }*/ String handle = HandleInterfaceGenerator.getHandleInterfaceName(self, s); /*List<IOAction> foo1 = new LinkedList<>(); List<EndpointState> bar1 = new LinkedList<>(); for (IOAction a : s.getAcceptable().stream().sorted(IOStateInterfaceGenerator.IOACTION_COMPARATOR).collect(Collectors.toList())) { EndpointState succ = s.accept(a); for (IOAction b : succ.getAcceptable().stream().sorted(IOStateInterfaceGenerator.IOACTION_COMPARATOR).collect(Collectors.toList())) { foo1.add(b); bar1.add(succ); } } System.out.println("BBB: " + handle);*/ //for (IOAction a : this.branchSuccs.get(handle)) // FIXME: move back into HandlerInterfaceGenerator for (EAction a : s.getActions().stream().sorted(IOStateInterfaceGenerator.IOACTION_COMPARATOR).collect(Collectors.toList())) { if (first) { first = false; } else { tmp += ", "; } /*if (foo1.contains(a)) { EndpointState succ = bar1.get(foo1.indexOf(a)); tmp += this.getSuccName.apply(succ.accept(a)); foo1.remove(a); bar1.remove(succ); } else { tmp += SuccessorInterfaceGenerator.getSuccessorInterfaceName(a); }*/ EState succ = s.getSuccessor(a); if (succ.isTerminal()) { tmp += ScribSocketGenerator.GENERATED_ENDSOCKET_NAME; } else { tmp += this.apigen.getSocketClassName(succ); } } handler.addInterfaces(handle + "<" + tmp + ">"); /*// Override abstract handle methods with default cast implementation for (IOAction a : s.getAcceptable().stream().sorted(IOStateInterfaceGenerator.IOACTION_COMPARATOR).collect(Collectors.toList())) { EndpointState succ = s.accept(a); this.iostates.get(HandleInterfaceGenerator.getHandleInterfaceName(self, succ)); MethodBuilder override = handler.newDefaultMethod(); //override.addModifiers(JavaBuilder.FINAL); // Default methods cannot be final HandlerInterfaceGenerator.setHandleMethodHeaderWithoutParamTypes(this.apigen, override); //HandleInterfaceGenerator.setHandleMethodSuccessorParam(this, self, succ, override); // FIXME: factor out with HandleInterfaceGenerator.setHandleMethodSuccessorParam String nextClass = this.apigen.getSocketClassName(succ); if (succ.isTerminal()) { override.addParameters(ScribSocketGenerator.ENDSOCKET_CLASS + "<?, ?> end"); } else { InterfaceBuilder next = getIOStateInterface(IOStateInterfaceGenerator.getIOStateInterfaceName(self, succ)); // Select/Receive/Branch String ret = next.getName() + "<"; //ret += "<" + next.getParameters().stream().map((p) -> "__Succ" + i).collect(Collectors.joining(", ")) + ">"; // FIXME: fragile? boolean bar = true; for (IOAction b : succ.getAcceptable().stream().sorted(IOStateInterfaceGenerator.IOACTION_COMPARATOR).collect(Collectors.toList())) // FIXME: factor out with getHandleInterfaceIOActionParams { if (bar) { bar = false; } else { ret += ", "; } EndpointState foo = succ.accept(b); if (foo.isTerminal()) { ret += ScribSocketGenerator.GENERATED_ENDSOCKET_NAME; } else { ret += this.apigen.getSocketClassName(foo); } } ret += ">"; override.addParameters(ret + " schan"); } HandlerInterfaceGenerator.addHandleMethodOpAndPayloadParams(this.apigen, a, override); // FIXME: factor out String ln = "receive(("; if (succ.isTerminal()) // factor out { ln += "EndSocket) end"; } else { ln += nextClass + ") schan"; } ln += ", op"; String args; if (a.mid.isOp()) // factor out { args = IntStream.rangeClosed(1, a.payload.elems.size()).mapToObj((i) -> "arg" + i).collect(Collectors.joining(", ")); if (!args.equals("")) { args = ", " + args; } } else { args = ", arg"; } ln += args + ");"; override.addBodyLine(ln); override.addAnnotations("@Override"); }*/ } visited.add(s); for (EAction a : s.getActions()) { addIOStateInterfacesToStateChannels(visited, s.getSuccessor(a)); } } private void addSupertypeInterfaces() { Map<String, InterfaceBuilder> subtypeifs = new HashMap<>(); for (InterfaceBuilder ib : this.iostates.values()) { String name = ib.getName(); if (!subtypeifs.containsKey(name)) { if (name.startsWith("Select") || name.startsWith("Handle")) { subtypeifs.put(name, ib); Map<String, InterfaceBuilder> res = new HashMap<>(); if (name.startsWith("Select")) { List<String> ifs = ib.getInterfaces(); // Could also use params to integrate with Handle List<String> outs = ifs.stream().filter((i) -> i.startsWith("Out")) .map((o) -> (o = o.substring(0, o.indexOf("<"))).substring(o.indexOf("_") + 1, o.length())) .sorted((s1, s2) -> s1.compareTo(s2)) .collect(Collectors.toList()); buildIOStateSuperInterfaces(res, outs, "Select", "Out", "Out"); } else //if (base.startsWith("Handle")) { List<String> params = ib.getParameters(); List<String> ins = params.stream().map((i) -> i.substring(i.indexOf("Succ_In_") + "Succ_In_".length())) .sorted((s1, s2) -> s1.compareTo(s2)) .collect(Collectors.toList()); //buildSuperHandleInterfaces(res, ins); buildIOStateSuperInterfaces(res, ins, "Handle", "Callback", "In"); //buildIOStateSuperInterfaces(res, ins, "Branch", "", "In"); // No: subtyping for branch interfaces unsafe -- that's the point of handle interfaces /*Map<String, InterfaceBuilder> tmp = new HashMap<>(); buildIOStateSuperInterfaces(tmp, ins, "Branch", "", "In"); for (InterfaceBuilder bra : tmp.values()) { addBranchSupertypeInterfaceMethods(ib, bra, ins, getSelf()); } res.putAll(tmp);*/ } res.values().forEach((r) -> subtypeifs.put(r.getName(), r)); } } } subtypeifs.values().forEach((r) -> this.iostates.put(r.getName(), r)); for (InterfaceBuilder ib : this.iostates.values()) { String name = ib.getName(); if (name.startsWith("Select")) { List<String> ifs = ib.getInterfaces(); List<String> outs = ifs.stream().filter((i) -> i.startsWith("Out")) .map((o) -> (o = o.substring(0, o.indexOf("<"))).substring(o.indexOf("_") + 1, o.length())) .sorted((s1, s2) -> s1.compareTo(s2)) .collect(Collectors.toList()); addIOStateSuperInterfaces(ib, outs, "Select", "Out"); } else if (name.startsWith("Handle")) { List<String> params = ib.getParameters(); List<String> ins = params.stream().map((i) -> i.substring(i.indexOf("Succ_In_") + "Succ_In_".length())) .sorted((s1, s2) -> s1.compareTo(s2)) .collect(Collectors.toList()); addIOStateSuperInterfaces(ib, ins, "Handle", "In"); } /*else if (name.startsWith("Branch")) // No: subtyping for branch interfaces unsafe -- that's the point of handle interfaces { List<String> params = ib.getParameters(); List<String> ins = params.stream().map((i) -> i.substring(i.indexOf("Succ_In_") + "Succ_In_".length())) .sorted((s1, s2) -> s1.compareTo(s2)) .collect(Collectors.toList()); addIOStateSuperInterfaces(ib, ins, "Branch", "In"); }*/ } /*// Generate Succ I/f supertype "to" cast methods -- moved earlier, to be supported by to-overriding pass (i.e. when a direct I/O State I/f inherits duplicate to's from Succs) Role self = getSelf(); for (EndpointState s : this.preds.keySet()) { if (s.isTerminal()) { continue; } String name = IOStateInterfaceGenerator.getIOStateInterfaceName(self, s); if (name.startsWith("Select")) { System.out.println("AAA: " + name); List<String> ifs = this.iostates.get(name).getInterfaces(); List<String> outs = ifs.stream().filter((i) -> i.startsWith("Out")) .map((o) -> (o = o.substring(0, o.indexOf("<"))).substring(o.indexOf("_") + 1, o.length())) .sorted((s1, s2) -> s1.compareTo(s2)) .collect(Collectors.toList()); addSupertypeInterfaceToMethods(s, outs, "Select", "Out"); } /*else if (name.startsWith("Branch")) { List<String> params = this.iostates.get(name).getParameters(); List<String> ins = params.stream().map((i) -> i.substring(i.indexOf("Succ_In_") + "Succ_In_".length())) .sorted((s1, s2) -> s1.compareTo(s2)) .collect(Collectors.toList()); addSupertypeInterfaceToMethods(s, ins, "Branch", "In"); }* / }*/ } // a = e.g. C_1_Int // Pre: as sorted private void buildIOStateSuperInterfaces(Map<String, InterfaceBuilder> res, List<String> as, String superPref, String actPref, String succPref) { for (String exclude : as) { List<String> foo = as.stream().filter((s) -> !s.equals(exclude)).collect(Collectors.toList()); if (foo.size() > 0) { String tmp = foo.stream().collect(Collectors.joining("__")); String superName = superPref + "_" + getSelf() + "_" + tmp; if (!this.iostates.containsKey(superName) && !res.containsKey(superName)) { InterfaceBuilder ib = new InterfaceBuilder(superName); ib.setPackage(getIOInterfacePackageName(this.gpn, getSelf())); ib.addModifiers(JavaBuilder.PUBLIC); int i = 1; for (String a : foo) { ib.addParameters("__Succ" + i + " extends " + "Succ_" + succPref + "_" + a); if (!superPref.equals("Branch")) // Hacky { ib.addInterfaces(actPref + "_" + a + "<__Succ" + i + ">"); } i++; } FieldBuilder cast = ib.newField("cast"); // FIXME: factor out cast.addModifiers(JavaBuilder.PUBLIC, JavaBuilder.STATIC, JavaBuilder.FINAL); cast.setType(superName + "<" + IntStream.range(0, foo.size()).mapToObj((x) -> "?").collect(Collectors.joining(", ")) + ">"); cast.setExpression("null"); res.put(superName, ib); buildIOStateSuperInterfaces(res, foo, superPref, actPref, succPref); } } } } /*//private void addBranchSupertypeInterfaceMethods(EndpointState s, InterfaceBuilder ib, Role self) private void addBranchSupertypeInterfaceMethods(InterfaceBuilder root, InterfaceBuilder ib, List<String> foo, Role self) { //Duplicated from BranchInterfaceGenerator /*AbstractMethodBuilder bra = ib.newAbstractMethod("branch"); String ret = CaseInterfaceGenerator.getCasesInterfaceName(self, s) + "<" + IntStream.range(1, as.size()+1).mapToObj((x) -> "__Succ" + x).collect(Collectors.joining(", ")) + ">"; // FIXME: factor out bra.setReturn(ret); bra.addParameters(SessionApiGenerator.getRoleClassName(as.iterator().next().peer) + " role"); bra.addExceptions(StateChannelApiGenerator.SCRIBBLERUNTIMEEXCEPTION_CLASS, "java.io.IOException", "ClassNotFoundException");* / List<String> params = ib.getParameters(); List<String> ins = params.stream().map((i) -> i.substring(i.indexOf("Succ_In_") + "Succ_In_".length())) .sorted((s1, s2) -> s1.compareTo(s2)) .collect(Collectors.toList()); String handleName = "Handle" + ib.getName().substring("Branch".length(), ib.getName().length()); System.out.println("AAA: " + ib.getName() + ", " + handleName); //String peer = ib.getName().substring(ib.getName().indexOf("_") + 1, ib.getName().indexOf("_", ib.getName().indexOf("_") + 1)); String peer = ins.get(0).substring(0, ins.get(0).indexOf("_")); AbstractMethodBuilder bra2 = ib.newAbstractMethod("branch"); bra2.setReturn(JavaBuilder.VOID); bra2.addParameters(peer + " role"); String next = handleName + "<" + IntStream.range(1, ins.size() + 1).mapToObj((i) -> "__Succ" + i).collect(Collectors.joining(", ")) + ">"; bra2.addParameters(next + " handler"); bra2.addExceptions(StateChannelApiGenerator.SCRIBBLERUNTIMEEXCEPTION_CLASS, "java.io.IOException", "ClassNotFoundException"); //bra2.addBodyLine("branch(role, (" + root.getName() + ") handler);"); AbstractMethodBuilder bra3 = ib.newAbstractMethod("handle"); bra3.setReturn(JavaBuilder.VOID); bra3.addParameters(peer + " role"); String handle = handleName + "<" + ins.stream().map((in) -> "Succ_In_" + in).collect(Collectors.joining(", ")) + ">"; bra3.addParameters(handle + " handler"); bra3.addExceptions(StateChannelApiGenerator.SCRIBBLERUNTIMEEXCEPTION_CLASS, "java.io.IOException", "ClassNotFoundException"); //bra3.addBodyLine("branch(role, (" + root.getName() + ") handler);"); ib.addImports(SessionApiGenerator.getRolesPackageName(this.gpn) + ".*"); // No: generating "super" branch/handle methods for supertypes is not safe }*/ // a = e.g. C_1_Int // Pre: as sorted private void addIOStateSuperInterfaces(InterfaceBuilder ib, List<String> as, String superPref, String succPref) { for (String exclude : as) { List<String> foo = as.stream().filter((s) -> !s.equals(exclude)).collect(Collectors.toList()); if (foo.size() > 0) { List<String> params = new LinkedList<>(); for (String a : foo) { int i = 1; for (String param : ib.getParameters()) { if (param.endsWith("Succ_" + succPref + "_" + a)) { break; } i++; } params.add("__Succ" + i); } String tmp = foo.stream().collect(Collectors.joining("__")); String select = superPref + "_" + getSelf() + "_" + tmp + "<" + params.stream().collect(Collectors.joining(", ")) + ">"; ib.addInterfaces(select); } } } // Pre: s has "preds", i.e. s implements a Successor I/f private void addSupertypeInterfaceToMethods(EState s, List<String> as, String superPref, String succPref) { for (String exclude : as) { List<String> foo = as.stream().filter((x) -> !x.equals(exclude)).collect(Collectors.toList()); if (foo.size() > 0) { String tmp = foo.stream().collect(Collectors.joining("__")); String superName = superPref + "_" + getSelf() + "_" + tmp; for (InterfaceBuilder succif : this.preds.get(s)) { addToCastMethod(succif, superName + "<" + IntStream.range(0, foo.size()).mapToObj((f) -> "?").collect(Collectors.joining(", ")) +">"); } if (!this.iostates.containsKey(superName)) { addSupertypeInterfaceToMethods(s, foo, superPref, succPref); } } } } // Pre: ib is a Successor I/f for the cast IO State I/f // Returns MethodBuilder, or null if none built private static MethodBuilder addToCastMethod(InterfaceBuilder ib, String ret) { if (ib.getDefaultMethods().stream().filter((def) -> def.getReturn().equals(ret)).count() > 0) { // Merge states entered from multiple paths, don't want to add cast multiple times -- still true for this case? // But duplicate cast check cast still needed anyway? return null; } MethodBuilder mb = ib.newDefaultMethod("to"); mb.setReturn(ret); mb.addParameters(ret + " cast"); if (!ret.equals("EndSocket") || ib.getName().startsWith("Succ")) // HACK { mb.addBodyLine(JavaBuilder.RETURN + " (" + ret + ") this;"); } else { ib.addImports("org.scribble.main.RuntimeScribbleException"); mb.addBodyLine("throw new RuntimeScribbleException(\"Invalid cast to EndSocket: \" + cast);"); } return mb; } private static MethodBuilder addEndSocketToCastMethod(ClassBuilder cb, String ret, String body) { //if (ib.getMethods().stream().filter((def) -> def.getReturn().equals(ret)).count() > 0) if (cb.hasMethodSignature(ret, ret + " ")) // Hacky { // Merge states entered from multiple paths, don't want to add cast multiple times -- still true for this case? // But duplicate cast check cast still needed anyway? return null; } MethodBuilder mb = cb.newMethod("to"); mb.setReturn(ret); mb.addParameters(ret + " cast"); //mb.addBodyLine("throw new RuntimeScribbleException(\"Invalid cast of EndSocket: \" + cast);"); mb.addBodyLine(body); return mb; } private final Function<EState, String> getSuccName = (succ) -> (succ.isTerminal()) ? ScribSocketGenerator.GENERATED_ENDSOCKET_NAME : this.apigen.getSocketClassName(succ); private String getConcreteSuccessorParameters(EState s) { return "<" + s.getActions().stream().sorted(IOStateInterfaceGenerator.IOACTION_COMPARATOR) .map((a) -> this.getSuccName.apply(s.getSuccessor(a))).collect(Collectors.joining(", ")) + ">"; } private void putPreAction(EState s, EAction a) { putMapSet(this.preActions, s, a); } private void putBranchPostAction(EState s, EAction a) { putMapSet(this.branchPostActions, s, a); } private static <K, V> void putMapSet(Map<K, Set<V>> map, K k, V v) { Set<V> tmp = map.get(k); if (tmp == null) { tmp = new LinkedHashSet<>(); map.put(k, tmp); } tmp.add(v); } // Successor I/f's to be implemented by each I/O State I/f private void collectPreds() { for (EState s : this.preActions.keySet()) { Set<InterfaceBuilder> tmp = new HashSet<>(); for (EAction a : this.preActions.get(s)) // sort? { if (!a.isSend() && !a.isReceive()) // TODO (connect/disconnect) { throw new RuntimeException("TODO: " + a); } tmp.add(this.succs.get(a)); } this.preds.put(s, tmp); } } private void collectBranchSuccs() { Role self = getSelf(); for (EState s : this.branchPostActions.keySet()) { //String key = HandlerInterfaceGenerator.getHandlerInterfaceName(IOStateInterfaceGenerator.getIOStateInterfaceName(self, s)); String key = HandleInterfaceGenerator.getHandleInterfaceName(self, s); // HandleInterface name List<EAction> curr1 = new LinkedList<>(); this.branchPostActions.get(s).forEach((a) -> curr1.addAll(s.getSuccessor(a).getActions())); // TODO: flatmap //List<IOAction> curr2 = curr1.stream().sorted(IOStateInterfaceGenerator.IOACTION_COMPARATOR).collect(Collectors.toList()); List<EAction> tmp = this.branchSuccs.get(key); if (tmp == null) { tmp = new LinkedList<>(); //this.branchSuccs.put(key, tmp); tmp.addAll(curr1); } else { for (EAction a : curr1) { long n = curr1.stream().filter((x) -> x.equals(a)).count(); long m = tmp.stream().filter((x) -> x.equals(a)).count(); //System.out.println("EEE: " + curr1 + ",,, " + tmp); if (n > m) { for (int i = 0; i < n-m; i++) { tmp.add(a); } } } } this.branchSuccs.put(key, tmp.stream().sorted(IOStateInterfaceGenerator.IOACTION_COMPARATOR).collect(Collectors.toList())); //System.out.println("AAA: " + this.branchSuccs.get(key)); /*List<IOAction> tmp = this.branchSuccs.get(key); if (tmp == null) { tmp = new LinkedList<>(); this.branchSuccs.put(key, tmp); } //this.branchPostActions.get(s).stream()//.sorted(IOStateInterfaceGenerator.IOACTION_COMPARATOR) //.forEach((a) -> { tmp.add(this.succs.get(a)); }); for (IOAction a : this.branchPostActions.get(s)) // Already sorted -- guaranteed pairwise distinct (branch actions) //.sorted(IOStateInterfaceGenerator.IOACTION_COMPARATOR) { // Not necessarily distinct (actions of the branch successor state) for (IOAction b : s.accept(a).getAcceptable().stream().sorted(IOStateInterfaceGenerator.IOACTION_COMPARATOR).collect(Collectors.toList())) { //tmp.add(this.succs.get(b)); tmp.add(b); } } tmp = tmp.stream().so*/ } } protected Role getSelf() { return this.apigen.getSelf(); } protected static String getIOInterfacePackageName(GProtocolName gpn, Role self) { return SessionApiGenerator.getStateChannelPackageName(gpn, self) + ".ioifaces"; } protected InterfaceBuilder getIOStateInterface(String name) { return this.iostates.get(name); } }