summaryrefslogtreecommitdiff
path: root/BJC-Utils2/src/main/java/bjc/utils/cli/fds
diff options
context:
space:
mode:
Diffstat (limited to 'BJC-Utils2/src/main/java/bjc/utils/cli/fds')
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/cli/fds/FDS.java69
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/cli/fds/FDSCommand.java26
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/cli/fds/FDSException.java32
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/cli/fds/FDSMode.java94
-rw-r--r--BJC-Utils2/src/main/java/bjc/utils/cli/fds/SimpleFDSMode.java142
5 files changed, 363 insertions, 0 deletions
diff --git a/BJC-Utils2/src/main/java/bjc/utils/cli/fds/FDS.java b/BJC-Utils2/src/main/java/bjc/utils/cli/fds/FDS.java
new file mode 100644
index 0000000..6dc0337
--- /dev/null
+++ b/BJC-Utils2/src/main/java/bjc/utils/cli/fds/FDS.java
@@ -0,0 +1,69 @@
+package bjc.utils.cli.fds;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import bjc.utils.ioutils.Block;
+import bjc.utils.ioutils.BlockReader;
+
+/**
+ * Runs a FDS (FDiskScript) interface.
+ *
+ * This is a rudimentary console interface inspired heavily by FDisk's interface
+ * style.
+ *
+ * Commands are denoted by a single character, but can invoke submodes.
+ *
+ * @author bjculkin
+ *
+ */
+public class FDS {
+ /**
+ * Run a provided FDS mode until it is exited or there is no more input.
+ *
+ * @param comin
+ * The command input source for the FDS mode.
+ *
+ * @param datain
+ * The data input source for the FDS mode.
+ *
+ * @param out
+ * The output source for the FDS mode.
+ *
+ * @param initialMode
+ * The mode to start in.
+ *
+ * @param initialState
+ * The initial state for the mode.
+ *
+ * @return The final state of the mode.
+ *
+ * @throws FDSException
+ * If something went wrong during mode execution.
+ */
+ public static <S> S runFDS(InputStream comin, InputStream datain, OutputStream out, FDSMode<S> initialMode,
+ S initialState) throws FDSException {
+ PrintStream printer = new PrintStream(out);
+
+ try (BlockReader blockSource = new BlockReader("\\R", new InputStreamReader(comin))) {
+ printer.print("Enter a command (m for help): ");
+
+ while (blockSource.hasNext()) {
+ Block comBlock = blockSource.next();
+
+ String comString = comBlock.contents.trim();
+
+ char comChar = comString.charAt(0);
+
+ printer.println(String.format("\nRecieved command '%s'\n", comChar));
+
+ printer.print("Enter a command (m for help): ");
+ }
+ } catch (Exception ex) {
+ throw new FDSException("Unexpected I/O error", ex);
+ }
+
+ return initialState;
+ }
+}
diff --git a/BJC-Utils2/src/main/java/bjc/utils/cli/fds/FDSCommand.java b/BJC-Utils2/src/main/java/bjc/utils/cli/fds/FDSCommand.java
new file mode 100644
index 0000000..0a6d29c
--- /dev/null
+++ b/BJC-Utils2/src/main/java/bjc/utils/cli/fds/FDSCommand.java
@@ -0,0 +1,26 @@
+package bjc.utils.cli.fds;
+
+import java.util.Iterator;
+
+/**
+ * A command attached to an FDS interface.
+ *
+ * @author bjculkin
+ *
+ * @param <S>
+ * The state type of the interface.
+ */
+public interface FDSCommand<S> {
+ /**
+ * Run this command.
+ *
+ * @param state
+ * The current FDS state.
+ *
+ * @param input
+ * The source for data input.
+ *
+ * @return The new state, after running the command.
+ */
+ S run(S state, Iterator<String> input);
+}
diff --git a/BJC-Utils2/src/main/java/bjc/utils/cli/fds/FDSException.java b/BJC-Utils2/src/main/java/bjc/utils/cli/fds/FDSException.java
new file mode 100644
index 0000000..7569d95
--- /dev/null
+++ b/BJC-Utils2/src/main/java/bjc/utils/cli/fds/FDSException.java
@@ -0,0 +1,32 @@
+package bjc.utils.cli.fds;
+
+/**
+ * Exception thrown when something goes wrong with FDS.
+ *
+ * @author bjculkin
+ *
+ */
+public class FDSException extends Exception {
+ /**
+ * Create a new FDS exception with a message and a cause.
+ *
+ * @param message
+ * The message for the exception.
+ *
+ * @param cause
+ * The cause of the exception.
+ */
+ public FDSException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * Create a new FDS exception with a message.
+ *
+ * @param message
+ * The message for the exception.
+ */
+ public FDSException(String message) {
+ super(message);
+ }
+}
diff --git a/BJC-Utils2/src/main/java/bjc/utils/cli/fds/FDSMode.java b/BJC-Utils2/src/main/java/bjc/utils/cli/fds/FDSMode.java
new file mode 100644
index 0000000..acdcbcb
--- /dev/null
+++ b/BJC-Utils2/src/main/java/bjc/utils/cli/fds/FDSMode.java
@@ -0,0 +1,94 @@
+package bjc.utils.cli.fds;
+
+import bjc.utils.cli.CommandHelp;
+import bjc.utils.cli.NullHelp;
+
+/**
+ * A collection of related FDS commands.
+ *
+ * @author bjculkin
+ *
+ * @param <S>
+ * The FDS state type.
+ */
+public interface FDSMode<S> {
+ /**
+ * Get all the characters that are registered to something in this mode.
+ *
+ * In this context, something means a command or submode.
+ *
+ * @return All of the characters registered to something in this mode.
+ */
+ char[] registeredChars();
+
+ /*
+ * Check for the existence of commands/submodes.
+ */
+
+ /**
+ * Check if there is a command registered to the given character.
+ *
+ * @param c
+ * The character to check
+ *
+ * @return Whether or not there is a command bound to that character.
+ */
+ boolean hasCommand(char c);
+
+ /**
+ * Check if there is a submode registered to the given character.
+ *
+ * @param c
+ * The character to check
+ *
+ * @return Whether or not there is a submode bound to that character.
+ */
+ boolean hasSubmode(char c);
+
+ /*
+ * Get commands and submodes.
+ */
+
+ /**
+ * Get the command attached to a given character.
+ *
+ * @param c
+ * The character to get the command for.
+ *
+ * @return The command bound to that character.
+ *
+ * @throws FDSException
+ * If there is no command bound to that character.
+ */
+ FDSCommand<S> getCommand(char c) throws FDSException;
+
+ /**
+ * Get the command attached to a given character.
+ *
+ * @param c
+ * The character to get the command for.
+ *
+ * @return The command bound to that character.
+ *
+ * @throws FDSException
+ * If there is no command bound to that character.
+ */
+ FDSMode<S> getSubmode(char c) throws FDSException;
+
+ /*
+ * Help utilities
+ */
+ /**
+ * Get the help for what's bound to a character.
+ *
+ * This should be one line.
+ *
+ * @param c
+ * The character to look at the help for.
+ *
+ * @return The help for what's bound to the character.
+ */
+ default CommandHelp getHelp(char c) {
+ return new NullHelp();
+ }
+}
diff --git a/BJC-Utils2/src/main/java/bjc/utils/cli/fds/SimpleFDSMode.java b/BJC-Utils2/src/main/java/bjc/utils/cli/fds/SimpleFDSMode.java
new file mode 100644
index 0000000..2f6e660
--- /dev/null
+++ b/BJC-Utils2/src/main/java/bjc/utils/cli/fds/SimpleFDSMode.java
@@ -0,0 +1,142 @@
+package bjc.utils.cli.fds;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import bjc.utils.cli.CommandHelp;
+
+import static java.lang.String.format;
+
+/**
+ * Simple implementation of {@link FDSMode}.
+ *
+ * @author bjculkin
+ *
+ * @param <S>
+ * The FDS state type.
+ */
+public class SimpleFDSMode<S> implements FDSMode<S> {
+ private Map<Character, FDSCommand<S>> commands;
+ private Map<Character, FDSMode<S>> modes;
+ private Map<Character, CommandHelp> help;
+
+ private Set<Character> registered;
+ private char[] registeredArray;
+ private boolean changed;
+
+ /**
+ * Create a new empty FDS mode.
+ */
+ public SimpleFDSMode() {
+ commands = new HashMap<>();
+ modes = new HashMap<>();
+ help = new HashMap<>();
+
+ registered = new HashSet<>();
+ changed = true;
+ }
+
+ /**
+ * Add a command to the mode.
+ *
+ * @param c
+ * The character to bind to the command.
+ *
+ * @param comm
+ * The command to add.
+ *
+ * @param hlp
+ * The help for the command.
+ *
+ * @throws FDSException
+ * If the character is already bound to a command.
+ */
+ public void addCommand(char c, FDSCommand<S> comm, CommandHelp hlp) throws FDSException {
+ if (comm == null)
+ throw new NullPointerException("Command must not be null");
+ else if (commands.containsKey(c) || modes.containsKey(c))
+ throw new FDSException(format("Character '%s' is already bound"));
+
+ commands.put(c, comm);
+
+ registered.add(c);
+ if (!changed) changed = true;
+ }
+
+ /**
+ * Add a submode to the mode.
+ *
+ * @param c
+ * The character to bind to the submode.
+ *
+ * @param mode
+ * The submode to add.
+ *
+ * @throws FDSException
+ * If the character is already bound to a submode.
+ */
+ public void addSubmode(char c, FDSMode<S> mode) throws FDSException {
+ if (mode == null)
+ throw new NullPointerException("Mode must not be null");
+ else if (modes.containsKey(c) || commands.containsKey(c))
+ throw new FDSException(format("Character '%s' is already bound"));
+
+ modes.put(c, mode);
+
+ registered.add(c);
+ if (!changed) changed = true;
+ }
+
+ @Override
+ public char[] registeredChars() {
+ if (!changed) return registeredArray;
+
+ registeredArray = new char[registered.size()];
+
+ int i = 0;
+ for (char c : registered) {
+ registeredArray[i] = c;
+
+ i += 1;
+ }
+
+ changed = false;
+
+ return registeredArray;
+ }
+
+ @Override
+ public boolean hasCommand(char c) {
+ return commands.containsKey(c);
+ }
+
+ @Override
+ public boolean hasSubmode(char c) {
+ return modes.containsKey(c);
+ }
+
+ @Override
+ public FDSCommand<S> getCommand(char c) throws FDSException {
+ if (!commands.containsKey(c)) {
+ throw new FDSException(String.format("No command bound to '%s'", c));
+ }
+
+ return commands.get(c);
+ }
+
+ @Override
+ public FDSMode<S> getSubmode(char c) throws FDSException {
+ if (!modes.containsKey(c)) {
+ throw new FDSException(String.format("No mode bound to '%s'", c));
+ }
+
+ return modes.get(c);
+ }
+
+ @Override
+ public CommandHelp getHelp(char c) {
+ return help.get(c);
+ }
+} \ No newline at end of file