summaryrefslogtreecommitdiff
path: root/BJC-Utils2/src
diff options
context:
space:
mode:
authorbjculkin <bjculkin@mix.wvu.edu>2017-03-20 19:15:16 -0400
committerbjculkin <bjculkin@mix.wvu.edu>2017-03-20 19:15:16 -0400
commit6b659fe44d6d1d3381a4404c1b8685763f40f344 (patch)
tree19498909b56be415d089bb3fba6e3871c105e738 /BJC-Utils2/src
parent0468941a27e0171a8ea19e8d14757a26bfa31c2b (diff)
Add BlockReader
Adds BlockReader, which allows you to read blocks of characters delimited by a given sequence from an arbitrary input source. It provides line numbering relative to that source, as well as block numbering. The main caveat is that to prevent spurious NoSuchElementExceptions, EOF is always treated as a valid delimiter, so blocks may be shorter than you expect them to be. This is only meant as a data input utility, not a data validation utility.
Diffstat (limited to 'BJC-Utils2/src')
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/parserutils/BlockReader.java203
1 files changed, 203 insertions, 0 deletions
diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/BlockReader.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/BlockReader.java
new file mode 100644
index 0000000..2083e1a
--- /dev/null
+++ b/BJC-Utils2/src/main/java/bjc/utils/parserutils/BlockReader.java
@@ -0,0 +1,203 @@
+package bjc.utils.parserutils;
+
+import java.io.LineNumberReader;
+import java.io.Reader;
+import java.util.NoSuchElementException;
+import java.util.Scanner;
+import java.util.function.Consumer;
+
+/**
+ * Implements reading numbered blocks from a source.
+ *
+ * A block is a series of characters, seperated 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
+ *
+ */
+public class BlockReader implements AutoCloseable {
+ /**
+ * Represents a block of text read in from a source.
+ *
+ * @author EVE
+ *
+ */
+ public static 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, endLine,
+ startLine, contents.length());
+ }
+ }
+
+ /*
+ * 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);
+ blockReader.useDelimiter(String.format("(?:%s)|\\Z", blockDelim));
+ }
+
+ /**
+ * Check if this reader has an available block.
+ *
+ * @return Whether or not another block is available.
+ */
+ public boolean hasNextBlock() {
+ return blockReader.hasNext();
+ }
+
+ /**
+ * Get the current block.
+ *
+ * @return The current block, or null if there is no current block.
+ */
+ public Block getBlock() {
+ return currBlock;
+ }
+
+ /**
+ * Move to the next block.
+ *
+ * @return Whether or not the next block was succesfully 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;
+ }
+ }
+
+ /**
+ * Execute an action for each remaining block.
+ *
+ * @param action
+ * The action to execute for each block
+ */
+ public void forEachBlock(Consumer<Block> action) {
+ while(hasNextBlock()) {
+ nextBlock();
+
+ action.accept(currBlock);
+ }
+ }
+
+ /**
+ * Retrieve the number of blocks that have been read so far.
+ *
+ * @return The number of blocks read so far.
+ */
+ public int getBlockCount() {
+ return blockNo;
+ }
+
+ @Override
+ public void close() throws Exception {
+ blockReader.close();
+
+ lnReader.close();
+ }
+}