diff --git a/gclc-socket/pom.xml b/gclc-socket/pom.xml index f883aaa..cbe6360 100644 --- a/gclc-socket/pom.xml +++ b/gclc-socket/pom.xml @@ -87,7 +87,7 @@ of Emmanuel Bigeon. --> fr.bigeon gclc - 1.1.2 + 1.2.0-SNAPSHOT fr.bigeon diff --git a/gclc-socket/src/main/java/fr/bigeon/gclc/socket/ThreadedServerConsoleManager.java b/gclc-socket/src/main/java/fr/bigeon/gclc/socket/ThreadedServerConsoleManager.java index 3297dac..aa9dcef 100644 --- a/gclc-socket/src/main/java/fr/bigeon/gclc/socket/ThreadedServerConsoleManager.java +++ b/gclc-socket/src/main/java/fr/bigeon/gclc/socket/ThreadedServerConsoleManager.java @@ -71,6 +71,10 @@ public class ThreadedServerConsoleManager implements ConsoleManager { private BufferedReader input; /** the prompting status */ private boolean doPrompt; + /** + * + */ + private boolean closed = false; /** Create the console manager. * @@ -159,6 +163,14 @@ public class ThreadedServerConsoleManager implements ConsoleManager { @Override public void close() throws IOException { // Do nothing + this.closed = true; + } + + /* (non-Javadoc) + * @see fr.bigeon.gclc.manager.ConsoleManager#isClosed() */ + @Override + public boolean isClosed() { + return closed; } } diff --git a/gclc-socket/src/test/java/fr/bigeon/gclc/socket/ConsoleRunnableTest.java b/gclc-socket/src/test/java/fr/bigeon/gclc/socket/ConsoleRunnableTest.java index 0208d98..af4fa31 100644 --- a/gclc-socket/src/test/java/fr/bigeon/gclc/socket/ConsoleRunnableTest.java +++ b/gclc-socket/src/test/java/fr/bigeon/gclc/socket/ConsoleRunnableTest.java @@ -116,6 +116,13 @@ public class ConsoleRunnableTest { public void close() throws IOException { // do nothing } + + /* (non-Javadoc) + * @see fr.bigeon.gclc.manager.ConsoleManager#isClosed() */ + @Override + public boolean isClosed() { + return i == cmds.length; + } } /** Test method for @@ -136,7 +143,7 @@ public class ConsoleRunnableTest { Object lock = new Object(); ConsoleApplication app = new ConsoleTestApplication( new ConsoleManagerTestImplementation( - new String[] {"test", ConsoleTestApplication.EXIT})); + new String[] {"test", ConsoleTestApplication.EXIT})); //$NON-NLS-1$ ConsoleRunnable runnable = new ConsoleRunnable(app, lock); Thread th = new Thread(runnable); @@ -151,7 +158,7 @@ public class ConsoleRunnableTest { Object lock = new Object(); ConsoleApplication app = new ConsoleTestApplication( new ConsoleManagerTestImplementation( - new String[] {"test", ConsoleTestApplication.EXIT})); + new String[] {"test", ConsoleTestApplication.EXIT})); //$NON-NLS-1$ ConsoleRunnable runnable = new ConsoleRunnable(app, lock); runnable.stop(); Thread th = new Thread(runnable); @@ -166,7 +173,7 @@ public class ConsoleRunnableTest { Object lock = new Object(); ConsoleApplication app = new ConsoleTestApplication( new ConsoleManagerTestImplementation( - new String[] {"test", ConsoleTestApplication.EXIT})); + new String[] {"test", ConsoleTestApplication.EXIT})); //$NON-NLS-1$ ConsoleRunnable runnable = new ConsoleRunnable(app, lock); runnable.run(); } diff --git a/gclc-socket/src/test/java/fr/bigeon/gclc/socket/ConsoleTestApplication.java b/gclc-socket/src/test/java/fr/bigeon/gclc/socket/ConsoleTestApplication.java index 202cbf0..c6ea6bf 100644 --- a/gclc-socket/src/test/java/fr/bigeon/gclc/socket/ConsoleTestApplication.java +++ b/gclc-socket/src/test/java/fr/bigeon/gclc/socket/ConsoleTestApplication.java @@ -34,12 +34,15 @@ */ package fr.bigeon.gclc.socket; +import java.io.IOException; + import fr.bigeon.gclc.ConsoleApplication; import fr.bigeon.gclc.command.Command; -import fr.bigeon.gclc.command.ICommand; +import fr.bigeon.gclc.command.ExitCommand; +import fr.bigeon.gclc.command.HelpExecutor; +import fr.bigeon.gclc.exception.CommandRunException; import fr.bigeon.gclc.exception.InvalidCommandName; import fr.bigeon.gclc.manager.ConsoleManager; -import fr.bigeon.gclc.prompt.CLIPrompterMessages; /** A test-purpose application * @@ -56,7 +59,7 @@ public class ConsoleTestApplication extends ConsoleApplication { "See you"); try { add(new ExitCommand(EXIT, this)); - addHelpCommand("help"); + add(new HelpExecutor("help", manager, this.getRoot())); add(new Command("test") { @Override @@ -65,63 +68,16 @@ public class ConsoleTestApplication extends ConsoleApplication { } @Override - public void execute(String... args) { - manager.println("Test command ran fine"); + public void execute(String... args) throws CommandRunException { + try { + manager.println("Test command ran fine"); + } catch (IOException e) { + throw new CommandRunException("manager closed", e); + } } }); } catch (final InvalidCommandName e) { e.printStackTrace(); } } - - /**

- * A command to exit a {@link ConsoleApplication}. - * - * @author Emmanuel BIGEON */ - class ExitCommand implements ICommand { - /** 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_TIP = "exit.tip"; //$NON-NLS-1$ - /** The application that will be exited when this command runs */ - private final ConsoleApplication app; - /** The exit command name */ - private final String name; - - /** @param name the name of the command - * @param app the application to exit */ - public ExitCommand(String name, ConsoleApplication app) { - this.name = name; - this.app = app; - } - - /** The actions to take before exiting */ - public void beforeExit() { - // Do nothing by default - } - - @Override - public final void execute(String... args) { - beforeExit(); - app.exit(); - } - - /* (non-Javadoc) - * @see fr.bigeon.gclc.command.ICommand#getCommandName() */ - @Override - public String getCommandName() { - return name; - } - - @Override - public void help(ConsoleManager helpManager, String... args) { - helpManager.println( - CLIPrompterMessages.getString(EXIT_MAN, (Object[]) args)); - } - - @Override - public String tip() { - return CLIPrompterMessages.getString(EXIT_TIP); - } - } } diff --git a/gclc-swt/pom.xml b/gclc-swt/pom.xml index 2b1b9ed..1d6c158 100644 --- a/gclc-swt/pom.xml +++ b/gclc-swt/pom.xml @@ -51,13 +51,18 @@ fr.bigeon gclc - 1.1.2 + 1.2.0-SNAPSHOT - org.eclipse.swt.gtk.linux - x86_64 - 3.3.0-v3346 + org.eclipse.swt + org.eclipse.swt.gtk.linux.x86_64 + 4.3 + + org.eclipse.swt + org.eclipse.swt.win32.win32.x86_64 + 4.3 + fr.bigeon collections diff --git a/gclc-swt/src/main/java/fr/bigeon/gclc/swt/SWTConsole.java b/gclc-swt/src/main/java/fr/bigeon/gclc/swt/SWTConsole.java index 6d57cc0..6b2c692 100644 --- a/gclc-swt/src/main/java/fr/bigeon/gclc/swt/SWTConsole.java +++ b/gclc-swt/src/main/java/fr/bigeon/gclc/swt/SWTConsole.java @@ -100,9 +100,9 @@ public class SWTConsole extends Composite implements ConsoleManager { if (e.keyCode == SWT.ARROW_UP && currentIndex < commands.size() - 1) { currentIndex++; - consoleInput.setText( - commands.get(commands.size() - currentIndex - 1)); - consoleInput.setSelection(consoleInput.getText().length()); + String cmd = commands.get(commands.size() - currentIndex - 1); + consoleInput.setText(cmd); + consoleInput.setSelection(cmd.length()); } // Lower arrow retrieves next commands @@ -111,9 +111,10 @@ public class SWTConsole extends Composite implements ConsoleManager { currentIndex--; consoleInput.setText(new String()); } else if (currentIndex > 0) { - consoleInput.setText(commands - .get(commands.size() - (--currentIndex) - 1)); - consoleInput.setSelection(consoleInput.getText().length()); + String cmd = commands + .get(commands.size() - (--currentIndex) - 1); + consoleInput.setText(cmd); + consoleInput.setSelection(cmd.length()); } } } @@ -245,8 +246,11 @@ public class SWTConsole extends Composite implements ConsoleManager { /* (non-Javadoc) * @see fr.bigeon.gclc.ConsoleManager#prompt() */ @Override - public String prompt() { + public String prompt() throws IOException { synchronized (promptLock) { + if (isDisposed()) { + throw new IOException(); + } try { Display.getDefault().syncExec(new Runnable() { @SuppressWarnings("synthetic-access") @@ -268,8 +272,11 @@ public class SWTConsole extends Composite implements ConsoleManager { /* (non-Javadoc) * @see fr.bigeon.gclc.ConsoleManager#prompt(java.lang.String) */ @Override - public String prompt(final String message) { + public String prompt(final String message) throws IOException { synchronized (promptLock) { + if (isDisposed()) { + throw new IOException(); + } try { Display.getDefault().syncExec(new Runnable() { @SuppressWarnings("synthetic-access") @@ -284,6 +291,9 @@ public class SWTConsole extends Composite implements ConsoleManager { }); prompting = true; promptLock.wait(); + if (isDisposed()) { + throw new IOException(); + } } catch (final InterruptedException e) { command = null; } finally { @@ -292,7 +302,8 @@ public class SWTConsole extends Composite implements ConsoleManager { @Override public void run() { lblPromptlabel.setText(prompt); - lblPromptlabel.pack(); + // relayout + SWTConsole.this.layout(); } }); } @@ -317,7 +328,8 @@ public class SWTConsole extends Composite implements ConsoleManager { @Override public void run() { lblPromptlabel.setText(prompt); - lblPromptlabel.pack(); + // relayout + SWTConsole.this.layout(); } }); } @@ -325,9 +337,22 @@ public class SWTConsole extends Composite implements ConsoleManager { /* (non-Javadoc) * @see fr.bigeon.gclc.manager.ConsoleManager#close() */ @Override - public void close() throws IOException { + public void close() { + synchronized (promptLock) { + promptLock.notify(); + } + if (consoleInput.isDisposed()) { + return; + } consoleInput.setEnabled(false); consoleOutput.setEnabled(false); } + /* (non-Javadoc) + * @see fr.bigeon.gclc.manager.ConsoleManager#isClosed() */ + @Override + public boolean isClosed() { + return isDisposed(); + } + } diff --git a/gclc-swt/src/main/java/fr/bigeon/gclc/swt/SWTConsoleShell.java b/gclc-swt/src/main/java/fr/bigeon/gclc/swt/SWTConsoleShell.java index 91feb86..e4f0d6f 100644 --- a/gclc-swt/src/main/java/fr/bigeon/gclc/swt/SWTConsoleShell.java +++ b/gclc-swt/src/main/java/fr/bigeon/gclc/swt/SWTConsoleShell.java @@ -79,4 +79,12 @@ public class SWTConsoleShell extends Shell { public ConsoleManager getManager() { return console; } + + /* (non-Javadoc) + * @see org.eclipse.swt.widgets.Shell#dispose() */ + @Override + public void dispose() { + super.dispose(); + console.close(); + } } diff --git a/gclc-swt/src/test/java/fr/bigeon/gclc/swt/AppTest.java b/gclc-swt/src/test/java/fr/bigeon/gclc/swt/AppTest.java deleted file mode 100644 index f706949..0000000 --- a/gclc-swt/src/test/java/fr/bigeon/gclc/swt/AppTest.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright E. Bigeon (2015) - * - * emmanuel@bigeon.fr - * - * This software is a computer program whose purpose is to - * provide a swt window 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. - */ -package fr.bigeon.gclc.swt; - -import junit.framework.Test; -import junit.framework.TestCase; -import junit.framework.TestSuite; - -/** Unit test for simple App. */ -public class AppTest extends TestCase { - protected static final long TWO_SECONDS = 2000; - - /** Create the test case - * - * @param testName name of the test case */ - public AppTest(String testName) { - super(testName); - } - - /** @return the suite of tests being tested */ - public static Test suite() { - return new TestSuite(AppTest.class); - } - - /** Rigourous Test :-) */ - @SuppressWarnings("static-method") - public void testApp() { -// Display display = new Display(); -// final Shell shell = new Shell(display); -// shell.setLayout(new GridLayout(1, false)); -// SWTConsole swtConsole = new SWTConsole(shell, SWT.NONE); -// swtConsole.setLayoutData( -// new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); -// final ConsoleApplication appl = new ConsoleApplication(swtConsole, -// "exit", "Hello", "See you"); -// try { -// appl.add(new Command("long") { -// -// @Override -// public String tip() { -// return "a long running command"; -// } -// -// @Override -// public void execute(String... args) { -// try { -// Thread.sleep(TWO_SECONDS); -// } catch (InterruptedException e) { -// // TODO Auto-generated catch block -// e.printStackTrace(); -// } -// } -// }); -// } catch (InvalidCommandName e) { -// // TODO Auto-generated catch block -// e.printStackTrace(); -// } -// shell.pack(); -// shell.open(); -// Thread applThread = new Thread(new Runnable() { -// -// @Override -// public void run() { -// appl.start(); -// Display.getDefault().syncExec(new Runnable() { -// @Override -// public void run() { -// shell.dispose(); -// } -// }); -// } -// }); -// applThread.start(); -// while (!shell.isDisposed()) { -// if (!display.readAndDispatch()) { -// display.sleep(); -// } -// } -// display.dispose(); - assertTrue(true); - } -} diff --git a/gclc-swt/src/test/java/fr/bigeon/gclc/swt/SWTConsoleShellTest.java b/gclc-swt/src/test/java/fr/bigeon/gclc/swt/SWTConsoleShellTest.java new file mode 100644 index 0000000..f057ce1 --- /dev/null +++ b/gclc-swt/src/test/java/fr/bigeon/gclc/swt/SWTConsoleShellTest.java @@ -0,0 +1,100 @@ +/** + * gclc-swt:fr.bigeon.gclc.swt.SWTConsoleShellTest.java + * Created on: Jun 8, 2016 + */ +package fr.bigeon.gclc.swt; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.eclipse.swt.widgets.Display; +import org.junit.Test; + +import fr.bigeon.gclc.ConsoleApplication; +import fr.bigeon.gclc.command.Command; +import fr.bigeon.gclc.exception.InvalidCommandName; +import fr.bigeon.gclc.manager.ConsoleManager; + +/** + *

+ * TODO + * + * @author Emmanuel Bigeon + * + */ +public class SWTConsoleShellTest { + + protected static final long TWO_SECONDS = 2000; + + @Test + public void test() { + Display display = new Display(); + final SWTConsoleShell shell = new SWTConsoleShell(display); + ConsoleManager swtConsole = shell.getManager(); + try { + final ConsoleApplication appl = new ConsoleApplication(swtConsole, + "exit", "Hello", "See you"); + appl.add(new Command("long") { + + @Override + public String tip() { + return "a long running command"; + } + + @Override + public void execute(String... args) { + try { + Thread.sleep(TWO_SECONDS); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + }); +// shell.pack(); + shell.open(); + Thread applThread = new Thread(new Runnable() { + + @Override + public void run() { + appl.start(); + } + }); + Thread testThread = new Thread(new Runnable() { + + @Override + public void run() { + try { + Thread.sleep(TWO_SECONDS); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + Display.getDefault().syncExec(new Runnable() { + @Override + public void run() { + shell.dispose(); + } + }); + } + }); + applThread.start(); + testThread.start(); + while (!shell.isDisposed()) { + if (!display.readAndDispatch()) { + display.sleep(); + } + } +// display.dispose(); + assertTrue(appl.getManager().isClosed()); + Thread.sleep(TWO_SECONDS); + assertFalse(appl.isRunning()); + } catch (InvalidCommandName e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } +} diff --git a/gclc/pom.xml b/gclc/pom.xml index fd343ae..30f1cf1 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.1.3-SNAPSHOT + 1.2.0-SNAPSHOT jar http://www.bigeon.fr/emmanuel diff --git a/gclc/src/main/java/fr/bigeon/gclc/ConsoleApplication.java b/gclc/src/main/java/fr/bigeon/gclc/ConsoleApplication.java index 49c171b..69a8fa4 100644 --- a/gclc/src/main/java/fr/bigeon/gclc/ConsoleApplication.java +++ b/gclc/src/main/java/fr/bigeon/gclc/ConsoleApplication.java @@ -36,12 +36,14 @@ * Created on: Sep 6, 2014 */ package fr.bigeon.gclc; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; +import fr.bigeon.gclc.command.ExitCommand; import fr.bigeon.gclc.command.HelpExecutor; import fr.bigeon.gclc.command.ICommand; import fr.bigeon.gclc.command.ICommandProvider; @@ -53,7 +55,6 @@ import fr.bigeon.gclc.exception.InvalidCommandName; import fr.bigeon.gclc.i18n.Messages; import fr.bigeon.gclc.manager.ConsoleManager; import fr.bigeon.gclc.manager.SystemConsoleManager; -import fr.bigeon.gclc.prompt.CLIPrompterMessages; /**

* A {@link ConsoleApplication} is an application that require the user to input @@ -64,7 +65,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(ICommand) add}("my_command", new {@link ICommand MyCommand()});
- * app.start();
+ * app.{@link ConsoleApplication#start start}();
  * 
*

* That will launch in the console application that will display "welcome", @@ -107,7 +108,10 @@ public class ConsoleApplication implements ICommandProvider { * @param welcome the header message to display on launch of this * application * @param goodbye the message to display on exit - * @throws InvalidCommandName if the exit command name is invalid */ + * @throws InvalidCommandName if the exit command name is invalid + * @deprecated since 1.2.0, use the {@link #add(ICommand)} method to add the + * exit command */ + @Deprecated public ConsoleApplication(ConsoleManager manager, String exit, String welcome, String goodbye) throws InvalidCommandName { this(manager, welcome, goodbye); @@ -118,7 +122,12 @@ public class ConsoleApplication implements ICommandProvider { * @param welcome the header message to display on launch of this * application * @param goodbye the message to display on exit - * @throws InvalidCommandName if the exit command name is invalid */ + * @throws InvalidCommandName if the exit command name is invalid + * @deprecated since 1.2.0, use the {@link #add(ICommand)} method to add the + * command and create this console using the + * {@link #ConsoleApplication(ConsoleManager, String, String)} + * method with a {@link SystemConsoleManager} as argument */ + @Deprecated public ConsoleApplication(String exit, String welcome, String goodbye) throws InvalidCommandName { this(new SystemConsoleManager(), welcome, goodbye); @@ -134,12 +143,19 @@ public class ConsoleApplication implements ICommandProvider { * * @param cmd the handle for help * @return if the help command was added - * @throws InvalidCommandName if the help command was not valid */ + * @throws InvalidCommandName if the help command was not valid + * @deprecated since 1.2.0 use the {@link #add(ICommand)} with a + * {@link HelpExecutor} instead */ + @Deprecated public final boolean addHelpCommand(String cmd) throws InvalidCommandName { return root.add(new HelpExecutor(cmd, manager, root)); } - /** @param listener the listener to remove. */ + /** Add a command request listener. + *

+ * A listener can listen several times to the same application. + * + * @param listener the listener to add. */ public final void addListener(CommandRequestListener listener) { listeners.add(listener); } @@ -149,12 +165,14 @@ public class ConsoleApplication implements ICommandProvider { * fr.bigeon.gclc.command.ICommandProvider#executeSub(java.lang.String, * java.lang.String[]) */ @Override - public final void executeSub(String command, String... args) { + public final void executeSub(String command, + String... args) throws CommandRunException { root.executeSub(command, args); } /** Exit this running application before next command prompt */ public final void exit() { + LOGGER.fine("Request exiting application..."); //$NON-NLS-1$ running = false; } @@ -170,11 +188,12 @@ public class ConsoleApplication implements ICommandProvider { return manager; } - /** @param cmd the command to interpret */ - public final void interpretCommand(String cmd) { + /** @param cmd the command to interpret + * @throws IOException if the manager was closed */ + public final void interpretCommand(String cmd) throws IOException { List args; try { - args = splitCommand(cmd); + args = GCLCConstants.splitCommand(cmd); } catch (CommandParsingException e1) { manager.println("Command line cannot be parsed"); //$NON-NLS-1$ LOGGER.log(Level.INFO, "Invalid user command " + cmd, e1); //$NON-NLS-1$ @@ -193,63 +212,7 @@ public class ConsoleApplication implements ICommandProvider { } } - /** Splits a command in the diferrent arguments - * - * @param cmd the command to split in its parts - * @return the list of argument preceded by the command name - * @throws CommandParsingException if the parsing of the command failed */ - private static List splitCommand(String cmd) throws CommandParsingException { - final 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()) { - char c = cmd.charAt(index); - index++; - if (escaped || c == '\\') { - escaped = !escaped; - continue; - } - if (c == ' ' && !inString) { - final String arg = cmd.substring(startIndex, index - 1); - if (!arg.isEmpty()) { - args.add(arg); - } - startIndex = index; - } else if (c == '"') { - if (inString) { - inString = false; - args.add(endOfString(cmd, startIndex, index)); - index++; - startIndex = index; - } - inString = startIndex == index - 1; - } - } - final String arg = cmd.substring(startIndex, cmd.length()); - if (!arg.isEmpty()) { - args.add(arg); - } - return args; - } - - /** @param cmd the command to parse - * @param startIndex the starting point of the parsing - * @param index the index of the current position - * @return the argument - * @throws CommandParsingException if the end of string does not mark end of - * command and is not followed by a space */ - private static String endOfString(String cmd, int startIndex, - int index) throws CommandParsingException { - if (index + 1 < cmd.length() && cmd.charAt(index + 1) != ' ') { - throw new CommandParsingException("Misplaced quote"); //$NON-NLS-1$ - } - return cmd.substring(startIndex + 1, index - 1); - } - - /** @param listener the listener to remove */ + /** @param listener the listener to remove (once) */ public final void removeListener(CommandRequestListener listener) { for (int i = 0; i < listeners.size(); i++) { if (listeners.get(i) == listener) { @@ -261,73 +224,41 @@ public class ConsoleApplication implements ICommandProvider { /** Launches the prompting application */ public final void start() { - if (header != null) { - manager.println(header); - } - running = true; - do { - final String cmd = manager.prompt(); - if (cmd.isEmpty()) { - continue; + try { + running = true; + if (header != null) { + manager.println(header); } - for (final CommandRequestListener listener : listeners) { - listener.commandRequest(cmd); + do { + final String cmd = manager.prompt(); + if (cmd == null || cmd.isEmpty()) { + continue; + } + for (final CommandRequestListener listener : listeners) { + listener.commandRequest(cmd); + } + interpretCommand(cmd); + } while (running); + if (footer != null) { + manager.println(footer); } - interpretCommand(cmd); - } while (running); - if (footer != null) { - manager.println(footer); + LOGGER.fine("Exiting application."); //$NON-NLS-1$ + } catch (IOException e) { + // The manager was closed + running = false; + LOGGER.log(Level.WARNING, + "The console manager was closed. Closing the application as no one can reach it.", //$NON-NLS-1$ + e); } } -} -/**

- * A command to exit a {@link ConsoleApplication}. - * - * @author Emmanuel BIGEON */ -class ExitCommand implements ICommand { - /** 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 exit command name */ - private final String name; - - /** @param name the name of the command - * @param app the application to exit */ - public ExitCommand(String name, ConsoleApplication app) { - this.name = name; - this.app = app; + /** @return if the application is running */ + public boolean isRunning() { + return running; } - /** The actions to take before exiting */ - public void beforeExit() { - // Do nothing by default - } - - @Override - public final void execute(String... args) { - beforeExit(); - app.exit(); - } - - /* (non-Javadoc) - * @see fr.bigeon.gclc.command.ICommand#getCommandName() */ - @Override - public String getCommandName() { - return name; - } - - @Override - public void help(ConsoleManager manager, String... args) { - manager.println( - CLIPrompterMessages.getString(EXIT_MAN, (Object[]) args)); - } - - @Override - public String tip() { - return CLIPrompterMessages.getString(EXIT); + /** @return the root */ + public SubedCommand getRoot() { + return root; } } diff --git a/gclc/src/main/java/fr/bigeon/gclc/GCLCConstants.java b/gclc/src/main/java/fr/bigeon/gclc/GCLCConstants.java new file mode 100644 index 0000000..e16977a --- /dev/null +++ b/gclc/src/main/java/fr/bigeon/gclc/GCLCConstants.java @@ -0,0 +1,129 @@ +/* + * 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.GCLCConstants.java + * Created on: Jun 8, 2016 + */ +package fr.bigeon.gclc; + +import java.util.ArrayList; +import java.util.List; + +import fr.bigeon.gclc.exception.CommandParsingException; + +/** A Utility class for GCLC + *

+ * This class offers a method to split a line or arguments into the list of + * arguments. + * + * @author Emmanuel Bigeon */ +public class GCLCConstants { + + /** Hide utility class constructor */ + private GCLCConstants() { + // utility class + } + + /** Splits a command in the diferrent arguments + * + * @param cmd the command to split in its parts + * @return the list of argument preceded by the command name + * @throws CommandParsingException if the parsing of the command failed */ + public static List splitCommand(String cmd) throws CommandParsingException { + final 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()) { + char c = cmd.charAt(index); + index++; + if (escaped || c == '\\') { + escaped = !escaped; + continue; + } + if (c == ' ' && !inString) { + final String arg = cmd.substring(startIndex, index - 1); + if (!arg.isEmpty()) { + args.add(removeEscaped(arg)); + } + startIndex = index; + } else if (c == '"') { + if (inString) { + args.add(endOfString(cmd, startIndex, index)); + index++; + startIndex = index; + } + inString = startIndex == index - 1; + } + } + final String arg = cmd.substring(startIndex, cmd.length()); + if (!arg.isEmpty()) { + args.add(arg); + } + return args; + } + + /** @param arg the string to remove excaping character from + * @return the string without escape character */ + private static String removeEscaped(String arg) { + StringBuilder builder = new StringBuilder(); + int index = 0; + int endIndex = arg.indexOf('\\'); + while (endIndex != -1) { + builder.append(arg.subSequence(index, endIndex)); + index = endIndex + 1; + endIndex = arg.indexOf('\\', index + 1); + } + builder.append(arg.substring(index)); + return builder.toString(); + } + + /** @param cmd the command to parse + * @param startIndex the starting point of the parsing + * @param index the index of the current position + * @return the argument + * @throws CommandParsingException if the end of string does not mark end of + * command and is not followed by a space */ + private static String endOfString(String cmd, int startIndex, + int index) throws CommandParsingException { + if (index < cmd.length() && cmd.charAt(index) != ' ') { + throw new CommandParsingException("Misplaced quote"); //$NON-NLS-1$ + } + return cmd.substring(startIndex + 1, index - 1); + } + +} 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 2fba973..e22433f 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,25 @@ */ package fr.bigeon.gclc.command; +import java.io.IOException; + import fr.bigeon.gclc.manager.ConsoleManager; /**

* A command to execute. It is mandatory that it has a name and that name cannot - * start with minus character or contain space. + * start with minus character or contain spaces. *

* A command can be executed, with parameters that will be provided as an array * of strings. *

- * The help mechanism can be overwritten, but is by default doing the following: + * The help mechanism is doing the following: *

*

* The default behavior for the brief message is to print the tip preceeded by a @@ -92,7 +94,8 @@ public abstract class Command implements ICommand { * @see fr.bigeon.gclc.command.ICommand#help(fr.bigeon.gclc.ConsoleManager, * java.lang.String) */ @Override - public final void help(ConsoleManager manager, String... args) { + public final void help(ConsoleManager manager, + String... args) throws IOException { manager.println(getCommandName()); manager.println(brief()); manager.println(); 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 e9152a7..2f97d17 100644 --- a/gclc/src/main/java/fr/bigeon/gclc/command/CommandProvider.java +++ b/gclc/src/main/java/fr/bigeon/gclc/command/CommandProvider.java @@ -39,7 +39,9 @@ package fr.bigeon.gclc.command; import java.util.HashSet; import java.util.Set; +import fr.bigeon.gclc.exception.CommandRunException; import fr.bigeon.gclc.exception.InvalidCommandName; +import fr.bigeon.gclc.i18n.Messages; /**

* A command provider is a map of key word to command to execute @@ -52,14 +54,11 @@ public class CommandProvider implements ICommandProvider { private static final String SPACE = " "; //$NON-NLS-1$ /** The commands map */ protected final Set commands; - /** The error command to be executed when the command isn't recognized */ - protected final ICommand error; - /** @param error the error command */ - public CommandProvider(ICommand error) { + /** Create a command provider */ + public CommandProvider() { super(); commands = new HashSet<>(); - this.error = error; } /* (non-Javadoc) @@ -75,14 +74,16 @@ public class CommandProvider implements ICommandProvider { } @Override - public void executeSub(String cmd, String... args) { + public void executeSub(String cmd, + String... args) throws CommandRunException { for (final ICommand command : commands) { if (command.getCommandName().equals(cmd)) { command.execute(args); return; } } - error.execute(cmd); + throw new CommandRunException( + Messages.getString("CommandProvider.unrecognized0", cmd)); //$NON-NLS-1$ } /* (non-Javadoc) diff --git a/gclc/src/main/java/fr/bigeon/gclc/command/ExitCommand.java b/gclc/src/main/java/fr/bigeon/gclc/command/ExitCommand.java new file mode 100644 index 0000000..7a54eb6 --- /dev/null +++ b/gclc/src/main/java/fr/bigeon/gclc/command/ExitCommand.java @@ -0,0 +1,97 @@ +/* + * 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.ExitCommand.java + * Created on: Jun 8, 2016 + */ +package fr.bigeon.gclc.command; + +import java.io.IOException; + +import fr.bigeon.gclc.ConsoleApplication; +import fr.bigeon.gclc.manager.ConsoleManager; +import fr.bigeon.gclc.prompt.CLIPrompterMessages; + +/**

+ * A command to exit a {@link ConsoleApplication}. + * + * @author Emmanuel BIGEON */ +public class ExitCommand implements ICommand { + /** 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 exit command name */ + private final String name; + + /** @param name the name of the command + * @param app the application to exit */ + public ExitCommand(String name, ConsoleApplication app) { + this.name = name; + this.app = app; + } + + /** The actions to take before exiting */ + public void beforeExit() { + // Do nothing by default + } + + @Override + public final void execute(String... args) { + beforeExit(); + app.exit(); + } + + /* (non-Javadoc) + * @see fr.bigeon.gclc.command.ICommand#getCommandName() */ + @Override + public final String getCommandName() { + return name; + } + + @Override + public void help(ConsoleManager manager, + String... args) throws IOException { + manager.println( + CLIPrompterMessages.getString(EXIT_MAN, (Object[]) args)); + } + + @Override + public String tip() { + return CLIPrompterMessages.getString(EXIT); + } +} \ No newline at end of file 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 ccaa55f..6eee60b 100644 --- a/gclc/src/main/java/fr/bigeon/gclc/command/HelpExecutor.java +++ b/gclc/src/main/java/fr/bigeon/gclc/command/HelpExecutor.java @@ -38,6 +38,9 @@ */ package fr.bigeon.gclc.command; +import java.io.IOException; + +import fr.bigeon.gclc.exception.CommandRunException; import fr.bigeon.gclc.manager.ConsoleManager; import fr.bigeon.gclc.prompt.CLIPrompterMessages; @@ -70,8 +73,12 @@ public class HelpExecutor extends Command { /* (non-Javadoc) * @see fr.bigeon.gclc.command.Command#execute(java.lang.String[]) */ @Override - public void execute(String... args) { - cmd.help(consoleManager, args); + public void execute(String... args) throws CommandRunException { + try { + cmd.help(consoleManager, args); + } catch (IOException e) { + throw new CommandRunException("Console manager closed", e); //$NON-NLS-1$ + } } /* (non-Javadoc) diff --git a/gclc/src/main/java/fr/bigeon/gclc/command/ICommand.java b/gclc/src/main/java/fr/bigeon/gclc/command/ICommand.java index 263eb84..c1ac9ae 100644 --- a/gclc/src/main/java/fr/bigeon/gclc/command/ICommand.java +++ b/gclc/src/main/java/fr/bigeon/gclc/command/ICommand.java @@ -38,6 +38,9 @@ */ package fr.bigeon.gclc.command; +import java.io.IOException; + +import fr.bigeon.gclc.exception.CommandRunException; import fr.bigeon.gclc.manager.ConsoleManager; /** The contract of commands @@ -47,8 +50,10 @@ import fr.bigeon.gclc.manager.ConsoleManager; * @author Emmanuel Bigeon */ public interface ICommand { - /** @param args the arguments of the command (some expect an empty array) */ - void execute(String... args); + /** @param args the arguments of the command (some expect an empty array) + * @throws CommandRunException if the execution of the command failed for + * any reason */ + void execute(String... args) throws CommandRunException; /** @return the command's name */ String getCommandName(); @@ -68,8 +73,9 @@ public interface ICommand { * * * @param manager the manager to print the data - * @param args the arguments called with the help */ - void help(ConsoleManager manager, String... args); + * @param args the arguments called with the help + * @throws IOException if the manager was closed */ + void help(ConsoleManager manager, String... args) throws IOException; /** @return a tip on the command */ String tip(); 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 f217aa9..294a419 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,7 @@ * Created on: Sep 6, 2014 */ package fr.bigeon.gclc.command; +import fr.bigeon.gclc.exception.CommandRunException; import fr.bigeon.gclc.exception.InvalidCommandName; /**

@@ -62,8 +63,10 @@ public interface ICommandProvider { * 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); + * @param args the arguments for the command + * @throws CommandRunException if the command failed to run */ + public void executeSub(String command, + String... args) throws CommandRunException; /**

* This method provide the command with the given name found. If no command diff --git a/gclc/src/main/java/fr/bigeon/gclc/command/ParametrizedCommand.java b/gclc/src/main/java/fr/bigeon/gclc/command/ParametrizedCommand.java index 141f7fa..aad57a7 100644 --- a/gclc/src/main/java/fr/bigeon/gclc/command/ParametrizedCommand.java +++ b/gclc/src/main/java/fr/bigeon/gclc/command/ParametrizedCommand.java @@ -38,11 +38,13 @@ */ package fr.bigeon.gclc.command; +import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import fr.bigeon.gclc.exception.CommandRunException; import fr.bigeon.gclc.manager.ConsoleManager; /**

@@ -115,13 +117,12 @@ public abstract class ParametrizedCommand extends Command { * @see fr.bigeon.gclc.command.Command#execute(java.lang.String[]) */ @SuppressWarnings("boxing") @Override - public final void execute(String... args) { + public final void execute(String... args) throws CommandRunException { final 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; + throw new CommandRunException("Unable to read arguments"); //$NON-NLS-1$ } final List toProvide = new ArrayList<>(); for (final String string : params.keySet()) { @@ -133,13 +134,27 @@ public abstract class ParametrizedCommand extends Command { } } // for each needed parameters that is missing, prompt the user. + fillParameters(toProvide, parameters); + doExecute(parameters); + } + + /** @param parameters the parameter list to complete + * @param toProvide the parameters to ask for + * @throws CommandRunException if the manager was closed */ + private void fillParameters(List toProvide, + CommandParameters parameters) throws CommandRunException { for (final String string : toProvide) { - String value = manager.prompt(string); - while (value.isEmpty()) { + String value; + try { value = manager.prompt(string); + while (value.isEmpty()) { + value = manager.prompt(string); + } + } catch (IOException e) { + throw new CommandRunException( + "Interactive command but manager closed...", e); //$NON-NLS-1$ } parameters.set(string, value); } - doExecute(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 3709c5a..dc3e26b 100644 --- a/gclc/src/main/java/fr/bigeon/gclc/command/SubedCommand.java +++ b/gclc/src/main/java/fr/bigeon/gclc/command/SubedCommand.java @@ -36,8 +36,10 @@ * Created on: Sep 6, 2014 */ package fr.bigeon.gclc.command; +import java.io.IOException; import java.util.Arrays; +import fr.bigeon.gclc.exception.CommandRunException; import fr.bigeon.gclc.exception.InvalidCommandName; import fr.bigeon.gclc.manager.ConsoleManager; @@ -61,44 +63,40 @@ public class SubedCommand implements ICommandProvider, ICommand { /** The name of the command */ private final String name; - /** @param name the name of the command - * @param error the error to execute when called with wrong usage */ - public SubedCommand(String name, ICommand error) { + /** @param name the name of the command */ + public SubedCommand(String name) { this.name = name; - provider = new CommandProvider(error); + provider = new CommandProvider(); noArgCommand = null; tip = null; } /** @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(String name, ICommand error, ICommand noArgCommand) { + * provided */ + public SubedCommand(String name, ICommand noArgCommand) { this.name = name; - provider = new CommandProvider(error); + provider = new CommandProvider(); this.noArgCommand = noArgCommand; tip = null; } /** @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(String name, ICommand error, ICommand noArgCommand, + public SubedCommand(String name, ICommand noArgCommand, String tip) { this.name = name; - provider = new CommandProvider(error); + provider = new CommandProvider(); this.noArgCommand = noArgCommand; this.tip = tip; } /** @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(String name, ICommand error, String tip) { + public SubedCommand(String name, String tip) { this.name = name; - provider = new CommandProvider(error); + provider = new CommandProvider(); noArgCommand = null; this.tip = tip; } @@ -111,12 +109,12 @@ public class SubedCommand implements ICommandProvider, ICommand { /* (non-Javadoc) * @see fr.bigeon.acide.Command#execute(java.lang.String[]) */ @Override - public void execute(String... args) { + public void execute(String... args) throws CommandRunException { if (args.length == 0 || args[0].startsWith("-")) { //$NON-NLS-1$ if (noArgCommand != null) { noArgCommand.execute(args); } else { - provider.error.execute(args); + throw new CommandRunException("Unrecognized command"); //$NON-NLS-1$ } } else { executeSub(args[0], Arrays.copyOfRange(args, 1, args.length)); @@ -128,7 +126,8 @@ public class SubedCommand implements ICommandProvider, ICommand { * fr.bigeon.gclc.command.ICommandProvider#executeSub(java.lang.String, * java.lang.String[]) */ @Override - public void executeSub(String command, String... args) { + public void executeSub(String command, + String... args) throws CommandRunException { provider.executeSub(command, args); } @@ -147,28 +146,27 @@ public class SubedCommand implements ICommandProvider, ICommand { /* (non-Javadoc) * @see fr.bigeon.gclc.command.Command#help() */ @Override - public void help(ConsoleManager manager, String... args) { + public void help(ConsoleManager manager, + String... args) throws IOException { if (args.length != 0 && !args[0].startsWith("-")) { //$NON-NLS-1$ // Specific final ICommand 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 && noArgCommand.tip() != null) { - manager.println(TAB + noArgCommand.tip()); - } - for (final ICommand cmd : provider.commands) { - if (cmd.tip() == null) { - manager.println(TAB + cmd.getCommandName()); - } else { - manager.println(TAB + cmd.getCommandName() + ": " + //$NON-NLS-1$ - cmd.tip()); + if (noArgCommand != null && noArgCommand.tip() != null) { + manager.println(TAB + noArgCommand.tip()); + } + for (final ICommand cmd : provider.commands) { + if (cmd.tip() == null) { + manager.println(TAB + cmd.getCommandName()); + } else { + manager.println(TAB + cmd.getCommandName() + ": " + //$NON-NLS-1$ + cmd.tip()); + } } - } } } diff --git a/gclc/src/main/java/fr/bigeon/gclc/command/UnrecognizedCommand.java b/gclc/src/main/java/fr/bigeon/gclc/command/UnrecognizedCommand.java index fc82832..777f7b4 100644 --- a/gclc/src/main/java/fr/bigeon/gclc/command/UnrecognizedCommand.java +++ b/gclc/src/main/java/fr/bigeon/gclc/command/UnrecognizedCommand.java @@ -38,6 +38,9 @@ */ package fr.bigeon.gclc.command; +import java.io.IOException; + +import fr.bigeon.gclc.exception.CommandRunException; import fr.bigeon.gclc.manager.ConsoleManager; import fr.bigeon.gclc.prompt.CLIPrompterMessages; @@ -62,12 +65,17 @@ public final class UnrecognizedCommand implements ICommand { } @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)); + public void execute(String... args) throws CommandRunException { + + try { + if (args.length > 0) { + manager.println(CLIPrompterMessages.getString(UNRECOGNIZED_CMD, + (Object[]) args)); + } else { + manager.println(CLIPrompterMessages.getString(EXPECTED_CMD)); + } + } catch (IOException e) { + throw new CommandRunException("Manager closed", e); //$NON-NLS-1$ } } diff --git a/gclc/src/main/java/fr/bigeon/gclc/exception/CommandRunException.java b/gclc/src/main/java/fr/bigeon/gclc/exception/CommandRunException.java index 35a14a5..d4713ee 100644 --- a/gclc/src/main/java/fr/bigeon/gclc/exception/CommandRunException.java +++ b/gclc/src/main/java/fr/bigeon/gclc/exception/CommandRunException.java @@ -42,7 +42,7 @@ package fr.bigeon.gclc.exception; * An exception thrown when a command failed to run correctly. * * @author Emmanuel BIGEON */ -public class CommandRunException extends RuntimeException { +public class CommandRunException extends Exception { /** * diff --git a/gclc/src/main/java/fr/bigeon/gclc/manager/ConsoleManager.java b/gclc/src/main/java/fr/bigeon/gclc/manager/ConsoleManager.java index 37cc72e..b3d9c14 100644 --- a/gclc/src/main/java/fr/bigeon/gclc/manager/ConsoleManager.java +++ b/gclc/src/main/java/fr/bigeon/gclc/manager/ConsoleManager.java @@ -52,21 +52,32 @@ public interface ConsoleManager { /** @return the prompt prefix */ String getPrompt(); - /** @param text the message to print (without line break at the end). */ - void print(String text); + /** @param text the message to print (without line break at the end). + * @throws IOException if the manager is closed or could not read the + * prompt */ + void print(String text) throws IOException; - /** Prints an end of line */ - void println(); + /** Prints an end of line + * + * @throws IOException if the manager is closed or could not read the + * prompt */ + void println() throws IOException; - /** @param message the message to print */ - void println(String message); + /** @param message the message to print + * @throws IOException if the manager is closed or could not read the + * prompt */ + void println(String message) throws IOException; - /** @return the user inputed string */ - String prompt(); + /** @return the user inputed string + * @throws IOException if the manager is closed or could not read the + * prompt */ + String prompt() throws IOException; /** @param message the message to prompt the user - * @return the user inputed string */ - String prompt(String message); + * @return the user inputed string + * @throws IOException if the manager is closed or could not read the + * prompt */ + String prompt(String message) throws IOException; /**

* Set a prompting prefix. @@ -74,6 +85,11 @@ public interface ConsoleManager { * @param prompt the prompt */ void setPrompt(String prompt); - /** @throws IOException if the close raised an exception */ + /** Closes the manager. + * + * @throws IOException if the close raised an exception */ void close() throws IOException; + + /** @return if the manager is closed. */ + boolean isClosed(); } diff --git a/gclc/src/main/java/fr/bigeon/gclc/manager/SystemConsoleManager.java b/gclc/src/main/java/fr/bigeon/gclc/manager/SystemConsoleManager.java index 286edb6..e985b75 100644 --- a/gclc/src/main/java/fr/bigeon/gclc/manager/SystemConsoleManager.java +++ b/gclc/src/main/java/fr/bigeon/gclc/manager/SystemConsoleManager.java @@ -38,8 +38,10 @@ */ package fr.bigeon.gclc.manager; +import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.io.PrintStream; import java.util.logging.Level; import java.util.logging.Logger; @@ -64,13 +66,15 @@ public class SystemConsoleManager implements ConsoleManager { // NOSONAR /** The print stream */ private final PrintStream out; /** The input stream */ - private final InputStream in; + private final BufferedReader in; + + /** If the manager is closed */ + private boolean closed = false; /** This default constructor relies on the system defined standart output * and input stream. */ public SystemConsoleManager() { - out = System.out; // NOSONAR - in = System.in; + this(System.out, System.in); } /** @param out the output stream @@ -78,7 +82,7 @@ public class SystemConsoleManager implements ConsoleManager { // NOSONAR public SystemConsoleManager(PrintStream out, InputStream in) { super(); this.out = out; - this.in = in; + this.in = new BufferedReader(new InputStreamReader(in)); } /** @return the prompt */ @@ -90,47 +94,60 @@ public class SystemConsoleManager implements ConsoleManager { // NOSONAR /* (non-Javadoc) * @see fr.bigeon.gclc.ConsoleManager#print(java.lang.Object) */ @Override - public void print(String object) { + public void print(String object) throws IOException { + if (closed) { + throw new IOException(); + } out.print(object); } /* (non-Javadoc) * @see fr.bigeon.gclc.ConsoleManager#println() */ @Override - public void println() { + public void println() throws IOException { + if (closed) { + throw new IOException(); + } out.println(); } /* (non-Javadoc) * @see fr.bigeon.gclc.ConsoleManager#println(java.lang.Object) */ @Override - public void println(String object) { + public void println(String object) throws IOException { + if (closed) { + throw new IOException(); + } out.println(object); } /* (non-Javadoc) * @see fr.bigeon.gclc.ConsoleManager#prompt() */ @Override - public String prompt() { + public String prompt() throws IOException { return prompt(new String() + prompt); } /* (non-Javadoc) * @see fr.bigeon.gclc.ConsoleManager#prompt(java.lang.String) */ @Override - public String prompt(String message) { + public String prompt(String message) throws IOException { + if (closed) { + throw new IOException(); + } String result = new String(); out.print(message + ' '); char c; try { - c = (char) in.read(); - while (c != System.lineSeparator().charAt(0)) { - result += c; - c = (char) in.read(); - } - while (in.available() != 0) { - in.read(); - } + result = in.readLine(); +// c = (char) in.read(); +// while (c != System.lineSeparator().charAt(0)) { +// result += c; +// c = (char) in.read(); +// } +// while (in.available() != 0) { +// in.read(); +// } } catch (final IOException e) { LOGGER.log(Level.SEVERE, "Unable to read prompt", e); //$NON-NLS-1$ } @@ -147,7 +164,14 @@ public class SystemConsoleManager implements ConsoleManager { // NOSONAR * @see fr.bigeon.gclc.manager.ConsoleManager#close() */ @Override public void close() throws IOException { - in.close(); + closed = true; + } + + /* (non-Javadoc) + * @see fr.bigeon.gclc.manager.ConsoleManager#isClosed() */ + @Override + public boolean isClosed() { + return closed; } } 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 adf5d43..efd9c39 100644 --- a/gclc/src/main/java/fr/bigeon/gclc/prompt/CLIPrompter.java +++ b/gclc/src/main/java/fr/bigeon/gclc/prompt/CLIPrompter.java @@ -36,6 +36,7 @@ * 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; @@ -80,9 +81,10 @@ public class CLIPrompter { * @param choices the choices * @param cancel the cancel option if it exists * @return the number of choices plus one (or the number of choices if there - * is a cancel) */ + * is a cancel) + * @throws IOException if the manager was closed */ private static int listChoices(ConsoleManager manager, List choices, - String cancel) { + String cancel) throws IOException { int index = 0; for (final U u : choices) { manager.println(index++ + ") " + u); //$NON-NLS-1$ @@ -95,9 +97,10 @@ public class CLIPrompter { /** @param manager the manager * @param message the prompting message - * @return the choice */ + * @return the choice + * @throws IOException if the manager was closed */ public static boolean promptBoolean(ConsoleManager manager, - String message) { + String message) throws IOException { String result = manager .prompt(message + CLIPrompterMessages.getString(BOOL_CHOICES)); boolean first = true; @@ -134,11 +137,12 @@ public class CLIPrompter { * @param message the message * @param cancel the cancel option, or null * @param the type of elements - * @return the choice */ + * @return the choice + * @throws IOException if the manager was closed */ @SuppressWarnings("boxing") public static U promptChoice(ConsoleManager manager, List keys, List choices, String message, - String cancel) { + String cancel) throws IOException { final Integer index = promptChoice(manager, keys, message, cancel); if (index == null) { return null; @@ -153,10 +157,11 @@ public class CLIPrompter { * @param choicesMap the map of label to actual objects * @param message the prompting message * @param cancel the cancel option if it exists (null otherwise) - * @return the chosen object */ + * @return the chosen object + * @throws IOException if the manager was closed */ public static T promptChoice(ConsoleManager manager, List choices, Map choicesMap, String message, - String cancel) { + String cancel) throws IOException { return choicesMap.get(choices.get( promptChoice(manager, choices, message, cancel).intValue())); } @@ -166,11 +171,12 @@ public class CLIPrompter { * @param choices the list of choices * @param message the prompting message * @param cancel the cancel option, or null - * @return the index of the choice */ + * @return the index of the choice + * @throws IOException if the manager was closed */ @SuppressWarnings("boxing") public static Integer promptChoice(ConsoleManager manager, List choices, String message, - String cancel) { + String cancel) throws IOException { manager.println(message); final int index = listChoices(manager, choices, cancel); String result = ""; //$NON-NLS-1$ @@ -207,18 +213,21 @@ public class CLIPrompter { * @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 */ + * @return the chosen object + * @throws IOException if the manager was closed */ public static T promptChoice(ConsoleManager manager, Map choicesMap, String message, - String cancel) { + String cancel) throws IOException { return promptChoice(manager, new ArrayList<>(choicesMap.keySet()), choicesMap, message, cancel); } /** @param manager the manager * @param message the prompt message - * @return the integer */ - public static int promptInteger(ConsoleManager manager, String message) { + * @return the integer + * @throws IOException if the manager was closed */ + public static int promptInteger(ConsoleManager manager, + String message) throws IOException { boolean still = true; int r = 0; while (still) { @@ -243,9 +252,10 @@ public class CLIPrompter { * * @param manager the manager * @param message the message - * @return the list of user inputs */ + * @return the list of user inputs + * @throws IOException if the manager was closed */ public static List promptList(ConsoleManager manager, - String message) { + String message) throws IOException { return promptList(manager, message, CLIPrompterMessages.getString("promptlist.exit.defaultkey")); //$NON-NLS-1$ } @@ -255,9 +265,11 @@ public class CLIPrompter { * @param manager the manager * @param message the message * @param ender the ending sequence for the list - * @return the list of user inputs */ + * @return the list of user inputs + * @throws IOException if the manager was closed */ public static List promptList(ConsoleManager manager, - String message, String ender) { + String message, + String ender) throws IOException { final List strings = new ArrayList<>(); manager.println( message + CLIPrompterMessages.getString(LIST_DISP_KEY, ender)); @@ -275,9 +287,10 @@ public class CLIPrompter { * * @param manager the manager * @param message the prompting message - * @return the text */ + * @return the text + * @throws IOException if the manager was closed */ public static String promptLongText(ConsoleManager manager, - String message) { + String message) throws IOException { return promptLongText(manager, message, CLIPrompterMessages .getString("promptlongtext.exit.defaultkey")); //$NON-NLS-1$ } @@ -287,9 +300,10 @@ public class CLIPrompter { * @param manager the manager * @param message the prompting message * @param ender the ender character - * @return the text */ + * @return the text + * @throws IOException if the manager was closed */ public static String promptLongText(ConsoleManager manager, String message, - String ender) { + String ender) throws IOException { manager.println(message + CLIPrompterMessages .getString("promptlongtext.exit.dispkey", ender)); //$NON-NLS-1$ String res = manager.prompt(PROMPT); @@ -308,11 +322,12 @@ public class CLIPrompter { * @param choices the real choices * @param message the message * @param the type of elements - * @return the choice */ + * @return the choice + * @throws IOException if the manager was closed */ public static List promptMultiChoice(ConsoleManager manager, List keys, List choices, - String message) { + String message) throws IOException { final List indices = promptMultiChoice(manager, keys, message); final List userChoices = new ArrayList<>(); for (final Integer integer : indices) { @@ -327,11 +342,12 @@ public class CLIPrompter { * @param choices the list of labels (in order to be displayed) * @param choicesMap the map of label to actual objects * @param message the prompting message - * @return the chosen objects (or an empty list) */ + * @return the chosen objects (or an empty list) + * @throws IOException if the manager was closed */ public static List promptMultiChoice(ConsoleManager manager, List choices, Map choicesMap, - String message) { + String message) throws IOException { final List chs = promptMultiChoice(manager, choices, message); final List userChoices = new ArrayList<>(); for (final Integer integer : chs) { @@ -344,11 +360,12 @@ public class CLIPrompter { * @param the type of choices * @param choices the list of choices * @param message the prompting message - * @return the indices of the choices */ + * @return the indices of the choices + * @throws IOException if the manager was closed */ @SuppressWarnings("boxing") public static List promptMultiChoice(ConsoleManager manager, List choices, - String message) { + String message) throws IOException { manager.println(message); final int index = listChoices(manager, choices, null); String result = ""; //$NON-NLS-1$ @@ -404,10 +421,11 @@ public class CLIPrompter { * @param The real choices objects * @param choicesMap the map of label to actual objects * @param message the prompting message - * @return the chosen objects */ + * @return the chosen objects + * @throws IOException if the manager was closed */ public static List promptMultiChoice(ConsoleManager manager, Map choicesMap, - String message) { + String message) throws IOException { return promptMultiChoice(manager, new ArrayList<>(choicesMap.keySet()), choicesMap, message); } @@ -415,9 +433,10 @@ public class CLIPrompter { /** @param manager the manager * @param prompt the prompting message * @param reprompt the prompting message after empty input - * @return the non empty input */ + * @return the non empty input + * @throws IOException if the manager was closed */ public static String promptNonEmpty(ConsoleManager manager, String prompt, - String reprompt) { + String reprompt) throws IOException { String res = manager.prompt(prompt); while (res.isEmpty()) { res = manager.prompt(reprompt); diff --git a/gclc/src/main/resources/fr/bigeon/gclc/l10n/messages.properties b/gclc/src/main/resources/fr/bigeon/gclc/l10n/messages.properties index 4b2f309..380b34f 100644 --- a/gclc/src/main/resources/fr/bigeon/gclc/l10n/messages.properties +++ b/gclc/src/main/resources/fr/bigeon/gclc/l10n/messages.properties @@ -1 +1,2 @@ +CommandProvider.unrecognized=Unrecognized command '{0}' ConsoleApplication.cmd.failed=The command '{0}' failed due to : diff --git a/gclc/src/test/java/fr/bigeon/gclc/ConsoleApplicationTest.java b/gclc/src/test/java/fr/bigeon/gclc/ConsoleApplicationTest.java new file mode 100644 index 0000000..758cbdb --- /dev/null +++ b/gclc/src/test/java/fr/bigeon/gclc/ConsoleApplicationTest.java @@ -0,0 +1,118 @@ +/* + * 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.ConsoleApplicationTest.java + * Created on: Jun 9, 2016 + */ +package fr.bigeon.gclc; + +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.io.InputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import java.io.PrintStream; +import java.io.PrintWriter; + +import org.junit.Test; + +import fr.bigeon.gclc.manager.SystemConsoleManager; + +/** Test class for ConsoleApplication + * + * @author Emmanuel Bigeon */ +@SuppressWarnings("static-method") +public class ConsoleApplicationTest { + + protected static final long THREE_SECONDS = 3000; + + /** Test the base of a console application */ + @Test + public void test() { + ConsoleTestApplication app = new ConsoleTestApplication( + new SystemConsoleManager()); + app.exit(); + } + + @Test + public void executionTest() { + try { + final PipedOutputStream src = new PipedOutputStream(); + InputStream in = new PipedInputStream(src); + + final PipedInputStream snk = new PipedInputStream(); + PrintStream out = new PrintStream(new PipedOutputStream(snk)); + final ConsoleTestApplication app = new ConsoleTestApplication( + new SystemConsoleManager(out, in)); + Thread th = new Thread(new Runnable() { + + @Override + public void run() { + app.start(); + } + }); + + th.start(); + + Thread test = new Thread(new Runnable() { + + @Override + public void run() { + try (PrintWriter writer = new PrintWriter(src, true)) { + writer.println("test"); + writer.println("long"); + writer.println("exit"); + } + } + }); + test.start(); + try { + th.join(); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + try { + test.join(); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } catch (IOException e) { + fail("pipe creation"); //$NON-NLS-1$ + } + } +} diff --git a/gclc/src/test/java/fr/bigeon/gclc/ConsoleTestApplication.java b/gclc/src/test/java/fr/bigeon/gclc/ConsoleTestApplication.java new file mode 100644 index 0000000..babe0ef --- /dev/null +++ b/gclc/src/test/java/fr/bigeon/gclc/ConsoleTestApplication.java @@ -0,0 +1,105 @@ +/* + * 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. + */ +package fr.bigeon.gclc; + +import java.io.IOException; + +import fr.bigeon.gclc.ConsoleApplication; +import fr.bigeon.gclc.command.Command; +import fr.bigeon.gclc.command.ExitCommand; +import fr.bigeon.gclc.command.HelpExecutor; +import fr.bigeon.gclc.exception.CommandRunException; +import fr.bigeon.gclc.exception.InvalidCommandName; +import fr.bigeon.gclc.manager.ConsoleManager; + +/** A test-purpose application + * + * @author Emmanuel Bigeon */ +public class ConsoleTestApplication extends ConsoleApplication { + + /** Exit command */ + public static final String EXIT = "exit"; //$NON-NLS-1$ + /** Two seconds in milliseconds */ + protected static final long TWO_SECONDS = 2000; + + /** @param manager the manager */ + @SuppressWarnings("nls") + public ConsoleTestApplication(final ConsoleManager manager) { + super(manager, "Welcome to the test application. Type help or test.", + "See you"); + try { + add(new ExitCommand(EXIT, this)); + add(new HelpExecutor("help", manager, this.getRoot())); + add(new Command("test") { + + @Override + public String tip() { + return "A test command"; + } + + @Override + public void execute(String... args) throws CommandRunException { + try { + manager.println("Test command ran fine"); + } catch (IOException e) { + throw new CommandRunException("manager closed", e); + } + } + }); + add(new Command("long") { + + @Override + public String tip() { + return "A long execution command"; + } + + @Override + public void execute(String... args) throws CommandRunException { + try { + manager.println("Waita minute"); + Thread.sleep(TWO_SECONDS); + manager.println("done!"); + } catch (IOException e) { + throw new CommandRunException("manager closed", e); + } catch (InterruptedException e) { + throw new CommandRunException("wait interrupted", e); + } + } + }); + } catch (final InvalidCommandName e) { + e.printStackTrace(); + } + } +} diff --git a/gclc/src/test/java/fr/bigeon/gclc/GCLCConstantsTest.java b/gclc/src/test/java/fr/bigeon/gclc/GCLCConstantsTest.java new file mode 100644 index 0000000..9641011 --- /dev/null +++ b/gclc/src/test/java/fr/bigeon/gclc/GCLCConstantsTest.java @@ -0,0 +1,137 @@ +/* + * 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.GCLCConstantsTest.java + * Created on: Jun 8, 2016 + */ +package fr.bigeon.gclc; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.List; + +import org.junit.Test; + +import fr.bigeon.gclc.exception.CommandParsingException; + +/** Test class for {@link GCLCConstants} + * + * @author Emmanuel Bigeon */ +@SuppressWarnings({"nls", "static-method"}) +public class GCLCConstantsTest { + + /** + * Test method for {@link fr.bigeon.gclc.GCLCConstants#splitCommand(java.lang.String)}. + */ + @Test + public void testSplitCommand() { + List res; + try { + res = GCLCConstants.splitCommand("aCommand"); + } catch (CommandParsingException e) { + fail("Unable to parse simple command"); //$NON-NLS-1$ + return; + } + assertTrue(res.size() == 1); + assertTrue(res.get(0).equals("aCommand")); + + try { + res = GCLCConstants.splitCommand("aCommand with some arguments"); + } catch (CommandParsingException e) { + fail("Unable to parse command with arguments"); //$NON-NLS-1$ + return; + } + assertTrue(res.size() == 4); + assertTrue(res.get(0).equals("aCommand")); + assertTrue(res.get(1).equals("with")); + assertTrue(res.get(2).equals("some")); + assertTrue(res.get(3).equals("arguments")); + try { + res = GCLCConstants.splitCommand("aCommand with some arguments"); + } catch (CommandParsingException e) { + fail("Unable to parse command with arguments and double whitspaces"); //$NON-NLS-1$ + return; + } + assertTrue(res.size() == 4); + assertTrue(res.get(0).equals("aCommand")); + assertTrue(res.get(1).equals("with")); + assertTrue(res.get(2).equals("some")); + assertTrue(res.get(3).equals("arguments")); + try { + res = GCLCConstants + .splitCommand("aCommand \"with some\" arguments"); + } catch (CommandParsingException e) { + fail("Unable to parse command with string argument"); //$NON-NLS-1$ + return; + } + assertTrue(res.size() == 3); + assertTrue(res.get(0).equals("aCommand")); + assertTrue(res.get(1).equals("with some")); + assertTrue(res.get(2).equals("arguments")); + try { + res = GCLCConstants.splitCommand("aCommand with\\ some arguments"); + } catch (CommandParsingException e) { + fail("Unable to parse command with arguments with escaped whitspaces"); //$NON-NLS-1$ + return; + } + assertTrue(res.size() == 3); + assertTrue(res.get(0).equals("aCommand")); + assertTrue(res.get(1).equals("with some")); + assertTrue(res.get(2).equals("arguments")); + try { + res = GCLCConstants + .splitCommand("aCommand wi\\\"th some arguments"); + } catch (CommandParsingException e) { + fail("Unable to parse command with string argument"); //$NON-NLS-1$ + return; + } + assertTrue(res.size() == 4); + assertTrue(res.get(0).equals("aCommand")); + assertTrue(res.get(1).equals("wi\"th")); + assertTrue(res.get(2).equals("some")); + assertTrue(res.get(3).equals("arguments")); + + // Wrong lines? + try { + res = GCLCConstants + .splitCommand("aCommand with \"some ar\"guments"); + fail("Parsing argument with string cut"); + } catch (CommandParsingException e) { + // OK + } + } + +}