diff options
| author | Ben Culkin <scorpress@gmail.com> | 2021-02-26 16:15:11 -0500 |
|---|---|---|
| committer | Ben Culkin <scorpress@gmail.com> | 2021-02-26 16:15:11 -0500 |
| commit | e55cb9852a106cff26517d7d1e85bc4b149884f3 (patch) | |
| tree | 2e5d8b8d1e4ec8a5893cc2372e84aade218e648a | |
| parent | d7b2e924d762e66b3f2be76242ee761e90113a8c (diff) | |
Update
| -rw-r--r-- | base/src/main/java/bjc/utils/cli/Command.java | 37 | ||||
| -rw-r--r-- | commander/.classpath | 28 | ||||
| -rw-r--r-- | commander/.gitignore | 1 | ||||
| -rw-r--r-- | commander/.project | 23 | ||||
| -rw-r--r-- | commander/.settings/org.eclipse.core.resources.prefs | 4 | ||||
| -rw-r--r-- | commander/.settings/org.eclipse.jdt.core.prefs | 8 | ||||
| -rw-r--r-- | commander/.settings/org.eclipse.m2e.core.prefs | 4 | ||||
| -rw-r--r-- | commander/pom.xml | 35 | ||||
| -rw-r--r-- | commander/src/example/java/bjc/commander/CommanderCLI.java | 27 | ||||
| -rw-r--r-- | commander/src/main/java/bjc/commander/Commander.java | 386 | ||||
| -rw-r--r-- | pom.xml | 5 |
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; + } +} @@ -34,5 +34,6 @@ <modules> <module>base</module> <module>clformat</module> - </modules> -</project> + <module>commander</module> + </modules> +</project>
\ No newline at end of file |
