From d766896972c9e9be4a9e0021ec5f4f0665901865 Mon Sep 17 00:00:00 2001 From: "Benjamin J. Culkin" Date: Sat, 9 Sep 2017 21:46:16 -0300 Subject: Update Most of it is documentation changes. The rest is more work on BlockReaders, as well as a simple command language for configuring them. --- .../bjc/utils/ioutils/RuleBasedConfigReader.java | 120 +++++++++++++++------ .../bjc/utils/ioutils/RuleBasedReaderPragmas.java | 48 ++++++--- .../main/java/bjc/utils/ioutils/blocks/Block.java | 7 +- .../java/bjc/utils/ioutils/blocks/BlockReader.java | 26 +++-- .../utils/ioutils/blocks/FilteredBlockReader.java | 97 +++++++++++++++++ .../utils/ioutils/blocks/LayeredBlockReader.java | 17 ++- .../utils/ioutils/blocks/MappedBlockReader.java | 44 ++++++++ .../utils/ioutils/blocks/PushbackBlockReader.java | 8 +- .../utils/ioutils/blocks/SerialBlockReader.java | 12 ++- .../utils/ioutils/blocks/SimpleBlockReader.java | 15 ++- .../utils/ioutils/blocks/TriggeredBlockReader.java | 5 +- 11 files changed, 321 insertions(+), 78 deletions(-) create mode 100644 BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/FilteredBlockReader.java create mode 100644 BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/MappedBlockReader.java (limited to 'BJC-Utils2/src/main/java/bjc/utils/ioutils') diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/RuleBasedConfigReader.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/RuleBasedConfigReader.java index 4216544..7c5205b 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/RuleBasedConfigReader.java +++ b/BJC-Utils2/src/main/java/bjc/utils/ioutils/RuleBasedConfigReader.java @@ -26,21 +26,27 @@ import bjc.utils.funcdata.IMap; * */ public class RuleBasedConfigReader { - // Function to execute when starting a rule - // Takes the tokenizer, and a pair of the read token and application - // state + /* Function to execute when starting a rule. + * Takes the tokenizer, and a pair of the read token and application state + */ private BiConsumer> start; - // Function to use when continuing a rule - // Takes a tokenizer and application state + /* + * Function to use when continuing a rule + * Takes a tokenizer and application state + */ private BiConsumer continueRule; - // Function to use when ending a rule - // Takes an application state + /* + * Function to use when ending a rule + * Takes an application state + */ private Consumer end; - // Map of pragma names to pragma actions - // Pragma actions are functions taking a tokenizer and application state + /* + * Map of pragma names to pragma actions + * Pragma actions are functions taking a tokenizer and application state + */ private final IMap> pragmas; /** @@ -71,8 +77,7 @@ public class RuleBasedConfigReader { * The function to execute when this pragma is read */ public void addPragma(final String name, final BiConsumer action) { - if (name == null) - throw new NullPointerException("Pragma name must not be null"); + if (name == null) throw new NullPointerException("Pragma name must not be null"); else if (action == null) throw new NullPointerException("Pragma action must not be null"); pragmas.put(name, action); @@ -85,23 +90,35 @@ public class RuleBasedConfigReader { else if (continueRule == null) throw new InputMismatchException("Rule continuation not supported for current grammar"); - // Accept the rule + /* + * Accept the rule + */ continueRule.accept(new FunctionalStringTokenizer(line.substring(1), " "), state); } private boolean endRule(final E state, final boolean isRuleOpen) { - // Ignore blank line without an open rule + /* + * Ignore blank line without an open rule + */ if (isRuleOpen == false) - // Do nothing + /* + * Do nothing + */ return false; else { - // Nothing happens on rule end + /* + * Nothing happens on rule end + */ if (end != null) { - // Process the rule ending + /* + * Process the rule ending + */ end.accept(state); } - // Return a closed rule + /* + * Return a closed rule + */ return false; } } @@ -118,37 +135,56 @@ public class RuleBasedConfigReader { public E fromStream(final InputStream input, final E initialState) { if (input == null) throw new NullPointerException("Input stream must not be null"); - // Application state: We're giving this back later + /* + * Application state: We're giving this back later + */ final E state = initialState; - // Prepare our input source + /* + * Prepare our input source + */ try (Scanner source = new Scanner(input)) { source.useDelimiter("\n"); - // This is true when a rule's open + /* + * This is true when a rule's open + */ final IHolder isRuleOpen = new Identity<>(false); - // Do something for every line of the file + /* + * Do something for every line of the file + */ source.forEachRemaining((line) -> { - // Skip comment lines + /* + * Skip comment lines + */ if (line.startsWith("#") || line.startsWith("//")) - // It's a comment + /* + * It's a comment + */ return; else if (line.equals("")) { - // End the rule + /* + * End the rule + */ isRuleOpen.replace(endRule(state, isRuleOpen.getValue())); } else if (line.startsWith("\t")) { - // Continue the rule + /* + * Continue the rule + */ continueRule(state, isRuleOpen.getValue(), line); } else { - // Open a rule + /* + * Open a rule + */ isRuleOpen.replace(startRule(state, isRuleOpen.getValue(), line)); } }); } - // Return the state that the user has created + /* + * Return the state that the user has created + */ return state; - } /** @@ -184,27 +220,41 @@ public class RuleBasedConfigReader { } private boolean startRule(final E state, boolean isRuleOpen, final String line) { - // Create the line tokenizer + /* + * Create the line tokenizer + */ final FunctionalStringTokenizer tokenizer = new FunctionalStringTokenizer(line, " "); - // Get the initial token + /* + * Get the initial token + */ final String nextToken = tokenizer.nextToken(); - // Handle pragmas + /* + * Handle pragmas + */ if (nextToken.equals("pragma")) { - // Get the pragma name + /* + * Get the pragma name + */ final String token = tokenizer.nextToken(); - // Handle pragmas + /* + * Handle pragmas + */ pragmas.getOrDefault(token, (tokenzer, stat) -> { throw new UnknownPragmaException("Unknown pragma " + token); }).accept(tokenizer, state); } else { - // Make sure input is correct + /* + * Make sure input is correct + */ if (isRuleOpen == true) throw new InputMismatchException("Nested rules are currently not supported"); - // Start a rule + /* + * Start a rule + */ start.accept(tokenizer, new Pair<>(nextToken, state)); isRuleOpen = true; diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/RuleBasedReaderPragmas.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/RuleBasedReaderPragmas.java index ccb73a4..e26a7ee 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/RuleBasedReaderPragmas.java +++ b/BJC-Utils2/src/main/java/bjc/utils/ioutils/RuleBasedReaderPragmas.java @@ -28,21 +28,32 @@ public class RuleBasedReaderPragmas { public static BiConsumer buildInteger(final String name, final BiConsumer consumer) { return (tokenizer, state) -> { - // Check our input is correct - if (!tokenizer.hasMoreTokens()) - throw new PragmaFormatException("Pragma " + name + " requires one integer argument"); + /* + * Check our input is correct + */ + if (!tokenizer.hasMoreTokens()) { + String fmt = "Pragma %s requires one integer argument"; - // Read the argument + throw new PragmaFormatException(String.format(fmt, name)); + } + + /* + * Read the argument + */ final String token = tokenizer.nextToken(); try { - // Run the pragma + /* + * Run the pragma + */ consumer.accept(Integer.parseInt(token), state); } catch (final NumberFormatException nfex) { - // Tell the user their argument isn't correct - final PragmaFormatException pfex = new PragmaFormatException( - "Argument " + token + " to " + name + " pragma isn't a valid integer. " - + "This pragma requires a integer argument"); + /* + * Tell the user their argument isn't correct + */ + String fmt = "Argument %s to %s pragma isn't a valid integer, and this pragma requires an integer argument."; + + final PragmaFormatException pfex = new PragmaFormatException(String.format(fmt, token, name)); pfex.initCause(nfex); @@ -66,14 +77,23 @@ public class RuleBasedReaderPragmas { public static BiConsumer buildStringCollapser( final String name, final BiConsumer consumer) { return (tokenizer, state) -> { - // Check our input - if (!tokenizer.hasMoreTokens()) throw new PragmaFormatException( - "Pragma " + name + " requires one or more string arguments"); + /* + * Check our input + */ + if (!tokenizer.hasMoreTokens()) { + String fmt = "Pragma %s requires one or more string arguments."; + + throw new PragmaFormatException(String.format(fmt, name)); + } - // Build our argument + /* + * Build our argument + */ final String collapsed = ListUtils.collapseTokens(tokenizer.toList()); - // Run the pragma + /* + * Run the pragma + */ consumer.accept(collapsed, state); }; } diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/Block.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/Block.java index b514d17..15f3510 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/Block.java +++ b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/Block.java @@ -81,7 +81,8 @@ public class Block { @Override public String toString() { - return String.format("Block #%d (from lines %d to %d), length: %d characters", blockNo, startLine, - endLine, contents.length()); + String fmt = "Block #%d (from lines %d to %d), length: %d characters"; + + return String.format(fmt, blockNo, startLine, endLine, contents.length()); } -} \ No newline at end of file +} diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/BlockReader.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/BlockReader.java index dac535e..3c695c6 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/BlockReader.java +++ b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/BlockReader.java @@ -33,6 +33,20 @@ public interface BlockReader extends AutoCloseable, Iterator { */ boolean nextBlock(); + /** + * Retrieve the number of blocks that have been read so far. + * + * @return The number of blocks read so far. + */ + int getBlockCount(); + + @Override + void close() throws IOException; + + /* + * Methods with default impls. + */ + /** * Execute an action for each remaining block. * @@ -45,16 +59,6 @@ public interface BlockReader extends AutoCloseable, Iterator { } } - /** - * Retrieve the number of blocks that have been read so far. - * - * @return The number of blocks read so far. - */ - int getBlockCount(); - - @Override - void close() throws IOException; - @Override default boolean hasNext() { return hasNextBlock(); @@ -66,4 +70,4 @@ public interface BlockReader extends AutoCloseable, Iterator { return getBlock(); } -} \ No newline at end of file +} diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/FilteredBlockReader.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/FilteredBlockReader.java new file mode 100644 index 0000000..a9e5923 --- /dev/null +++ b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/FilteredBlockReader.java @@ -0,0 +1,97 @@ +package bjc.utils.ioutils.blocks; + +import java.io.IOException; + +import java.util.function.Consumer; +import java.util.function.Predicate; + +public class FilteredBlockReader implements BlockReader { + /* + * The source of blocks. + */ + private BlockReader source; + + /* + * The current and next block. + * + * Both have already been checked for the predicate. + */ + private Block current; + private Block pending; + + /* + * Number of blocks that passed the predicate. + */ + private int passed; + + /* + * The predicate blocks must pass. + */ + private Predicate pred; + + /* + * The action to call on failure, if there is one. + */ + private Consumer failAction; + + public FilteredBlockReader(BlockReader src, Predicate predic) { + this(src, predic, null); + } + + public FilteredBlockReader(BlockReader src, Predicate predic, Consumer failAct) { + source = src; + pred = predic; + failAction = failAct; + + passed = 0; + } + + @Override + public boolean hasNextBlock() { + if(pending != null) return true; + + while(source.hasNextBlock()) { + /* + * Only say we have a next block if the next block would + * pass the predicate. + */ + pending = source.next(); + + if(pred.test(pending)) { + passed += 1; + return true; + } else { + failAction.accept(pending); + } + } + + return false; + } + + @Override + public Block getBlock() { + return current; + } + + @Override + public boolean nextBlock() { + if(pending != null || hasNextBlock()) { + current = pending; + pending = null; + + return true; + } + + return false; + } + + @Override + public int getBlockCount() { + return passed; + } + + @Override + public void close() throws IOException { + source.close(); + } +} diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/LayeredBlockReader.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/LayeredBlockReader.java index 54010fe..967a1f2 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/LayeredBlockReader.java +++ b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/LayeredBlockReader.java @@ -14,9 +14,15 @@ import java.io.IOException; * */ public class LayeredBlockReader implements BlockReader { - private final BlockReader first; - private final BlockReader second; + /* + * The readers to drain from. + */ + private final BlockReader first; + private final BlockReader second; + /* + * The current block number. + */ private int blockNo; /** @@ -42,13 +48,16 @@ public class LayeredBlockReader implements BlockReader { public Block getBlock() { final Block firstBlock = first.getBlock(); + /* + * Only drain a block from the second reader if none are + * available in the first reader. + */ return firstBlock == null ? second.getBlock() : firstBlock; } @Override public boolean nextBlock() { final boolean gotFirst = first.nextBlock(); - final boolean succ = gotFirst ? gotFirst : second.nextBlock(); if (succ) { @@ -69,4 +78,4 @@ public class LayeredBlockReader implements BlockReader { first.close(); } -} \ No newline at end of file +} diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/MappedBlockReader.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/MappedBlockReader.java new file mode 100644 index 0000000..1996421 --- /dev/null +++ b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/MappedBlockReader.java @@ -0,0 +1,44 @@ +package bjc.utils.ioutils.blocks; + +import java.io.IOException; + +import java.util.function.UnaryOperator; + +public class MappedBlockReader implements BlockReader { + private BlockReader reader; + + private Block current; + + private UnaryOperator transform; + + public MappedBlockReader(BlockReader source, UnaryOperator trans) { + reader = source; + transform = trans; + } + + @Override + public boolean hasNextBlock() { + return reader.hasNextBlock(); + } + + @Override + public Block getBlock() { + return current; + } + + @Override + public boolean nextBlock() { + if(hasNextBlock()) { + current = trans.apply(reader.next()); + + return true; + } + + return false; + } + + @Override + public int getBlockCount() { + return reader.getBlockCount(); + } +} diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/PushbackBlockReader.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/PushbackBlockReader.java index d7ba247..0cc9dea 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/PushbackBlockReader.java +++ b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/PushbackBlockReader.java @@ -14,6 +14,9 @@ import java.util.LinkedList; public class PushbackBlockReader implements BlockReader { private final BlockReader source; + /* + * The queue of pushed-back blocks. + */ private final Deque waiting; private Block curBlock; @@ -44,6 +47,9 @@ public class PushbackBlockReader implements BlockReader { @Override public boolean nextBlock() { + /* + * Drain pushed-back blocks first. + */ if (!waiting.isEmpty()) { curBlock = waiting.pop(); @@ -97,4 +103,4 @@ public class PushbackBlockReader implements BlockReader { return String.format("PushbackBlockReader [waiting=%s, curBlock=%s, blockNo=%s]", waiting, curBlock, blockNo); } -} \ No newline at end of file +} diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/SerialBlockReader.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/SerialBlockReader.java index 7735981..c229da1 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/SerialBlockReader.java +++ b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/SerialBlockReader.java @@ -31,10 +31,15 @@ public class SerialBlockReader implements BlockReader { public boolean hasNextBlock() { if (readerQueue.isEmpty()) return false; + /* + * Attempt to get a block from the first reader. + */ boolean hasBlock = readerQueue.peek().hasNextBlock(); - boolean cont = hasBlock || readerQueue.isEmpty(); + /* + * Close/dispose of readers until we get an open one. + */ while (!cont) { try { readerQueue.pop().close(); @@ -43,7 +48,6 @@ public class SerialBlockReader implements BlockReader { } hasBlock = readerQueue.peek().hasNextBlock(); - cont = hasBlock || readerQueue.isEmpty(); } @@ -62,7 +66,6 @@ public class SerialBlockReader implements BlockReader { if (readerQueue.isEmpty()) return false; boolean gotBlock = readerQueue.peek().nextBlock(); - boolean cont = gotBlock || readerQueue.isEmpty(); while (!cont) { @@ -73,7 +76,6 @@ public class SerialBlockReader implements BlockReader { } gotBlock = readerQueue.peek().nextBlock(); - cont = gotBlock || readerQueue.isEmpty(); } @@ -97,4 +99,4 @@ public class SerialBlockReader implements BlockReader { reader.close(); } } -} \ No newline at end of file +} diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/SimpleBlockReader.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/SimpleBlockReader.java index ca04d8f..734bde8 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/SimpleBlockReader.java +++ b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/SimpleBlockReader.java @@ -21,14 +21,18 @@ public class SimpleBlockReader implements BlockReader { /* * I/O source for blocks. */ - private final Scanner blockReader; + private final Scanner blockReader; /* * The current block. */ - private Block currBlock; - private int blockNo; - private int lineNo; + private Block currBlock; + + /* + * Info about the current block. + */ + private int blockNo; + private int lineNo; /** * Create a new block reader. @@ -64,6 +68,9 @@ public class SimpleBlockReader implements BlockReader { @Override public boolean nextBlock() { try { + /* + * Read in a new block, and keep the line numbers sane. + */ final int blockStartLine = lineNo; final String blockContents = blockReader.next(); final int blockEndLine = lineNo + StringUtils.countMatches(blockContents, "\\R"); diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/TriggeredBlockReader.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/TriggeredBlockReader.java index 0e50ad6..74076bb 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/TriggeredBlockReader.java +++ b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/TriggeredBlockReader.java @@ -11,6 +11,9 @@ import java.io.IOException; public class TriggeredBlockReader implements BlockReader { private final BlockReader source; + /* + * The action to fire. + */ private final Runnable action; /** @@ -59,4 +62,4 @@ public class TriggeredBlockReader implements BlockReader { public String toString() { return String.format("TriggeredBlockReader [source=%s]", source); } -} \ No newline at end of file +} -- cgit v1.2.3