diff options
Diffstat (limited to 'base/src/main/java')
11 files changed, 252 insertions, 109 deletions
diff --git a/base/src/main/java/bjc/utils/cli/StreamTerminal.java b/base/src/main/java/bjc/utils/cli/StreamTerminal.java index a45a22e..185891f 100644 --- a/base/src/main/java/bjc/utils/cli/StreamTerminal.java +++ b/base/src/main/java/bjc/utils/cli/StreamTerminal.java @@ -6,7 +6,16 @@ import java.io.*; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.locks.*; +import java.util.function.Consumer; +import bjc.data.Either; + +/** + * Implementation of {@link Terminal} using {@link Reader} and {@link Writer} + * + * @author bjcul + * + */ public class StreamTerminal implements Terminal, Runnable { private SortedSet<Long> pendingRequests; private ConcurrentMap<Long, String> pendingReplies; @@ -21,9 +30,32 @@ public class StreamTerminal implements Terminal, Runnable { private Scanner inputScanner; private Writer output; + private String prefix; + private Consumer<String> mode; + private long currentRequest = -1; + /** + * Create a new stream terminal. + * + * @param input The input source + * @param output The output source + */ public StreamTerminal(Reader input, Writer output) { + this(input, output, null, null); + } + + /** + * Create a new stream terminal which backs an application + * + * @param input The input source + * @param output The output source + * @param prefix The string any input must start with to be directed to the + * terminal + * @param mode The place where all input not directed to the terminal is + * written. + */ + public StreamTerminal(Reader input, Writer output, String prefix, Consumer<String> mode) { this.inputScanner = new Scanner(input); this.output = output; @@ -33,6 +65,9 @@ public class StreamTerminal implements Terminal, Runnable { this.replyLock = new ReentrantLock(); this.replyCondition = replyLock.newCondition(); + + this.prefix = prefix; + this.mode = mode; } @Override @@ -40,6 +75,9 @@ public class StreamTerminal implements Terminal, Runnable { running = true; try { output.write(INFO_STARTCOMPROC.toString() + "\n"); + while (!pendingOutput.isEmpty()) + output.write(pendingOutput.remove()); + output.flush(); } catch (IOException e) { // TODO Consider if there is some better way to handle these throw new RuntimeException(e); @@ -49,57 +87,71 @@ public class StreamTerminal implements Terminal, Runnable { try { while (!pendingOutput.isEmpty()) output.write(pendingOutput.remove()); + output.flush(); String ln = inputScanner.nextLine(); - String com = ""; - int spcIdx = ln.indexOf(' '); - if (spcIdx == -1) { - com = ln; - } else { - com = ln.substring(0, spcIdx); - ln = ln.substring(spcIdx + 1); - } - comswt: switch (com) { - case "r": { - // General command format is 'r <request no.>,<reply> - String subRep = ln.substring(2); - // Process a reply - int comIndex = subRep.indexOf(','); - long repNo = 0; - if (comIndex == -1) { - // Reply to the oldest message by default - repNo = pendingRequests.first(); + if (prefix != null && ln.startsWith(prefix)) { + ln = ln.substring(prefix.length()); + String com = ""; + int spcIdx = ln.indexOf(' '); + if (spcIdx == -1) { + com = ln; } else { - String repStr = subRep.substring(0, comIndex); - try { - repNo = Long.parseLong(repStr); - } catch (NumberFormatException nfex) { - output.write(ERROR_INVREPNO.toString() + "\n"); - continue overall; - } - // Skip over the comma - subRep = subRep.substring(comIndex + 1); + com = ln.substring(0, spcIdx); + ln = ln.substring(spcIdx + 1); } - if (!pendingRequests.contains(repNo)) { - output.write(ERROR_UNKREPNO.toString() + "\n"); - continue overall; - } + comswt: switch (com) { + case "r": { + // General command format is 'r <request no.>,<reply> + String subRep = ln.substring(2); + // Process a reply + int comIndex = subRep.indexOf(','); + long repNo = 0; + if (comIndex == -1) { + // Reply to the oldest message by default + repNo = pendingRequests.first(); + } else { + String repStr = subRep.substring(0, comIndex); + try { + repNo = Long.parseLong(repStr); + } catch (NumberFormatException nfex) { + output.write(ERROR_INVREPNO.toString() + "\n"); + output.flush(); + continue overall; + } + // Skip over the comma + subRep = subRep.substring(comIndex + 1); + } - pendingRequests.remove(repNo); - pendingReplies.put(repNo, subRep); + if (!pendingRequests.contains(repNo)) { + output.write(ERROR_UNKREPNO.toString() + "\n"); + output.flush(); + continue overall; + } - replyLock.lock(); - replyCondition.signalAll(); - replyLock.unlock(); - break comswt; - } - case "q": - running = false; - break comswt; - default: - output.write(ERROR_UNRECCOM.toString() + "\n"); + pendingRequests.remove(repNo); + pendingReplies.put(repNo, subRep); + + replyLock.lock(); + replyCondition.signalAll(); + replyLock.unlock(); + break comswt; + } + case "q": + running = false; + break comswt; + default: + output.write(ERROR_UNRECCOM.toString() + "\n"); + output.flush(); + } + } else { + mode.accept(ln); } + + while (!pendingOutput.isEmpty()) + output.write(pendingOutput.remove()); + output.flush(); } catch (IOException ioex) { throw new RuntimeException(ioex); } @@ -107,7 +159,10 @@ public class StreamTerminal implements Terminal, Runnable { running = false; try { + while (!pendingOutput.isEmpty()) + output.write(pendingOutput.remove()); output.write(INFO_ENDCOMPROC.toString() + "\n"); + output.flush(); } catch (IOException e) { throw new RuntimeException(e); } @@ -139,6 +194,25 @@ public class StreamTerminal implements Terminal, Runnable { } @Override + public Optional<String> awaitReply(long id, TimeUnit unit, long delay) throws InterruptedException { + if (pendingReplies.containsKey(id)) + return Optional.of(pendingReplies.get(id)); + while (true) { + replyLock.lock(); + boolean stat = replyCondition.await(delay, unit); + replyLock.unlock(); + + // If we timed out, say so + if (stat == false) + return Optional.empty(); + // Explanation: Since the reply map is add-only, the lock isn't actually + // protecting anything. We just want to wait until a response is received. + if (pendingReplies.containsKey(id)) + return Optional.of(pendingReplies.get(id)); + } + } + + @Override public Optional<String> checkReply(long id) { return Optional.ofNullable(pendingReplies.get(id)); } @@ -147,6 +221,29 @@ public class StreamTerminal implements Terminal, Runnable { public String submitRequestSync(String req) throws InterruptedException { return awaitReply(submitRequest(req)); } + + @Override + public Either<String, Long> submitRequestSync(String req, TimeUnit unit, long delay) throws InterruptedException { + long id = submitRequest(req); + Optional<String> rep = awaitReply(id, unit, delay); + return rep.isEmpty() ? Either.right(id) : Either.left(rep.get()); + } - // TODO add variants of the two blocking methods above with timeout support + /** + * Add a string to be printed next time output is printed. + * + * @param out The output + */ + public void addOutput(String out) { + pendingOutput.add(out); + } + + /** + * Set the mode for handling non-terminal input + * + * @param mode The new mode for handling non-terminal input + */ + public void setMode(Consumer<String> mode) { + this.mode = mode; + } }
\ No newline at end of file diff --git a/base/src/main/java/bjc/utils/cli/Terminal.java b/base/src/main/java/bjc/utils/cli/Terminal.java index a10d82e..e8b3029 100644 --- a/base/src/main/java/bjc/utils/cli/Terminal.java +++ b/base/src/main/java/bjc/utils/cli/Terminal.java @@ -1,6 +1,9 @@ package bjc.utils.cli; import java.util.*; +import java.util.concurrent.TimeUnit; + +import bjc.data.Either; /** * A terminal with support for asking multiple questions, and retrieving the @@ -17,6 +20,7 @@ public interface Terminal { * Submit a request to the terminal * * @param req The body of the request + * * @return The ID of the request */ long submitRequest(String req); @@ -26,15 +30,32 @@ public interface Terminal { * response is available. * * @param id The ID of the request + * * @return The response to that request + * * @throws InterruptedException If we are interrupted waiting for the reply */ String awaitReply(long id) throws InterruptedException; + + /** + * Await a reply for a given request. Will block the current thread until a + * response is available or the specified timeout occurs. + * + * @param id The ID of the request + * @param unit The unit of time to wait + * @param delay The amount of units to wait before timing out + * + * @return The response to that request, or an empty optional if it timed out + * + * @throws InterruptedException If we are interrupted waiting for the reply + */ + Optional<String> awaitReply(long id, TimeUnit unit, long delay) throws InterruptedException; /** * Check if a reply for a request is available, without blocking. * * @param id The ID of the request + * * @return The reply to the request if one is available, and empty otherwise */ Optional<String> checkReply(long id); @@ -47,5 +68,20 @@ public interface Terminal { * @return The reply to the request * @throws InterruptedException If we are interrupted waiting for the reply */ - String submitRequestSync(String req) throws InterruptedException;; + String submitRequestSync(String req) throws InterruptedException; + + /** + * Submit a request and await the reply to it. Will block the current thread + * until a response is received or the timeout occurs. + * + * @param req The request to submit + * @param unit The unit of time to wait + * @param delay The amount of units to wait before timing out + * + * @return The reply to the request, or the ID of the request if the timeout occurred + * + * @throws InterruptedException If we are interrupted waiting for the reply + */ + Either<String, Long> submitRequestSync(String req, TimeUnit unit, long delay) throws InterruptedException; + }
\ No newline at end of file diff --git a/base/src/main/java/bjc/utils/cli/TerminalCodes.java b/base/src/main/java/bjc/utils/cli/TerminalCodes.java index c1d7dfc..7e2206e 100644 --- a/base/src/main/java/bjc/utils/cli/TerminalCodes.java +++ b/base/src/main/java/bjc/utils/cli/TerminalCodes.java @@ -14,14 +14,22 @@ public enum TerminalCodes { // The general idea of the format would be a tab-separated value file, with the // first value being a command, and then the rest being the body of that command. // Would also have the line-continuation feature + /** Alert for starting processing. */ INFO_STARTCOMPROC("IOLPI00001", "STARTING PROCESSING"), + /** Alert for ending processing. */ INFO_ENDCOMPROC("IOLPI00002", "ENDING PROCESSING"), + /** Error for an unknown command */ ERROR_UNRECCOM("IOINE00001", "UNRECOGNIZED COMMAND"), + /** Error for an invalid number format when identifying a reply. */ ERROR_INVREPNO("IOINE00002", "INVALID REPLY NUMBER FORMAT"), + /** Error for specifying an unrecognized reply. */ ERROR_UNKREPNO("IOINE00002", "UNKNOWN REPLY NUMBER"), ; + + /** The code for this error. */ public final String code; + /** The summary message for this error. */ public final String message; private TerminalCodes(String code, String message) { diff --git a/base/src/main/java/bjc/utils/cli/objects/DelimSplitterCLI.java b/base/src/main/java/bjc/utils/cli/objects/DelimSplitterCLI.java index 53d6d1e..ba478f7 100644 --- a/base/src/main/java/bjc/utils/cli/objects/DelimSplitterCLI.java +++ b/base/src/main/java/bjc/utils/cli/objects/DelimSplitterCLI.java @@ -60,24 +60,22 @@ public class DelimSplitterCLI { * Run the tester interface. */ private void runLoop() { - final Scanner scn = new Scanner(System.in); + try (Scanner scn = new Scanner(System.in)) { + System.out.print("Enter a command (blank line to quit): "); + String inp = scn.nextLine().trim(); + System.out.println(); - System.out.print("Enter a command (blank line to quit): "); - String inp = scn.nextLine().trim(); - System.out.println(); + while (!inp.equals("")) { + handleCommand(inp, scn, true); - while (!inp.equals("")) { - handleCommand(inp, scn, true); + System.out.println(); - System.out.println(); + System.out.print("Enter a command (blank line to quit): "); + inp = scn.nextLine(); - System.out.print("Enter a command (blank line to quit): "); - inp = scn.nextLine(); - - System.out.println(); + System.out.println(); + } } - - scn.close(); } /* @@ -221,6 +219,8 @@ public class DelimSplitterCLI { } try (FileInputStream fis = new FileInputStream(pth)) { + @SuppressWarnings("resource") + // Handled by above final Scanner scn = new Scanner(fis); while (scn.hasNextLine()) { diff --git a/base/src/main/java/bjc/utils/funcutils/StringUtils.java b/base/src/main/java/bjc/utils/funcutils/StringUtils.java index 2d86083..7551cb3 100644 --- a/base/src/main/java/bjc/utils/funcutils/StringUtils.java +++ b/base/src/main/java/bjc/utils/funcutils/StringUtils.java @@ -129,8 +129,7 @@ public class StringUtils { * @return The sequence as an English list. */ public static String toEnglishList(final Object[] objects, final boolean and) { - if (and) return toEnglishList(objects, "and"); - else return toEnglishList(objects, "or"); + return and ? toEnglishList(objects, "and") : toEnglishList(objects, "or"); } /** Count the number of graphemes in a string. @@ -190,8 +189,7 @@ public class StringUtils { int idx = strang.indexOf(until); if (idx == -1) { - if (allowFail) return strang; - else return null; + return allowFail ? strang : null; } return strang.substring(0, strang.indexOf(until)); diff --git a/base/src/main/java/bjc/utils/funcutils/TreeUtils.java b/base/src/main/java/bjc/utils/funcutils/TreeUtils.java index daab8a1..22c4d3c 100644 --- a/base/src/main/java/bjc/utils/funcutils/TreeUtils.java +++ b/base/src/main/java/bjc/utils/funcutils/TreeUtils.java @@ -6,18 +6,22 @@ import java.util.function.*; import bjc.data.*; import bjc.funcdata.*; -/** Implements various utilities for trees. +/** + * Implements various utilities for trees. * - * @author Benjamin Culkin */ + * @author Benjamin Culkin + */ public class TreeUtils { - /** Convert a tree into a list of outline nodes that match a certain path. + /** + * Convert a tree into a list of outline nodes that match a certain path. * - * @param <T> The type contained in the tree. + * @param <T> The type contained in the tree. * - * @param tre The tree to outline. + * @param tre The tree to outline. * @param leafMarker The path to mark nodes with. * - * @return The list of marked paths. */ + * @return The list of marked paths. + */ public static <T> ListEx<ListEx<T>> outlineTree(Tree<T> tre, Predicate<T> leafMarker) { ListEx<ListEx<T>> paths = new FunctionalList<>(); @@ -30,13 +34,14 @@ public class TreeUtils { } /* Find a path in a tree. */ - private static <T> void findPath(Tree<T> subtree, LinkedList<T> path, - Predicate<T> leafMarker, ListEx<ListEx<T>> paths) { + private static <T> void findPath(Tree<T> subtree, LinkedList<T> path, Predicate<T> leafMarker, + ListEx<ListEx<T>> paths) { if (subtree.getChildrenCount() == 0 && leafMarker.test(subtree.getHead())) { /* We're at a matching leaf node. Add it. */ ListEx<T> finalPath = new FunctionalList<>(); - for (T ePath : path) finalPath.add(ePath); + for (T ePath : path) + finalPath.add(ePath); finalPath.add(subtree.getHead()); @@ -50,43 +55,39 @@ public class TreeUtils { path.removeLast(); } } - - /** Performs 'variable substitution' or something along those lines on a tree. + + /** + * Performs 'variable substitution' or something along those lines on a tree. * * @param <ContainedType> The type of element contained in the tree. * - * @param tree The tree to do expansion in. - * @param marker The function to mark which nodes should be expanded. - * @param expander The function to expand nodes. + * @param tree The tree to do expansion in. + * @param marker The function to mark which nodes should be expanded. + * @param expander The function to expand nodes. * - * @return A transformed copy of the tree. */ - public static <ContainedType> Tree<ContainedType> substitute( - Tree<ContainedType> tree, - Predicate<ContainedType> marker, - Function<ContainedType, Tree<ContainedType>> expander) { - tree.topDownTransform((contents) -> { - if (marker.test(contents)) return TopDownTransformResult.TRANSFORM; - else return TopDownTransformResult.PASSTHROUGH; - }, (node) -> { - return expander.apply(node.getHead()); - }); + * @return A transformed copy of the tree. + */ + public static <ContainedType> Tree<ContainedType> substitute(Tree<ContainedType> tree, + Predicate<ContainedType> marker, Function<ContainedType, Tree<ContainedType>> expander) { + Function<ContainedType, + TopDownTransformResult> picker = (contents) -> marker.test(contents) ? TopDownTransformResult.TRANSFORM + : TopDownTransformResult.PASSTHROUGH; + tree.topDownTransform(picker, (node) -> expander.apply(node.getHead())); return tree; } - - /** Performs 'variable substitution' or something along those lines on a tree. + + /** + * Performs 'variable substitution' or something along those lines on a tree. * * @param <ContainedType> The type of element contained in the tree. * - * @param tree The tree to do expansion in. - * @param environment A map which contains the variables to substitute. + * @param tree The tree to do expansion in. + * @param environment A map which contains the variables to substitute. * - * @return A transformed copy of the tree. */ - public static <ContainedType> Tree<ContainedType> substitute( - Tree<ContainedType> tree, + * @return A transformed copy of the tree. + */ + public static <ContainedType> Tree<ContainedType> substitute(Tree<ContainedType> tree, MapEx<ContainedType, Tree<ContainedType>> environment) { - return substitute( - tree, - environment::containsKey, - (element) -> environment.get(element).get()); + return substitute(tree, environment::containsKey, (element) -> environment.get(element).get()); } } diff --git a/base/src/main/java/bjc/utils/graph/AdjacencyMap.java b/base/src/main/java/bjc/utils/graph/AdjacencyMap.java index 04fe4c8..f03fb69 100644 --- a/base/src/main/java/bjc/utils/graph/AdjacencyMap.java +++ b/base/src/main/java/bjc/utils/graph/AdjacencyMap.java @@ -21,6 +21,7 @@ import bjc.utils.funcutils.FuncUtils; * * @param <T> * The type of the nodes in the graph + * @param <W> The type of the weights */ public class AdjacencyMap<T, W> { /** diff --git a/base/src/main/java/bjc/utils/graph/Edge.java b/base/src/main/java/bjc/utils/graph/Edge.java index 5b0eba3..bffa0a1 100644 --- a/base/src/main/java/bjc/utils/graph/Edge.java +++ b/base/src/main/java/bjc/utils/graph/Edge.java @@ -8,6 +8,7 @@ import java.util.Objects; * @author ben * * @param <T> The type of the nodes in the graph. + * @param <W> The type of the weight */ public class Edge<T, W> { /* The distance from initial to terminal node. */ diff --git a/base/src/main/java/bjc/utils/graph/Graph.java b/base/src/main/java/bjc/utils/graph/Graph.java index 81653fc..a007698 100644 --- a/base/src/main/java/bjc/utils/graph/Graph.java +++ b/base/src/main/java/bjc/utils/graph/Graph.java @@ -1,20 +1,11 @@ package bjc.utils.graph; -import java.util.ArrayList; -import java.util.HashSet; import java.util.List; import java.util.NoSuchElementException; -import java.util.PriorityQueue; -import java.util.Queue; -import java.util.Set; import java.util.function.BiConsumer; import java.util.function.BiPredicate; -import bjc.data.Holder; -import bjc.data.Identity; -import bjc.funcdata.FunctionalMap; -import bjc.funcdata.ListEx; -import bjc.funcdata.MapEx; +import bjc.funcdata.*; /** * A directed weighted graph, where the vertices have some arbitrary label. @@ -23,6 +14,7 @@ import bjc.funcdata.MapEx; * * @param <VertexLabel> * The label for vertices. + * @param <EdgeLabel> The label for edges */ public class Graph<VertexLabel, EdgeLabel> { /** diff --git a/base/src/main/java/bjc/utils/graph/Graphs.java b/base/src/main/java/bjc/utils/graph/Graphs.java index 2844a68..f504b55 100644 --- a/base/src/main/java/bjc/utils/graph/Graphs.java +++ b/base/src/main/java/bjc/utils/graph/Graphs.java @@ -5,11 +5,21 @@ import java.util.*; import bjc.data.Holder; import bjc.data.Identity; +/** + * General graph utilities + * @author bjcul + * + */ public class Graphs { /** * Uses Prim's algorithm to calculate a MST for the graph. * * If the graph is non-connected, this will lead to unpredictable results. + * + * @param grap The graph to calculate MST for + * @param comp The comparator for the edges + * @param <T> The vertex type + * @param <L> The edge type * * @return A list of edges that constitute the MST. */ diff --git a/base/src/main/java/bjc/utils/patterns/ComplexPattern.java b/base/src/main/java/bjc/utils/patterns/ComplexPattern.java index 69ee67c..3bac136 100644 --- a/base/src/main/java/bjc/utils/patterns/ComplexPattern.java +++ b/base/src/main/java/bjc/utils/patterns/ComplexPattern.java @@ -5,7 +5,6 @@ import java.util.function.*; import java.util.regex.*; import bjc.data.*; -import bjc.functypes.ID; import bjc.functypes.Unit; /** |
