summaryrefslogtreecommitdiff
path: root/base/src/main/java/bjc/utils/cli
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/utils/cli
parentc1d4716b079d4ef32650144709294525fed6badf (diff)
Work on StreamTerminal and the example
Some work on StreamTerminal and its corresponding example
Diffstat (limited to 'base/src/main/java/bjc/utils/cli')
-rw-r--r--base/src/main/java/bjc/utils/cli/StreamTerminal.java171
-rw-r--r--base/src/main/java/bjc/utils/cli/TerminalCodes.java9
2 files changed, 151 insertions, 29 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. */