summaryrefslogtreecommitdiff
path: root/BJC-Utils2/src/main/java/bjc/utils/ioutils
diff options
context:
space:
mode:
authorbculkin2442 <bjculkin@mix.wvu.edu>2017-03-26 22:55:16 -0400
committerbculkin2442 <bjculkin@mix.wvu.edu>2017-03-26 22:55:16 -0400
commit0220a17f18899715664812540949196e91ef8d10 (patch)
tree0fc3d419665582a0bdc5d11c913e607fdeb9fbf8 /BJC-Utils2/src/main/java/bjc/utils/ioutils
parent98cdf435d4974f4cca8f7b4eb4026da2c88cbc4c (diff)
Make BlockReader abstract, with varying impls.
This allows cool things to be accomplished through chaining BlockReaders together without having to handle the block plumbing yourself. The current set of implementations are a simple one that reads blocks from a scanner delimited by a pattern, one that has a queue of blocks it will attempt to pull from before reading, and one that triggers an action before a block is read. As an example use, for FDS, a combo of simple -> triggered -> pushback is used where the triggered is used for prompting the user, and the pushback supports macros.
Diffstat (limited to 'BJC-Utils2/src/main/java/bjc/utils/ioutils')
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/ioutils/BlockReader.java115
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/ioutils/PushbackBlockReader.java98
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/ioutils/SimpleBlockReader.java105
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/ioutils/TriggeredBlockReader.java62
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