summaryrefslogtreecommitdiff
path: root/BJC-Utils2/src/main/java/bjc/utils/ioutils
diff options
context:
space:
mode:
authorBenjamin J. Culkin <bjculkin@mix.wvu.edu>2017-09-09 21:46:16 -0300
committerBenjamin J. Culkin <bjculkin@mix.wvu.edu>2017-09-09 21:47:34 -0300
commitd766896972c9e9be4a9e0021ec5f4f0665901865 (patch)
tree1f6473300ef86e0697d682360bea0d28fc144baa /BJC-Utils2/src/main/java/bjc/utils/ioutils
parent40f3a28569366c4357fbda11d2fff3b77686d84f (diff)
Update
Most of it is documentation changes. The rest is more work on BlockReaders, as well as a simple command language for configuring them.
Diffstat (limited to 'BJC-Utils2/src/main/java/bjc/utils/ioutils')
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/ioutils/RuleBasedConfigReader.java120
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/ioutils/RuleBasedReaderPragmas.java48
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/Block.java7
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/BlockReader.java26
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/FilteredBlockReader.java97
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/LayeredBlockReader.java17
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/MappedBlockReader.java44
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/PushbackBlockReader.java8
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/SerialBlockReader.java12
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/SimpleBlockReader.java15
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/ioutils/blocks/TriggeredBlockReader.java5
11 files changed, 321 insertions, 78 deletions
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<E> {
- // 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<FunctionalStringTokenizer, IPair<String, E>> 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<FunctionalStringTokenizer, E> 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<E> 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<String, BiConsumer<FunctionalStringTokenizer, E>> pragmas;
/**
@@ -71,8 +77,7 @@ public class RuleBasedConfigReader<E> {
* The function to execute when this pragma is read
*/
public void addPragma(final String name, final BiConsumer<FunctionalStringTokenizer, E> 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<E> {
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<E> {
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<Boolean> 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<E> {
}
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 <StateType> BiConsumer<FunctionalStringTokenizer, StateType> buildInteger(final String name,
final BiConsumer<Integer, StateType> 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 <StateType> BiConsumer<FunctionalStringTokenizer, StateType> buildStringCollapser(
final String name, final BiConsumer<String, StateType> 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
@@ -34,6 +34,20 @@ public interface BlockReader extends AutoCloseable, Iterator<Block> {
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.
*
* @param action
@@ -45,16 +59,6 @@ public interface BlockReader extends AutoCloseable, Iterator<Block> {
}
}
- /**
- * 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<Block> {
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<Block> pred;
+
+ /*
+ * The action to call on failure, if there is one.
+ */
+ private Consumer<Block> failAction;
+
+ public FilteredBlockReader(BlockReader src, Predicate predic) {
+ this(src, predic, null);
+ }
+
+ public FilteredBlockReader(BlockReader src, Predicate predic, Consumer<Block> 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<Block> transform;
+
+ public MappedBlockReader(BlockReader source, UnaryOperator<Block> 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<Block> 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
+}