summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Culkin <scorpress@gmail.com>2021-02-26 16:15:11 -0500
committerBen Culkin <scorpress@gmail.com>2021-02-26 16:15:11 -0500
commite55cb9852a106cff26517d7d1e85bc4b149884f3 (patch)
tree2e5d8b8d1e4ec8a5893cc2372e84aade218e648a
parentd7b2e924d762e66b3f2be76242ee761e90113a8c (diff)
Update
-rw-r--r--base/src/main/java/bjc/utils/cli/Command.java37
-rw-r--r--commander/.classpath28
-rw-r--r--commander/.gitignore1
-rw-r--r--commander/.project23
-rw-r--r--commander/.settings/org.eclipse.core.resources.prefs4
-rw-r--r--commander/.settings/org.eclipse.jdt.core.prefs8
-rw-r--r--commander/.settings/org.eclipse.m2e.core.prefs4
-rw-r--r--commander/pom.xml35
-rw-r--r--commander/src/example/java/bjc/commander/CommanderCLI.java27
-rw-r--r--commander/src/main/java/bjc/commander/Commander.java386
-rw-r--r--pom.xml5
11 files changed, 546 insertions, 12 deletions
diff --git a/base/src/main/java/bjc/utils/cli/Command.java b/base/src/main/java/bjc/utils/cli/Command.java
index 5f4a65b..68351bf 100644
--- a/base/src/main/java/bjc/utils/cli/Command.java
+++ b/base/src/main/java/bjc/utils/cli/Command.java
@@ -7,15 +7,6 @@ package bjc.utils.cli;
*/
public interface Command {
/**
- * Create a command that serves as an alias to this one
- *
- * @return A command that serves as an alias to this one
- */
- default Command aliased() {
- return new DelegatingCommand(this);
- };
-
- /**
* Get the handler that executes this command
*
* @return The handler that executes this command
@@ -28,7 +19,16 @@ public interface Command {
* @return The help entry for this command
*/
CommandHelp getHelp();
-
+
+ /**
+ * Create a command that serves as an alias to this one
+ *
+ * @return A command that serves as an alias to this one
+ */
+ default Command aliased() {
+ return new DelegatingCommand(this);
+ };
+
/**
* Check if this command is an alias of another command
*
@@ -37,4 +37,21 @@ public interface Command {
default boolean isAlias() {
return false;
}
+
+ /**
+ * Create a new basic command.
+ *
+ * @param summary The summary of the command. This is used as a short help
+ * message displayed when listing commands.
+ * @param description The description of the command. This is what is shown
+ * when the detailed help for a command is asked for.
+ * @param handler The implementation for the command.
+ *
+ * @return A command with the given implementation.
+ */
+ static Command from(
+ String summary, String description, CommandHandler handler)
+ {
+ return new GenericCommand(handler, summary, description);
+ }
}
diff --git a/commander/.classpath b/commander/.classpath
new file mode 100644
index 0000000..7d66676
--- /dev/null
+++ b/commander/.classpath
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" output="target/classes" path="src/main/java">
+ <attributes>
+ <attribute name="optional" value="true"/>
+ <attribute name="maven.pomderived" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="src" output="target/test-classes" path="src/test/java">
+ <attributes>
+ <attribute name="test" value="true"/>
+ <attribute name="optional" value="true"/>
+ <attribute name="maven.pomderived" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="src" path="src/example/java"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
+ <attributes>
+ <attribute name="maven.pomderived" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
+ <attributes>
+ <attribute name="maven.pomderived" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/commander/.gitignore b/commander/.gitignore
new file mode 100644
index 0000000..b83d222
--- /dev/null
+++ b/commander/.gitignore
@@ -0,0 +1 @@
+/target/
diff --git a/commander/.project b/commander/.project
new file mode 100644
index 0000000..18dea93
--- /dev/null
+++ b/commander/.project
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>commander</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.m2e.core.maven2Builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>org.eclipse.m2e.core.maven2Nature</nature>
+ </natures>
+</projectDescription>
diff --git a/commander/.settings/org.eclipse.core.resources.prefs b/commander/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..f9fe345
--- /dev/null
+++ b/commander/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,4 @@
+eclipse.preferences.version=1
+encoding//src/main/java=UTF-8
+encoding//src/test/java=UTF-8
+encoding/<project>=UTF-8
diff --git a/commander/.settings/org.eclipse.jdt.core.prefs b/commander/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..2f5cc74
--- /dev/null
+++ b/commander/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,8 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore
+org.eclipse.jdt.core.compiler.release=disabled
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/commander/.settings/org.eclipse.m2e.core.prefs b/commander/.settings/org.eclipse.m2e.core.prefs
new file mode 100644
index 0000000..f897a7f
--- /dev/null
+++ b/commander/.settings/org.eclipse.m2e.core.prefs
@@ -0,0 +1,4 @@
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1
diff --git a/commander/pom.xml b/commander/pom.xml
new file mode 100644
index 0000000..eaba4a6
--- /dev/null
+++ b/commander/pom.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>bjc</groupId>
+ <artifactId>BJCUtils-Parent</artifactId>
+ <version>1.0.0</version>
+ </parent>
+
+ <artifactId>commander</artifactId>
+ <name>commander</name>
+
+ <url>http://maven.apache.org</url>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>3.8.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>bjc</groupId>
+ <artifactId>BJC-Utils2</artifactId>
+ <version>1.0.0</version>
+ <type>jar</type>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/commander/src/example/java/bjc/commander/CommanderCLI.java b/commander/src/example/java/bjc/commander/CommanderCLI.java
new file mode 100644
index 0000000..0f208b6
--- /dev/null
+++ b/commander/src/example/java/bjc/commander/CommanderCLI.java
@@ -0,0 +1,27 @@
+package bjc.commander;
+
+import java.io.*;
+import java.util.*;
+
+/** Runner for the general command-line interface for Commander.
+ *
+ * @author Ben Culkin */
+public class CommanderCLI {
+
+ /* :CLIArgsParsing */
+ /** Run the command line interface
+ *
+ * @param args
+ * Ignored CLI args. */
+ public static void main(String[] args) {
+ /* Create/configure I/O sources. */
+ Commander reader = new Commander();
+
+ reader.ioReaders.put("stdio", new InputStreamReader(System.in));
+
+ Scanner input = new Scanner(System.in);
+ reader.run(input, "console", true);
+ input.close();
+ }
+
+}
diff --git a/commander/src/main/java/bjc/commander/Commander.java b/commander/src/main/java/bjc/commander/Commander.java
new file mode 100644
index 0000000..0c62fd3
--- /dev/null
+++ b/commander/src/main/java/bjc/commander/Commander.java
@@ -0,0 +1,386 @@
+package bjc.commander;
+
+import static bjc.utils.cli.objects.Command.CommandStatus.*;
+
+import java.io.*;
+import java.util.*;
+import java.util.function.*;
+import java.util.logging.*;
+import java.util.regex.*;
+
+import bjc.utils.cli.objects.*;
+import bjc.utils.cli.objects.Command.*;
+import bjc.utils.ioutils.blocks.*;
+
+/** CLI interface for messing around with various java objects.
+ *
+ * @author Ben Culkin */
+public class Commander {
+ /** All of the configured block readers. */
+ public final Map<String, BlockReader> blockReaders;
+ /** All of the configured I/O sources. */
+ public final Map<String, Reader> ioReaders;
+
+ /* Logger. */
+ private final Logger LOGGER = Logger.getLogger(Commander.class.getName());
+
+ /** Create a new CLI for configuring BlockReaders. */
+ public Commander() {
+ ioReaders = new HashMap<>();
+ blockReaders = new HashMap<>();
+ }
+
+ /** Run the CLI on an input source.
+ *
+ * @param input
+ * The place to read input from.
+ *
+ * @param srcName
+ * The name of the place to read input from.
+ *
+ * @param interactive
+ * Whether or not the source is interactive */
+ public void run(Scanner input, String srcName, boolean interactive) {
+ int lno = 0;
+
+ do {
+ /* Print a prompt. */
+ if (interactive) System.out.printf("reader-conf(%d)>", lno);
+
+ /* Read a line. */
+ String ln = input.nextLine();
+ lno += 1;
+
+ /* Parse the command. */
+ Command com = Command.fromString(ln, lno, srcName);
+ /* Ignore blank commands. */
+ if (com == null) continue;
+
+ /* Handle a command. */
+ CommandStatus sts = handleCommand(com, interactive);
+ /* Exit if we finished or encountered a fatal error. */
+ if (sts == FINISH || sts == ERROR) return;
+ } while (input.hasNextLine());
+ }
+
+ /** Handle a command.
+ *
+ * @param com
+ * The command to handle
+ *
+ * @param interactive
+ * Whether the current input source is interactive or not.
+ *
+ * @return The status of the executed command. */
+ public CommandStatus handleCommand(Command com, boolean interactive) {
+ /* Handle each command. */
+ switch (com.name) {
+ case "def-filtered":
+ return defFiltered(com);
+ case "def-layered":
+ return defLayered(com);
+ case "def-pushback":
+ return defPushback(com);
+ case "def-simple":
+ return defSimple(com);
+ case "def-serial":
+ return defSerial(com);
+ case "def-toggled":
+ return defToggled(com);
+ case "}":
+ case "end":
+ case "exit":
+ case "quit":
+ if (interactive)
+ System.out.printf(
+ "Exiting reader-conf, %d readers configured in %d commands\n",
+ blockReaders.size(), com.lno);
+ return FINISH;
+ default:
+ LOGGER.severe(com.error("Unknown command '%s'\n", com.name));
+ return FAIL;
+ }
+ }
+
+ private CommandStatus defFiltered(Command com) {
+ /*
+ * Get the block name.
+ */
+
+ String blockName = com.trimTo(' ');
+ if (blockName == null) {
+ LOGGER.severe(com.error("No name argument for def-filtered.\n"));
+ return FAIL;
+ }
+
+ /*
+ * Check there isn't a reader already bound to this name.
+ */
+ if (blockReaders.containsKey(blockName)) {
+ LOGGER.warning(com.warn("Shadowing existing reader named %s\n", blockName));
+ }
+
+ /*
+ * Get the reader name.
+ */
+
+ String readerName = com.trimTo(' ');
+ if (readerName == null) {
+ LOGGER.severe(com.error("No reader-name argument for def-filtered.\n"));
+ return FAIL;
+ }
+
+ /*
+ * Check there is a reader bound to that name.
+ */
+ if (!blockReaders.containsKey(readerName)) {
+ LOGGER.severe(com.error("No source named %s\n", readerName));
+ return FAIL;
+ }
+
+ /*
+ * Get the pattern.
+ */
+ if (com.remn.equals("")) {
+ LOGGER.severe(com.error("No filter argument for def-filtered\n"));
+ return FAIL;
+ }
+
+ String filter = com.remn;
+
+ try {
+ Pattern pat = Pattern.compile(filter);
+
+ Predicate<Block> pred = block -> {
+ Matcher mat = pat.matcher(block.contents);
+
+ return mat.matches();
+ };
+
+ BlockReader reader
+ = new FilteredBlockReader(blockReaders.get(readerName), pred);
+
+ blockReaders.put(blockName, reader);
+ } catch (PatternSyntaxException psex) {
+ LOGGER.severe(com.error("Invalid regular expression '%s' for filter. (%s)\n",
+ filter, psex.getMessage()));
+ return FAIL;
+ }
+
+ return SUCCESS;
+ }
+
+ private CommandStatus defPushback(Command com) {
+ String[] parts = com.remn.split(" ");
+
+ if (parts.length != 2) {
+ LOGGER.severe(com.error(
+ "Incorrect number of arguments to def-pushback. Requires a block name and a reader name\n"));
+ return FAIL;
+ }
+
+ String blockName = parts[0];
+ if (blockReaders.containsKey(blockName)) {
+ LOGGER.warning(com.warn("Shadowing existing reader %s\n", blockName));
+ return FAIL;
+ }
+
+ String readerName = parts[1];
+ if (!blockReaders.containsKey(readerName)) {
+ LOGGER.severe(com.error("No reader named %s\n", readerName));
+ return FAIL;
+ }
+
+ BlockReader reader = new PushbackBlockReader(blockReaders.get(readerName));
+ blockReaders.put(blockName, reader);
+
+ return SUCCESS;
+ }
+
+ private CommandStatus defToggled(Command com) {
+ String[] parts = com.remn.split(" ");
+
+ if (parts.length != 3) {
+ LOGGER.severe(com.error(
+ "Incorrect number of arguments to def-toggled. Requires a block name and two reader names\n"));
+ return FAIL;
+ }
+
+ /*
+ * Get the block name.
+ */
+ String blockName = parts[0];
+ if (blockReaders.containsKey(blockName)) {
+ LOGGER.warning(com.warn("Shadowing existing reader named %s\n", blockName));
+ }
+
+ /*
+ * Make sure the component readers exist.
+ */
+ if (!blockReaders.containsKey(parts[1])) {
+ LOGGER.severe(com.error("No reader named %s\n", parts[1]));
+ return FAIL;
+ }
+
+ if (!blockReaders.containsKey(parts[2])) {
+ LOGGER.severe(com.error("No reader named %s\n", parts[2]));
+ return FAIL;
+ }
+
+ BlockReader reader = new ToggledBlockReader(blockReaders.get(parts[1]),
+ blockReaders.get(parts[2]));
+ blockReaders.put(blockName, reader);
+
+ return SUCCESS;
+ }
+
+ private CommandStatus defLayered(Command com) {
+ String[] parts = com.remn.split(" ");
+
+ if (parts.length != 3) {
+ LOGGER.severe(com.error(
+ "Incorrect number of arguments to def-layered. Requires a block name and two reader names\n"));
+ return FAIL;
+ }
+
+ /*
+ * Get the block name.
+ */
+ String blockName = parts[0];
+ if (blockReaders.containsKey(blockName)) {
+ LOGGER.warning(com.warn("Shadowing existing reader named %s\n", blockName));
+ }
+
+ /*
+ * Make sure the component readers exist.
+ */
+ if (!blockReaders.containsKey(parts[1])) {
+ LOGGER.severe(com.error("No reader named %s\n", parts[1]));
+ return FAIL;
+ }
+
+ if (!blockReaders.containsKey(parts[2])) {
+ LOGGER.severe(com.error("No reader named %s\n", parts[2]));
+ return FAIL;
+ }
+
+ BlockReader reader = new LayeredBlockReader(blockReaders.get(parts[1]),
+ blockReaders.get(parts[2]));
+ blockReaders.put(blockName, reader);
+
+ return SUCCESS;
+ }
+
+ private CommandStatus defSerial(Command com) {
+ String[] parts = com.remn.split(" ");
+
+ if (parts.length < 2) {
+ LOGGER.severe(com.error(
+ "Not enough arguments to def-serial. Requires at least a block name and at least one reader name\n"));
+ return FAIL;
+ }
+
+ /*
+ * Get the name for this BlockReader.
+ */
+ String blockName = parts[0];
+ /*
+ * Check there isn't a reader already bound to this name.
+ */
+ if (blockReaders.containsKey(blockName)) {
+ LOGGER.warning(com.warn("Shadowing existing reader named %s\n", blockName));
+ }
+
+ /*
+ * Get all of the component readers.
+ */
+ BlockReader[] readerArr = new BlockReader[parts.length - 1];
+ for (int i = 1; i < parts.length; i++) {
+ String readerName = parts[i];
+
+ /*
+ * Check there is a reader bound to that name.
+ */
+ if (!blockReaders.containsKey(readerName)) {
+ LOGGER.severe(com.error("No reader named %s\n", readerName));
+ return FAIL;
+ }
+
+ readerArr[i] = blockReaders.get(readerName);
+ }
+
+ BlockReader reader = new SerialBlockReader(readerArr);
+
+ blockReaders.put(blockName, reader);
+
+ return SUCCESS;
+ }
+
+ private CommandStatus defSimple(Command com) {
+ String remn = com.remn;
+
+ /*
+ * Get the block name.
+ */
+ /* :StringHandling */
+ int idx = remn.indexOf(' ');
+ if (idx == -1) {
+ LOGGER.severe(com.error("No name argument for def-simple.\n"));
+ return FAIL;
+ }
+ String blockName = remn.substring(0, idx).trim();
+ remn = remn.substring(idx).trim();
+
+ /*
+ * Check there isn't a reader already bound to this name.
+ */
+ if (blockReaders.containsKey(blockName)) {
+ LOGGER.warning(com.warn("Shadowing existing reader named %s\n", blockName));
+ }
+
+ /*
+ * Get the source name.
+ */
+ /* :StringHandling */
+ idx = remn.indexOf(' ');
+ if (idx == -1) {
+ LOGGER.severe(com.error("No source-name argument for def-simple.\n"));
+ return FAIL;
+ }
+ String sourceName = remn.substring(0, idx).trim();
+ remn = remn.substring(idx).trim();
+
+ /*
+ * Check there is a source bound to that name.
+ */
+ if (!ioReaders.containsKey(sourceName)) {
+ LOGGER.severe(com.error("No source named %s\n", sourceName));
+ return FAIL;
+ }
+
+ /*
+ * Get the pattern.
+ */
+ if (remn.equals("")) {
+ LOGGER.severe(com.error("No delimiter argument for def-simple\n"));
+ return FAIL;
+ }
+
+ String delim = remn;
+
+ /* Get the delimiter, and create the reader. */
+ try {
+ BlockReader reader
+ = new SimpleBlockReader(delim, ioReaders.get(sourceName));
+
+ blockReaders.put(blockName, reader);
+ } catch (PatternSyntaxException psex) {
+ LOGGER.severe(
+ com.error("Invalid regular expression '%s' for delimiter. (%s)\n",
+ delim, psex.getMessage()));
+ return FAIL;
+ }
+
+ return SUCCESS;
+ }
+}
diff --git a/pom.xml b/pom.xml
index e7e6a80..0aee431 100644
--- a/pom.xml
+++ b/pom.xml
@@ -34,5 +34,6 @@
<modules>
<module>base</module>
<module>clformat</module>
- </modules>
-</project>
+ <module>commander</module>
+ </modules>
+</project> \ No newline at end of file