diff --git a/gclc/changelog.md b/gclc/changelog.md new file mode 100644 index 0000000..b9d87b1 --- /dev/null +++ b/gclc/changelog.md @@ -0,0 +1,4 @@ +## Version 1.1.0 + +* Parsing quoted string as one argument +* Moved dependency of streams from system streams to external stream \ No newline at end of file diff --git a/gclc/pom.xml b/gclc/pom.xml index 65b2fcf..1a06f38 100644 --- a/gclc/pom.xml +++ b/gclc/pom.xml @@ -36,7 +36,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 gclc - 1.0.2-SNAPSHOT + 1.1.1-SNAPSHOT jar http://www.bigeon.fr/emmanuel @@ -52,7 +52,7 @@ fr.bigeon - bigeon-config + ebigeon-config 1.2 diff --git a/gclc/src/main/java/fr/bigeon/gclc/CommandRequestListener.java b/gclc/src/main/java/fr/bigeon/gclc/CommandRequestListener.java new file mode 100644 index 0000000..e7705d1 --- /dev/null +++ b/gclc/src/main/java/fr/bigeon/gclc/CommandRequestListener.java @@ -0,0 +1,48 @@ +/* + * Copyright Bigeon Emmanuel (2014) + * + * emmanuel@bigeon.fr + * + * This software is a computer program whose purpose is to + * provide a generic framework for console applications. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. + */ +/** + * gclc:fr.bigeon.gclc.CommandRequestListener.java + * Created on: Mar 19, 2015 + */ +package fr.bigeon.gclc; + +/**

+ * Command Request Listeners are listeners that are notified before a command is + * executed by the ConsoleApplication, + * + * @author Emmanuel Bigeon */ +public interface CommandRequestListener { + void commandRequest(String command); +} diff --git a/gclc/src/main/java/fr/bigeon/gclc/ConsoleApplication.java b/gclc/src/main/java/fr/bigeon/gclc/ConsoleApplication.java index 68a4219..f42fce7 100644 --- a/gclc/src/main/java/fr/bigeon/gclc/ConsoleApplication.java +++ b/gclc/src/main/java/fr/bigeon/gclc/ConsoleApplication.java @@ -37,13 +37,18 @@ package fr.bigeon.gclc; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import fr.bigeon.gclc.command.Command; import fr.bigeon.gclc.command.HelpExecutor; +import fr.bigeon.gclc.command.ICommandProvider; import fr.bigeon.gclc.command.SubedCommand; -import fr.bigeon.gclc.prompt.CLIPrompter; +import fr.bigeon.gclc.command.UnrecognizedCommand; +import fr.bigeon.gclc.exception.CommandRunException; +import fr.bigeon.gclc.exception.InvalidCommandName; import fr.bigeon.gclc.prompt.CLIPrompterMessages; +import fr.bigeon.gclc.system.SystemConsoleManager; /**

* A {@link ConsoleApplication} is an application that require the user to input @@ -53,7 +58,7 @@ import fr.bigeon.gclc.prompt.CLIPrompterMessages; * *

  * {@link ConsoleApplication} app = new {@link ConsoleApplication#ConsoleApplication(String, String, String) ConsoleApplication("exit", "welcome", "see you latter")};
- * app.{@link ConsoleApplication#add(String, Command) add}("my_command", new {@link Command MyCommand()});
+ * app.{@link ConsoleApplication#add(Command) add}("my_command", new {@link Command MyCommand()});
  * app.start();
  * 
*

@@ -63,60 +68,142 @@ import fr.bigeon.gclc.prompt.CLIPrompterMessages; * start method. * * @author Emmanuel BIGEON */ -public class ConsoleApplication { +public class ConsoleApplication implements ICommandProvider { - /** A space character */ - private static final String SPACE = " "; //$NON-NLS-1$ /** The welcome message */ private final String header; /** The good bye message */ private final String footer; + /** The console manager */ + protected final ConsoleManager manager; /** The container of commands */ - private final SubedCommand root = new SubedCommand( - new UnrecognizedCommand()); + private final SubedCommand root; /** The state of this application */ private boolean running; + /** The listeners */ + private final List listeners = new ArrayList<>(); + + /** @param manager the manager + * @param welcome the welcoming message + * @param goodbye the goodbye message */ + public ConsoleApplication(ConsoleManager manager, String welcome, + String goodbye) { + this.header = welcome; + this.footer = goodbye; + this.manager = manager; + root = new SubedCommand(new String(), new UnrecognizedCommand(manager)); + } + + /** @param manager the manager + * @param exit the keyword for the exit command of this application + * @param welcome the header message to display on launch of this + * application + * @param goodbye the message to display on exit */ + public ConsoleApplication(ConsoleManager manager, String exit, + String welcome, String goodbye) { + this(manager, welcome, goodbye); + try { + root.add(new ExitCommand(exit, this)); + } catch (InvalidCommandName e) { + throw new RuntimeException("Invalid exit command name", e); //$NON-NLS-1$ + } + } /** @param exit the keyword for the exit command of this application * @param welcome the header message to display on launch of this * application * @param goodbye the message to display on exit */ public ConsoleApplication(String exit, String welcome, String goodbye) { - super(); - this.header = welcome; - this.footer = goodbye; - root.add(exit, new ExitCommand(this)); + this(new SystemConsoleManager(), welcome, goodbye); + try { + root.add(new ExitCommand(exit, this)); + } catch (InvalidCommandName e) { + throw new RuntimeException("Invalid exit command name", e); //$NON-NLS-1$ + } } - /**

- * Add an executable command to this application - * - * @param key the command keyword - * @param cmd the command - * @return if the command was added */ - public boolean add(String key, Command cmd) { - return root.add(key, cmd); + @Override + public final boolean add(Command cmd) throws InvalidCommandName { + return root.add(cmd); } /** Launches the prompting application */ - public void start() { - if (header != null) System.out.println(header); + public final void start() { + if (header != null) manager.println(header); running = true; do { - String cmd = CLIPrompter.prompt(); - List args = new ArrayList<>(); - for (String string : cmd.split(SPACE)) { - if (!string.isEmpty()) args.add(string); + String cmd = manager.prompt(); + if (cmd.isEmpty()) { + continue; } - if (args.size() > 0) { - root.get(args.get(0)).execute(args.toArray(new String[0])); + for (CommandRequestListener listener : listeners) { + listener.commandRequest(cmd); } + interpretCommand(cmd); } while (running); - if (footer != null) System.out.println(footer); + if (footer != null) manager.println(footer); + } + + /** @param cmd the command to interpret */ + public final void interpretCommand(String cmd) { + List args = new ArrayList<>(); + // parse the string to separate arguments + int index = 0; + int startIndex = 0; + boolean escaped = false; + boolean inString = false; + while (index < cmd.length()) { + if (escaped) { + escaped = false; + } else if (cmd.charAt(index) == '\\') { + escaped = true; + } else if (cmd.charAt(index) == ' ') { + if (!inString) { + if (startIndex != index) { + String arg = cmd.substring(startIndex, index); + if (!arg.isEmpty()) { + args.add(arg); + } + } + startIndex = index + 1; + } + } else if (cmd.charAt(index) == '"') { + if (inString) { + inString = false; + args.add(cmd.substring(startIndex + 1, index)); + startIndex = index + 2; + index++; + if (index < cmd.length() && cmd.charAt(index) != ' ') { + manager.println("Command line cannot be parsed"); //$NON-NLS-1$ + return; + } + } else if (startIndex == index) { + inString = true; + } + } + index++; + } + if (startIndex < cmd.length()) { + String arg = cmd.substring(startIndex, index); + if (!arg.isEmpty()) { + args.add(arg); + } + } + if (args.size() > 0) { + try { + executeSub( + args.get(0), + Arrays.copyOfRange(args.toArray(new String[0]), 1, + args.size())); + } catch (CommandRunException e) { + manager.println("The command '" + cmd + "' failed due to:"); //$NON-NLS-1$ //$NON-NLS-2$ + manager.println(e.getLocalizedMessage()); + } + } } /** Exit this running application before next command prompt */ - public void exit() { + public final void exit() { running = false; } @@ -124,8 +211,48 @@ public class ConsoleApplication { * * @param cmd the handle for help * @return if the help command was added */ - public boolean addHelpCommand(String cmd) { - return root.add(cmd, new HelpExecutor(root)); + public final boolean addHelpCommand(String cmd) { + try { + return root.add(new HelpExecutor(cmd, manager, root)); + } catch (InvalidCommandName e) { + return false; + } + } + + /* (non-Javadoc) + * @see fr.bigeon.gclc.command.ICommandProvider#get(java.lang.String) */ + @Override + public final Command get(String command) { + return root.get(command); + } + + /* (non-Javadoc) + * @see + * fr.bigeon.gclc.command.ICommandProvider#executeSub(java.lang.String, + * java.lang.String[]) */ + @Override + public final void executeSub(String command, String... args) { + root.executeSub(command, args); + } + + /** @return the manager */ + public final ConsoleManager getManager() { + return manager; + } + + /** @param listener the listener to remove. */ + public final void addListener(CommandRequestListener listener) { + listeners.add(listener); + } + + /** @param listener the listener to remove */ + public final void removeListener(CommandRequestListener listener) { + for (int i = 0; i < listeners.size(); i++) { + if (listeners.get(i) == listener) { + listeners.remove(i); + return; + } + } } } @@ -133,15 +260,18 @@ public class ConsoleApplication { * A command to exit a {@link ConsoleApplication}. * * @author Emmanuel BIGEON */ -class ExitCommand implements Command { +class ExitCommand extends Command { + /** The exit command manual message key */ + private static final String EXIT_MAN = "exit.man"; //$NON-NLS-1$ + /** The tip of the exit command */ + private static final String EXIT = "exit.tip"; //$NON-NLS-1$ /** The application that will be exited when this command runs */ private final ConsoleApplication app; - /** The tip of the exit command */ - private final String EXIT = "exit.tip"; //$NON-NLS-1$ - /** @param app the application to exit */ - public ExitCommand(ConsoleApplication app) { - super(); + /** @param name the name of the command + * @param app the application to exit */ + public ExitCommand(String name, ConsoleApplication app) { + super(name); this.app = app; } @@ -151,37 +281,19 @@ class ExitCommand implements Command { } @Override - public void help(String[] args) { - System.out.println("\t" + tip()); //$NON-NLS-1$ + public void help(ConsoleManager manager, String... args) { + manager.println(CLIPrompterMessages + .getString(EXIT_MAN, (Object[]) args)); } @Override - public void execute(String[] args) { + public final void execute(String... args) { + beforeExit(); app.exit(); } -} -/**

- * The error message for unrecognized commands - * - * @author Emmanuel BIGEON */ -class UnrecognizedCommand implements Command { - /** The unrecognized command key */ - private static final String UNRECOGNIZED_CMD = "unrecognized.cmd"; //$NON-NLS-1$ - - @Override - public String tip() { - return null; - } - - @Override - public void help(String[] args) { - // Nothing to do (no help provided as this is one (usually) - } - - @Override - public void execute(String[] args) { - System.out.println(CLIPrompterMessages.getString(UNRECOGNIZED_CMD, - args[0])); + /** The actions to take before exiting */ + public void beforeExit() { + // Do nothing by default } } diff --git a/gclc/src/main/java/fr/bigeon/gclc/ConsoleManager.java b/gclc/src/main/java/fr/bigeon/gclc/ConsoleManager.java new file mode 100644 index 0000000..24ce4a1 --- /dev/null +++ b/gclc/src/main/java/fr/bigeon/gclc/ConsoleManager.java @@ -0,0 +1,74 @@ +/* + * Copyright Bigeon Emmanuel (2014) + * + * emmanuel@bigeon.fr + * + * This software is a computer program whose purpose is to + * provide a generic framework for console applications. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. + */ +/** + * gclc:fr.bigeon.gclc.ConsoleManager.java + * Created on: Dec 19, 2014 + */ +package fr.bigeon.gclc; + +/**

+ * A console manager is in charge of the basic prompts and prints on a console. + *

+ * Depending on the implementation, some manager may not support the prompt with + * a message, in that case, the usual behavior is to prompt normally. + * + * @author Emmanuel BIGEON */ +public interface ConsoleManager { + + /**

+ * Set a prompting prefix. + * + * @param prompt the prompt */ + void setPrompt(String prompt); + + /** @return the prompt prefix */ + String getPrompt(); + + /** @return the user inputed string */ + String prompt(); + + /** @param message the message to prompt the user + * @return the user inputed string */ + String prompt(String message); + + /** @param message the message to print */ + void println(String message); + + /** Prints an end of line */ + void println(); + + /** @param text the message to print (without line break at the end). */ + void print(String text); +} diff --git a/gclc/src/main/java/fr/bigeon/gclc/command/Command.java b/gclc/src/main/java/fr/bigeon/gclc/command/Command.java index 28f3954..012f75d 100644 --- a/gclc/src/main/java/fr/bigeon/gclc/command/Command.java +++ b/gclc/src/main/java/fr/bigeon/gclc/command/Command.java @@ -38,23 +38,62 @@ */ package fr.bigeon.gclc.command; -/** - *

- * A command to execute. +import fr.bigeon.gclc.ConsoleManager; + +/**

+ * A command to execute. It is mandatory that it has a name and that name cannot + * start with minus character or contain space * - * @author Emmanuel BIGEON - */ -public interface Command { - /** - * @param args the arguments of the command (some expect an empty array) - */ - public void execute(String[] args); + * @author Emmanuel BIGEON */ +public abstract class Command { + + /** The name of the command */ + protected final String name; + + /** @param name the command name */ + public Command(String name) { + super(); + this.name = name; + } + + /** @return the command's name */ + public final String getCommandName() { + return name; + } + + /** @param args the arguments of the command (some expect an empty array) */ + public abstract void execute(String... args); /** This prints the help associated to this command * + * @param manager the manager to print the data * @param args the arguments called with the help */ - public void help(String[] args); + public void help(ConsoleManager manager, String... args) { + manager.println(getCommandName()); + manager.println(brief()); + manager.println(); + manager.println("Usage:"); //$NON-NLS-1$ + manager.println(usagePattern()); + manager.println(); + manager.print(usageDetail()); + } + + /** @return the detailed help (should end with end of line or be empty) */ + @SuppressWarnings("static-method") + protected String usageDetail() { + return new String(); + } + + /** @return the usage pattern */ + protected String usagePattern() { + return getCommandName(); + } + + /** @return a brief description of the command */ + protected String brief() { + return " " + tip(); //$NON-NLS-1$ + } /** @return a tip on the command */ - public String tip(); + public abstract String tip(); } diff --git a/gclc/src/main/java/fr/bigeon/gclc/command/CommandParameters.java b/gclc/src/main/java/fr/bigeon/gclc/command/CommandParameters.java new file mode 100644 index 0000000..4e79e68 --- /dev/null +++ b/gclc/src/main/java/fr/bigeon/gclc/command/CommandParameters.java @@ -0,0 +1,134 @@ +/* + * Copyright Bigeon Emmanuel (2014) + * + * emmanuel@bigeon.fr + * + * This software is a computer program whose purpose is to + * provide a generic framework for console applications. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. + */ +/** + * gclc:fr.bigeon.gclc.command.CommandParameters.java + * Created on: Dec 24, 2014 + */ +package fr.bigeon.gclc.command; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Set; + +/**

+ * TODO + * + * @author Emmanuel BIGEON */ +public class CommandParameters { + private final HashMap boolArgs = new HashMap<>(); + private final HashMap stringArgs = new HashMap<>(); + private final boolean strict; + private final List additional = new ArrayList<>(); + + /** @param bools the boolean parameters + * @param strings the string parameters + * @param strict if the argument are restricted to the declared ones */ + @SuppressWarnings("boxing") + public CommandParameters(Set bools, Set strings, + boolean strict) { + for (String string : bools) { + boolArgs.put(string, false); + } + for (String string : strings) { + stringArgs.put(string, null); + } + this.strict = strict; + } + + /** @param args the arguments to parse + * @return if the arguments were parsed */ + @SuppressWarnings("boxing") + public boolean parseArgs(String... args) { + int i = 0; + while (i < args.length) { + String name = args[i]; + if (name.startsWith("-")) { //$NON-NLS-1$ + name = name.substring(1); + if (boolArgs.containsKey(name)) { + boolArgs.put(name, true); + } else if (stringArgs.containsKey(name)) { + i++; + if (!(args.length > i)) { + return false; + } + stringArgs.put(name, args[i]); + } else if (strict) { + return false; + } else { + additional.add(name); + } + } + i++; + } + return true; + } + + /** @param key the key + * @return the associated value, null if it was not specified */ + public String get(String key) { + return stringArgs.get(key); + } + + /** @param key the key + * @return if the key was specified */ + @SuppressWarnings("boxing") + public boolean getBool(String key) { + return boolArgs.get(key); + } + + /** @param string the key + * @param value the value */ + public void set(String string, String value) { + if (stringArgs.containsKey(string)) { + stringArgs.put(string, value); + } + } + + /** @return additional non parsed parameters */ + public List getAdditionals() { + return Collections.unmodifiableList(additional); + } + + /** @param string the key + * @param value the value */ + @SuppressWarnings("boxing") + public void set(String string, boolean value) { + if (boolArgs.containsKey(string)) { + boolArgs.put(string, value); + } + } +} diff --git a/gclc/src/main/java/fr/bigeon/gclc/command/CommandProvider.java b/gclc/src/main/java/fr/bigeon/gclc/command/CommandProvider.java index 7a014e7..d0cdec0 100644 --- a/gclc/src/main/java/fr/bigeon/gclc/command/CommandProvider.java +++ b/gclc/src/main/java/fr/bigeon/gclc/command/CommandProvider.java @@ -36,8 +36,10 @@ * Created on: Aug 6, 2014 */ package fr.bigeon.gclc.command; -import java.util.HashMap; -import java.util.Map; +import java.util.HashSet; +import java.util.Set; + +import fr.bigeon.gclc.exception.InvalidCommandName; /**

* A command provider is a map of key word to command to execute @@ -45,34 +47,49 @@ import java.util.Map; * @author Emmanuel BIGEON */ public class CommandProvider implements ICommandProvider { /** The commands map */ - protected final Map commands; + protected final Set commands; /** The error command to be executed when the command isn't recognized */ protected final Command error; /** @param error the error command */ public CommandProvider(Command error) { super(); - commands = new HashMap<>(); + commands = new HashSet<>(); this.error = error; } /* (non-Javadoc) * @see fr.bigeon.gclc.command.ICommandProvider#get(java.lang.String) */ @Override - public Command get(String command) { - Command cmd = commands.get(command); - if (cmd == null) return error; - return cmd; + public Command get(String commandName) { + for (Command command : commands) { + if (command.getCommandName().equals(commandName)) { + return command; + } + } + return null; } /* (non-Javadoc) * @see fr.bigeon.gclc.command.ICommandProvider#add(java.lang.String, * fr.bigeon.gclc.command.Command) */ @Override - public boolean add(String key, Command value) { - if (commands.containsKey(key)) return false; - commands.put(key, value); - return true; + public boolean add(Command value) throws InvalidCommandName { + String name = value.getCommandName(); + if (name == null || name.startsWith("-") || name.contains(" ")) { //$NON-NLS-1$ //$NON-NLS-2$ + throw new InvalidCommandName(); + } + return commands.add(value); } + @Override + public void executeSub(String cmd, String... args) { + for (Command command : commands) { + if (command.getCommandName().equals(cmd)) { + command.execute(args); + return; + } + } + error.execute(cmd); + } } diff --git a/gclc/src/main/java/fr/bigeon/gclc/command/HelpExecutor.java b/gclc/src/main/java/fr/bigeon/gclc/command/HelpExecutor.java index 09f3bec..d0ca323 100644 --- a/gclc/src/main/java/fr/bigeon/gclc/command/HelpExecutor.java +++ b/gclc/src/main/java/fr/bigeon/gclc/command/HelpExecutor.java @@ -38,40 +38,52 @@ */ package fr.bigeon.gclc.command; +import fr.bigeon.gclc.ConsoleManager; +import fr.bigeon.gclc.exception.InvalidCommandName; import fr.bigeon.gclc.prompt.CLIPrompterMessages; -/** - *

+/**

* TODO * - * @author Emmanuel BIGEON - * - */ -public class HelpExecutor implements Command { + * @author Emmanuel BIGEON */ +public class HelpExecutor extends Command { /** The command to execute the help of */ private final Command cmd; + /** The console manager */ + private final ConsoleManager consoleManager; - /** @param cmd the command to execute the help of */ - public HelpExecutor(Command cmd) { - super(); + /** @param cmdName the command name + * @param consoleManager the manager for the console + * @param cmd the command to execute the help of + * @throws InvalidCommandName if the name is invalid */ + public HelpExecutor(String cmdName, ConsoleManager consoleManager, + Command cmd) throws InvalidCommandName { + super(cmdName); this.cmd = cmd; + if (consoleManager == null) + throw new NullPointerException( + "Argument cannot be null: ConsoleManager"); //$NON-NLS-1$ + this.consoleManager = consoleManager; } /* (non-Javadoc) - * @see fr.bigeon.gclc.command.Command#execute(java.lang.String[]) - */ + * @see fr.bigeon.gclc.command.Command#execute(java.lang.String[]) */ @Override - public void execute(String[] args) { - cmd.help(args); + public void execute(String... args) { + cmd.help(consoleManager, args); } /* (non-Javadoc) - * @see fr.bigeon.gclc.command.Command#help() - */ + * @see fr.bigeon.gclc.command.Command#help() */ @Override - public void help(String[] args) { - cmd.help(args); + public void help(ConsoleManager manager, String... args) { + manager.println(getCommandName()); + manager.println(" A command to get help for other commands"); //$NON-NLS-1$ + manager.println(); + manager.println("Usage"); //$NON-NLS-1$ + manager.println(" " + getCommandName() + " "); //$NON-NLS-1$ //$NON-NLS-2$ + manager.println(); } /* (non-Javadoc) @@ -80,5 +92,4 @@ public class HelpExecutor implements Command { public String tip() { return CLIPrompterMessages.getString("help.cmd.tip"); //$NON-NLS-1$ } - } diff --git a/gclc/src/main/java/fr/bigeon/gclc/command/ICommandProvider.java b/gclc/src/main/java/fr/bigeon/gclc/command/ICommandProvider.java index 058d471..b4f888c 100644 --- a/gclc/src/main/java/fr/bigeon/gclc/command/ICommandProvider.java +++ b/gclc/src/main/java/fr/bigeon/gclc/command/ICommandProvider.java @@ -36,6 +36,8 @@ * Created on: Sep 6, 2014 */ package fr.bigeon.gclc.command; +import fr.bigeon.gclc.exception.InvalidCommandName; + /**

* An ICommadProvider is a provider of commands that can register commands under * some keywords. @@ -43,17 +45,35 @@ package fr.bigeon.gclc.command; * @author Emmanuel BIGEON */ public interface ICommandProvider { - /** @param command the name of the command the user wishes to execute + /**

+ * This method provide the command with the given name found. If no command + * with this name is found, an error command is usually returned. If there + * are several commands with the same name, the behavior is unspecified. + * Depending on the implementation, it may return an error command or the + * first command with this name found. + * + * @param command the name of the command the user wishes to execute * @return the command to execute */ public Command get(String command); /**

- * Adds a command to this provider, if ne command was associated with the + * Adds a command to this provider, if no command was associated with the * given key * - * @param key the command key * @param value the command to execute - * @return if the command was added */ - public boolean add(String key, Command value); + * @return if the command was added + * @throws InvalidCommandName if the command name is invalid */ + public boolean add(Command value) throws InvalidCommandName; + + /**

+ * This method executes the command with the given name found. If no command + * with this name is found, an error command is usually executed. If there + * are several commands with the same name, the behavior is unspecified. + * Depending on the implementation, it may run an error command or prompt + * the user for a choice. + * + * @param command the name of the command the user wishes to execute + * @param args the arguments for the command */ + public void executeSub(String command, String... args); } \ No newline at end of file diff --git a/gclc/src/main/java/fr/bigeon/gclc/command/ParametrizedCommand.java b/gclc/src/main/java/fr/bigeon/gclc/command/ParametrizedCommand.java new file mode 100644 index 0000000..2bb862e --- /dev/null +++ b/gclc/src/main/java/fr/bigeon/gclc/command/ParametrizedCommand.java @@ -0,0 +1,133 @@ +/* + * Copyright Bigeon Emmanuel (2014) + * + * emmanuel@bigeon.fr + * + * This software is a computer program whose purpose is to + * provide a generic framework for console applications. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. + */ +/** + * gclc:fr.bigeon.gclc.command.ParametrizedCommand.java + * Created on: Dec 24, 2014 + */ +package fr.bigeon.gclc.command; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import fr.bigeon.gclc.ConsoleManager; + +/**

+ * TODO + * + * @author Emmanuel BIGEON */ +public abstract class ParametrizedCommand extends Command { + + private boolean interactive = true; + protected final ConsoleManager manager; + private final Map boolParams = new HashMap<>(); + private final Map stringParams = new HashMap<>(); + private final Map params = new HashMap<>(); + private final boolean strict; + + /** @param manager the manager + * @param name the name */ + public ParametrizedCommand(ConsoleManager manager, String name) { + this(manager, name, true); + } + + /** @param manager the manager + * @param name the name + * @param strict if the arguments are restricted to the declared ones */ + public ParametrizedCommand(ConsoleManager manager, String name, + boolean strict) { + super(name); + this.manager = manager; + interactive = manager != null; + this.strict = strict; + } + + /**

+ * Add a parameter to the defined parameters + * + * @param param the parameter identification + * @param stringOrBool if the parameter is a parameter with an argument + * @param needed if the parameter is required */ + @SuppressWarnings("boxing") + protected void addParameter(String param, boolean stringOrBool, + boolean needed) { + if (params.containsKey(param)) { + return; + } + params.put(param, needed); + if (stringOrBool) { + stringParams.put(param, needed); + } else { + if (needed) { + // ERROR the boolean parameters cannot be needed + } + boolParams.put(param, needed); + } + } + + /* (non-Javadoc) + * @see fr.bigeon.gclc.command.Command#execute(java.lang.String[]) */ + @SuppressWarnings("boxing") + @Override + public final void execute(String... args) { + CommandParameters parameters = new CommandParameters( + boolParams.keySet(), stringParams.keySet(), strict); + if (!parameters.parseArgs(args)) { + // ERROR the parameters could not be correctly parsed + manager.println("Unable to read arguments"); //$NON-NLS-1$ + return; + } + List toProvide = new ArrayList<>(); + for (String string : params.keySet()) { + if (params.get(string) && parameters.get(string) == null) { + if (!interactive) return; + toProvide.add(string); + } + } + // for each needed parameters that is missing, prompt the user. + for (String string : toProvide) { + String value = manager.prompt(string); + while (value.isEmpty()) { + value = manager.prompt(string); + } + parameters.set(string, value); + } + doExecute(parameters); + } + + /** @param parameters the command parameters */ + protected abstract void doExecute(CommandParameters parameters); +} diff --git a/gclc/src/main/java/fr/bigeon/gclc/command/SubedCommand.java b/gclc/src/main/java/fr/bigeon/gclc/command/SubedCommand.java index 6783271..478f3c2 100644 --- a/gclc/src/main/java/fr/bigeon/gclc/command/SubedCommand.java +++ b/gclc/src/main/java/fr/bigeon/gclc/command/SubedCommand.java @@ -38,11 +38,15 @@ package fr.bigeon.gclc.command; import java.util.Arrays; +import fr.bigeon.gclc.ConsoleManager; +import fr.bigeon.gclc.exception.InvalidCommandName; + /**

- * TODO + * A subed command is a command that can execute sub commands depending on the + * first argument. * * @author Emmanuel BIGEON */ -public class SubedCommand extends CommandProvider implements Command { +public class SubedCommand extends Command implements ICommandProvider { /**

* The command to execute when this command is called with no sub arguments. @@ -50,36 +54,47 @@ public class SubedCommand extends CommandProvider implements Command { private final Command noArgCommand; /** A tip on this command. */ private final String tip; + /** The provider */ + private final CommandProvider provider; - /** @param error the error to execute when called with wrong usage */ - public SubedCommand(Command error) { - super(error); + /** @param name the name of the command + * @param error the error to execute when called with wrong usage */ + public SubedCommand(String name, Command error) { + super(name); + provider = new CommandProvider(error); noArgCommand = null; tip = null; } - /** @param error the error to execute when called with wrong usage + /** @param name the name of the command + * @param error the error to execute when called with wrong usage * @param tip the help tip associated */ - public SubedCommand(Command error, String tip) { - super(error); + public SubedCommand(String name, Command error, String tip) { + super(name); + provider = new CommandProvider(error); noArgCommand = null; this.tip = tip; } - /** @param noArgCommand the command to execute + /** @param name the name of the command + * @param noArgCommand the command to execute * @param error the error to execute when called with wrong usage * @param tip the help tip associated */ - public SubedCommand(Command error, Command noArgCommand, String tip) { - super(error); + public SubedCommand(String name, Command error, Command noArgCommand, + String tip) { + super(name); + provider = new CommandProvider(error); this.noArgCommand = noArgCommand; this.tip = tip; } - /** @param noArgCommand the command to execute when no extra parameter are + /** @param name the name of the command + * @param noArgCommand the command to execute when no extra parameter are * provided * @param error the error to execute when called with wrong usage */ - public SubedCommand(Command error, Command noArgCommand) { - super(error); + public SubedCommand(String name, Command error, Command noArgCommand) { + super(name); + provider = new CommandProvider(error); this.noArgCommand = noArgCommand; tip = null; } @@ -87,34 +102,38 @@ public class SubedCommand extends CommandProvider implements Command { /* (non-Javadoc) * @see fr.bigeon.acide.Command#execute(java.lang.String[]) */ @Override - public void execute(String[] args) { - if (args.length == 1) { + public void execute(String... args) { + if (args.length == 0 || args[0].startsWith("-")) { //$NON-NLS-1$ if (noArgCommand != null) noArgCommand.execute(args); - else error.execute(args); + else provider.error.execute(args); } else { - get(args[1]).execute(Arrays.copyOfRange(args, 1, args.length)); + executeSub(args[0], Arrays.copyOfRange(args, 1, args.length)); } } /* (non-Javadoc) * @see fr.bigeon.gclc.command.Command#help() */ @Override - public void help(String[] args) { - // TODO Auto-generated method stub - if (args.length != 1) { + public void help(ConsoleManager manager, String... args) { + if (args.length != 0 && !args[0].startsWith("-")) { //$NON-NLS-1$ // Specific - get(args[1]).help(Arrays.copyOfRange(args, 1, args.length)); + Command c = get(args[0]); + if (c != null) + c.help(manager, Arrays.copyOfRange(args, 1, args.length)); + else { + provider.error.execute(args); + } } else { // Generic if (noArgCommand != null) if (noArgCommand.tip() != null) - System.out.println("\t" + noArgCommand.tip()); //$NON-NLS-1$ - for (String string : commands.keySet()) { - if (commands.get(string).tip() == null) - System.out.println("\t" + string); //$NON-NLS-1$ - else System.out.println("\t" + string + ": " + //$NON-NLS-1$ //$NON-NLS-2$ - commands.get(string).tip()); + manager.println("\t" + noArgCommand.tip()); //$NON-NLS-1$ + for (Command cmd : provider.commands) { + if (cmd.tip() == null) + manager.println("\t" + cmd.getCommandName()); //$NON-NLS-1$ + else manager.println("\t" + cmd.getCommandName() + ": " + //$NON-NLS-1$ //$NON-NLS-2$ + cmd.tip()); } } } @@ -125,4 +144,31 @@ public class SubedCommand extends CommandProvider implements Command { public String tip() { return tip; } + + @Override + public Command get(String commandName) { + return provider.get(commandName); + } + + @Override + public boolean add(Command value) throws InvalidCommandName { + return provider.add(value); + } + + /* (non-Javadoc) + * @see + * fr.bigeon.gclc.command.ICommandProvider#executeSub(java.lang.String, + * java.lang.String[]) */ + @Override + public void executeSub(String command, String... args) { + provider.executeSub(command, args); + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() */ + @Override + public String toString() { + return "SubedCommand " + provider; //$NON-NLS-1$ + } + } diff --git a/gclc/src/main/java/fr/bigeon/gclc/command/UnrecognizedCommand.java b/gclc/src/main/java/fr/bigeon/gclc/command/UnrecognizedCommand.java new file mode 100644 index 0000000..c9222f2 --- /dev/null +++ b/gclc/src/main/java/fr/bigeon/gclc/command/UnrecognizedCommand.java @@ -0,0 +1,84 @@ +/* + * Copyright Bigeon Emmanuel (2014) + * + * emmanuel@bigeon.fr + * + * This software is a computer program whose purpose is to + * provide a generic framework for console applications. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. + */ +/** + * gclc:fr.bigeon.gclc.UnrecognizedCommand.java + * Created on: Dec 23, 2014 + */ +package fr.bigeon.gclc.command; + +import fr.bigeon.gclc.ConsoleManager; +import fr.bigeon.gclc.prompt.CLIPrompterMessages; + +/**

+ * The error message for unrecognized commands + * + * @author Emmanuel BIGEON */ +public final class UnrecognizedCommand extends Command { + /** The unrecognized command key */ + private static final String UNRECOGNIZED_CMD = "unrecognized.cmd"; //$NON-NLS-1$ + /** The unrecognized command key */ + private static final String EXPECTED_CMD = "expected.cmd"; //$NON-NLS-1$ + /** The manager */ + private final ConsoleManager manager; + + /** @param manager the console manager to use */ + public UnrecognizedCommand(ConsoleManager manager) { + super(new String()); + if (manager == null) { + throw new NullPointerException("The argument cannot be null"); //$NON-NLS-1$ + } + this.manager = manager; + } + + @Override + public String tip() { + return null; + } + + @Override + public void help(@SuppressWarnings("hiding") ConsoleManager manager, + String... args) { + // Nothing to do (no help provided as this is not a user command + // (usually) + } + + @Override + public void execute(String... args) { + if (args.length > 0) + manager.println(CLIPrompterMessages.getString(UNRECOGNIZED_CMD, + (Object[]) args)); + else manager.println(CLIPrompterMessages.getString(EXPECTED_CMD)); + } +} \ No newline at end of file diff --git a/gclc/src/main/java/fr/bigeon/gclc/exception/CommandRunException.java b/gclc/src/main/java/fr/bigeon/gclc/exception/CommandRunException.java new file mode 100644 index 0000000..8a31ae5 --- /dev/null +++ b/gclc/src/main/java/fr/bigeon/gclc/exception/CommandRunException.java @@ -0,0 +1,74 @@ +/* + * Copyright Bigeon Emmanuel (2014) + * + * emmanuel@bigeon.fr + * + * This software is a computer program whose purpose is to + * provide a generic framework for console applications. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. + */ +/** + * gclc:fr.bigeon.gclc.exception.CommandRunException.java + * Created on: Feb 10, 2015 + */ +package fr.bigeon.gclc.exception; + +/**

+ * An exception thrown when a command failed to run correctly. + * + * @author Emmanuel BIGEON */ +public class CommandRunException extends RuntimeException { + + /** + * + */ + private static final long serialVersionUID = 1L; + + /** @param message a message + * @param cause the cause */ + public CommandRunException(String message, Throwable cause) { + super(message, cause); + } + + /** @param message a message */ + public CommandRunException(String message) { + super(message); + } + + /* (non-Javadoc) + * @see java.lang.Throwable#getLocalizedMessage() */ + @Override + public String getLocalizedMessage() { + if (getCause() != null) { + return super.getLocalizedMessage() + ": " + //$NON-NLS-1$ + getCause().getLocalizedMessage(); + } + return super.getLocalizedMessage(); + } + +} diff --git a/gclc/src/main/java/fr/bigeon/gclc/exception/InvalidCommandName.java b/gclc/src/main/java/fr/bigeon/gclc/exception/InvalidCommandName.java new file mode 100644 index 0000000..7e4d27d --- /dev/null +++ b/gclc/src/main/java/fr/bigeon/gclc/exception/InvalidCommandName.java @@ -0,0 +1,50 @@ +/* + * Copyright Bigeon Emmanuel (2014) + * + * emmanuel@bigeon.fr + * + * This software is a computer program whose purpose is to + * provide a generic framework for console applications. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. + */ +/** + * gclc:fr.bigeon.gclc.exception.InvalidCommandName.java + * Created on: Dec 23, 2014 + */ +package fr.bigeon.gclc.exception; + +/** + *

+ * TODO + * + * @author Emmanuel BIGEON + * + */ +public class InvalidCommandName extends Exception { + +} diff --git a/gclc/src/main/java/fr/bigeon/gclc/prompt/CLIPrompter.java b/gclc/src/main/java/fr/bigeon/gclc/prompt/CLIPrompter.java index 8a89326..a5dc533 100644 --- a/gclc/src/main/java/fr/bigeon/gclc/prompt/CLIPrompter.java +++ b/gclc/src/main/java/fr/bigeon/gclc/prompt/CLIPrompter.java @@ -36,11 +36,12 @@ * Created on: Jul 31, 2014 */ package fr.bigeon.gclc.prompt; -import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; +import fr.bigeon.gclc.ConsoleManager; + /**

* The {@link CLIPrompter} class is a utility class that provides method to * prompt the user. @@ -54,56 +55,76 @@ public class CLIPrompter { private static final String LIST_DISP_KEY = "promptlist.exit.dispkey"; //$NON-NLS-1$ @SuppressWarnings("javadoc") private static final String PROMPT = "prompt.lineprompt"; //$NON-NLS-1$ - /** @param choices the choices - * @param cancel the cancel option if it exists - * @return the number of choices plus one */ - private static int listChoices(List choices, String cancel) { - int index = 0; - for (U u : choices) { - System.out.println((index++) + ") " + u); //$NON-NLS-1$ - } - if (cancel != null) { - System.out.println((index++) + ") " + cancel); //$NON-NLS-1$ - } - return --index; + + /** @param manager the manager + * @param prompt the prompting message + * @param reprompt the prompting message after empty input + * @return the non empty input */ + public static String promptNonEmpty(ConsoleManager manager, String prompt, + String reprompt) { + String res = manager.prompt(prompt); + while (res.isEmpty()) + res = manager.prompt(reprompt); + return res; } - /**

- * Prompts the user for an input - * - * @return the user input */ - public static String prompt() { - - // TODO Auto-generated method stub - return prompt(CLIPrompterMessages.getString(PROMPT) + " "); //$NON-NLS-1$ - } - - /** This method prompts for a user input + /** Prompt for a text with several lines. * + * @param manager the manager * @param message the prompting message - * @return the user entered line */ - public static String prompt(String message) { - String result = ""; //$NON-NLS-1$ - System.out.print(message + " "); //$NON-NLS-1$ - char c; - try { - c = (char) System.in.read(); - while (c != System.lineSeparator().charAt(0)) { - result += c; - c = (char) System.in.read(); - } - while (System.in.available() != 0) - System.in.read(); - } catch (IOException e) { - e.printStackTrace(); + * @param ender the ender character + * @return the text */ + public static String promptLongText(ConsoleManager manager, String message, + String ender) { + manager.println(message + + CLIPrompterMessages.getString( + "promptlongtext.exit.dispkey", ender)); //$NON-NLS-1$ + String res = manager.prompt(PROMPT); + String line = res; + while (!line.equals(ender)) { + line = manager.prompt(PROMPT); + if (!line.equals(ender)) res += System.lineSeparator() + line; } - return result; + return res.equals(ender) ? "" : res; //$NON-NLS-1$ } - /** @param message the prompting message + /** Prompt for a text with several lines. + * + * @param manager the manager + * @param message the prompting message + * @return the text */ + public static String promptLongText(ConsoleManager manager, String message) { + return promptLongText(manager, message, + CLIPrompterMessages.getString("promptlongtext.exit.defaultkey")); //$NON-NLS-1$ + } + + /** @param manager the manager + * @param message the prompt message + * @return the integer */ + public static int promptInteger(ConsoleManager manager, String message) { + boolean still = true; + int r = 0; + while (still) { + String result = manager.prompt(message); + try { + if (result.isEmpty()) { + still = true; + continue; + } + r = Integer.parseInt(result); + still = false; + } catch (Exception e) { + still = true; + } + } + return r; + } + + /** @param manager the manager + * @param message the prompting message * @return the choice */ - public static boolean promptBoolean(String message) { - String result = prompt(message + + public static boolean promptBoolean(ConsoleManager manager, String message) { + String result = manager.prompt(message + CLIPrompterMessages.getString(BOOL_CHOICES)); boolean first = true; String choices = CLIPrompterMessages @@ -121,10 +142,10 @@ public class CLIPrompter { result) || CLIPrompterMessages.getString( "promptbool.choices.yes2").equalsIgnoreCase(result))) { //$NON-NLS-1$ if (!first) { - - System.out.println(CLIPrompterMessages.getString( + + manager.println(CLIPrompterMessages.getString( "promptbool.choices.invalid", choices)); //$NON-NLS-1$ - result = prompt(message + + result = manager.prompt(message + CLIPrompterMessages.getString(BOOL_CHOICES)); } first = false; @@ -135,56 +156,8 @@ public class CLIPrompter { .getString("promptbool.choices.yes2")); //$NON-NLS-1$ } - /** @param the type of choices - * @param choices the list of choices - * @param message the prompting message - * @param cancel the cancel option, or null - * @return the index of the choice */ - @SuppressWarnings("boxing") - public static Integer promptChoice(List choices, String message, - String cancel) { - System.out.println(message); - int index = listChoices(choices, cancel); - String result = ""; //$NON-NLS-1$ - boolean keepOn = true; - int r = -1; - while (keepOn) { - result = prompt(CLIPrompterMessages.getString(PROMPT)); - try { - r = Integer.parseInt(result); - if (r >= 0 && r <= index) - keepOn = false; - else { - System.out.println(CLIPrompterMessages.getString( - "promptchoice.outofbounds", 0, index)); //$NON-NLS-1$ - listChoices(choices, cancel); - } - - } catch (NumberFormatException e) { - keepOn = true; - System.out.println(CLIPrompterMessages.getString( - "promptchoice.formaterr", 0, index)); //$NON-NLS-1$ - listChoices(choices, cancel); - } - } - if (r == index && cancel != null) return null; - return r; - } - - /** @param keys the keys to be printed - * @param choices the real choices - * @param message the message - * @param cancel the cancel option, or null - * @return the choice */ - @SuppressWarnings("boxing") - public static U promptChoice(List keys, List choices, - String message, String cancel) { - Integer index = promptChoice(keys, message, cancel); - if (index == null) return null; - return choices.get(index); - } - - /** @param The choices labels type + /** @param manager the manager + * @param The choices labels type * @param The real choices objects * @param choices the list of labels (in order to be displayed) * @param choicesMap the map of label to actual objects @@ -192,132 +165,153 @@ public class CLIPrompter { * @param cancel the cancel option if it exists (null otherwise) * @return the chosen object */ @SuppressWarnings("boxing") - public static T promptChoice(List choices, Map choicesMap, + public static T promptChoice(ConsoleManager manager, + List choices, + Map choicesMap, String message, String cancel) { - System.out.println(message); - int index = listChoices(choices, cancel); + manager.println(message); + int index = listChoices(manager, choices, cancel); String result = ""; //$NON-NLS-1$ boolean keepOn = true; int r = -1; while (keepOn) { - result = prompt(CLIPrompterMessages.getString(PROMPT)); + result = manager.prompt(CLIPrompterMessages.getString(PROMPT)); try { r = Integer.parseInt(result); if (r >= 0 && r <= index) keepOn = false; else { - System.out.println(CLIPrompterMessages.getString( + manager.println(CLIPrompterMessages.getString( "promptchoice.outofbounds", 0, index)); //$NON-NLS-1$ - listChoices(choices, cancel); + listChoices(manager, choices, cancel); } } catch (NumberFormatException e) { keepOn = true; - System.out.println(CLIPrompterMessages.getString( + manager.println(CLIPrompterMessages.getString( "promptchoice.formaterr", 0, index)); //$NON-NLS-1$ - listChoices(choices, cancel); + listChoices(manager, choices, cancel); } } if (r == index && cancel != null) return null; return choicesMap.get(choices.get(r)); } - /** @param The choices labels type + /** @param manager the manager + * @param The choices labels type * @param The real choices objects * @param choicesMap the map of label to actual objects * @param message the prompting message * @param cancel the cancel option (or null) * @return the chosen object */ - public static T promptChoice(Map choicesMap, String message, + public static T promptChoice(ConsoleManager manager, + Map choicesMap, + String message, String cancel) { - return promptChoice(new ArrayList<>(choicesMap.keySet()), choicesMap, + return promptChoice(manager, new ArrayList<>(choicesMap.keySet()), + choicesMap, message, cancel); } - /** @param message the prompt message - * @return the integer */ - public static int promptInteger(String message) { - boolean still = true; - int r = 0; - while (still) { - String result = prompt(message); + /** @param manager the manager + * @param the type of choices + * @param choices the list of choices + * @param message the prompting message + * @param cancel the cancel option, or null + * @return the index of the choice */ + @SuppressWarnings("boxing") + public static Integer promptChoice(ConsoleManager manager, + List choices, + String message, + String cancel) { + manager.println(message); + int index = listChoices(manager, choices, cancel); + String result = ""; //$NON-NLS-1$ + boolean keepOn = true; + int r = -1; + while (keepOn) { + result = manager.prompt(CLIPrompterMessages.getString(PROMPT)); try { - if (result.isEmpty()) { - still = true; - continue; - } r = Integer.parseInt(result); - still = false; - } catch (Exception e) { - still = true; + if (r >= 0 && r <= index) + keepOn = false; + else { + manager.println(CLIPrompterMessages.getString( + "promptchoice.outofbounds", 0, index)); //$NON-NLS-1$ + listChoices(manager, choices, cancel); + } + + } catch (NumberFormatException e) { + keepOn = true; + manager.println(CLIPrompterMessages.getString( + "promptchoice.formaterr", 0, index)); //$NON-NLS-1$ + listChoices(manager, choices, cancel); } } + if (r == index && cancel != null) return null; return r; } - /** This methods prompt the user for a list of elements - * + /** @param manager the manager + * @param keys the keys to be printed + * @param choices the real choices * @param message the message - * @return the list of user inputs */ - public static List promptList(String message) { - return promptList(message, - CLIPrompterMessages.getString("promptlist.exit.defaultkey")); //$NON-NLS-1$ + * @param cancel the cancel option, or null + * @param the type of elements + * @return the choice */ + @SuppressWarnings("boxing") + public static U promptChoice(ConsoleManager manager, List keys, + List choices, + String message, String cancel) { + Integer index = promptChoice(manager, keys, message, cancel); + if (index == null) return null; + return choices.get(index); + } + + /** @param manager the manager + * @param choices the choices + * @param cancel the cancel option if it exists + * @return the number of choices plus one */ + private static int listChoices(ConsoleManager manager, List choices, + String cancel) { + int index = 0; + for (U u : choices) { + manager.println((index++) + ") " + u); //$NON-NLS-1$ + } + if (cancel != null) { + manager.println((index++) + ") " + cancel); //$NON-NLS-1$ + } + return --index; } /** This methods prompt the user for a list of elements * + * @param manager the manager * @param message the message * @param ender the ending sequence for the list * @return the list of user inputs */ - public static List promptList(String message, String ender) { + public static List promptList(ConsoleManager manager, + String message, + String ender) { List strings = new ArrayList<>(); - System.out.println(message + + manager.println(message + CLIPrompterMessages.getString(LIST_DISP_KEY, ender)); String res = null; while (!ender.equals(res)) { - res = CLIPrompter.prompt(CLIPrompterMessages.getString(PROMPT)); + res = manager.prompt(CLIPrompterMessages.getString(PROMPT)); if (!res.equals(ender)) strings.add(res); } return strings; } - /** Prompt for a text with several lines. + /** This methods prompt the user for a list of elements * - * @param message the prompting message - * @return the text */ - public static String promptLongText(String message) { - return promptLongText(message, - CLIPrompterMessages.getString("promptlongtext.exit.defaultkey")); //$NON-NLS-1$ + * @param manager the manager + * @param message the message + * @return the list of user inputs */ + public static List promptList(ConsoleManager manager, String message) { + return promptList(manager, message, + CLIPrompterMessages.getString("promptlist.exit.defaultkey")); //$NON-NLS-1$ } - /** Prompt for a text with several lines. - * - * @param message the prompting message - * @param ender the ender character - * @return the text */ - public static String promptLongText(String message, String ender) { - System.out.println(message + - CLIPrompterMessages.getString( - "promptlongtext.exit.dispkey", ender)); //$NON-NLS-1$ - String res = CLIPrompter.prompt(PROMPT); - String line = res; - while (!line.equals(ender)) { - line = CLIPrompter.prompt(PROMPT); - if (!line.equals(ender)) res += System.lineSeparator() + line; - } - return res.equals(ender) ? "" : res; //$NON-NLS-1$ - } - - /** @param prompt the prompting message - * @param reprompt the prompting message after empty input - * @return the non empty input */ - public static String promptNonEmpty(String prompt, String reprompt) { - String res = prompt(prompt); - while (res.isEmpty()) - res = prompt(reprompt); - return res; - } - - /** Utility class hidden constructor */ - private CLIPrompter() {} } diff --git a/gclc/src/main/java/fr/bigeon/gclc/system/SystemConsoleManager.java b/gclc/src/main/java/fr/bigeon/gclc/system/SystemConsoleManager.java new file mode 100644 index 0000000..8012ce3 --- /dev/null +++ b/gclc/src/main/java/fr/bigeon/gclc/system/SystemConsoleManager.java @@ -0,0 +1,125 @@ +/* + * Copyright Bigeon Emmanuel (2014) + * + * emmanuel@bigeon.fr + * + * This software is a computer program whose purpose is to + * provide a generic framework for console applications. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. + */ +/** + * gclc:fr.bigeon.gclc.system.SystemConsoleManager.java + * Created on: Dec 19, 2014 + */ +package fr.bigeon.gclc.system; + +import java.io.IOException; + +import fr.bigeon.gclc.ConsoleManager; + +/** + *

+ * TODO + * + * @author Emmanuel BIGEON + * + */ +public class SystemConsoleManager implements ConsoleManager { + + /** The default prompt */ + public static final String DEFAULT_PROMPT = ">"; //$NON-NLS-1$ + + /** The command prompt. It can be changed. */ + private String prompt = DEFAULT_PROMPT; + + /** @return the prompt */ + @Override + public String getPrompt() { + return prompt; + } + + /** @param prompt the prompt to set */ + @Override + public void setPrompt(String prompt) { + this.prompt = prompt; + } + + /* (non-Javadoc) + * @see fr.bigeon.gclc.ConsoleManager#prompt() + */ + @Override + public String prompt() { + return prompt(new String() + prompt); + } + + /* (non-Javadoc) + * @see fr.bigeon.gclc.ConsoleManager#prompt(java.lang.String) + */ + @Override + public String prompt(String message) { + String result = new String(); + System.out.print(message + ' '); + char c; + try { + c = (char) System.in.read(); + while (c != System.lineSeparator().charAt(0)) { + result += c; + c = (char) System.in.read(); + } + while (System.in.available() != 0) + System.in.read(); + } catch (IOException e) { + e.printStackTrace(); + } + return result; + } + + /* (non-Javadoc) + * @see fr.bigeon.gclc.ConsoleManager#println(java.lang.Object) + */ + @Override + public void println(String object) { + System.out.println(object); + } + + /* (non-Javadoc) + * @see fr.bigeon.gclc.ConsoleManager#print(java.lang.Object) + */ + @Override + public void print(String object) { + System.out.print(object); + } + + /* (non-Javadoc) + * @see fr.bigeon.gclc.ConsoleManager#println() */ + @Override + public void println() { + System.out.println(); + } + +} diff --git a/gclc/src/main/java/fr/bigeon/gclc/system/package-info.java b/gclc/src/main/java/fr/bigeon/gclc/system/package-info.java new file mode 100644 index 0000000..445b10f --- /dev/null +++ b/gclc/src/main/java/fr/bigeon/gclc/system/package-info.java @@ -0,0 +1,46 @@ +/* + * Copyright Bigeon Emmanuel (2014) + * + * emmanuel@bigeon.fr + * + * This software is a computer program whose purpose is to + * provide a generic framework for console applications. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. + */ +/** + * gclc:fr.bigeon.gclc.system.package-info.java + * Created on: Dec 19, 2014 + */ +/** + *

+ * The basic system based console manager elements + * + * @author Emmanuel BIGEON + * + */ +package fr.bigeon.gclc.system; \ No newline at end of file diff --git a/gclc/src/main/java/fr/bigeon/gclc/tools/PrintUtils.java b/gclc/src/main/java/fr/bigeon/gclc/tools/PrintUtils.java new file mode 100644 index 0000000..50533a1 --- /dev/null +++ b/gclc/src/main/java/fr/bigeon/gclc/tools/PrintUtils.java @@ -0,0 +1,99 @@ +/* + * Copyright Bigeon Emmanuel (2014) + * + * emmanuel@bigeon.fr + * + * This software is a computer program whose purpose is to + * provide a generic framework for console applications. + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. + */ +/** + * acide:fr.bigeon.acide.tools.PrintUtils.java + * Created on: Jan 20, 2015 + */ +package fr.bigeon.gclc.tools; + +import java.util.ArrayList; +import java.util.List; + +/** + *

+ * TODO + * + * @author Emmanuel BIGEON + * + */ +public class PrintUtils { + /** @param text the text to print + * @param nbCharacters the number of characters of the resulting text + * @param indicateTooLong if an indication shell be given that the text + * didn't fit + * @return the text to print (will be of exactly nbCharacters). */ + public static String print(String text, int nbCharacters, + boolean indicateTooLong) { + String res = text; + if (res.length() > nbCharacters) { + // Cut + if (indicateTooLong) { + // With suspension dots + res = res.substring(0, nbCharacters - 3) + "..."; //$NON-NLS-1$ + } else { + res = res.substring(0, nbCharacters); + } + } + while (res.length() < nbCharacters) { + // Add trailing space + res = res + ' '; + } + return res; + } + + /** @param description the element to wrap in lines + * @param i the length of the wrap + * @return the list of resulting strings */ + public static List wrap(String description, int i) { + String[] originalLines = description.split(System.lineSeparator()); + List result = new ArrayList<>(); + for (String string : originalLines) { + String toCut = string; + while (toCut.length() > i) { + int index = toCut.lastIndexOf(" ", i); //$NON-NLS-1$ + if (index == -1) { + result.add(toCut.substring(0, i)); + index = i - 1; + } else { + result.add(toCut.substring(0, index)); + } + toCut = toCut.substring(index + 1); + } + result.add(toCut); + result.add(new String()); + } + return result; + } +} diff --git a/gclc/src/main/resources/fr/bigeon/gclc/messages.properties b/gclc/src/main/resources/fr/bigeon/gclc/messages.properties index 072284d..573c359 100644 --- a/gclc/src/main/resources/fr/bigeon/gclc/messages.properties +++ b/gclc/src/main/resources/fr/bigeon/gclc/messages.properties @@ -21,3 +21,4 @@ promptlongtext.exit.defaultkey=\\q promptlongtext.exit.dispkey=\ (exit with a new line made of "{0}") unrecognized.cmd=Unrecognized command "{0}" +expected.cmd=A Command was expected but nothing was provided...