summaryrefslogtreecommitdiff
path: root/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks
diff options
context:
space:
mode:
authorbjculkin <bjculkin@mix.wvu.edu>2017-04-06 14:14:37 -0400
committerbjculkin <bjculkin@mix.wvu.edu>2017-04-06 14:14:37 -0400
commit25382427eeafda30aa06a27f37c65fdaf8b67eba (patch)
tree109a2c5c4cd74ce47b961710952a26520bc17f90 /BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks
parent622653daa9e991e9608852210034a4e09cb94167 (diff)
Reorganize blocks
Diffstat (limited to 'BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks')
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/Block.java87
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/BlockReader.java68
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/BlockReaders.java81
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/LayeredBlockReader.java70
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/PushbackBlockReader.java98
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/SerialBlockReader.java98
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/SimpleBlockReader.java107
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/TriggeredBlockReader.java62
8 files changed, 671 insertions, 0 deletions
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
new file mode 100644
index 0000000..e92644e
--- /dev/null
+++ b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/Block.java
@@ -0,0 +1,87 @@
+package bjc.utils.ioutils.blocks;
+
+/**
+ * Represents a block of text read in from a source.
+ *
+ * @author EVE
+ *
+ */
+public class Block {
+ /**
+ * The contents of this block.
+ */
+ public final String contents;
+
+ /**
+ * The line of the source this block started on.
+ */
+ public final int startLine;
+
+ /**
+ * The line of the source this block ended on.
+ */
+ public final int endLine;
+
+ /**
+ * The number of this block.
+ */
+ public final int blockNo;
+
+ /**
+ * Create a new block.
+ *
+ * @param blockNo
+ * The number of this block.
+ * @param contents
+ * The contents of this block.
+ * @param startLine
+ * The line this block started on.
+ * @param endLine
+ * The line this block ended.
+ */
+ public Block(int blockNo, String contents, int startLine, int endLine) {
+ this.contents = contents;
+ this.startLine = startLine;
+ this.endLine = endLine;
+ this.blockNo = blockNo;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+
+ result = prime * result + blockNo;
+ result = prime * result + ((contents == null) ? 0 : contents.hashCode());
+ result = prime * result + endLine;
+ result = prime * result + startLine;
+
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if(this == obj) return true;
+ if(obj == null) return false;
+ if(!(obj instanceof Block)) return false;
+
+ Block other = (Block) obj;
+
+ if(blockNo != other.blockNo) return false;
+
+ if(contents == null) {
+ if(other.contents != null) return false;
+ } else if(!contents.equals(other.contents)) return false;
+
+ if(endLine != other.endLine) return false;
+ if(startLine != other.startLine) return false;
+
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Block #%d (from lines %d to %d), length: %d characters", 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
new file mode 100644
index 0000000..d45a4f3
--- /dev/null
+++ b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/BlockReader.java
@@ -0,0 +1,68 @@
+package bjc.utils.ioutils.blocks;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.function.Consumer;
+
+/**
+ * A source of blocks of characters, marked with line numbers as to block
+ * start/block end.
+ *
+ * @author bjculkin
+ *
+ */
+public interface BlockReader extends AutoCloseable, Iterator<Block> {
+ /**
+ * Check if this reader has an available block.
+ *
+ * @return Whether or not another block is available.
+ */
+ boolean hasNextBlock();
+
+ /**
+ * Get the current block.
+ *
+ * @return The current block, or null if there is no current block.
+ */
+ Block getBlock();
+
+ /**
+ * Move to the next block.
+ *
+ * @return Whether or not the next block was successfully read.
+ */
+ boolean nextBlock();
+
+ /**
+ * Execute an action for each remaining block.
+ *
+ * @param action
+ * The action to execute for each block
+ */
+ default void forEachBlock(Consumer<Block> action) {
+ while (hasNext()) {
+ action.accept(next());
+ }
+ }
+
+ /**
+ * Retrieve the number of blocks that have been read so far.
+ *
+ * @return The number of blocks read so far.
+ */
+ int getBlockCount();
+
+ void close() throws IOException;
+
+ @Override
+ default boolean hasNext() {
+ return hasNextBlock();
+ }
+
+ @Override
+ default Block next() {
+ nextBlock();
+
+ return getBlock();
+ }
+} \ No newline at end of file
diff --git a/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/BlockReaders.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/BlockReaders.java
new file mode 100644
index 0000000..ca82b51
--- /dev/null
+++ b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/BlockReaders.java
@@ -0,0 +1,81 @@
+package bjc.utils.ioutils.blocks;
+
+import java.io.Reader;
+
+/**
+ * Utility methods for constructing instances of {@link BlockReader}
+ *
+ * @author bjculkin
+ *
+ */
+public class BlockReaders {
+ /**
+ * Create a new simple block reader that works off a regex.
+ *
+ * @param blockDelim
+ * The regex that separates blocks.
+ *
+ * @param source
+ * The reader to get blocks from.
+ *
+ * @return A configured simple reader.
+ */
+ public static SimpleBlockReader simple(String blockDelim, Reader source) {
+ return new SimpleBlockReader(blockDelim, source);
+ }
+
+ /**
+ * Create a new pushback block reader.
+ *
+ * @param src
+ * The block reader to read blocks from.
+ *
+ * @return A configured pushback reader.
+ */
+ public static PushbackBlockReader pushback(BlockReader src) {
+ return new PushbackBlockReader(src);
+ }
+
+ /**
+ * Create a new triggered block reader.
+ *
+ * @param source
+ * The block reader to read blocks from.
+ *
+ * @param action
+ * The action to execute before reading a block.
+ *
+ * @return A configured triggered block reader.
+ */
+ public static BlockReader trigger(BlockReader source, Runnable action) {
+ return new TriggeredBlockReader(source, action);
+ }
+
+ /**
+ * Create a new layered block reader.
+ *
+ * @param primary
+ * The first source to read blocks from.
+ *
+ * @param secondary
+ * The second source to read blocks from.
+ *
+ * @return A configured layered block reader.
+ */
+ public static BlockReader layered(BlockReader primary, BlockReader secondary) {
+ return new LayeredBlockReader(primary, secondary);
+ }
+
+ /**
+ * Create a new serial block reader.
+ *
+ * @param readers
+ * The readers to pull from, in the order to pull from
+ * them.
+ *
+ * @return A configured serial block reader.
+ */
+ public static BlockReader serial(BlockReader... readers) {
+ return new SerialBlockReader(readers);
+ }
+} \ No newline at end of file
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
new file mode 100644
index 0000000..9ece6df
--- /dev/null
+++ b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/LayeredBlockReader.java
@@ -0,0 +1,70 @@
+package bjc.utils.ioutils.blocks;
+
+import java.io.IOException;
+
+/**
+ * A block reader that supports draining all the blocks from one reading before
+ * swapping to another.
+ *
+ * This is more a 'prioritize blocks from one over the other', than a 'read all
+ * the blocks from one, then all the blocks from the other'. If you need that,
+ * look at {@link SerialBlockReader}.
+ *
+ * @author bjculkin
+ *
+ */
+public class LayeredBlockReader implements BlockReader {
+ private BlockReader first;
+ private BlockReader second;
+
+ private int blockNo;
+
+ /**
+ * Create a new layered block reader.
+ *
+ * @param primary
+ * The first source to read blocks from.
+ *
+ * @param secondary
+ * The second source to read blocks from.
+ */
+ public LayeredBlockReader(BlockReader primary, BlockReader secondary) {
+ first = primary;
+ second = secondary;
+ }
+
+ @Override
+ public boolean hasNextBlock() {
+ return first.hasNextBlock() || second.hasNextBlock();
+ }
+
+ @Override
+ public Block getBlock() {
+ Block firstBlock = first.getBlock();
+
+ return firstBlock == null ? second.getBlock() : firstBlock;
+ }
+
+ @Override
+ public boolean nextBlock() {
+ boolean gotFirst = first.nextBlock();
+
+ boolean succ = gotFirst ? gotFirst : second.nextBlock();
+
+ if (succ) blockNo += 1;
+
+ return succ;
+ }
+
+ @Override
+ public int getBlockCount() {
+ return blockNo;
+ }
+
+ @Override
+ public void close() throws IOException {
+ second.close();
+
+ first.close();
+ }
+} \ No newline at end of file
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
new file mode 100644
index 0000000..96906ae
--- /dev/null
+++ b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/PushbackBlockReader.java
@@ -0,0 +1,98 @@
+package bjc.utils.ioutils.blocks;
+
+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/blocks/SerialBlockReader.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/SerialBlockReader.java
new file mode 100644
index 0000000..2363468
--- /dev/null
+++ b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/SerialBlockReader.java
@@ -0,0 +1,98 @@
+package bjc.utils.ioutils.blocks;
+
+import java.io.IOException;
+import java.util.Deque;
+
+/**
+ * Provides a means of concatenating two block readers.
+ *
+ * @author bjculkin
+ *
+ */
+public class SerialBlockReader implements BlockReader {
+ private Deque<BlockReader> readerQueue;
+
+ private int blockNo;
+
+ /**
+ * Create a new serial block reader.
+ *
+ * @param readers
+ * The readers to pull from, in the order to pull from
+ * them.
+ */
+ public SerialBlockReader(BlockReader... readers) {
+ for (BlockReader reader : readers) {
+ readerQueue.add(reader);
+ }
+ }
+
+ @Override
+ public boolean hasNextBlock() {
+ if (readerQueue.isEmpty()) return false;
+
+ boolean hasBlock = readerQueue.peek().hasNextBlock();
+
+ boolean cont = hasBlock || readerQueue.isEmpty();
+
+ while (!cont) {
+ try {
+ readerQueue.pop().close();
+ } catch (IOException ioex) {
+ throw new IllegalStateException("Exception thrown by discarded reader", ioex);
+ }
+
+ hasBlock = readerQueue.peek().hasNextBlock();
+
+ cont = hasBlock || readerQueue.isEmpty();
+ }
+
+ return hasBlock;
+ }
+
+ @Override
+ public Block getBlock() {
+ if (readerQueue.isEmpty())
+ return null;
+ else return readerQueue.peek().getBlock();
+ }
+
+ @Override
+ public boolean nextBlock() {
+ if (readerQueue.isEmpty()) return false;
+
+ boolean gotBlock = readerQueue.peek().nextBlock();
+
+ boolean cont = gotBlock || readerQueue.isEmpty();
+
+ while (!cont) {
+ try {
+ readerQueue.pop().close();
+ } catch (IOException ioex) {
+ throw new IllegalStateException("Exception thrown by discarded reader", ioex);
+ }
+
+ gotBlock = readerQueue.peek().nextBlock();
+
+ cont = gotBlock || readerQueue.isEmpty();
+ }
+
+ if (cont) blockNo += 1;
+
+ return cont;
+ }
+
+ @Override
+ public int getBlockCount() {
+ return blockNo;
+ }
+
+ @Override
+ public void close() throws IOException {
+ while (!readerQueue.isEmpty()) {
+ BlockReader reader = readerQueue.pop();
+
+ 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
new file mode 100644
index 0000000..6ee1d57
--- /dev/null
+++ b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/SimpleBlockReader.java
@@ -0,0 +1,107 @@
+package bjc.utils.ioutils.blocks;
+
+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) {
+ currBlock = null;
+
+ 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/blocks/TriggeredBlockReader.java b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/TriggeredBlockReader.java
new file mode 100644
index 0000000..cfe72c2
--- /dev/null
+++ b/BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/TriggeredBlockReader.java
@@ -0,0 +1,62 @@
+package bjc.utils.ioutils.blocks;
+
+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