diff options
| author | Ben Culkin <scorpress@gmail.com> | 2020-10-31 12:45:15 -0400 |
|---|---|---|
| committer | Ben Culkin <scorpress@gmail.com> | 2020-10-31 12:45:15 -0400 |
| commit | d5eaaa14adc980e9b0588a3caf7ee7d81df1183e (patch) | |
| tree | 866603edc90f0e1538e869db8a7732ce8c4bf03c | |
| parent | e8b01037e47884c10d9f910192ac59cef14d28bf (diff) | |
General cleanup
This does a bunch of structural cleanups to make the code better
| -rw-r--r-- | src/main/java/bjc/everge/BadReplParse.java | 6 | ||||
| -rw-r--r-- | src/main/java/bjc/everge/ControlledString.java | 136 | ||||
| -rw-r--r-- | src/main/java/bjc/everge/Everge.java | 261 | ||||
| -rw-r--r-- | src/main/java/bjc/everge/ReplPair.java | 701 | ||||
| -rw-r--r-- | src/main/java/bjc/everge/ReplPairError.java (renamed from src/main/java/bjc/everge/ReplError.java) | 68 | ||||
| -rw-r--r-- | src/main/java/bjc/everge/ReplPairOptions.java (renamed from src/main/java/bjc/everge/ReplOpts.java) | 37 | ||||
| -rw-r--r-- | src/main/java/bjc/everge/ReplPairParser.java | 704 | ||||
| -rw-r--r-- | src/main/java/bjc/everge/ReplPairSet.java (renamed from src/main/java/bjc/everge/ReplSet.java) | 42 | ||||
| -rw-r--r-- | src/test/java/bjc/everge/ReplPairTest.java | 4 | ||||
| -rw-r--r-- | src/test/java/bjc/everge/TestUtils.java | 12 |
10 files changed, 1009 insertions, 962 deletions
diff --git a/src/main/java/bjc/everge/BadReplParse.java b/src/main/java/bjc/everge/BadReplParse.java index 0278e76..c22e3ba 100644 --- a/src/main/java/bjc/everge/BadReplParse.java +++ b/src/main/java/bjc/everge/BadReplParse.java @@ -17,7 +17,7 @@ public class BadReplParse extends RuntimeException { /** * The errors that were encountered during parsing. */ - public List<ReplError> errs; + public List<ReplPairError> errs; /** * Create a new exception for ReplPair parsing failing. @@ -37,7 +37,7 @@ public class BadReplParse extends RuntimeException { * @param errs * The list of errors encountered while parsing. */ - public BadReplParse(String msg, List<ReplError> errs) { + public BadReplParse(String msg, List<ReplPairError> errs) { super(msg); this.errs = errs; @@ -81,7 +81,7 @@ public class BadReplParse extends RuntimeException { if (errs.size() > 0) { errString.append("\n\t"); - for (ReplError err : errs) { + for (ReplPairError err : errs) { errString.append(err.toPrintString("\t")); errString.append("\n\t"); } diff --git a/src/main/java/bjc/everge/ControlledString.java b/src/main/java/bjc/everge/ControlledString.java index 75e1fdc..f27b3d2 100644 --- a/src/main/java/bjc/everge/ControlledString.java +++ b/src/main/java/bjc/everge/ControlledString.java @@ -2,6 +2,11 @@ package bjc.everge; import java.util.Arrays; +/* @FixMe Ben Culkin Oct. 31, 2020 - :LeadingControl + * + * At the moment, this only parses a single control that is at the start of the + * string. Should this be improved? + */ /** * Represents a string with a set of control flags attached to it. * @@ -38,6 +43,8 @@ public class ControlledString { * The name of the control. */ public Control(String nam) { + this(); + name = nam; } @@ -50,7 +57,8 @@ public class ControlledString { * The arguments of the control. */ public Control(String nam, String... ars) { - name = nam; + this(nam); + args = ars; } @@ -73,6 +81,8 @@ public class ControlledString { * @return The argument at that position. */ public String get(int i) { + // @Cleanup: I'm pretty sure Java will auto-throw these, so we should + // remove this stuff. --bculkin, Oct. 31, 2020 if (i < 0) { String msg = String.format( "Control argument index must be greater than 0 (was %d)", i); @@ -175,74 +185,78 @@ public class ControlledString { * * @author Ben Culkin */ - public static class ParseStrings { + public static class ControlledStringParseOptions { /** * The indicator for separating controls from the regular string. */ - public String contInd; + public String controlIndicator; /** * The indicator for separating individual controls. */ - public String contSep; + public String controlSeparator; /** * The indicator for separating arguments to a control. */ - public String contArg; + public String controlArgumentSeparator; /** * The indicator for escaping any of the indicators (including itself) */ - public String contEsc; + public String controlEscape; /** * Create a new set of parse strings. * - * @param contInd + * @param controlIndicator * The control indicator. - * @param contSep + * @param controlSeparator * The control separator. - * @param contArg + * @param controlArgumentSeparator * The argument separator. - * @param contEsc + * @param controlEscape * The control escape. */ - public ParseStrings(String contInd, String contSep, String contArg, - String contEsc) { - this.contInd = contInd; - this.contSep = contSep; - this.contArg = contArg; - this.contEsc = contEsc; + public ControlledStringParseOptions(String controlIndicator, + String controlSeparator, String controlArgumentSeparator, + String controlEscape) { + this.controlIndicator = controlIndicator; + this.controlSeparator = controlSeparator; + this.controlArgumentSeparator = controlArgumentSeparator; + this.controlEscape = controlEscape; } /** * Convenient static constructor. * - * @param contInd + * @param controlIndicator * The control indicator. - * @param contSep + * @param controlSeparator * The control separator. - * @param contArg + * @param controlArgumentSeparator * The argument separator. - * @param contEsc + * @param controlEscape * The control escape. * @return A new set of control strings. */ - public static ParseStrings PS(String contInd, String contSep, String contArg, - String contEsc) { - return new ParseStrings(contInd, contSep, contArg, contEsc); + public static ControlledStringParseOptions CSPS(String controlIndicator, + String controlSeparator, String controlArgumentSeparator, + String controlEscape) { + return new ControlledStringParseOptions(controlIndicator, controlSeparator, controlArgumentSeparator, controlEscape); } } /** * The string the controls apply to. */ - public String strang; + public String body; /** * The controls that apply to the string. */ + // @NOTE Why is this an array? Would it make more sense for it to be a List + // of some sort? --bculkin, Oct 31, 2020 public Control[] controls; /** @@ -259,9 +273,9 @@ public class ControlledString { * The string to use. */ public ControlledString(String strung) { - strang = strung; - - controls = new Control[0]; + this(); + + body = strung; } /** @@ -273,7 +287,7 @@ public class ControlledString { * The controls that apply to the string. */ public ControlledString(String strung, Control... controls) { - strang = strung; + this(strung); this.controls = controls; } @@ -307,51 +321,59 @@ public class ControlledString { * The object to read the strings from * @return A parsed control string. */ - public static ControlledString parse(String lne, ParseStrings strangs) { - if (!lne.startsWith(strangs.contInd)) { - return new ControlledString(lne); - } + public static ControlledString parse(String lne, ControlledStringParseOptions strangs) { + if (!lne.startsWith(strangs.controlIndicator)) return new ControlledString(lne); - String[] bits = StringUtils.escapeSplit(strangs.contEsc, strangs.contInd, lne); + // Split off initial control + String[] controlIntervals = StringUtils.escapeSplit(strangs.controlEscape, strangs.controlIndicator, lne); - if (bits.length < 2) { + if (controlIntervals.length < 2) { String msg = "Did not find control terminator (%s) where it should be"; - msg = String.format(msg, strangs.contInd); + msg = String.format(msg, strangs.controlIndicator); throw new IllegalArgumentException(msg); } - ControlledString cs = new ControlledString(bits[0]); - if (bits.length > 2) - cs.strang = bits[2]; - - bits = StringUtils.escapeSplit(strangs.contEsc, strangs.contSep, bits[1]); + ControlledString controlString = new ControlledString(controlIntervals[0]); + /* :LeadingControl + * ... Is this even correct? It would seem that we are discarding any + * text that came before the control. + * + * Ideally, what we would want to do is concatenate any non-control text, + * and then process each control interval by itself. + */ + if (controlIntervals.length > 2) controlString.body = controlIntervals[2]; - cs.controls = new Control[bits.length]; + // Split the individual controls from the string + String[] unparsedControls = StringUtils.escapeSplit(strangs.controlEscape, + strangs.controlSeparator, controlIntervals[1]); + controlString.controls = new Control[unparsedControls.length]; - for (int i = 0; i < bits.length; i++) { - String bit = bits[i]; + for (int i = 0; i < unparsedControls.length; i++) { + String controlText = unparsedControls[i]; - String[] bots - = StringUtils.escapeSplit(strangs.contEsc, strangs.contArg, bit); + // Get the control arguments + String[] controlArguments + = StringUtils.escapeSplit(strangs.controlEscape, strangs.controlArgumentSeparator, controlText); - Control cont = new Control(bots[0]); + Control control = new Control(controlArguments[0]); - if (cont.name.length() > 1) { - cont.name = cont.name.toUpperCase(); + if (control.name.length() > 1) { + // Only single-character controls can be lower-case + control.name = control.name.toUpperCase(); } - if (bots.length > 1) { - cont.args = new String[bots.length - 1]; - for (int j = 1; j < bots.length; j++) { - cont.args[j - 1] = bots[j]; + if (controlArguments.length > 1) { + control.args = new String[controlArguments.length - 1]; + for (int j = 1; j < controlArguments.length; j++) { + control.args[j - 1] = controlArguments[j]; } } - cs.controls[i] = cont; + controlString.controls[i] = control; } - return cs; + return controlString; } @Override @@ -360,12 +382,10 @@ public class ControlledString { sb.append("//"); - for (Control cont : controls) { - sb.append(cont); - } + for (Control control : controls) sb.append(control); sb.append("//"); - sb.append(strang); + sb.append(body); return sb.toString(); } diff --git a/src/main/java/bjc/everge/Everge.java b/src/main/java/bjc/everge/Everge.java index d5090db..4638fb5 100644 --- a/src/main/java/bjc/everge/Everge.java +++ b/src/main/java/bjc/everge/Everge.java @@ -28,47 +28,62 @@ public class Everge { */ LINE, /** - * Process the input, splitting it around occurances of a regex. + * Process the input, splitting it around occurrences of a regular expression. */ REGEX; } - // Options for doing repl-pairs - private ReplOpts ropts = new ReplOpts(); + /** + * Options for doing repl-pairs + */ + private ReplPairOptions replOptions = new ReplPairOptions(); - // Pair repository - private ReplSet replSet = new ReplSet(); + /** + * Repository for ReplPairs + */ + private ReplPairSet replSet = new ReplPairSet(); - // Input status - private InputStatus inputStat = InputStatus.ALL; + /** + * Input status. + * + * How the input to run replacements on should be processed. + */ + private InputStatus inputStatus = InputStatus.ALL; - // Are we processing CLI args? (haven't seen a -- yet) + /** + * Are we processing CLI args? (haven't seen a -- yet) + */ private boolean doingArgs = true; - // Should an NL be printed after each replace? - private boolean printNL = true; + /** + * Should an NL be printed after each replace? + */ + private boolean printNLAfterReplace = true; - // Verbosity level + /** + * Verbosity level + */ private int verbosity; - // The pattern to use for REGEX input mode - private String pattern; - - // The queue of arguments to process - private Deque<String> argQue = new LinkedList<>(); + /** + * The pattern to use for REGEX input mode + */ + private String regexPattern; - // Used to prevent inter-mixing argument alterations with input processing. + /** + * Used to prevent inter-mixing argument alterations with input processing. + */ private ReadWriteLock argLock = new ReentrantReadWriteLock(); // Input/output streams /** * Stream to use for normal output. */ - private PrintStream outStream = System.out; + private PrintStream outputStream = System.out; /** * Stream to use for error output. */ - private LogStream errStream = new LogStream(System.err); + private LogStream errorStream = new LogStream(System.err); /** * Set the output stream. @@ -77,7 +92,7 @@ public class Everge { * The output stream.. */ public void setOutput(PrintStream out) { - outStream = out; + outputStream = out; } /** @@ -87,7 +102,7 @@ public class Everge { * The output stream.. */ public void setOutput(OutputStream out) { - outStream = new PrintStream(out); + setOutput(new PrintStream(out)); } /** @@ -97,7 +112,7 @@ public class Everge { * The error stream. */ public void setError(PrintStream err) { - errStream = new LogStream(err); + errorStream = new LogStream(err); } /** @@ -107,7 +122,7 @@ public class Everge { * The error stream. */ public void setError(OutputStream err) { - errStream = new LogStream(new PrintStream(err)); + setError(new PrintStream(err)); } /** @@ -127,7 +142,7 @@ public class Everge { * * @param args * The arguments to process. - * @return Whether we processed succesfully or not. + * @return Whether we processed successfully or not. */ public boolean processArgs(String... args) { List<String> errs = new ArrayList<>(); @@ -136,19 +151,16 @@ public class Everge { if (verbosity >= 2) { String argString = args.length > 0 ? "arguments" : "argument"; - errStream.infof("[INFO] Processed %d %s\n", args.length, argString); - int argc = 0; + errorStream.infof("[INFO] Processed %d %s\n", args.length, argString); + int argCount = 0; if (verbosity >= 3) { - String arg = args[argc++]; - errStream.tracef("[TRACE]\tArg %d: '%s\n", argc, arg); + String arg = args[argCount++]; + errorStream.tracef("[TRACE]\tArg %d: '%s\n", argCount, arg); } } - if (!stat) { - for (String err : errs) { - errStream.errorf("%s\n", err); - } - } + if (!stat) + for (String err : errs) errorStream.errorf("%s\n", err); return stat; } @@ -160,35 +172,36 @@ public class Everge { * The arguments to process. * @param errs * The list to stash errors in. - * @return Whether we processed succesfully or not. + * @return Whether we processed successfully or not. */ public boolean processArgs(List<String> errs, String... args) { argLock.writeLock().lock(); - boolean retStat = true; + boolean returnStatus = true; try { - loadQueue(args); + Deque<String> argQueue = loadQueue(args); - // Process CLI args - while (argQue.size() > 0) { - String arg = argQue.pop(); + // Process CLI arguments + while (argQueue.size() > 0) { + String arg = argQueue.pop(); - retStat = processArg(errs, retStat, arg); + returnStatus = processArg(errs, returnStatus, arg, argQueue); } } finally { argLock.writeLock().unlock(); } - return retStat; + return returnStatus; } - private boolean processArg(List<String> errs, boolean retStat, String arg) { - boolean newRet = retStat; + private boolean processArg(List<String> errs, boolean retStat, String arg, Deque<String> argQueue) { + boolean newReturnStatus = retStat; if (arg.equals("--")) { doingArgs = false; - return newRet; + + return newReturnStatus; } // Process an argument @@ -196,7 +209,7 @@ public class Everge { String argName = arg; String argBody = ""; - // Process arguments to arguments + // Process 'joined' arguments (a=b) int idx = arg.indexOf("="); if (idx != -1) { argName = arg.substring(0, idx); @@ -206,76 +219,83 @@ public class Everge { switch (argName) { case "-n": case "--newline": - printNL = true; + printNLAfterReplace = true; break; case "-N": case "--no-newline": - printNL = false; + printNLAfterReplace = false; break; + case "-v": case "--verbose": verbosity += 1; - errStream.louder(); + errorStream.louder(); System.err.printf("[TRACE] Incremented verbosity\n"); break; case "-q": case "--quiet": verbosity -= 1; - errStream.quieter(); + errorStream.quieter(); System.err.printf("[TRACE] Decremented verbosity\n"); break; case "--verbosity": - if (argQue.size() < 1) { + if (argQueue.size() < 1) { errs.add("[ERROR] No parameter to --verbosity"); - newRet = false; + newReturnStatus = false; break; } - argBody = argQue.pop(); + argBody = argQueue.pop(); case "-V": try { verbosity = Integer.parseInt(argBody); - errStream.verbosity(verbosity); + errorStream.verbosity(verbosity); System.err.printf("[TRACE] Set verbosity to %d\n", verbosity); } catch (NumberFormatException nfex) { String msg = String.format( "[ERROR] Invalid verbosity: '%s' is not an integer", argBody); errs.add(msg); - newRet = false; + newReturnStatus = false; } break; + case "--pattern": - if (argQue.size() < 1) { + if (argQueue.size() < 1) { errs.add("[ERROR] No parameter to --pattern"); - newRet = false; + newReturnStatus = false; break; } - argBody = argQue.pop(); + argBody = argQueue.pop(); case "-p": + if (inputStatus != InputStatus.REGEX) + errorStream.warn("[WARN] Specified pattern will be ignored unless input mode is switched to REGEX"); + try { - pattern = argBody; + regexPattern = argBody; Pattern.compile(argBody); } catch (PatternSyntaxException psex) { String msg = String.format("[ERROR] Pattern '%s' is invalid: %s", - pattern, psex.getMessage()); + regexPattern, psex.getMessage()); errs.add(msg); - newRet = false; + newReturnStatus = false; } break; + case "--file": - if (argQue.size() < 1) { + if (argQueue.size() < 1) { errs.add("[ERROR] No argument to --file"); - newRet = false; + newReturnStatus = false; break; } - argBody = argQue.pop(); + argBody = argQueue.pop(); case "-f": try (FileInputStream fis = new FileInputStream(argBody); Scanner scn = new Scanner(fis)) { - List<ReplError> ferrs = new ArrayList<>(); + List<ReplPairError> ferrs = new ArrayList<>(); List<ReplPair> lrp = new ArrayList<>(); - lrp = ReplPair.readList(lrp, scn, ferrs, ropts); + ReplPairParser parser = new ReplPairParser(); + lrp = parser.readList(lrp, scn, ferrs, replOptions); if (ferrs.size() > 0) { StringBuilder sb = new StringBuilder(); @@ -284,19 +304,18 @@ public class Everge { if (ferrs.size() > 1) errString = String.format("%d errors", ferrs.size()); - { - String msg = String.format( - "[ERROR] Encountered %s parsing data file'%s'\n", - errString, argBody); - sb.append(msg); - } - - for (ReplError err : ferrs) { + + String msg = String.format( + "[ERROR] Encountered %s parsing data file'%s'\n", + errString, argBody); + sb.append(msg); + + for (ReplPairError err : ferrs) { sb.append(String.format("\t%s\n", err)); } errs.add(sb.toString()); - newRet = false; + newReturnStatus = false; } replSet.addPairs(lrp); @@ -304,21 +323,22 @@ public class Everge { String msg = String.format( "[ERROR] Could not open data file '%s' for input", argBody); errs.add(msg); - newRet = false; + newReturnStatus = false; } catch (IOException ioex) { String msg = String.format( "[ERROR] Unknown I/O error reading data file '%s': %s", argBody, ioex.getMessage()); errs.add(msg); - newRet = false; + newReturnStatus = false; } break; + case "--arg-file": - if (argQue.size() < 1) { + if (argQueue.size() < 1) { errs.add("[ERROR] No argument to --arg-file"); break; } - argBody = argQue.pop(); + argBody = argQueue.pop(); case "-F": try (FileInputStream fis = new FileInputStream(argBody); Scanner scn = new Scanner(fis)) { @@ -341,47 +361,48 @@ public class Everge { "[ERROR] Could not open argument file '%s' for input", argBody); errs.add(msg); - newRet = false; + newReturnStatus = false; } catch (IOException ioex) { String msg = String.format( "[ERROR] Unknown I/O error reading input file '%s': %s", argBody, ioex.getMessage()); errs.add(msg); - newRet = false; + newReturnStatus = false; } break; + case "--input-status": - if (argQue.size() < 1) { + if (argQueue.size() < 1) { errs.add("[ERROR] No argument to --input-status"); break; } - argBody = argQue.pop(); + argBody = argQueue.pop(); case "-I": try { - inputStat = InputStatus.valueOf(argBody.toUpperCase()); + inputStatus = InputStatus.valueOf(argBody.toUpperCase()); } catch (IllegalArgumentException iaex) { String msg = String.format("[ERROR] '%s' is not a valid input status", argBody); errs.add(msg); } break; + default: { - String msg = String - .format("[ERROR] Unrecognised CLI argument name '%s'\n", argName); + String msg = String.format( + "[ERROR] Unrecognised CLI argument name '%s'\n", argName); errs.add(msg); - newRet = false; + newReturnStatus = false; } } } else { String tmp = arg; // Strip off an escaped initial dash - if (tmp.startsWith("\\-")) - tmp = tmp.substring(1); + if (tmp.startsWith("\\-")) tmp = tmp.substring(1); processInputFile(tmp); } - return newRet; + return newReturnStatus; } /** @@ -395,11 +416,7 @@ public class Everge { List<String> errs = new ArrayList<>(); boolean stat = processInputFile(errs, fle); - if (!stat) { - for (String err : errs) { - errStream.errorf("%s\n", err); - } - } + if (!stat) for (String err : errs) errorStream.errorf("%s\n", err); return stat; } @@ -419,16 +436,15 @@ public class Everge { // Read in and do replacements on a file try { if (verbosity > 2) { - errStream.printf("[TRACE] Reading file (%s) in mode (%s)\n", fle, - inputStat); + errorStream.printf("[TRACE] Reading file (%s) in mode (%s)\n", fle, + inputStatus); } - if (inputStat == InputStatus.ALL) { + if (inputStatus == InputStatus.ALL) { Path pth = Paths.get(fle); if (!Files.isReadable(pth)) { - String msg - = String.format("[ERROR] File '%s' is not readable\n", fle); + String msg = String.format("[ERROR] File '%s' is not readable\n", fle); errs.add(msg); return false; } @@ -438,26 +454,23 @@ public class Everge { String strang = new String(inp, Charset.forName("UTF-8")); processString(strang); - } else if (inputStat == InputStatus.LINE) { - try (FileInputStream fis = new FileInputStream(fle); + } else if (inputStatus == InputStatus.LINE) { + try ( + FileInputStream fis = new FileInputStream(fle); Scanner scn = new Scanner(fis)) { - while (scn.hasNextLine()) { - processString(scn.nextLine()); - } + while (scn.hasNextLine()) processString(scn.nextLine()); } - } else if (inputStat == InputStatus.REGEX) { + } else if (inputStatus == InputStatus.REGEX) { try (FileInputStream fis = new FileInputStream(fle); Scanner scn = new Scanner(fis)) { - scn.useDelimiter(pattern); + scn.useDelimiter(regexPattern); - while (scn.hasNext()) { - processString(scn.next()); - } + while (scn.hasNext()) processString(scn.next()); } } else { String msg = String.format( "[INTERNAL-ERROR] Input status '%s' is not yet implemented\n", - inputStat); + inputStatus); errs.add(msg); return false; } @@ -487,52 +500,56 @@ public class Everge { String strang = inp; if (verbosity >= 3) { - errStream.infof( + errorStream.infof( "[INFO] Processing replacements for string '%s' in mode %s\n", - strang, inputStat); + strang, inputStatus); if (!inp.equals(inp.trim())) { - errStream.infof("[INFO] String '%s' has trailing spaces on it\n", inp); + errorStream.infof("[INFO] String '%s' has trailing spaces on it\n", inp); } } strang = replSet.apply(inp); - outStream.print(strang); - if (printNL) - outStream.println(); + outputStream.print(strang); + if (printNLAfterReplace) outputStream.println(); } finally { argLock.readLock().unlock(); } } // Load arguments into the argument queue. - private void loadQueue(String... args) { + private Deque<String> loadQueue(String... args) { + Deque<String> argQueue = new ArrayDeque<>(args.length); + boolean doArgs = true; for (String arg : args) { if (arg.equals("--")) { doArgs = false; + continue; } - // Handle things like -nNv correctly if (doArgs) { if (arg.startsWith("-") && !arg.startsWith("--")) { + // Handle things like -nNv correctly char[] car = arg.substring(1).toCharArray(); if (verbosity >= 3) { - errStream.infof("[INFO] Adding stream of args: %s", car); + errorStream.infof("[INFO] Adding collection of single-char arguments: %s", car); } for (char c : car) { String argstr = String.format("-%c", c); - argQue.add(argstr); + argQueue.add(argstr); } } else { - argQue.add(arg); + argQueue.add(arg); } } else { - argQue.add(arg); + argQueue.add(arg); } } + + return argQueue; } } diff --git a/src/main/java/bjc/everge/ReplPair.java b/src/main/java/bjc/everge/ReplPair.java index 153efb3..e8ca1b0 100644 --- a/src/main/java/bjc/everge/ReplPair.java +++ b/src/main/java/bjc/everge/ReplPair.java @@ -1,11 +1,6 @@ package bjc.everge; -import java.util.*; import java.util.function.*; -import java.util.regex.*; - -import bjc.data.IntHolder; -import bjc.everge.ControlledString.*; /** * String pairs for replacements. @@ -14,13 +9,13 @@ import bjc.everge.ControlledString.*; */ public class ReplPair implements Comparable<ReplPair>, UnaryOperator<String> { // Line number we read this pair from - private int lno; + int lineNumber; // Stage this pair is in - private int stage; + int stage; // Status of this pair with regards to doing staging stuff - private StageStatus stat = StageStatus.BOTH; + StageStatus stat = StageStatus.BOTH; /** * The priority for this replacement. @@ -120,351 +115,6 @@ public class ReplPair implements Comparable<ReplPair>, UnaryOperator<String> { priority = p; } - /** - * Read a list of replacement pairs from an input source. - * - * @param scn - * The source to read the replacements from. - * @return The list of replacements. - */ - public static List<ReplPair> readList(Scanner scn) { - List<ReplPair> lst = new ArrayList<>(); - - return readList(lst, scn); - } - - /** - * Read a list of replacement pairs from an input source, adding them to an - * existing list. - * - * @param detals - * The list to add the replacements to. - * @param scn - * The source to read the replacements from. - * @return The list of replacements. - */ - public static List<ReplPair> readList(List<ReplPair> detals, Scanner scn) { - List<ReplError> errList = new ArrayList<>(); - - List<ReplPair> rplPar = readList(detals, scn, errList); - - if (errList.size() != 0) { - throw new BadReplParse("", errList); - } - - return rplPar; - } - - /** - * Read a list of replacement pairs from an input source, adding them to an - * existing list. - * - * @param detals - * The list to add the replacements to. - * @param scn - * The source to read the replacements from. - * @param errs - * The list to stick errors in. - * @return The list of replacements. - */ - public static List<ReplPair> readList(List<ReplPair> detals, Scanner scn, - List<ReplError> errs) { - return readList(detals, scn, errs, new ReplOpts()); - } - - /** - * Read a list of replacement pairs from an input source, adding them to an - * existing list. - * - * @param detals - * The list to add the replacements to. - * @param scn - * The source to read the replacements from. - * @param errs - * The list to stick errors in. - * @param ropts - * The options to use when reading the pairs. - * @return The list of replacements. - */ - public static List<ReplPair> readList(List<ReplPair> detals, Scanner scn, - List<ReplError> errs, ReplOpts ropts) { - IntHolder lno = new IntHolder(); - IntHolder pno = new IntHolder(); - - List<List<ReplPair>> stages = new ArrayList<>(); - stages.add(new ArrayList<ReplPair>()); - - // For every line in the source... - while (scn.hasNextLine()) { - String name = scn.nextLine().trim(); - lno.incr(); - - // If its commented or blank, skip it - if (name.equals("")) - continue; - if (name.startsWith("#")) - continue; - - // Global control. Process it. - if (name.startsWith("|//")) { - readGlobal(name, errs, ropts, lno, pno); - - continue; - } - - ReplPair rp = new ReplPair(); - - rp.priority = ropts.defPrior; - rp.stat = ropts.defStatus; - rp.lno = lno.get(); - rp.stage = ropts.defStage; - - boolean isMulti = ropts.defMulti; - - { - String tmpName = readName(name, scn, errs, rp, ropts, lno, pno); - if (tmpName == null) - continue; - name = tmpName; - } - - rp.find = name; - if (rp.name == null) - rp.name = name; - - // We started to process the pair, mark it as being - // started - pno.incr(); - String body = null; - - // Read in the next uncommented line - do { - if (!scn.hasNextLine()) - break; - - body = scn.nextLine().trim(); - lno.incr(); - } while (body.startsWith("#")); - - if (body == null) { - String msg = String.format( - "Ran out of input looking for replacement body for raw name '%s'", - name); - - errs.add(new ReplError(lno, pno, msg, null)); - break; - } - - isMulti = ropts.defMulti; - - ControlledString cs = getControls(body, errs, lno, pno, "body"); - // Body has attached controls, process them. - if (cs.hasControls()) { - for (Control cont : cs.controls) { - switch (cont.name) { - case "MULTITRUE": - case "MULTIT": - case "MT": - isMulti = true; - break; - case "MULTIFALSE": - case "MULTIF": - case "MF": - isMulti = false; - break; - case "MULTI": - case "M": - if (cont.count() != 1) { - String errMsg = String.format( - "Expected one multi flag (got %d)", cont.count()); - errs.add(new ReplError(lno, pno, errMsg, body)); - } else { - isMulti = Boolean.parseBoolean(cont.get(0)); - } - break; - default: { - String errMsg - = String.format("Invalid control name '%s'", cont.name); - errs.add(new ReplError(lno, pno, errMsg, body)); - } - break; - } - } - - body = cs.strang; - } - - if (isMulti) { - String tmp = readMultiLine(body, scn, ropts, "body", lno); - if (tmp == null) - continue; - body = tmp; - } - - rp.replace = body; - - List<ReplPair> stageList = null; - if (rp.stage == 0 || stages.size() < (rp.stage - 1)) { - stageList = stages.get(rp.stage); - - if (stageList == null) { - stageList = new ArrayList<>(); - - stages.add(rp.stage, stageList); - } - } else { - for (int i = stages.size(); i <= rp.stage; i++) { - stages.add(new ArrayList<>()); - } - - stageList = stages.get(rp.stage); - } - - if (ropts.isTrace) { - ropts.errStream.printf("\t[DEBUG] Stage %d: Added %s\n\t\tContents: %s\n", - rp.stage, rp, stageList); - } - - stageList.add(rp); - } - - // Special-case one-stage processing. - if (stages.size() == 1) { - if (ropts.isTrace) - ropts.errStream.printf("\t[DEBUG] Executing single-stage bypass\n"); - - for (ReplPair rp : stages.iterator().next()) { - if (rp.stat == StageStatus.INTERNAL) { - if (ropts.isTrace) - ropts.errStream.printf("\t[DEBUG] Excluding internal RP %s\n", - rp); - - continue; - } - - detals.add(rp); - } - - detals.sort(null); - - return detals; - } - - // Handle stages - List<ReplPair> tmpList = new ArrayList<>(); - tmpList.addAll(detals); - - if (ropts.isTrace) - ropts.errStream.printf("\t[DEBUG] Stages: %s\n", stages); - - int procStg = 0; - for (List<ReplPair> stageList : stages) { - procStg += 1; - List<ReplPair> curStage = new ArrayList<>(); - - if (ropts.isTrace) - ropts.errStream.printf("\t[DEBUG] Staging stage %d of %d: %s\n", procStg, - stageList.size(), stageList); - - for (ReplPair rp : stageList) { - // Process through every pair in the previous - // stages - for (ReplPair curPar : tmpList) { - String tmp = rp.replace.replaceAll(curPar.find, curPar.replace); - - if (ropts.isTrace && !rp.replace.equals(tmp)) { - ropts.errStream.printf("\t[DEBUG] Staged '%s' -> '%s'\t%s\n", - rp.replace, tmp, curPar); - } - - rp.replace = tmp; - } - - // If we're external; add straight to the output - if (rp.stat == StageStatus.EXTERNAL) { - if (ropts.isTrace) { - ropts.errStream.printf( - "\t[DEBUG] Skipped external for staging: %s\n", rp); - } - - detals.add(rp); - } else { - if (ropts.isTrace) { - ropts.errStream.printf( - "\t[DEBUG] Added to stage %d: %s\n\t\tContents: %s\n", - procStg, rp, curStage); - } - - curStage.add(rp); - } - } - - tmpList.addAll(curStage); - tmpList.sort(null); - } - - // Copy over to output, excluding internals - for (ReplPair rp : tmpList) { - if (rp.stat == StageStatus.INTERNAL) { - if (ropts.isTrace) - ropts.errStream.printf("\t[DEBUG] Excluded internal: %s\n", rp); - - continue; - } - - detals.add(rp); - } - - detals.sort(null); - - if (ropts.isTrace) { - ropts.errStream.printf("\t[DEBUG] Final output: %s\n", detals); - } - - return detals; - } - - private static String readMultiLine(String lead, Scanner src, ReplOpts ropts, - String typ, IntHolder lno) { - String tmp = lead; - - if (ropts.isTrace && tmp.endsWith("\\")) - ropts.errStream.printf("\t[TRACE] Starting multi-line parse for %s '%s'\n", - typ, tmp); - - boolean didMulti = tmp.endsWith("\\"); - while (tmp.endsWith("\\")) { - boolean incNL = tmp.endsWith("|\\"); - - if (!src.hasNextLine()) - break; - - String nxt = src.nextLine().trim(); - lno.incr(); - - if (nxt.startsWith("#")) - continue; - - String nlStr = incNL ? "\n" : ""; - - if (tmp.endsWith("\\")) { - if (incNL) { - tmp = tmp.substring(0, tmp.length() - 2); - } else { - tmp = tmp.substring(0, tmp.length() - 1); - } - } - - tmp = String.format("%s%s%s", tmp, nlStr, nxt); - } - - if (ropts.isTrace && didMulti) - ropts.errStream.printf("\t[TRACE] Finished multi-line parse for %s:\n%s\n.\n", - typ, tmp); - - return tmp; - } - @Override public String apply(String inp) { if (guard != null) { @@ -492,7 +142,7 @@ public class ReplPair implements Comparable<ReplPair>, UnaryOperator<String> { @Override public int compareTo(ReplPair rp) { if (this.priority == rp.priority) - return this.lno - rp.lno; + return this.lineNumber - rp.lineNumber; return rp.priority - this.priority; } @@ -539,347 +189,4 @@ public class ReplPair implements Comparable<ReplPair>, UnaryOperator<String> { return false; return true; } - - private static String readName(String nam, Scanner scn, List<ReplError> errs, - ReplPair rp, ReplOpts ropts, IntHolder lno, IntHolder pno) { - ControlledString cs = getControls(nam, errs, lno, pno, "name"); - - boolean isMulti = ropts.defMulti; - - String name = cs.strang; - - if (cs.hasControls()) { - for (Control cont : cs.controls) { - switch (cont.name) { - case "NAME": - case "N": - if (cont.count() != 1) { - String errMsg = String.format( - "One name argument was expected (got %d)", cont.count()); - - errs.add(new ReplError(lno, pno, errMsg, nam)); - } else { - rp.name = cont.get(0); - } - break; - case "GUARD": - case "G": - if (cont.count() != 1) { - String errMsg = String.format( - "One guard argument was expected (got %d)", cont.count()); - - errs.add(new ReplError(lno, pno, errMsg, nam)); - } else { - String pat = cont.get(0); - - try { - Pattern.compile(pat); - } catch (PatternSyntaxException psex) { - String errMsg = String.format( - "Guard argument '%s' is not a valid regex (%s)", pat, - psex.getMessage()); - - errs.add(new ReplError(lno, pno, errMsg, nam)); - } - - rp.guard = cont.get(0); - } - break; - case "PRIORITY": - case "PRIOR": - case "P": - try { - if (cont.count() != 1) { - String errMsg = String.format( - "One priority argument was expected (got %d", - cont.count()); - - errs.add(new ReplError(lno, pno, errMsg, nam)); - } else { - rp.priority = Integer.parseInt(cont.get(0)); - } - } catch (NumberFormatException nfex) { - String errMsg = String.format( - "'%s' is not a valid priority (must be an integer)", - cont.get(0)); - - errs.add(new ReplError(lno, pno, errMsg, nam)); - } - break; - case "STAGE": - case "S": - try { - if (cont.count() != 1) { - String errMsg = String.format( - "One stage argument was expected (got %d", - cont.count()); - - errs.add(new ReplError(lno, pno, errMsg, nam)); - } else { - int tmpStage = Integer.parseInt(cont.get(0)); - if (tmpStage < 0) { - String errMsg = String.format( - "'%s' is not a valid stage (must be a positive integer)", - cont.get(0)); - errs.add(new ReplError(lno, pno, errMsg, nam)); - - break; - } - rp.stage = tmpStage; - } - } catch (NumberFormatException nfex) { - String errMsg = String.format( - "'%s' is not a valid stage (must be a positive integer)", - cont.get(0)); - - errs.add(new ReplError(lno, pno, errMsg, nam)); - } - break; - case "MULTITRUE": - case "MULTIT": - case "MT": - isMulti = true; - break; - case "MULTIFALSE": - case "MULTIF": - case "MF": - isMulti = false; - break; - case "MULTI": - case "M": - if (cont.count() != 1) { - String errMsg = String.format( - "One multi-flag argument was expected (got %d", - cont.count()); - - errs.add(new ReplError(lno, pno, errMsg, nam)); - } else { - isMulti = Boolean.parseBoolean(cont.get(0)); - } - break; - case "INTERNAL": - case "INT": - case "I": - rp.stat = StageStatus.INTERNAL; - break; - case "EXTERNAL": - case "EXT": - case "E": - rp.stat = StageStatus.EXTERNAL; - break; - case "BOTH": - case "B": - rp.stat = StageStatus.BOTH; - break; - default: { - String errMsg = String.format( - "Unknown control name '%s' for name '%s'", cont.name, nam); - - ReplError erd = new ReplError(lno, pno, errMsg, nam); - - errs.add(erd); - } - break; - } - } - - name = cs.strang; - } - - // Multi-line name with a trailer - if (isMulti) { - String tmp = readMultiLine(name, scn, ropts, "name", lno); - if (tmp == null) - return null; - name = tmp; - } - - return name; - } - - private static void readGlobal(String nam, List<ReplError> errs, - ReplOpts ropts, IntHolder lno, IntHolder pno) { - ControlledString cs - = getControls(nam.substring(1), errs, lno, pno, "global"); - - for (Control cont : cs.controls) { - switch (cont.name) { - case "PRIORITY": - case "PRIOR": - case "P": - try { - if (cont.count() != 1) { - String errMsg = String.format( - "Must specify 1 priority (%d specified)", cont.count()); - - errs.add(new ReplError(lno, pno, errMsg, nam)); - } else { - int tmp = Integer.parseInt(cont.get(0)); - ropts.defPrior = tmp; - } - } catch (NumberFormatException nfex) { - String errMsg = String.format( - "'%s' is not a valid priority (must be an integer)", - cont.get(0)); - - errs.add(new ReplError(lno, pno, errMsg, nam)); - } - break; - case "STAGE": - case "S": - try { - if (cont.count() != 1) { - String errMsg = String.format( - "Must specify 1 stage (%d specified)", cont.count()); - - errs.add(new ReplError(lno, pno, errMsg, nam)); - } else { - int tmpStage = Integer.parseInt(cont.get(0)); - - if (tmpStage < 0) { - String errMsg = String.format( - "'%s' is not a valid stage (must be a positive integer)", - cont.get(0)); - - errs.add(new ReplError(lno, pno, errMsg, nam)); - break; - } - - ropts.defStage = tmpStage; - } - } catch (NumberFormatException nfex) { - String errMsg = String.format( - "'%s' is not a valid stage (must be a positive integer)", - cont.get(0)); - - errs.add(new ReplError(lno, pno, errMsg, nam)); - } - break; - case "MULTITRUE": - case "MULTIT": - case "MT": - ropts.defMulti = true; - break; - case "MULTIFALSE": - case "MULTIF": - case "MF": - ropts.defMulti = false; - break; - case "MULTI": - case "M": - if (cont.count() != 1) { - String errMsg = String.format( - "Must specify 1 multi-flag (%d specified)", cont.count()); - - errs.add(new ReplError(lno, pno, errMsg, nam)); - } else { - ropts.defMulti = Boolean.parseBoolean(cont.get(0)); - } - break; - case "INTERNAL": - case "INT": - case "I": - ropts.defStatus = StageStatus.INTERNAL; - break; - case "EXTERNAL": - case "EXT": - case "E": - ropts.defStatus = StageStatus.EXTERNAL; - break; - case "BOTH": - case "B": - ropts.defStatus = StageStatus.BOTH; - break; - case "DEBUGTRUE": - case "DEBUGT": - case "DT": - ropts.isDebug = true; - break; - case "DEBUGFALSE": - case "DEBUGF": - case "DF": - ropts.isDebug = false; - break; - case "DEBUG": - case "D": - if (cont.count() != 1) { - String errMsg = String.format( - "Must specify 1 debug flag (%d specified)", cont.count()); - - errs.add(new ReplError(lno, pno, errMsg, nam)); - } else { - ropts.isDebug = Boolean.parseBoolean(cont.get(0)); - } - break; - case "TRACETRUE": - case "TRACET": - case "TT": - ropts.isTrace = true; - break; - case "TRACEFALSE": - case "TRACEF": - case "TF": - ropts.isTrace = false; - break; - case "TRACE": - case "T": - if (cont.count() != 1) { - String errMsg = String.format( - "Must specify 1 trace flag (%d specified)", cont.count()); - - errs.add(new ReplError(lno, pno, errMsg, nam)); - } else { - ropts.isTrace = Boolean.parseBoolean(cont.get(0)); - } - break; - case "PERFTRUE": - case "PERFT": - case "PRFT": - ropts.isPerf = true; - break; - case "PERFFALSE": - case "PERFF": - case "PRFF": - ropts.isPerf = false; - break; - case "PERF": - case "PRF": - if (cont.count() != 1) { - String errMsg = String.format( - "Must specify 1 perf. flag (%d specified)", cont.count()); - - errs.add(new ReplError(lno, pno, errMsg, nam)); - } else { - ropts.isPerf = Boolean.parseBoolean(cont.get(0)); - } - break; - default: { - String msg = String.format("Invalid global control name '%s'", cont.name); - ReplError err = new ReplError(lno, pno, msg, nam); - errs.add(err); - } - break; - } - - if (ropts.isTrace) - ropts.errStream.printf("\t[TRACE] Processed global control '%s'\n", cont); - } - - return; - } - - private static ControlledString getControls(String lne, List<ReplError> errs, - IntHolder lno, IntHolder pno, String type) { - try { - return ControlledString.parse(lne, new ParseStrings("//", ";", "/", "|")); - } catch (IllegalArgumentException iaex) { - String msg = "Did not find control terminator (//) in %s where it should be"; - msg = String.format(msg, type); - - ReplError re = new ReplError(lno, pno, msg, lne); - errs.add(re); - - return null; - } - } } diff --git a/src/main/java/bjc/everge/ReplError.java b/src/main/java/bjc/everge/ReplPairError.java index f9a30a9..64edbd6 100644 --- a/src/main/java/bjc/everge/ReplError.java +++ b/src/main/java/bjc/everge/ReplPairError.java @@ -7,72 +7,72 @@ import bjc.data.IntHolder; * * @author Ben Culkin */ -public class ReplError { +public class ReplPairError { /** * The line the error occurred on. */ - public int line; + public int lineNumber; /** * The number of pairs we have processed so far. */ - public int numPairs; + public int numPairsSoFar; /** * The text of the line we errored on. */ - public String txt; + public String lineText; /** * The message of the error. */ - public String msg; + public String errorMessage; /** * Create a new ReplPair parse error. * - * @param lne + * @param lineNo * The line the error occured on. - * @param nPairs + * @param numPairsSoFar * The number of pairs processed up to this point. - * @param msg + * @param errorMessage * The message detailing the error. - * @param txt + * @param lineText * The text that caused the error. */ - public ReplError(IntHolder lne, IntHolder nPairs, String msg, String txt) { - this(lne.get(), nPairs.get(), msg, txt); + public ReplPairError(IntHolder lineNo, IntHolder numPairsSoFar, + String errorMessage, String lineText) { + this(lineNo.get(), numPairsSoFar.get(), errorMessage, lineText); } /** * Create a new ReplPair parse error. * - * @param lne + * @param lineNo * The line the error occured on. - * @param nPairs + * @param numPairsSoFar * The number of pairs processed up to this point. - * @param msg + * @param errorMessage * The message detailing the error. - * @param txt + * @param lineText * The text that caused the error. */ - public ReplError(int lne, int nPairs, String msg, String txt) { - line = lne; - numPairs = nPairs; + public ReplPairError(int lineNo, int numPairsSoFar, String errorMessage, + String lineText) { + this.lineNumber = lineNo; + this.numPairsSoFar = numPairsSoFar; - this.txt = txt; - this.msg = msg; + this.lineText = lineText; + this.errorMessage = errorMessage; } @Override public String toString() { String errString; - if (txt == null) - errString = "No associated line"; - else if (txt.equals("")) - errString = "Text of line was empty"; - else - errString = "Text of line was: " + txt; + if (lineText == null) errString = "No associated line"; + else if (lineText.equals("")) errString = "Text of line was empty"; + else errString = "Text of line was: " + lineText; - return String.format("line %d, pair %d:%s\n\t%s", line, numPairs, msg, errString); + return String.format("line %d, pair %d:%s\n\t%s", + lineNumber, numPairsSoFar, errorMessage, errString); } /** @@ -94,14 +94,12 @@ public class ReplError { */ public String toPrintString(String hdr) { String errString; - if (txt == null) - errString = "No associated line"; - else if (txt.equals("")) - errString = "Text of line was empty"; - else - errString = "Text of line was: " + txt; + + if (lineText == null) errString = "No associated line"; + else if (lineText.equals("")) errString = "Text of line was empty"; + else errString = "Text of line was: " + lineText; - return String.format("[ERROR] line %d, pair %d: %s\n%s\tContext: %s", line, - numPairs, msg, hdr, errString); + return String.format("[ERROR] line %d, pair %d: %s\n%s\tContext: %s", + lineNumber, numPairsSoFar, errorMessage, hdr, errString); } } diff --git a/src/main/java/bjc/everge/ReplOpts.java b/src/main/java/bjc/everge/ReplPairOptions.java index debb26e..2c4062f 100644 --- a/src/main/java/bjc/everge/ReplOpts.java +++ b/src/main/java/bjc/everge/ReplPairOptions.java @@ -7,26 +7,22 @@ import java.io.PrintStream; * * @author Ben Culkin. */ -public class ReplOpts { - /** - * The default priority. - */ - public int defPrior = 0; +public class ReplPairOptions { + /** The default priority. */ + public int defaultPriority = 0; - /** - * The default stage. - */ - public int defStage = 0; + /** The default stage. */ + public int defaultStage = 0; /** * Whether to process multi-line defns. */ - public boolean defMulti = false; + public boolean defaultMulti = false; /** * Default status. */ - public StageStatus defStatus = StageStatus.BOTH; + public StageStatus defaultStatus = StageStatus.BOTH; /** * Enable debug info. @@ -47,6 +43,7 @@ public class ReplOpts { * The stream to print normal output on. */ public PrintStream outStream = System.out; + /** * The stream to print error output on. */ @@ -56,10 +53,10 @@ public class ReplOpts { public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + (defMulti ? 1231 : 1237); - result = prime * result + defPrior; - result = prime * result + defStage; - result = prime * result + ((defStatus == null) ? 0 : defStatus.hashCode()); + result = prime * result + (defaultMulti ? 1231 : 1237); + result = prime * result + defaultPriority; + result = prime * result + defaultStage; + result = prime * result + ((defaultStatus == null) ? 0 : defaultStatus.hashCode()); result = prime * result + (isDebug ? 1231 : 1237); result = prime * result + (isPerf ? 1231 : 1237); result = prime * result + (isTrace ? 1231 : 1237); @@ -74,14 +71,14 @@ public class ReplOpts { return false; if (getClass() != obj.getClass()) return false; - ReplOpts other = (ReplOpts) obj; - if (defMulti != other.defMulti) + ReplPairOptions other = (ReplPairOptions) obj; + if (defaultMulti != other.defaultMulti) return false; - if (defPrior != other.defPrior) + if (defaultPriority != other.defaultPriority) return false; - if (defStage != other.defStage) + if (defaultStage != other.defaultStage) return false; - if (defStatus != other.defStatus) + if (defaultStatus != other.defaultStatus) return false; if (isDebug != other.isDebug) return false; diff --git a/src/main/java/bjc/everge/ReplPairParser.java b/src/main/java/bjc/everge/ReplPairParser.java new file mode 100644 index 0000000..f456488 --- /dev/null +++ b/src/main/java/bjc/everge/ReplPairParser.java @@ -0,0 +1,704 @@ +package bjc.everge; + +import java.util.*; +import java.util.regex.*; + +import bjc.data.*; +import bjc.everge.ControlledString.*; + +/** + * Parses instances of ReplPair. + * + * @author Ben Culkin + * + */ +public class ReplPairParser { + /** + * Read a list of replacement pairs from an input source. + * + * @param scn + * The source to read the replacements from. + * @return The list of replacements. + */ + public List<ReplPair> readList(Scanner scn) { + List<ReplPair> lst = new ArrayList<>(); + + return readList(lst, scn); + } + + /** + * Read a list of replacement pairs from an input source, adding them to an + * existing list. + * + * @param detals + * The list to add the replacements to. + * @param scn + * The source to read the replacements from. + * @return The list of replacements. + */ + public List<ReplPair> readList(List<ReplPair> detals, Scanner scn) { + List<ReplPairError> errList = new ArrayList<>(); + + List<ReplPair> rplPar = readList(detals, scn, errList); + + if (errList.size() != 0) { + throw new BadReplParse("", errList); + } + + return rplPar; + } + + /** + * Read a list of replacement pairs from an input source, adding them to an + * existing list. + * + * @param detals + * The list to add the replacements to. + * @param scn + * The source to read the replacements from. + * @param errs + * The list to stick errors in. + * @return The list of replacements. + */ + public List<ReplPair> readList(List<ReplPair> detals, Scanner scn, + List<ReplPairError> errs) { + return readList(detals, scn, errs, new ReplPairOptions()); + } + + /** + * Read a list of replacement pairs from an input source, adding them to an + * existing list. + * + * @param detals + * The list to add the replacements to. + * @param scn + * The source to read the replacements from. + * @param errs + * The list to stick errors in. + * @param ropts + * The options to use when reading the pairs. + * @return The list of replacements. + */ + public List<ReplPair> readList(List<ReplPair> detals, Scanner scn, + List<ReplPairError> errs, ReplPairOptions ropts) { + IntHolder lno = new IntHolder(); + IntHolder pno = new IntHolder(); + + List<List<ReplPair>> stages = new ArrayList<>(); + stages.add(new ArrayList<ReplPair>()); + + // For every line in the source... + while (scn.hasNextLine()) { + String name = scn.nextLine().trim(); + lno.incr(); + + // If its commented or blank, skip it + if (name.equals("")) + continue; + if (name.startsWith("#")) + continue; + + // Global control. Process it. + if (name.startsWith("|//")) { + readGlobal(name, errs, ropts, lno, pno); + + continue; + } + + ReplPair rp = new ReplPair(); + + rp.priority = ropts.defaultPriority; + rp.stat = ropts.defaultStatus; + rp.lineNumber = lno.get(); + rp.stage = ropts.defaultStage; + + boolean isMulti = ropts.defaultMulti; + + { + String tmpName = readName(name, scn, errs, rp, ropts, lno, pno); + if (tmpName == null) + continue; + name = tmpName; + } + + rp.find = name; + if (rp.name == null) + rp.name = name; + + // We started to process the pair, mark it as being + // started + pno.incr(); + String body = null; + + // Read in the next uncommented line + do { + if (!scn.hasNextLine()) + break; + + body = scn.nextLine().trim(); + lno.incr(); + } while (body.startsWith("#")); + + if (body == null) { + String msg = String.format( + "Ran out of input looking for replacement body for raw name '%s'", + name); + + errs.add(new ReplPairError(lno, pno, msg, null)); + break; + } + + isMulti = ropts.defaultMulti; + + ControlledString cs = getControls(body, errs, lno, pno, "body"); + // Body has attached controls, process them. + if (cs.hasControls()) { + for (Control cont : cs.controls) { + switch (cont.name) { + case "MULTITRUE": + case "MULTIT": + case "MT": + isMulti = true; + break; + case "MULTIFALSE": + case "MULTIF": + case "MF": + isMulti = false; + break; + case "MULTI": + case "M": + if (cont.count() != 1) { + String errMsg = String.format( + "Expected one multi flag (got %d)", cont.count()); + errs.add(new ReplPairError(lno, pno, errMsg, body)); + } else { + isMulti = Boolean.parseBoolean(cont.get(0)); + } + break; + default: { + String errMsg + = String.format("Invalid control name '%s'", cont.name); + errs.add(new ReplPairError(lno, pno, errMsg, body)); + } + break; + } + } + + body = cs.body; + } + + if (isMulti) { + String tmp = readMultiLine(body, scn, ropts, "body", lno); + if (tmp == null) + continue; + body = tmp; + } + + rp.replace = body; + + List<ReplPair> stageList = null; + if (rp.stage == 0 || stages.size() < (rp.stage - 1)) { + stageList = stages.get(rp.stage); + + if (stageList == null) { + stageList = new ArrayList<>(); + + stages.add(rp.stage, stageList); + } + } else { + for (int i = stages.size(); i <= rp.stage; i++) { + stages.add(new ArrayList<>()); + } + + stageList = stages.get(rp.stage); + } + + if (ropts.isTrace) { + ropts.errStream.printf("\t[DEBUG] Stage %d: Added %s\n\t\tContents: %s\n", + rp.stage, rp, stageList); + } + + stageList.add(rp); + } + + // Special-case one-stage processing. + if (stages.size() == 1) { + if (ropts.isTrace) + ropts.errStream.printf("\t[DEBUG] Executing single-stage bypass\n"); + + for (ReplPair rp : stages.iterator().next()) { + if (rp.stat == StageStatus.INTERNAL) { + if (ropts.isTrace) + ropts.errStream.printf("\t[DEBUG] Excluding internal RP %s\n", + rp); + + continue; + } + + detals.add(rp); + } + + detals.sort(null); + + return detals; + } + + // Handle stages + List<ReplPair> tmpList = new ArrayList<>(); + tmpList.addAll(detals); + + if (ropts.isTrace) + ropts.errStream.printf("\t[DEBUG] Stages: %s\n", stages); + + int procStg = 0; + for (List<ReplPair> stageList : stages) { + procStg += 1; + List<ReplPair> curStage = new ArrayList<>(); + + if (ropts.isTrace) + ropts.errStream.printf("\t[DEBUG] Staging stage %d of %d: %s\n", procStg, + stageList.size(), stageList); + + for (ReplPair rp : stageList) { + // Process through every pair in the previous + // stages + for (ReplPair curPar : tmpList) { + String tmp = rp.replace.replaceAll(curPar.find, curPar.replace); + + if (ropts.isTrace && !rp.replace.equals(tmp)) { + ropts.errStream.printf("\t[DEBUG] Staged '%s' -> '%s'\t%s\n", + rp.replace, tmp, curPar); + } + + rp.replace = tmp; + } + + // If we're external; add straight to the output + if (rp.stat == StageStatus.EXTERNAL) { + if (ropts.isTrace) { + ropts.errStream.printf( + "\t[DEBUG] Skipped external for staging: %s\n", rp); + } + + detals.add(rp); + } else { + if (ropts.isTrace) { + ropts.errStream.printf( + "\t[DEBUG] Added to stage %d: %s\n\t\tContents: %s\n", + procStg, rp, curStage); + } + + curStage.add(rp); + } + } + + tmpList.addAll(curStage); + tmpList.sort(null); + } + + // Copy over to output, excluding internals + for (ReplPair rp : tmpList) { + if (rp.stat == StageStatus.INTERNAL) { + if (ropts.isTrace) + ropts.errStream.printf("\t[DEBUG] Excluded internal: %s\n", rp); + + continue; + } + + detals.add(rp); + } + + detals.sort(null); + + if (ropts.isTrace) { + ropts.errStream.printf("\t[DEBUG] Final output: %s\n", detals); + } + + return detals; + } + + private String readMultiLine(String lead, Scanner src, ReplPairOptions ropts, + String typ, IntHolder lno) { + String tmp = lead; + + if (ropts.isTrace && tmp.endsWith("\\")) + ropts.errStream.printf("\t[TRACE] Starting multi-line parse for %s '%s'\n", + typ, tmp); + + boolean didMulti = tmp.endsWith("\\"); + while (tmp.endsWith("\\")) { + boolean incNL = tmp.endsWith("|\\"); + + if (!src.hasNextLine()) + break; + + String nxt = src.nextLine().trim(); + lno.incr(); + + if (nxt.startsWith("#")) + continue; + + String nlStr = incNL ? "\n" : ""; + + if (tmp.endsWith("\\")) { + if (incNL) { + tmp = tmp.substring(0, tmp.length() - 2); + } else { + tmp = tmp.substring(0, tmp.length() - 1); + } + } + + tmp = String.format("%s%s%s", tmp, nlStr, nxt); + } + + if (ropts.isTrace && didMulti) + ropts.errStream.printf("\t[TRACE] Finished multi-line parse for %s:\n%s\n.\n", + typ, tmp); + + return tmp; + } + + private String readName(String nam, Scanner scn, List<ReplPairError> errs, + ReplPair rp, ReplPairOptions ropts, IntHolder lno, IntHolder pno) { + ControlledString cs = getControls(nam, errs, lno, pno, "name"); + + boolean isMulti = ropts.defaultMulti; + + String name = cs.body; + + if (cs.hasControls()) { + for (Control cont : cs.controls) { + switch (cont.name) { + case "NAME": + case "N": + if (cont.count() != 1) { + String errMsg = String.format( + "One name argument was expected (got %d)", cont.count()); + + errs.add(new ReplPairError(lno, pno, errMsg, nam)); + } else { + rp.name = cont.get(0); + } + break; + case "GUARD": + case "G": + if (cont.count() != 1) { + String errMsg = String.format( + "One guard argument was expected (got %d)", cont.count()); + + errs.add(new ReplPairError(lno, pno, errMsg, nam)); + } else { + String pat = cont.get(0); + + try { + Pattern.compile(pat); + } catch (PatternSyntaxException psex) { + String errMsg = String.format( + "Guard argument '%s' is not a valid regex (%s)", pat, + psex.getMessage()); + + errs.add(new ReplPairError(lno, pno, errMsg, nam)); + } + + rp.guard = cont.get(0); + } + break; + case "PRIORITY": + case "PRIOR": + case "P": + try { + if (cont.count() != 1) { + String errMsg = String.format( + "One priority argument was expected (got %d", + cont.count()); + + errs.add(new ReplPairError(lno, pno, errMsg, nam)); + } else { + rp.priority = Integer.parseInt(cont.get(0)); + } + } catch (NumberFormatException nfex) { + String errMsg = String.format( + "'%s' is not a valid priority (must be an integer)", + cont.get(0)); + + errs.add(new ReplPairError(lno, pno, errMsg, nam)); + } + break; + case "STAGE": + case "S": + try { + if (cont.count() != 1) { + String errMsg = String.format( + "One stage argument was expected (got %d", + cont.count()); + + errs.add(new ReplPairError(lno, pno, errMsg, nam)); + } else { + int tmpStage = Integer.parseInt(cont.get(0)); + if (tmpStage < 0) { + String errMsg = String.format( + "'%s' is not a valid stage (must be a positive integer)", + cont.get(0)); + errs.add(new ReplPairError(lno, pno, errMsg, nam)); + + break; + } + rp.stage = tmpStage; + } + } catch (NumberFormatException nfex) { + String errMsg = String.format( + "'%s' is not a valid stage (must be a positive integer)", + cont.get(0)); + + errs.add(new ReplPairError(lno, pno, errMsg, nam)); + } + break; + case "MULTITRUE": + case "MULTIT": + case "MT": + isMulti = true; + break; + case "MULTIFALSE": + case "MULTIF": + case "MF": + isMulti = false; + break; + case "MULTI": + case "M": + if (cont.count() != 1) { + String errMsg = String.format( + "One multi-flag argument was expected (got %d", + cont.count()); + + errs.add(new ReplPairError(lno, pno, errMsg, nam)); + } else { + isMulti = Boolean.parseBoolean(cont.get(0)); + } + break; + case "INTERNAL": + case "INT": + case "I": + rp.stat = StageStatus.INTERNAL; + break; + case "EXTERNAL": + case "EXT": + case "E": + rp.stat = StageStatus.EXTERNAL; + break; + case "BOTH": + case "B": + rp.stat = StageStatus.BOTH; + break; + default: { + String errMsg = String.format( + "Unknown control name '%s' for name '%s'", cont.name, nam); + + ReplPairError erd = new ReplPairError(lno, pno, errMsg, nam); + + errs.add(erd); + } + break; + } + } + + name = cs.body; + } + + // Multi-line name with a trailer + if (isMulti) { + String tmp = readMultiLine(name, scn, ropts, "name", lno); + if (tmp == null) + return null; + name = tmp; + } + + return name; + } + + private void readGlobal(String nam, List<ReplPairError> errs, + ReplPairOptions ropts, IntHolder lno, IntHolder pno) { + ControlledString cs + = getControls(nam.substring(1), errs, lno, pno, "global"); + + for (Control cont : cs.controls) { + switch (cont.name) { + case "PRIORITY": + case "PRIOR": + case "P": + try { + if (cont.count() != 1) { + String errMsg = String.format( + "Must specify 1 priority (%d specified)", cont.count()); + + errs.add(new ReplPairError(lno, pno, errMsg, nam)); + } else { + int tmp = Integer.parseInt(cont.get(0)); + ropts.defaultPriority = tmp; + } + } catch (NumberFormatException nfex) { + String errMsg = String.format( + "'%s' is not a valid priority (must be an integer)", + cont.get(0)); + + errs.add(new ReplPairError(lno, pno, errMsg, nam)); + } + break; + case "STAGE": + case "S": + try { + if (cont.count() != 1) { + String errMsg = String.format( + "Must specify 1 stage (%d specified)", cont.count()); + + errs.add(new ReplPairError(lno, pno, errMsg, nam)); + } else { + int tmpStage = Integer.parseInt(cont.get(0)); + + if (tmpStage < 0) { + String errMsg = String.format( + "'%s' is not a valid stage (must be a positive integer)", + cont.get(0)); + + errs.add(new ReplPairError(lno, pno, errMsg, nam)); + break; + } + + ropts.defaultStage = tmpStage; + } + } catch (NumberFormatException nfex) { + String errMsg = String.format( + "'%s' is not a valid stage (must be a positive integer)", + cont.get(0)); + + errs.add(new ReplPairError(lno, pno, errMsg, nam)); + } + break; + case "MULTITRUE": + case "MULTIT": + case "MT": + ropts.defaultMulti = true; + break; + case "MULTIFALSE": + case "MULTIF": + case "MF": + ropts.defaultMulti = false; + break; + case "MULTI": + case "M": + if (cont.count() != 1) { + String errMsg = String.format( + "Must specify 1 multi-flag (%d specified)", cont.count()); + + errs.add(new ReplPairError(lno, pno, errMsg, nam)); + } else { + ropts.defaultMulti = Boolean.parseBoolean(cont.get(0)); + } + break; + case "INTERNAL": + case "INT": + case "I": + ropts.defaultStatus = StageStatus.INTERNAL; + break; + case "EXTERNAL": + case "EXT": + case "E": + ropts.defaultStatus = StageStatus.EXTERNAL; + break; + case "BOTH": + case "B": + ropts.defaultStatus = StageStatus.BOTH; + break; + case "DEBUGTRUE": + case "DEBUGT": + case "DT": + ropts.isDebug = true; + break; + case "DEBUGFALSE": + case "DEBUGF": + case "DF": + ropts.isDebug = false; + break; + case "DEBUG": + case "D": + if (cont.count() != 1) { + String errMsg = String.format( + "Must specify 1 debug flag (%d specified)", cont.count()); + + errs.add(new ReplPairError(lno, pno, errMsg, nam)); + } else { + ropts.isDebug = Boolean.parseBoolean(cont.get(0)); + } + break; + case "TRACETRUE": + case "TRACET": + case "TT": + ropts.isTrace = true; + break; + case "TRACEFALSE": + case "TRACEF": + case "TF": + ropts.isTrace = false; + break; + case "TRACE": + case "T": + if (cont.count() != 1) { + String errMsg = String.format( + "Must specify 1 trace flag (%d specified)", cont.count()); + + errs.add(new ReplPairError(lno, pno, errMsg, nam)); + } else { + ropts.isTrace = Boolean.parseBoolean(cont.get(0)); + } + break; + case "PERFTRUE": + case "PERFT": + case "PRFT": + ropts.isPerf = true; + break; + case "PERFFALSE": + case "PERFF": + case "PRFF": + ropts.isPerf = false; + break; + case "PERF": + case "PRF": + if (cont.count() != 1) { + String errMsg = String.format( + "Must specify 1 perf. flag (%d specified)", cont.count()); + + errs.add(new ReplPairError(lno, pno, errMsg, nam)); + } else { + ropts.isPerf = Boolean.parseBoolean(cont.get(0)); + } + break; + default: { + String msg = String.format("Invalid global control name '%s'", cont.name); + ReplPairError err = new ReplPairError(lno, pno, msg, nam); + errs.add(err); + } + break; + } + + if (ropts.isTrace) + ropts.errStream.printf("\t[TRACE] Processed global control '%s'\n", cont); + } + + return; + } + + private ControlledString getControls(String lne, List<ReplPairError> errs, + IntHolder lno, IntHolder pno, String type) { + try { + return ControlledString.parse(lne, new ControlledStringParseOptions("//", ";", "/", "|")); + } catch (IllegalArgumentException iaex) { + String msg = "Did not find control terminator (//) in %s where it should be"; + msg = String.format(msg, type); + + ReplPairError re = new ReplPairError(lno, pno, msg, lne); + errs.add(re); + + return null; + } + } + +} diff --git a/src/main/java/bjc/everge/ReplSet.java b/src/main/java/bjc/everge/ReplPairSet.java index 0daa725..3d173c7 100644 --- a/src/main/java/bjc/everge/ReplSet.java +++ b/src/main/java/bjc/everge/ReplPairSet.java @@ -9,15 +9,15 @@ import java.util.*; * * @author Ben Culkin */ -public class ReplSet { +public class ReplPairSet { // The list of pairs - private List<ReplPair> parList; + private List<ReplPair> pairList; /** * Create a new blank set of pairs. */ - public ReplSet() { - parList = new ArrayList<>(); + public ReplPairSet() { + pairList = new ArrayList<>(); } /** @@ -26,28 +26,30 @@ public class ReplSet { * Changes to the list of pairs will carry across to the ReplSet, so be careful * about that. * - * @param lst + * @param list * The list of pairs to use. */ - public ReplSet(List<ReplPair> lst) { - parList = lst; + public ReplPairSet(List<ReplPair> list) { + pairList = list; } /** * Load a ReplSet from a file. * - * @param fName + * @param fileName * The file to load the ReplSet from. * @return A ReplSet, loaded from the file. * @throws IOException * if something goes badly reading it. */ - public static ReplSet fromFile(String fName) throws IOException { - ReplSet rs = new ReplSet(); + public static ReplPairSet fromFile(String fileName) throws IOException { + ReplPairSet rs = new ReplPairSet(); - try (FileInputStream fis = new FileInputStream(fName); + try (FileInputStream fis = new FileInputStream(fileName); Scanner scn = new Scanner(fis)) { - rs.parList = ReplPair.readList(scn); + ReplPairParser parser = new ReplPairParser(); + + rs.pairList = parser.readList(scn); } return rs; @@ -56,16 +58,14 @@ public class ReplSet { /** * Adds more pairs to the ReplSet. * - * @param pars + * @param pairs * The pairs to add to the ReplSet. */ - public void addPairs(List<ReplPair> pars) { - for (ReplPair par : pars) { - parList.add(par); - } + public void addPairs(List<ReplPair> pairs) { + for (ReplPair par : pairs) pairList.add(par); // Resort the pairs into priority order - parList.sort(null); + pairList.sort(null); } /** @@ -76,11 +76,11 @@ public class ReplSet { */ public void addPairs(ReplPair... pars) { for (ReplPair par : pars) { - parList.add(par); + pairList.add(par); } // Resort the pairs into priority order - parList.sort(null); + pairList.sort(null); } /** @@ -94,7 +94,7 @@ public class ReplSet { public String apply(String val) { String ret = val; - for (ReplPair par : parList) { + for (ReplPair par : pairList) { System.err.printf("Applying pair '%s' to string '%s' (original was '%s')\n", par, ret, val); String tmp = par.apply(ret); diff --git a/src/test/java/bjc/everge/ReplPairTest.java b/src/test/java/bjc/everge/ReplPairTest.java index f88279b..860a420 100644 --- a/src/test/java/bjc/everge/ReplPairTest.java +++ b/src/test/java/bjc/everge/ReplPairTest.java @@ -26,7 +26,9 @@ public class ReplPairTest { try (FileInputStream fis = new FileInputStream(fName); Scanner scn = new Scanner(fis)) { - lrp = ReplPair.readList(scn); + ReplPairParser parser = new ReplPairParser(); + + lrp = parser.readList(scn); assertTrue(lrp.size() == 0); } catch (IOException ioex) { diff --git a/src/test/java/bjc/everge/TestUtils.java b/src/test/java/bjc/everge/TestUtils.java index f3898c7..80d009c 100644 --- a/src/test/java/bjc/everge/TestUtils.java +++ b/src/test/java/bjc/everge/TestUtils.java @@ -1,7 +1,7 @@ package bjc.everge; import bjc.everge.ControlledString.Control; -import bjc.everge.ControlledString.ParseStrings; +import bjc.everge.ControlledString.ControlledStringParseOptions; import java.io.FileInputStream; @@ -25,7 +25,8 @@ public class TestUtils { public static void assertThrownMessage(boolean logMsg, String msg, String fle) { try (FileInputStream fis = new FileInputStream(fle); Scanner scn = new Scanner(fis)) { - ReplPair.readList(new ArrayList<>(), scn); + ReplPairParser parser = new ReplPairParser(); + parser.readList(new ArrayList<>(), scn); assertTrue(false); } catch (BadReplParse rpex) { @@ -62,7 +63,8 @@ public class TestUtils { try (FileInputStream fis = new FileInputStream(fle); Scanner scn = new Scanner(fis)) { - lrp = ReplPair.readList(scn); + ReplPairParser parser = new ReplPairParser(); + lrp = parser.readList(scn); } catch (BadReplParse rpex) { System.err.println(rpex.toPrintString()); @@ -156,13 +158,13 @@ public class TestUtils { public static void assertIsControl(boolean doLog, String inp, String strang, Control... args) { ControlledString cs - = ControlledString.parse(inp, new ParseStrings("//", ";", "/", "|")); + = ControlledString.parse(inp, new ControlledStringParseOptions("//", ";", "/", "|")); if (doLog) { System.err.printf("[LOG] CS: %s\n", cs); } - assertEquals(strang, cs.strang); + assertEquals(strang, cs.body); assertEquals("array length mismatch:", args.length, cs.count()); |
