diff options
Diffstat (limited to 'BJC-Utils2/src/main/java/bjc/utils')
4 files changed, 282 insertions, 98 deletions
diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/BlockReader.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/BlockReader.java index fed74fe..f019e09 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/ioutils/BlockReader.java +++ b/BJC-Utils2/src/main/java/bjc/utils/ioutils/BlockReader.java @@ -1,95 +1,37 @@ package bjc.utils.ioutils; -import java.io.LineNumberReader; -import java.io.Reader; +import java.io.IOException; import java.util.Iterator; -import java.util.NoSuchElementException; -import java.util.Scanner; import java.util.function.Consumer; -import java.util.regex.Pattern; /** - * Implements reading numbered blocks from a source. + * A source of blocks of characters, marked with line numbers as to block + * start/block end. * - * A block is a series of characters, separated by some regular expression. - * - * NOTE: The EOF marker is always treated as a delimiter. You are expected to - * handle blocks that may be shorter than you expect. - * - * @author EVE + * @author bjculkin * */ -public class BlockReader implements AutoCloseable, Iterator<Block> { - /* - * I/O source for blocks. - */ - private LineNumberReader lnReader; - private Scanner blockReader; - - /* - * The current block. - */ - private Block currBlock; - private int blockNo; - - /** - * Create a new block reader. - * - * @param blockDelim - * The pattern that separates blocks. Note that the end - * of file is always considered to end a block. - * - * @param source - * The source to read blocks from. - */ - public BlockReader(String blockDelim, Reader source) { - lnReader = new LineNumberReader(source); - - blockReader = new Scanner(lnReader); - - String pattern = String.format("(?:%s)|\\Z", blockDelim); - Pattern pt = Pattern.compile(pattern, Pattern.MULTILINE); - - blockReader.useDelimiter(pt); - } - +public interface BlockReader extends AutoCloseable, Iterator<Block> { /** * Check if this reader has an available block. * * @return Whether or not another block is available. */ - public boolean hasNextBlock() { - return blockReader.hasNext(); - } + boolean hasNextBlock(); /** * Get the current block. * * @return The current block, or null if there is no current block. */ - public Block getBlock() { - return currBlock; - } + Block getBlock(); /** * Move to the next block. * * @return Whether or not the next block was successfully read. */ - public boolean nextBlock() { - try { - int blockStartLine = lnReader.getLineNumber(); - String blockContents = blockReader.next(); - int blockEndLine = lnReader.getLineNumber(); - blockNo += 1; - - currBlock = new Block(blockNo, blockContents, blockStartLine, blockEndLine); - - return true; - } catch (NoSuchElementException nseex) { - return false; - } - } + boolean nextBlock(); /** * Execute an action for each remaining block. @@ -97,11 +39,9 @@ public class BlockReader implements AutoCloseable, Iterator<Block> { * @param action * The action to execute for each block */ - public void forEachBlock(Consumer<Block> action) { - while (hasNextBlock()) { - nextBlock(); - - action.accept(currBlock); + default void forEachBlock(Consumer<Block> action) { + while (hasNext()) { + action.accept(next()); } } @@ -110,40 +50,19 @@ public class BlockReader implements AutoCloseable, Iterator<Block> { * * @return The number of blocks read so far. */ - public int getBlockCount() { - return blockNo; - } - - @Override - public void close() throws Exception { - blockReader.close(); - - lnReader.close(); - } - - /** - * Set the delimiter used to separate blocks. - * - * @param delim - * The delimiter used to separate blocks. - */ - public void setDelimiter(String delim) { - blockReader.useDelimiter(delim); - } + int getBlockCount(); - /* - * Iterator implementation. - */ + void close() throws IOException; @Override - public boolean hasNext() { - return blockReader.hasNext(); + default boolean hasNext() { + return hasNextBlock(); } @Override - public Block next() { + default Block next() { nextBlock(); return getBlock(); } -} +}
\ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/PushbackBlockReader.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/PushbackBlockReader.java new file mode 100644 index 0000000..d67e01a --- /dev/null +++ b/BJC-Utils2/src/main/java/bjc/utils/ioutils/PushbackBlockReader.java @@ -0,0 +1,98 @@ +package bjc.utils.ioutils; + +import java.io.IOException; +import java.util.Deque; +import java.util.LinkedList; + +/** + * A block reader that supports pushing blocks onto the input queue so that they + * are provided before blocks read from an input source. + * + * @author bjculkin + * + */ +public class PushbackBlockReader implements BlockReader { + private BlockReader source; + + private Deque<Block> waiting; + + private Block curBlock; + + private int blockNo; + + /** + * Create a new pushback block reader. + * + * @param src + * The block reader to use when no blocks are queued. + */ + public PushbackBlockReader(BlockReader src) { + source = src; + + waiting = new LinkedList<>(); + } + + @Override + public boolean hasNextBlock() { + return !waiting.isEmpty() || source.hasNextBlock(); + } + + @Override + public Block getBlock() { + return curBlock; + } + + @Override + public boolean nextBlock() { + if (!waiting.isEmpty()) { + curBlock = waiting.pop(); + + blockNo += 1; + + return true; + } else { + boolean succ = source.nextBlock(); + curBlock = source.getBlock(); + + if (succ) blockNo += 1; + + return succ; + } + } + + @Override + public int getBlockCount() { + return blockNo; + } + + @Override + public void close() throws IOException { + source.close(); + } + + /** + * Insert a block at the back of the queue of pending blocks. + * + * @param blk + * The block to put at the back. + */ + public void addBlock(Block blk) { + waiting.add(blk); + } + + /** + * Insert a block at the front of the queue of pending blocks. + * + * @param blk + * The block to put at the front. + */ + public void pushBlock(Block blk) { + waiting.push(blk); + } + + @Override + public String toString() { + 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/SimpleBlockReader.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/SimpleBlockReader.java new file mode 100644 index 0000000..329effc --- /dev/null +++ b/BJC-Utils2/src/main/java/bjc/utils/ioutils/SimpleBlockReader.java @@ -0,0 +1,105 @@ +package bjc.utils.ioutils; + +import java.io.IOException; +import java.io.LineNumberReader; +import java.io.Reader; +import java.util.NoSuchElementException; +import java.util.Scanner; +import java.util.regex.Pattern; + +/** + * Simple implementation of {@link BlockReader} + * + * NOTE: The EOF marker is always treated as a delimiter. You are expected to + * handle blocks that may be shorter than you expect. + * + * @author EVE + * + */ +public class SimpleBlockReader implements BlockReader { + /* + * I/O source for blocks. + */ + private LineNumberReader lnReader; + private Scanner blockReader; + + /* + * The current block. + */ + private Block currBlock; + private int blockNo; + + /** + * Create a new block reader. + * + * @param blockDelim + * The pattern that separates blocks. Note that the end + * of file is always considered to end a block. + * + * @param source + * The source to read blocks from. + */ + public SimpleBlockReader(String blockDelim, Reader source) { + lnReader = new LineNumberReader(source); + + blockReader = new Scanner(lnReader); + + String pattern = String.format("(?:%s)|\\Z", blockDelim); + Pattern pt = Pattern.compile(pattern, Pattern.MULTILINE); + + blockReader.useDelimiter(pt); + } + + @Override + public boolean hasNextBlock() { + return blockReader.hasNext(); + } + + @Override + public Block getBlock() { + return currBlock; + } + + @Override + public boolean nextBlock() { + try { + int blockStartLine = lnReader.getLineNumber(); + String blockContents = blockReader.next(); + int blockEndLine = lnReader.getLineNumber(); + blockNo += 1; + + currBlock = new Block(blockNo, blockContents, blockStartLine, blockEndLine); + + return true; + } catch (NoSuchElementException nseex) { + return false; + } + } + + @Override + public int getBlockCount() { + return blockNo; + } + + @Override + public void close() throws IOException { + blockReader.close(); + + lnReader.close(); + } + + /** + * Set the delimiter used to separate blocks. + * + * @param delim + * The delimiter used to separate blocks. + */ + public void setDelimiter(String delim) { + blockReader.useDelimiter(delim); + } + + @Override + public String toString() { + return String.format("SimpleBlockReader [currBlock=%s, blockNo=%s]", currBlock, blockNo); + } +} diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/TriggeredBlockReader.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/TriggeredBlockReader.java new file mode 100644 index 0000000..dd89223 --- /dev/null +++ b/BJC-Utils2/src/main/java/bjc/utils/ioutils/TriggeredBlockReader.java @@ -0,0 +1,62 @@ +package bjc.utils.ioutils; + +import java.io.IOException; + +/** + * A block reader that fires an action before a block is actually read. + * + * @author bjculkin + * + */ +public class TriggeredBlockReader implements BlockReader { + private BlockReader source; + + private Runnable action; + + /** + * Create a new triggered reader with the specified source/action. + * + * @param source + * The block reader to read blocks from. + * + * @param action + * The action to execute before reading a block. + */ + public TriggeredBlockReader(BlockReader source, Runnable action) { + super(); + this.source = source; + this.action = action; + } + + @Override + public boolean hasNextBlock() { + action.run(); + + return source.hasNextBlock(); + } + + @Override + public Block getBlock() { + return source.getBlock(); + } + + @Override + public boolean nextBlock() { + return source.nextBlock(); + } + + @Override + public int getBlockCount() { + return source.getBlockCount(); + } + + @Override + public void close() throws IOException { + source.close(); + } + + @Override + public String toString() { + return String.format("TriggeredBlockReader [source=%s]", source); + } +}
\ No newline at end of file |
