summaryrefslogtreecommitdiff
path: root/base/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'base/src/main/java')
-rw-r--r--base/src/main/java/bjc/utils/cli/StreamTerminal.java185
-rw-r--r--base/src/main/java/bjc/utils/cli/Terminal.java38
-rw-r--r--base/src/main/java/bjc/utils/cli/TerminalCodes.java8
-rw-r--r--base/src/main/java/bjc/utils/cli/objects/DelimSplitterCLI.java26
-rw-r--r--base/src/main/java/bjc/utils/funcutils/StringUtils.java6
-rw-r--r--base/src/main/java/bjc/utils/funcutils/TreeUtils.java73
-rw-r--r--base/src/main/java/bjc/utils/graph/AdjacencyMap.java1
-rw-r--r--base/src/main/java/bjc/utils/graph/Edge.java1
-rw-r--r--base/src/main/java/bjc/utils/graph/Graph.java12
-rw-r--r--base/src/main/java/bjc/utils/graph/Graphs.java10
-rw-r--r--base/src/main/java/bjc/utils/patterns/ComplexPattern.java1
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;
/**