summaryrefslogtreecommitdiff
path: root/base/src/main/java/bjc
diff options
context:
space:
mode:
authorBenjamin Culkin <scorpress@gmail.com>2024-06-04 17:10:35 -0400
committerBenjamin Culkin <scorpress@gmail.com>2024-06-04 17:10:35 -0400
commitf9d171dbd78c287220c262766637060ec57a882e (patch)
treeb5b3d9186c9832cbeaea28ac2d2e5a725fa05ee8 /base/src/main/java/bjc
parentc1d4716b079d4ef32650144709294525fed6badf (diff)
Work on StreamTerminal and the example
Some work on StreamTerminal and its corresponding example
Diffstat (limited to 'base/src/main/java/bjc')
-rw-r--r--base/src/main/java/bjc/utils/cli/StreamTerminal.java171
-rw-r--r--base/src/main/java/bjc/utils/cli/TerminalCodes.java9
-rw-r--r--base/src/main/java/bjc/utils/parserutils/TokenUtils.java56
3 files changed, 179 insertions, 57 deletions
diff --git a/base/src/main/java/bjc/utils/cli/StreamTerminal.java b/base/src/main/java/bjc/utils/cli/StreamTerminal.java
index 185891f..607d87d 100644
--- a/base/src/main/java/bjc/utils/cli/StreamTerminal.java
+++ b/base/src/main/java/bjc/utils/cli/StreamTerminal.java
@@ -7,13 +7,16 @@ import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.*;
import java.util.function.Consumer;
+import java.util.function.Function;
import bjc.data.Either;
+import bjc.utils.funcutils.StringUtils;
+import bjc.utils.parserutils.TokenUtils;
/**
* Implementation of {@link Terminal} using {@link Reader} and {@link Writer}
*
- * @author bjcul
+ * @author Ben Culkin
*
*/
public class StreamTerminal implements Terminal, Runnable {
@@ -25,12 +28,18 @@ public class StreamTerminal implements Terminal, Runnable {
private Queue<String> pendingOutput;
- private boolean running;
+ private boolean isRunning;
+ private boolean isParagraphMode;
+ private boolean isPromptOnContinue;
- private Scanner inputScanner;
+ private Scanner input;
private Writer output;
+ private Writer status;
private String prefix;
+ private String paragraphTerminator = "";
+ private String prompt = "..> ";
+
private Consumer<String> mode;
private long currentRequest = -1;
@@ -56,8 +65,9 @@ public class StreamTerminal implements Terminal, Runnable {
* written.
*/
public StreamTerminal(Reader input, Writer output, String prefix, Consumer<String> mode) {
- this.inputScanner = new Scanner(input);
+ this.input = new Scanner(input);
this.output = output;
+ this.status = output;
this.pendingRequests = new TreeSet<>();
this.pendingReplies = new ConcurrentHashMap<>();
@@ -72,24 +82,24 @@ public class StreamTerminal implements Terminal, Runnable {
@Override
public void run() {
- running = true;
+ isRunning = true;
try {
- output.write(INFO_STARTCOMPROC.toString() + "\n");
- while (!pendingOutput.isEmpty())
- output.write(pendingOutput.remove());
+ status.write(INFO_STARTCOMPROC.toString() + "\n");
+ status.flush();
+ writePendingOutput();
output.flush();
} catch (IOException e) {
- // TODO Consider if there is some better way to handle these
throw new RuntimeException(e);
}
- overall: while (running && inputScanner.hasNextLine()) {
+ StringBuilder pendingInput = new StringBuilder();
+
+ overall: while (isRunning && input.hasNextLine()) {
try {
- while (!pendingOutput.isEmpty())
- output.write(pendingOutput.remove());
+ writePendingOutput();
output.flush();
- String ln = inputScanner.nextLine();
+ String ln = input.nextLine();
if (prefix != null && ln.startsWith(prefix)) {
ln = ln.substring(prefix.length());
String com = "";
@@ -102,6 +112,9 @@ public class StreamTerminal implements Terminal, Runnable {
}
comswt: switch (com) {
+ case "c":
+ handleConfigure(ln);
+ break;
case "r": {
// General command format is 'r <request no.>,<reply>
String subRep = ln.substring(2);
@@ -116,8 +129,8 @@ public class StreamTerminal implements Terminal, Runnable {
try {
repNo = Long.parseLong(repStr);
} catch (NumberFormatException nfex) {
- output.write(ERROR_INVREPNO.toString() + "\n");
- output.flush();
+ status.write(ERROR_INVREPNO.toString() + "\n");
+ status.flush();
continue overall;
}
// Skip over the comma
@@ -125,8 +138,8 @@ public class StreamTerminal implements Terminal, Runnable {
}
if (!pendingRequests.contains(repNo)) {
- output.write(ERROR_UNKREPNO.toString() + "\n");
- output.flush();
+ status.write(ERROR_UNKREPNO.toString() + "\n");
+ status.flush();
continue overall;
}
@@ -139,35 +152,127 @@ public class StreamTerminal implements Terminal, Runnable {
break comswt;
}
case "q":
- running = false;
+ isRunning = false;
break comswt;
+ case "?":
+ case "h":
+ handleHelp(ln);
+ break;
default:
- output.write(ERROR_UNRECCOM.toString() + "\n");
- output.flush();
+ status.write(ERROR_UNRECCOM.toString() + "\n");
+ status.flush();
}
} else {
- mode.accept(ln);
+ if (isParagraphMode) {
+ if (ln.equals(paragraphTerminator)) {
+ mode.accept(pendingInput.toString());
+ pendingInput = new StringBuilder();
+
+ writePendingOutput();
+ output.flush();
+ continue;
+ }
+
+ // Handle line-continuation
+ if (ln.endsWith("\\")) {
+ pendingInput.append(ln.substring(0, ln.length() - 1));
+ pendingInput.append(" ");
+ } else {
+ pendingInput.append(ln);
+ pendingInput.append("\n");
+ }
+
+ addOutput(prompt);
+ } else {
+ mode.accept(ln);
+ }
}
-
- while (!pendingOutput.isEmpty())
- output.write(pendingOutput.remove());
+
+ writePendingOutput();
output.flush();
} catch (IOException ioex) {
throw new RuntimeException(ioex);
}
}
- running = false;
+ isRunning = false;
try {
- while (!pendingOutput.isEmpty())
- output.write(pendingOutput.remove());
- output.write(INFO_ENDCOMPROC.toString() + "\n");
+ writePendingOutput();
output.flush();
+ status.write(INFO_ENDCOMPROC.toString() + "\n");
+ status.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
+ private void writePendingOutput() throws IOException {
+ while (!pendingOutput.isEmpty())
+ output.write(pendingOutput.remove());
+ }
+
+ private void handleHelp(String ln) {
+ addOutput("Help not yet fully implemented");
+ }
+
+ private void handleConfigure(String ln) {
+ int spaceIdx = ln.indexOf(" ");
+
+ String setting = ln.substring(0, spaceIdx);
+ String setArgs = ln.substring(spaceIdx).strip();
+ List<String> args = TokenUtils.processArguments(setArgs);
+
+ switch(setting) {
+ case "paraTerm":
+ paragraphTerminator = args.get(0);
+ break;
+ case "para":
+ isParagraphMode = !isParagraphMode;
+ break;
+ case "prompt?":
+ isPromptOnContinue = !isPromptOnContinue;
+ break;
+ case "prompt":
+ prompt = args.get(0);
+ break;
+ default:
+ addOutput(ERROR_UNKCONFSET.toString() + "\n");
+ }
+ }
+
+
+ public boolean isParagraphMode() {
+ return isParagraphMode;
+ }
+
+ public void setParagraphMode(boolean isParagraphMode) {
+ this.isParagraphMode = isParagraphMode;
+ }
+
+ public boolean isPromptOnContinue() {
+ return isPromptOnContinue;
+ }
+
+ public void setPromptOnContinue(boolean isPromptOnContinue) {
+ this.isPromptOnContinue = isPromptOnContinue;
+ }
+
+ public String getParagraphTerminator() {
+ return paragraphTerminator;
+ }
+
+ public void setParagraphTerminator(String paragraphTerminator) {
+ this.paragraphTerminator = paragraphTerminator;
+ }
+
+ public String getPrompt() {
+ return prompt;
+ }
+
+ public void setPrompt(String prompt) {
+ this.prompt = prompt;
+ }
+
@Override
public long submitRequest(String req) {
long reqNo = currentRequest + 1;
@@ -228,7 +333,7 @@ public class StreamTerminal implements Terminal, Runnable {
Optional<String> rep = awaitReply(id, unit, delay);
return rep.isEmpty() ? Either.right(id) : Either.left(rep.get());
}
-
+
/**
* Add a string to be printed next time output is printed.
*
@@ -246,4 +351,12 @@ public class StreamTerminal implements Terminal, Runnable {
public void setMode(Consumer<String> mode) {
this.mode = mode;
}
+
+ public Writer getStatus() {
+ return status;
+ }
+
+ public void setStatus(Writer status) {
+ this.status = status;
+ }
} \ 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 7e2206e..405d854 100644
--- a/base/src/main/java/bjc/utils/cli/TerminalCodes.java
+++ b/base/src/main/java/bjc/utils/cli/TerminalCodes.java
@@ -19,12 +19,21 @@ public enum TerminalCodes {
/** Alert for ending processing. */
INFO_ENDCOMPROC("IOLPI00002", "ENDING PROCESSING"),
+ /** Prompt on continuation turned on */
+ INFO_PROMPTON("IOINI00001", "CONTINUATION PROMPT ON"),
+ /** Prompt on continuation turned off */
+ INFO_PROMPTOFF("IOINI00001", "CONTINUATION PROMPT OFF"),
+
/** 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"),
+ /** Error for unknown configuration setting. */
+ ERROR_UNKCONFSET("IOINE00003", "UNRECOGNIZED CONFIGURE SETTING"),
+ /** Error for invalid value for configuration setting. */
+ ERROR_INVCONFSET("IOINE00003", "INVALID CONFIGURE VALUE"),
;
/** The code for this error. */
diff --git a/base/src/main/java/bjc/utils/parserutils/TokenUtils.java b/base/src/main/java/bjc/utils/parserutils/TokenUtils.java
index 20c8ed2..a734477 100644
--- a/base/src/main/java/bjc/utils/parserutils/TokenUtils.java
+++ b/base/src/main/java/bjc/utils/parserutils/TokenUtils.java
@@ -17,14 +17,14 @@ import bjc.utils.parserutils.splitter.TokenSplitter;
/**
* Utilities useful for operating on PL tokens.
*
- * @author EVE
+ * @author Ben Culkin
*
*/
public class TokenUtils {
/**
* Simple implementation of TokenSplitter for removing double-quoted strings.
*
- * @author EVE
+ * @author Ben Culkin
*
*/
public static class StringTokenSplitter implements TokenSplitter {
@@ -70,15 +70,15 @@ public class TokenUtils {
*
* Splits a string around instances of java-style double-quoted strings.
*
- * @param inp
+ * @param input
* The string to split.
*
* @return An list containing alternating bits of the string and the embedded
* double-quoted strings that separated them.
*/
- public static List<String> removeDQuotedStrings(final String inp) {
+ public static List<String> removeDQuotedStrings(final String input) {
/* Validate input. */
- if (inp == null)
+ if (input == null)
throw new NullPointerException("inp must not be null");
/*
@@ -90,16 +90,16 @@ public class TokenUtils {
/*
* Matcher for proper strings and single quotes.
*/
- final Matcher mt = doubleQuotePatt.matcher(inp);
- final Matcher corr = quotePatt.matcher(inp);
+ final Matcher mt = doubleQuotePatt.matcher(input);
+ final Matcher corr = quotePatt.matcher(input);
if (corr.find() && !corr.find()) {
/*
* There's a unmatched opening quote with no strings.
*/
final String msg = String.format(
- "Unclosed string literal '%s'. Opening quote was at position %d", inp,
- inp.indexOf("\""));
+ "Unclosed string literal '%s'. Opening quote was at position %d", input,
+ input.indexOf("\""));
throw new IllegalArgumentException(msg);
}
@@ -135,8 +135,8 @@ public class TokenUtils {
* There's a unmatched opening quote with at least one string.
*/
final String msg = String.format(
- "Unclosed string literal '%s'. Opening quote was at position %d", inp,
- inp.lastIndexOf("\""));
+ "Unclosed string literal '%s'. Opening quote was at position %d", input,
+ input.lastIndexOf("\""));
throw new IllegalArgumentException(msg);
}
@@ -157,23 +157,23 @@ public class TokenUtils {
* Use {@link StringDescaper} for customizable escapes. This only handles the
* ones that are built into Java strings.
*
- * @param inp
+ * @param input
* The string to replace escape sequences in.
*
* @return The string with escape sequences replaced by their equivalent
* characters.
*/
- public static String descapeString(final String inp) {
+ public static String descapeString(final String input) {
/* Validate input. */
- if (inp == null)
+ if (input == null)
throw new NullPointerException("inp must not be null");
/*
* Prepare the buffer and escape finder.
*/
final StringBuffer work = new StringBuffer();
- final Matcher possibleEscapeFinder = possibleEscapePatt.matcher(inp);
- final Matcher escapeFinder = escapePatt.matcher(inp);
+ final Matcher possibleEscapeFinder = possibleEscapePatt.matcher(input);
+ final Matcher escapeFinder = escapePatt.matcher(input);
/* Go through all possible escapes. */
while (possibleEscapeFinder.find()) {
@@ -292,29 +292,29 @@ public class TokenUtils {
* Check if a given string would be successfully converted to a double by
* {@link Double#parseDouble(String)}.
*
- * @param inp
+ * @param input
* The string to check.
* @return Whether the string is a valid double or not.
*/
- public static boolean isDouble(final String inp) {
- return DoubleMatcher.doubleLiteral.matcher(inp).matches();
+ public static boolean isDouble(final String input) {
+ return DoubleMatcher.doubleLiteral.matcher(input).matches();
}
/**
* Check if a given string would be successfully converted to a double by
* {@link Double#parseDouble(String)} if a given suffix was removed.
*
- * @param inp
+ * @param input
* The string to check.
* @param suffix
* The suffix to remove
* @return Whether the string is a valid double or not.
*/
- public static boolean isSuffixedDouble(final String inp, final String suffix) {
+ public static boolean isSuffixedDouble(final String input, final String suffix) {
Function<String, Pattern> patInit;
patInit = (String sfx) -> Pattern.compile("\\A" + DoubleMatcher.rawDouble + suffix + "\\Z");
- return SUFFIX_MAP.computeIfAbsent(suffix, patInit).matcher(inp).matches();
+ return SUFFIX_MAP.computeIfAbsent(suffix, patInit).matcher(input).matches();
}
/**
@@ -324,13 +324,13 @@ public class TokenUtils {
* NOTE: This only checks syntax. Using values out of the range of integers will
* still cause errors.
*
- * @param inp
+ * @param input
* The input to check.
* @return Whether the string is a valid integer or not.
*/
- public static boolean isInt(final String inp) {
+ public static boolean isInt(final String input) {
try {
- Integer.parseInt(inp);
+ Integer.parseInt(input);
return true;
} catch (NumberFormatException nfex) {
return false;
@@ -341,16 +341,16 @@ public class TokenUtils {
* Split a line into a series of space-separated arguments, including string
* literals.
*
- * @param com
+ * @param command
* The command to split from
* @return The split arguments.
*/
- public static List<String> processArguments(String com) {
+ public static List<String> processArguments(String command) {
List<String> strings = new ArrayList<>();
BooleanToggle togg = new BooleanToggle();
- for (String strang : removeDQuotedStrings(com)) {
+ for (String strang : removeDQuotedStrings(command)) {
if (togg.get()) {
strings.add(descapeString(strang));
} else {