diff options
Diffstat (limited to 'BJC-Utils2/src/main/java')
3 files changed, 319 insertions, 34 deletions
diff --git a/BJC-Utils2/src/main/java/bjc/utils/cli/objects/BlockReaderCLI.java b/BJC-Utils2/src/main/java/bjc/utils/cli/objects/BlockReaderCLI.java index ae5cff8..ec66fe2 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/cli/objects/BlockReaderCLI.java +++ b/BJC-Utils2/src/main/java/bjc/utils/cli/objects/BlockReaderCLI.java @@ -15,6 +15,9 @@ import java.util.regex.PatternSyntaxException; import bjc.utils.ioutils.Prompter; import bjc.utils.ioutils.blocks.*; +import static bjc.utils.cli.objects.Command.CommandStatus; +import static bjc.utils.cli.objects.Command.CommandStatus.*; + public class BlockReaderCLI { private final Logger LOGGER = Logger.getLogger(BlockReaderCLI.class.getName()); @@ -75,7 +78,10 @@ public class BlockReaderCLI { Command com = Command.fromString(ln, lno, ioSource); if(com == null) continue; - handleCommand(com, interactive); + CommandStatus stat = handleCommand(com, interactive); + if(stat == FINISH || stat == ERROR) { + return; + } } input.close(); @@ -84,26 +90,20 @@ public class BlockReaderCLI { /* * Handle a command. */ - public void handleCommand(Command com, boolean interactive) { + public CommandStatus handleCommand(Command com, boolean interactive) { switch(com.nameCommand) { case "def-filtered": - defFiltered(com); - break; + return defFiltered(com); case "def-layered": - defLayered(com); - break; + return defLayered(com); case "def-pushback": - defPushback(com); - break; + return defPushback(com); case "def-simple": - defSimple(com); - break; + return defSimple(com); case "def-serial": - defSerial(com); - break; + return defSerial(com); case "def-toggled": - defToggled(com); - break; + return defToggled(com); case "}": case "end": case "exit": @@ -111,14 +111,14 @@ public class BlockReaderCLI { if(interactive) System.out.printf("Exiting reader-conf, %d readers configured in %d commands\n", stat.readers.size(), com.lineNo); - break; + return FINISH; default: LOGGER.severe(com.error("Unknown command '%s'\n", com.nameCommand)); - break; + return FAIL; } } - private void defFiltered(Command com) { + private CommandStatus defFiltered(Command com) { String remn = com.remnCommand; /* @@ -127,6 +127,7 @@ public class BlockReaderCLI { int idx = remn.indexOf(' '); if(idx == -1) { LOGGER.severe(com.error("No name argument for def-filtered.\n")); + return FAIL; } String blockName = remn.substring(0, idx).trim(); remn = remn.substring(idx).trim(); @@ -144,6 +145,7 @@ public class BlockReaderCLI { idx = remn.indexOf(' '); if(idx == -1) { LOGGER.severe(com.error("No reader-name argument for def-filtered.\n")); + return FAIL; } String readerName = remn.substring(0, idx).trim(); remn = remn.substring(idx).trim(); @@ -153,7 +155,7 @@ public class BlockReaderCLI { */ if(!stat.readers.containsKey(readerName)) { LOGGER.severe(com.error("No source named %s\n", readerName)); - return; + return FAIL; } /* @@ -161,6 +163,7 @@ public class BlockReaderCLI { */ if(remn.equals("")) { LOGGER.severe(com.error("No filter argument for def-filtered\n")); + return FAIL; } String filter = remn; @@ -179,38 +182,44 @@ public class BlockReaderCLI { stat.readers.put(blockName, reader); } catch (PatternSyntaxException psex) { LOGGER.severe(com.error("Invalid regular expression '%s' for filter. (%s)\n", filter, psex.getMessage())); + return FAIL; } + + return SUCCESS; } - private void defPushback(Command com) { + private CommandStatus defPushback(Command com) { String[] parts = com.remnCommand.split(" "); if(parts.length != 2) { LOGGER.severe(com.error("Incorrect number of arguments to def-pushback. Requires a block name and a reader name\n")); - return; + return FAIL; } String blockName = parts[0]; if(stat.readers.containsKey(blockName)) { LOGGER.warning(com.warn("Shadowing existing reader %s\n", blockName)); + return FAIL; } String readerName = parts[1]; if(!stat.readers.containsKey(readerName)) { LOGGER.severe(com.error("No reader named %s\n", readerName)); - return; + return FAIL; } BlockReader reader = new PushbackBlockReader(stat.readers.get(readerName)); stat.readers.put(blockName, reader); + + return SUCCESS; } - private void defToggled(Command com) { + private CommandStatus defToggled(Command com) { String[] parts = com.remnCommand.split(" "); if(parts.length != 3) { LOGGER.severe(com.error("Incorrect number of arguments to def-toggled. Requires a block name and two reader names\n")); - return; + return FAIL; } /* @@ -226,24 +235,26 @@ public class BlockReaderCLI { */ if(!stat.readers.containsKey(parts[1])) { LOGGER.severe(com.error("No reader named %s\n", parts[1])); - return; + return FAIL; } if(!stat.readers.containsKey(parts[2])) { LOGGER.severe(com.error("No reader named %s\n", parts[2])); - return; + return FAIL; } BlockReader reader = new ToggledBlockReader(stat.readers.get(parts[1]), stat.readers.get(parts[2])); stat.readers.put(blockName, reader); + + return SUCCESS; } - private void defLayered(Command com) { + private CommandStatus defLayered(Command com) { String[] parts = com.remnCommand.split(" "); if(parts.length != 3) { LOGGER.severe(com.error("Incorrect number of arguments to def-layered. Requires a block name and two reader names\n")); - return; + return FAIL; } /* @@ -259,24 +270,26 @@ public class BlockReaderCLI { */ if(!stat.readers.containsKey(parts[1])) { LOGGER.severe(com.error("No reader named %s\n", parts[1])); - return; + return FAIL; } if(!stat.readers.containsKey(parts[2])) { LOGGER.severe(com.error("No reader named %s\n", parts[2])); - return; + return FAIL; } BlockReader reader = new LayeredBlockReader(stat.readers.get(parts[1]), stat.readers.get(parts[2])); stat.readers.put(blockName, reader); + + return SUCCESS; } - private void defSerial(Command com) { + private CommandStatus defSerial(Command com) { String[] parts = com.remnCommand.split(" "); if(parts.length < 2) { LOGGER.severe(com.error("Not enough arguments to def-serial. Requires at least a block name and at least one reader name\n")); - return; + return FAIL; } /* @@ -302,7 +315,7 @@ public class BlockReaderCLI { */ if(!stat.readers.containsKey(readerName)) { LOGGER.severe(com.error("No reader named %s\n", readerName)); - return; + return FAIL; } readerArr[i] = stat.readers.get(readerName); @@ -311,9 +324,11 @@ public class BlockReaderCLI { BlockReader reader = new SerialBlockReader(readerArr); stat.readers.put(blockName, reader); + + return SUCCESS; } - private void defSimple(Command com) { + private CommandStatus defSimple(Command com) { String remn = com.remnCommand; /* @@ -322,6 +337,7 @@ public class BlockReaderCLI { int idx = remn.indexOf(' '); if(idx == -1) { LOGGER.severe(com.error("No name argument for def-simple.\n")); + return FAIL; } String blockName = remn.substring(0, idx).trim(); remn = remn.substring(idx).trim(); @@ -339,6 +355,7 @@ public class BlockReaderCLI { idx = remn.indexOf(' '); if(idx == -1) { LOGGER.severe(com.error("No source-name argument for def-simple.\n")); + return FAIL; } String sourceName = remn.substring(0, idx).trim(); remn = remn.substring(idx).trim(); @@ -348,7 +365,7 @@ public class BlockReaderCLI { */ if(!stat.sources.containsKey(sourceName)) { LOGGER.severe(com.error("No source named %s\n", sourceName)); - return; + return FAIL; } /* @@ -356,6 +373,7 @@ public class BlockReaderCLI { */ if(remn.equals("")) { LOGGER.severe(com.error("No delimiter argument for def-simple\n")); + return FAIL; } String delim = remn; @@ -366,6 +384,9 @@ public class BlockReaderCLI { stat.readers.put(blockName, reader); } catch (PatternSyntaxException psex) { LOGGER.severe(com.error("Invalid regular expression '%s' for delimiter. (%s)\n", delim, psex.getMessage())); + return FAIL; } + + return SUCCESS; } } diff --git a/BJC-Utils2/src/main/java/bjc/utils/cli/objects/Command.java b/BJC-Utils2/src/main/java/bjc/utils/cli/objects/Command.java index a0d4493..e605a2b 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/cli/objects/Command.java +++ b/BJC-Utils2/src/main/java/bjc/utils/cli/objects/Command.java @@ -1,6 +1,28 @@ package bjc.utils.cli.objects; public class Command { + /** + * Command status values. + */ + public static enum CommandStatus { + /** + * The command succeded. + */ + SUCCESS, + /** + * The command failed non-fatally. + */ + FAIL, + /** + * The command failed fatally. + */ + ERROR, + /** + * The command was the last one. + */ + FINISH, + } + public final int lineNo; public final String fullCommand; diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/StringDescaper.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/StringDescaper.java new file mode 100644 index 0000000..096656a --- /dev/null +++ b/BJC-Utils2/src/main/java/bjc/utils/parserutils/StringDescaper.java @@ -0,0 +1,242 @@ +package bjc.utils.parserutils; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.UnaryOperator; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +import static java.util.Map.Entry; + +import static bjc.utils.PropertyDB.applyFormat; +import static bjc.utils.PropertyDB.getCompiledRegex; +import static bjc.utils.PropertyDB.getRegex; + +public class StringDescaper { + private Logger LOGGER = Logger.getLogger(StringDescaper.class.getName()); + + /* + * Patterns and pattern parts. + */ + private static String rPossibleEscapeString = getRegex("possibleStringEscape"); + private static Pattern possibleEscapePatt = Pattern.compile(rPossibleEscapeString); + + private static String rShortEscape = getRegex("shortFormStringEscape"); + private static String rOctalEscape = getRegex("octalStringEscape"); + private static String rUnicodeEscape = getRegex("unicodeStringEscape"); + + private String rEscapeString; + private Pattern escapePatt; + + private static String rDoubleQuoteString = applyFormat("doubleQuotes", getRegex("nonStringEscape"), rPossibleEscapeString); + private static Pattern doubleQuotePatt = Pattern.compile(rDoubleQuoteString); + + private static Pattern quotePatt = getCompiledRegex("unescapedQuote"); + + private Map<String, String> literalEscapes; + private Map<Pattern, UnaryOperator<String>> specialEscapes; + + public StringDescaper() { + literalEscapes = new HashMap<>(); + specialEscapes = new HashMap<>(); + + rEscapeString = String.format("\\\\(%1$s|%2$s|%3$s)"); + escapePatt = Pattern.compile(rEscapeString); + } + + public void addLiteralEscape(String escape, String val) { + if(literalEscapes.containsKey(escape)) { + LOGGER.warning(String.format("Shadowing literal escape '%s'\n", escape)); + } + + literalEscapes.put(escape, val); + } + + public void addSpecialEscape(String escape, UnaryOperator<String> val) { + if(specialEscapes.containsKey(escape)) { + LOGGER.warning(String.format("Shadowing special escape '%s'\n", escape)); + } + + /* + * Make sure this special escape is a valid regex. + */ + + Pattern patt = null; + try { + patt = Pattern.compile(escape); + } catch (PatternSyntaxException psex) { + String msg = String.format("Invalid special escape '%s'", escape); + + IllegalArgumentException iaex = new IllegalArgumentException(msg); + iaex.initCause(psex); + + throw psex; + } + + specialEscapes.put(patt, val); + } + + public void compileEscapes() { + StringBuilder work = new StringBuilder(); + + for(String litEscape : literalEscapes.keySet()) { + work.append("|(?:"); + work.append(Pattern.quote(litEscape)); + work.append(")"); + } + + for(Pattern specEscape : specialEscapes.keySet()) { + work.append("|(?:"); + work.append(specEscape.toString()); + work.append(")"); + } + + /* + * Convert user-defined escapes to a regex for matching. + * We don't need a bar before %4 because the string has it. + */ + rEscapeString = String.format("\\(%1$s|%2$s|%3$s%4$s)", rShortEscape, rOctalEscape, rUnicodeEscape, work.toString()); + escapePatt = Pattern.compile(rEscapeString); + } + + /** + * Replace escape characters with their actual equivalents. + * + * @param inp + * The string to replace escape sequences in. + * + * @return The string with escape sequences replaced by their equivalent + * characters. + */ + public String descapeString(final String inp) { + if (inp == null) { + throw new NullPointerException("Input to descapeString 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); + + while (possibleEscapeFinder.find()) { + if (!escapeFinder.find()) { + /* + * Found a possible escape that isn't actually an + * escape. + */ + final String msg = String.format("Illegal escape sequence '%s' at position %d of string '%s'", + possibleEscapeFinder.group(), possibleEscapeFinder.start(), inp); + throw new IllegalArgumentException(msg); + } + + final String escapeSeq = escapeFinder.group(); + + /* + * Convert the escape to a string. + */ + String escapeRep = ""; + switch (escapeSeq) { + case "\\b": + escapeRep = "\b"; + break; + case "\\t": + escapeRep = "\t"; + break; + case "\\n": + escapeRep = "\n"; + break; + case "\\f": + escapeRep = "\f"; + break; + case "\\r": + escapeRep = "\r"; + break; + case "\\\"": + escapeRep = "\""; + break; + case "\\'": + escapeRep = "'"; + break; + case "\\\\": + /* + * Skip past the second slash. + */ + possibleEscapeFinder.find(); + escapeRep = "\\"; + break; + default: + if (escapeSeq.startsWith("u")) { + escapeRep = handleUnicodeEscape(escapeSeq.substring(1)); + } else if(escapeSeq.startsWith("O")) { + escapeRep = handleOctalEscape(escapeSeq.substring(1)); + } else if(literalEscapes.containsKey(escapeSeq)) { + escapeRep = literalEscapes.get(escapeSeq); + } else { + for(Entry<Pattern, UnaryOperator<String>> ent : specialEscapes.entrySet()) { + Pattern pat = ent.getKey(); + + Matcher mat = pat.matcher(escapeSeq); + if(mat.matches()) { + escapeRep = ent.getValue().apply(escapeSeq); + break; + } + } + } + } + + escapeFinder.appendReplacement(work, escapeRep); + } + + escapeFinder.appendTail(work); + + return work.toString(); + } + + /* + * Handle a unicode codepoint. + */ + private static String handleUnicodeEscape(final String seq) { + try { + final int codepoint = Integer.parseInt(seq, 16); + + return new String(Character.toChars(codepoint)); + } catch (final IllegalArgumentException iaex) { + final String msg = String.format("'%s' is not a valid Unicode escape sequence'", seq); + + final IllegalArgumentException reiaex = new IllegalArgumentException(msg); + + reiaex.initCause(iaex); + + throw reiaex; + } + } + + /* + * Handle a octal codepoint. + */ + private static String handleOctalEscape(final String seq) { + try { + final int codepoint = Integer.parseInt(seq, 8); + + if (codepoint > 255) { + final String msg = String.format("'%d' is outside the range of octal escapes', codepoint"); + + throw new IllegalArgumentException(msg); + } + + return new String(Character.toChars(codepoint)); + } catch (final IllegalArgumentException iaex) { + final String msg = String.format("'%s' is not a valid octal escape sequence'", seq); + + final IllegalArgumentException reiaex = new IllegalArgumentException(msg); + + reiaex.initCause(iaex); + + throw reiaex; + } + } +} |
