diff options
| -rw-r--r-- | base/src/examples/java/bjc/utils/examples/cli/StreamTerminalExample.java | 28 | ||||
| -rw-r--r-- | base/src/main/java/bjc/utils/cli/StreamTerminal.java | 171 | ||||
| -rw-r--r-- | base/src/main/java/bjc/utils/cli/TerminalCodes.java | 9 | ||||
| -rw-r--r-- | base/src/main/java/bjc/utils/parserutils/TokenUtils.java | 56 | ||||
| -rw-r--r-- | clformat/pom.xml | 2 |
5 files changed, 205 insertions, 61 deletions
diff --git a/base/src/examples/java/bjc/utils/examples/cli/StreamTerminalExample.java b/base/src/examples/java/bjc/utils/examples/cli/StreamTerminalExample.java index 208ecf7..33c5827 100644 --- a/base/src/examples/java/bjc/utils/examples/cli/StreamTerminalExample.java +++ b/base/src/examples/java/bjc/utils/examples/cli/StreamTerminalExample.java @@ -1,11 +1,33 @@ package bjc.utils.examples.cli; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; import java.util.Scanner; +import java.util.function.Consumer; + +import bjc.data.IntHolder; +import bjc.utils.cli.StreamTerminal; public class StreamTerminalExample { public static void main(String[] args) { - Scanner sysIn = new Scanner(System.in); - - // TODO Figure out a good way to demo this + try (Scanner sysIn = new Scanner(System.in)) { + + InputStreamReader inStream = new InputStreamReader(System.in); + OutputStreamWriter outStream = new OutputStreamWriter(System.out); + + StreamTerminal sterm = new StreamTerminal(inStream, outStream, "#", null); + IntHolder comNo = new IntHolder(); + Consumer<String> defMode = (lne) -> { + sterm.addOutput(lne + "\n"); + + sterm.addOutput(String.format("%d > ", comNo.incr())); + }; + + sterm.setMode(defMode); + sterm.addOutput("0 > "); + sterm.setParagraphMode(true); + sterm.setPromptOnContinue(true); + sterm.run(); + } } } 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 { diff --git a/clformat/pom.xml b/clformat/pom.xml index 324d119..1fec73d 100644 --- a/clformat/pom.xml +++ b/clformat/pom.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> - <parent> + <parent> <groupId>com.ashardalon</groupId> <artifactId>BJCUtils-Parent</artifactId> <version>2.0-SNAPSHOT</version> |
